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 , &param );
605 
606     m_DirectPrint.ChangeDisplaybuffer( reinterpret_cast< void* > ( param ) ,
607                                       GL_RGB565 ,
608                                       nn::math::VEC2( 320 , 240 ) );
609 }
610 
611 
612 
613 }}}
614