1 /*---------------------------------------------------------------------------*
2 Project: Horizon
3 File: dbg_ExceptionScreen.cpp
4
5 Copyright (C)2009-2012 Nintendo Co., Ltd. All rights reserved.
6
7 These coded instructions, statements, and computer programs contain
8 proprietary information of Nintendo of America Inc. and/or Nintendo
9 Company Ltd., and are protected by Federal copyright law. They may
10 not be disclosed to third parties or copied or duplicated in any form,
11 in whole or in part, without the prior written consent of Nintendo.
12
13 $Rev: 49370 $
14 *---------------------------------------------------------------------------*/
15
16 #include <nn/nstd.h>
17 #include <nn/os.h>
18 #include <nn/fs.h>
19 #include <nn/hid.h>
20 #include <nn/gx.h>
21 #include <nn/ro.h>
22 #include <nn/dbg.h>
23 #include <cwchar>
24 #include <cstring>
25
26 namespace nn { namespace dbg { namespace CTR {
27
28 ExceptionScreen* ExceptionScreen::s_ExceptionScreen = NULL;
29
30 namespace
31 {
IsCodeAddress(uptr addr)32 bool IsCodeAddress(uptr addr)
33 {
34 const uptr begin = os::GetCodeRegionAddress();
35 const uptr end = begin + os::GetCodeRegionSize();
36 const bool inStatic = ( (begin <= addr) && (addr < end) );
37
38 if( inStatic )
39 {
40 return true;
41 }
42
43 if( nn::ro::detail::IsCodeAddress != NULL )
44 {
45 return nn::ro::detail::IsCodeAddress(addr);
46 }
47 else
48 {
49 return false;
50 }
51 }
52
FindThreadStackBottomByStackAddr(uptr addr)53 uptr FindThreadStackBottomByStackAddr(uptr addr)
54 {
55 if( os::ManagedThread::IsEnabled() )
56 {
57 // Use information from ManagedThread
58 os::ManagedThread* pFound = os::ManagedThread::FindByStackAddress(addr);
59 if( pFound != NULL )
60 {
61 return pFound->GetStackBottom();
62 }
63 }
64
65 // If not ManagedThread, then as is appropriate
66 const uptr bottom = addr + 16 * 1024;
67
68 // Save the main thread
69 if( (addr < NN_OS_ADDR_STACK_END)
70 && (NN_OS_ADDR_STACK_END < bottom) )
71 {
72 return NN_OS_ADDR_STACK_END;
73 }
74
75 return bottom;
76 }
77
EscapeForPath(wchar_t * pOut,const wchar_t * pIn)78 void EscapeForPath(wchar_t* pOut, const wchar_t* pIn)
79 {
80 int i;
81 for ( i = 0; i < sizeof(pIn) / sizeof(wchar_t) - 1; ++ i )
82 {
83 if ( pIn[ i ] == L'/' || pIn[ i ] == L' ' || pIn[ i ] == L':' )
84 {
85 pOut[ i ] = L'_';
86 }
87 else if ( pIn[ i ] == L'\0' )
88 {
89 break;
90 }
91 else
92 {
93 pOut[ i ] = pIn[ i ];
94 }
95 }
96
97 pOut[ i ] = L'\0';
98 }
99
IsArmBranchOperation(bit32 op)100 bool IsArmBranchOperation(bit32 op)
101 {
102 return
103 ( ((op & 0x0f000000) == 0x0b000000) // BL command
104 || ((op & 0xfe000000) == 0xfa000000) // BLX(1) command
105 || ((op & 0x0ffffff0) == 0x012fff30) ); // BLX(2) command
106 }
107
IsArmBranchOperationAddress(uptr opAddr)108 bool IsArmBranchOperationAddress(uptr opAddr)
109 {
110 if( IsCodeAddress(opAddr) )
111 {
112 // Check whether branch command
113 bit32 op = *reinterpret_cast<bit32*>(opAddr);
114 if( IsArmBranchOperation(op) )
115 {
116 return true;
117 }
118 }
119
120 return false;
121 }
122
IsThumbBranchOperationAddress(uptr opAddr)123 bool IsThumbBranchOperationAddress(uptr opAddr)
124 {
125 if( IsCodeAddress(opAddr) )
126 {
127 // Check whether branch command
128 bit16 op = *reinterpret_cast<bit16*>(opAddr);
129 if ((op & 0xe800) == 0xe800) // Second half of BL and BLX(1) commands
130 {
131 uptr opAddr2 = opAddr - 2;
132
133 if( IsCodeAddress(opAddr2) )
134 {
135 bit16 op2 = *reinterpret_cast<bit16*>(opAddr2);
136 if ((op2 & 0xf800) == 0xf000) // First half of BL and BLX(1) commands
137 {
138 return true;
139 }
140 }
141 }
142 else if ((op & 0xff87) == 0x4780) // BLX(2) command
143 {
144 return true;
145 }
146 }
147
148 return false;
149 }
150
IsSavedLrValue(bit32 data)151 bool IsSavedLrValue(bit32 data)
152 {
153 // Judges numerical values that may be program addresses.
154 if ((data & 0x3) == 0)
155 {
156 // ARM code when LR is a multiple of 4
157 // Gets the previous command, and checks whether the address is contained in the program region
158 bit32 opAddr = data - 4;
159 if( IsArmBranchOperationAddress(opAddr) )
160 {
161 return true;
162 }
163 }
164 else if ((data & 0x1) == 0x1)
165 {
166 // Thumb code when LR is odd
167 // Gets the previous command, and checks whether the address is contained in the program region
168 bit32 opAddr = (data & ~1) - 2;
169 if( IsThumbBranchOperationAddress(opAddr) )
170 {
171 return true;
172 }
173 }
174
175 return false;
176 }
177
178 void WriteToSd(const char* msg, int length, const wchar_t* pBuildInfo, const wchar_t* pSuffix) NN_IS_UNUSED_VAR;
WriteToSd(const char * msg,int length,const wchar_t * pBuildInfo,const wchar_t * pSuffix)179 void WriteToSd(const char* msg, int length, const wchar_t* pBuildInfo, const wchar_t* pSuffix)
180 {
181 wchar_t buildInfo[ExceptionScreen::MAX_BUILD_INFO_CHARS];
182
183 EscapeForPath(buildInfo, pBuildInfo);
184
185 wchar_t filepath[ 64 ];
186 nn::nstd::TSNPrintf( filepath , 63 ,
187 L"sdmc:/exception_%ls_%ls.log" ,
188 buildInfo , pSuffix );
189 filepath[ 63 ] = L'\0';
190
191 nn::fs::FileOutputStream fs;
192
193 nn::fs::MountSdmc();
194 nn::Result result = fs.TryInitialize( filepath , true );
195 if ( result.IsSuccess() )
196 {
197 fs.Write( msg , length, true );
198 fs.Finalize();
199 }
200 nn::fs::Unmount( "sdmc:" );
201 }
202
PutHaltMessage(nn::dbg::DirectPrint * pdp,const nn::math::VEC2 & pos,const char * msg)203 bool PutHaltMessage(nn::dbg::DirectPrint* pdp, const nn::math::VEC2& pos, const char* msg )
204 {
205 pdp->PutString( pos, msg );
206 if ( pdp->GetLastCursorPos().y < 240 )
207 {
208 return true;
209 }
210 else
211 {
212 return false;
213 }
214 }
215
ClearMessage(nn::dbg::DirectPrint * pdp,const nn::math::VEC2 & pos,const char * msg)216 void ClearMessage(nn::dbg::DirectPrint* pdp, const nn::math::VEC2& pos, const char* msg )
217 {
218 nn::util::Color8 tmp = pdp->GetCharColor();
219 pdp->SetCharColor( pdp->GetBgColor() );
220 PutHaltMessage( pdp, pos, msg );
221 pdp->SetCharColor( tmp );
222 }
223
ShowWithAutoScroll(nn::dbg::DirectPrint * pdp,const char * msg)224 void ShowWithAutoScroll(nn::dbg::DirectPrint* pdp, const char* msg)
225 {
226 nn::math::VEC2 pos( 0 , 0 );
227
228 while( 1 )
229 {
230 bool ret = PutHaltMessage( pdp, pos, msg );
231
232 // Flush
233 pdp->Flush();
234
235 // Wait.
236 nn::os::Tick t = nn::os::Tick::GetSystemCurrent();
237 while( t.ToTimeSpan().GetMilliSeconds() <
238 nn::os::Tick::GetSystemCurrent().ToTimeSpan().GetMilliSeconds() - 400 ) {}
239
240 // Erases image in previous frame.
241 ClearMessage( pdp, pos, msg );
242
243 // Coordinate update.
244 if ( !ret )
245 {
246 pos.y -= DirectPrint::FONT_HEIGHT;
247 }
248 else
249 {
250 pos.y = 0;
251 break;
252 }
253 }
254 }
255
ShowWithManualScroll(nn::dbg::DirectPrint * pdp,const char * msg)256 void ShowWithManualScroll(nn::dbg::DirectPrint* pdp, const char* msg)
257 {
258 nn::hid::Initialize();
259 nn::hid::PadReader pad;
260 nn::hid::PadStatus status;
261
262 nn::math::VEC2 pos( 0 , 0 );
263
264 bool update = true;
265 bool inDrawBox = false;
266
267 for(;;)
268 {
269 if ( update )
270 {
271 inDrawBox = PutHaltMessage( pdp, pos, msg );
272 // Flush
273 pdp->Flush();
274 }
275
276 update = false;
277
278 // Wait.
279 nn::os::Tick t = nn::os::Tick::GetSystemCurrent();
280 while( t.ToTimeSpan().GetMilliSeconds() <
281 nn::os::Tick::GetSystemCurrent().ToTimeSpan().GetMilliSeconds() - 16 ){}
282
283 // Moves position depending on input.
284 if ( pad.ReadLatest( &status ) )
285 {
286 if ( status.trigger & nn::hid::BUTTON_DOWN )
287 {
288 // Moves down.
289 if ( !inDrawBox )
290 {
291 // Erases image in previous frame.
292 ClearMessage( pdp, pos, msg );
293 // Coordinate movement.
294 pos.y -= DirectPrint::FONT_HEIGHT;
295 update = true;
296 }
297 }
298 else if ( status.trigger & nn::hid::BUTTON_UP )
299 {
300 // Moves up.
301 if ( pos.y <= -DirectPrint::FONT_HEIGHT )
302 {
303 // Erases image in previous frame.
304 ClearMessage( pdp, pos, msg );
305 // Coordinate movement.
306 pos.y += DirectPrint::FONT_HEIGHT;
307 update = true;
308 }
309 }
310 else if ( status.trigger & nn::hid::BUTTON_START )
311 {
312 break;
313 }
314 }
315 }
316 }
317
IsDeviceMemoryAddress(uptr addr)318 bool IsDeviceMemoryAddress(uptr addr)
319 {
320 size_t size = os::GetDeviceMemorySize();
321 uptr begin = os::GetDeviceMemoryAddress();
322 uptr end = begin + size;
323
324 return (begin <= addr) && (addr < end);
325 }
326
StealDisplaybuffer(nn::dbg::DirectPrint * pdp)327 void StealDisplaybuffer(nn::dbg::DirectPrint* pdp)
328 {
329 GLint format;
330 GLint width;
331 GLint height;
332 GLint addr;
333
334 nngxActiveDisplay( NN_GX_DISPLAY1 );
335 nngxGetDisplaybufferParameteri(NN_GX_DISPLAYBUFFER_ADDRESS, &addr );
336 nngxGetDisplaybufferParameteri(NN_GX_DISPLAYBUFFER_FORMAT, &format);
337 nngxGetDisplaybufferParameteri(NN_GX_DISPLAYBUFFER_WIDTH, &width);
338 nngxGetDisplaybufferParameteri(NN_GX_DISPLAYBUFFER_HEIGHT, &height);
339
340 if( IsDeviceMemoryAddress(addr) )
341 {
342 pdp->ChangeDisplaybuffer( reinterpret_cast< void* > (addr) ,
343 format,
344 nn::math::VEC2( height , width ) );
345 nngxSwapBuffers( NN_GX_DISPLAY1 );
346 }
347 else
348 {
349 NN_TLOG_("DirectPrint: Current displaybuffer is not on DeviceMemory. So can't use it for the DirectPrint.\n");
350 }
351 }
352
353 void WaitKeyCombination() NN_IS_UNUSED_FUNC;
WaitKeyCombination()354 void WaitKeyCombination()
355 {
356 const int numKeyPatterns = 4;
357 const bit32 keyPattern[ numKeyPatterns ] =
358 {
359 nn::hid::CTR::BUTTON_A ,
360 nn::hid::CTR::BUTTON_B ,
361 nn::hid::CTR::BUTTON_X ,
362 nn::hid::CTR::BUTTON_Y ,
363 };
364
365 int index = 0;
366 nn::hid::PadReader pad;
367 nn::hid::PadStatus status;
368
369 for(;;)
370 {
371 if ( pad.ReadLatest( &status ) && status.trigger != 0 )
372 {
373 if ( status.trigger & keyPattern[ index ] )
374 {
375 ++ index;
376 }
377 else
378 {
379 index = 0;
380 }
381 if ( index == numKeyPatterns )
382 {
383 break;
384 }
385 }
386
387 nn::os::Tick t = nn::os::Tick::GetSystemCurrent();
388 while ( t.ToTimeSpan().GetMilliSeconds() <
389 nn::os::Tick::GetSystemCurrent().ToTimeSpan().GetMilliSeconds() - 16 ) {}
390 }
391 }
392 }
393
394 //------------------------------------------------------------------------------
395
Create(nn::fnd::IAllocator * allocator,bit32 option)396 void ExceptionScreen::Create( nn::fnd::IAllocator* allocator, bit32 option )
397 {
398 NN_POINTER_TASSERT_(allocator);
399
400 s_ExceptionScreen = new (allocator->Allocate(sizeof(*s_ExceptionScreen), 8)) ExceptionScreen(option);
401 s_ExceptionScreen->RegisterHandler();
402 }
403 //------------------------------------------------------------------------------
404
ExceptionScreen(bit32 option)405 ExceptionScreen::ExceptionScreen(bit32 option)
406 : m_Option(option)
407 {
408 std::memset( m_BuildInfo, 0, sizeof( m_BuildInfo ) );
409 std::memset( m_Suffix, 0, sizeof( m_Suffix ) );
410 m_DirectPrint.SetFillSpace(false);
411 }
412
413 //------------------------------------------------------------------------------
414
~ExceptionScreen()415 ExceptionScreen::~ExceptionScreen()
416 {
417 }
418
419 //------------------------------------------------------------------------------
420
RegisterHandler()421 void ExceptionScreen::RegisterHandler()
422 {
423 nn::os::ARM::SetUserExceptionHandler(
424 ExceptionHandler,
425 m_Stack.GetStackBottom() );
426 }
427
428 //------------------------------------------------------------------------------
429
PutBackTraceString(char * pOut,size_t size,uptr stackPtr,bit32 lr)430 s32 ExceptionScreen::PutBackTraceString(char* pOut, size_t size, uptr stackPtr, bit32 lr)
431 {
432 NN_POINTER_TASSERT_(pOut);
433
434 int pos = 0;
435 const bit32* stack = reinterpret_cast< const bit32* > ( stackPtr );
436 uptr stackBottom = FindThreadStackBottomByStackAddr(stackPtr);
437
438 pos += nn::nstd::TSPrintf( pOut + pos , " back trace:\n" );
439 pos += nn::nstd::TSPrintf( pOut + pos , " lr 0x%08x\n", lr & ~1);
440
441 // Outputs LR that is saved in stack
442 if (stackBottom > 0)
443 {
444 const int end = (reinterpret_cast<const bit32*>(stackBottom) - stack);
445
446 int count = 0; // Back traces to 10 items to avoid buffer overflow
447 // Searches to bottom of stack
448 for (int i = 0; i < end; i++)
449 {
450 const bit32 data = stack[i];
451
452 if( IsSavedLrValue(data) )
453 {
454 pos += nn::nstd::TSNPrintf( pOut + pos , size - pos,
455 " [0x%03x] 0x%08x\n", i * sizeof(bit32), data );
456 ++ count;
457
458 if( (count >= 10) || (pos >= size) )
459 {
460 break;
461 }
462 }
463 }
464 }
465
466 return pos;
467 }
468
469 //------------------------------------------------------------------------------
470
OnHalt(const char * msg)471 void ExceptionScreen::OnHalt( const char* msg )
472 {
473 if( m_Option.IsSet(OPTION_WAIT_KEY) )
474 {
475 // Start displaying with button input
476 // Display if the A, B, X, and Y buttons are pressed in this order
477 WaitKeyCombination();
478 }
479
480 PrepareDisplaybufferImpl();
481
482 // Automatic scroll.
483 ShowWithAutoScroll(&m_DirectPrint, msg);
484
485 // Manual scroll.
486 ShowWithManualScroll(&m_DirectPrint, msg);
487 }
488
489
490 //------------------------------------------------------------------------------
491 #include <nn/hw/ARM/code32.h>
ExceptionHandler(nn::os::ARM::ExceptionInfo * pei,nn::os::ARM::ExceptionContext * pec)492 void ExceptionScreen::ExceptionHandler(nn::os::ARM::ExceptionInfo* pei, nn::os::ARM::ExceptionContext* pec)
493 {
494 nn::os::Thread::ChangeCurrentPriority( 0 ); // Very high
495
496 s_ExceptionScreen->ExceptionHandlerBody( pei , pec );
497
498 NN_TPANIC_("exception handler end.");
499 }
500 #include <nn/hw/ARM/codereset.h>
HandleException(nn::os::ARM::ExceptionInfo * pei,nn::os::ARM::ExceptionContext * pec)501 void ExceptionScreen::HandleException(nn::os::ARM::ExceptionInfo* pei, nn::os::ARM::ExceptionContext* pec)
502 {
503 s_ExceptionScreen->ExceptionHandlerBody( pei , pec );
504 }
ExceptionHandlerBody(nn::os::ARM::ExceptionInfo * pei,nn::os::ARM::ExceptionContext * pec)505 void ExceptionScreen::ExceptionHandlerBody(nn::os::ARM::ExceptionInfo* pei, nn::os::ARM::ExceptionContext* pec)
506 {
507 char buf[ 0x400 ] = {};
508 int pos;
509 int prevPos = 0;
510 NN_UNUSED_VAR(prevPos);
511
512 pos = nn::nstd::TSNPrintf( buf , sizeof(buf) ,
513 "Exception Occured.\n"
514 " type : %d\n"
515 " fsr : 0x%08x\n"
516 " far : 0x%08x\n"
517 " fpexc : 0x%08x\n"
518 " fpinst : 0x%08x\n"
519 " fpinst2 : 0x%08x\n"
520 , pei->type.Get()
521 , pei->fsr
522 , pei->far
523 , pei->fpexc
524 , pei->fpinst
525 , pei->fpinst2
526 );
527 NN_PUT(buf + prevPos, pos - prevPos);
528 prevPos = pos;
529 //====== Display information for exception
530
531 for (int i = 0; i < 16; i++)
532 {
533 pos += nn::nstd::TSPrintf( buf + pos , " r%02d: 0x%08x\n", i, pec->r[i] );
534 NN_PUT(buf + prevPos, pos - prevPos);
535 prevPos = pos;
536 }
537 {
538 pos += nn::nstd::TSPrintf( buf + pos , " cpsr: 0x%08x\n", pec->cpsr );
539 NN_PUT(buf + prevPos, pos - prevPos);
540 prevPos = pos;
541 }
542
543 //====== Display back trace
544 {
545 pos += PutBackTraceString(buf + pos, sizeof(buf) - pos, pec->r[13], pec->r[14]);
546 NN_PUT(buf + prevPos, pos - prevPos);
547 prevPos = pos;
548 }
549
550 if( m_Option.IsSet(OPTION_WRITE_TO_SD) )
551 {
552 WriteToSd(buf, pos, m_BuildInfo, m_Suffix);
553 }
554
555 //====== Call loop that displays information
556 if( m_Option.IsSet(OPTION_DIRECT_PRINT) )
557 {
558 OnHalt(buf);
559 }
560 }
561 //------------------------------------------------------------------------------
562
SetBuildInfoImpl(const wchar_t * pBuildInfo)563 void ExceptionScreen::SetBuildInfoImpl( const wchar_t* pBuildInfo )
564 {
565 std::wcsncpy( m_BuildInfo , pBuildInfo , sizeof(m_BuildInfo) / sizeof(wchar_t) - 1 );
566 m_BuildInfo[ sizeof(m_BuildInfo) / sizeof(wchar_t) - 1 ] = L'\0';
567 }
568
SetSuffixImpl(const wchar_t * pSuffix)569 void ExceptionScreen::SetSuffixImpl( const wchar_t* pSuffix )
570 {
571 std::wcsncpy( m_Suffix , pSuffix , sizeof(m_Suffix) / sizeof(wchar_t) - 1 );
572 m_Suffix[ sizeof(m_Suffix) / sizeof(wchar_t) - 1 ] = L'\0';
573 }
574 //------------------------------------------------------------------------------
575
PrepareDisplaybufferImpl()576 void ExceptionScreen::PrepareDisplaybufferImpl()
577 {
578 if (m_DirectPrint.GetDisplaybuffer() == NULL)
579 {
580 // If the display buffer is not explicitely specified
581 // the current display buffer is used
582 StealDisplaybuffer(&m_DirectPrint);
583 }
584 if (m_DirectPrint.GetDisplaybuffer() == NULL)
585 {
586 // If a display buffer hasn't been set to begin with
587 // a newly prepared display buffer is selected
588 AssignNewDisplaybuffer();
589 }
590 }
591
AssignNewDisplaybuffer()592 void ExceptionScreen::AssignNewDisplaybuffer()
593 {
594 NN_TLOG_("DirectPrint: Try to create new displaybuffer.\n");
595
596 nngxActiveDisplay( NN_GX_DISPLAY1 );
597 nngxGenDisplaybuffers( 1 , m_Display );
598 nngxBindDisplaybuffer( m_Display[ 0 ] );
599 nngxDisplaybufferStorage( GL_RGB565 , 240 , 320 , NN_GX_MEM_FCRAM );
600 nngxDisplayEnv( 0 , 0 );
601 nngxSwapBuffers( NN_GX_DISPLAY1 );
602
603 GLint param;
604 nngxGetDisplaybufferParameteri( NN_GX_DISPLAYBUFFER_ADDRESS , ¶m );
605
606 m_DirectPrint.ChangeDisplaybuffer( reinterpret_cast< void* > ( param ) ,
607 GL_RGB565 ,
608 nn::math::VEC2( 320 , 240 ) );
609 }
610
611
612
613 }}}
614