ArmPkg/DefaultExceptionHandlerLib ARM: avoid endless loop in RELEASE builds
[mirror_edk2.git] / ArmPkg / Library / DefaultExceptionHandlerLib / Arm / DefaultExceptionHandler.c
1 /** @file\r
2   Default exception handler\r
3 \r
4   Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>\r
5   Copyright (c) 2012, ARM Ltd. All rights reserved.<BR>\r
6 \r
7   This program and the accompanying materials\r
8   are licensed and made available under the terms and conditions of the BSD License\r
9   which accompanies this distribution.  The full text of the license may be found at\r
10   http://opensource.org/licenses/bsd-license.php\r
11 \r
12   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
13   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
14 \r
15 **/\r
16 \r
17 #include <Uefi.h>\r
18 #include <Library/BaseLib.h>\r
19 #include <Library/DebugLib.h>\r
20 #include <Library/PeCoffGetEntryPointLib.h>\r
21 #include <Library/PrintLib.h>\r
22 #include <Library/ArmDisassemblerLib.h>\r
23 #include <Library/SerialPortLib.h>\r
24 \r
25 #include <Guid/DebugImageInfoTable.h>\r
26 \r
27 #include <Protocol/DebugSupport.h>\r
28 #include <Library/DefaultExceptionHandlerLib.h>\r
29 \r
30 //\r
31 // The number of elements in a CHAR8 array, including the terminating NUL, that\r
32 // is meant to hold the string rendering of the CPSR.\r
33 //\r
34 #define CPSR_STRING_SIZE 32\r
35 \r
36 typedef struct {\r
37   UINT32  BIT;\r
38   CHAR8   Char;\r
39 } CPSR_CHAR;\r
40 \r
41 CHAR8 *\r
42 GetImageName (\r
43   IN  UINTN  FaultAddress,\r
44   OUT UINTN  *ImageBase,\r
45   OUT UINTN  *PeCoffSizeOfHeaders\r
46   );\r
47 \r
48 /**\r
49   Convert the Current Program Status Register (CPSR) to a string. The string is\r
50   a defacto standard in the ARM world.\r
51 \r
52   It is possible to add extra bits by adding them to CpsrChar array.\r
53 \r
54   @param  Cpsr         ARM CPSR register value\r
55   @param  ReturnStr    CPSR_STRING_SIZE byte string that contains string\r
56                        version of CPSR\r
57 \r
58 **/\r
59 VOID\r
60 CpsrString (\r
61   IN  UINT32  Cpsr,\r
62   OUT CHAR8   *ReturnStr\r
63   )\r
64 {\r
65   UINTN     Index;\r
66   CHAR8*    Str;\r
67   CHAR8*    ModeStr;\r
68   CPSR_CHAR CpsrChar[] = {\r
69     { 31, 'n' },\r
70     { 30, 'z' },\r
71     { 29, 'c' },\r
72     { 28, 'v' },\r
73 \r
74     { 9,  'e' },\r
75     { 8,  'a' },\r
76     { 7,  'i' },\r
77     { 6,  'f' },\r
78     { 5,  't' },\r
79     { 0,  '?' }\r
80   };\r
81 \r
82   Str = ReturnStr;\r
83 \r
84   for (Index = 0; CpsrChar[Index].BIT != 0; Index++, Str++) {\r
85     *Str = CpsrChar[Index].Char;\r
86     if ((Cpsr & (1 << CpsrChar[Index].BIT)) != 0) {\r
87       // Concert to upper case if bit is set\r
88       *Str &= ~0x20;\r
89     }\r
90   }\r
91 \r
92   *Str++ = '_';\r
93   *Str = '\0';\r
94 \r
95   switch (Cpsr & 0x1f) {\r
96   case 0x10:\r
97     ModeStr = "usr";\r
98     break;\r
99   case 0x011:\r
100     ModeStr = "fiq";\r
101     break;\r
102   case 0x12:\r
103     ModeStr = "irq";\r
104     break;\r
105   case 0x13:\r
106     ModeStr = "svc";\r
107     break;\r
108   case 0x16:\r
109     ModeStr = "mon";\r
110     break;\r
111   case 0x17:\r
112     ModeStr = "abt";\r
113     break;\r
114   case 0x1b:\r
115     ModeStr = "und";\r
116     break;\r
117   case 0x1f:\r
118     ModeStr = "sys";\r
119     break;\r
120 \r
121   default:\r
122     ModeStr = "???";\r
123     break;\r
124   }\r
125 \r
126   //\r
127   // See the interface contract in the leading comment block.\r
128   //\r
129   AsciiStrCatS (Str, CPSR_STRING_SIZE - (Str - ReturnStr), ModeStr);\r
130 }\r
131 \r
132 CHAR8 *\r
133 FaultStatusToString (\r
134   IN  UINT32  Status\r
135   )\r
136 {\r
137   CHAR8 *FaultSource;\r
138 \r
139   switch (Status) {\r
140     case 0x01: FaultSource = "Alignment fault"; break;\r
141     case 0x02: FaultSource = "Debug event fault"; break;\r
142     case 0x03: FaultSource = "Access Flag fault on Section"; break;\r
143     case 0x04: FaultSource = "Cache maintenance operation fault[2]"; break;\r
144     case 0x05: FaultSource = "Translation fault on Section"; break;\r
145     case 0x06: FaultSource = "Access Flag fault on Page"; break;\r
146     case 0x07: FaultSource = "Translation fault on Page"; break;\r
147     case 0x08: FaultSource = "Precise External Abort"; break;\r
148     case 0x09: FaultSource = "Domain fault on Section"; break;\r
149     case 0x0b: FaultSource = "Domain fault on Page"; break;\r
150     case 0x0c: FaultSource = "External abort on translation, first level"; break;\r
151     case 0x0d: FaultSource = "Permission fault on Section"; break;\r
152     case 0x0e: FaultSource = "External abort on translation, second level"; break;\r
153     case 0x0f: FaultSource = "Permission fault on Page"; break;\r
154     case 0x16: FaultSource = "Imprecise External Abort"; break;\r
155     default:   FaultSource = "No function"; break;\r
156     }\r
157 \r
158   return FaultSource;\r
159 }\r
160 \r
161 STATIC CHAR8 *gExceptionTypeString[] = {\r
162   "Reset",\r
163   "Undefined OpCode",\r
164   "SVC",\r
165   "Prefetch Abort",\r
166   "Data Abort",\r
167   "Undefined",\r
168   "IRQ",\r
169   "FIQ"\r
170 };\r
171 \r
172 /**\r
173   This is the default action to take on an unexpected exception\r
174 \r
175   Since this is exception context don't do anything crazy like try to allcoate memory.\r
176 \r
177   @param  ExceptionType    Type of the exception\r
178   @param  SystemContext    Register state at the time of the Exception\r
179 \r
180 \r
181 **/\r
182 VOID\r
183 DefaultExceptionHandler (\r
184   IN     EFI_EXCEPTION_TYPE           ExceptionType,\r
185   IN OUT EFI_SYSTEM_CONTEXT           SystemContext\r
186   )\r
187 {\r
188   CHAR8     Buffer[100];\r
189   UINTN     CharCount;\r
190   UINT32    DfsrStatus;\r
191   UINT32    IfsrStatus;\r
192   BOOLEAN   DfsrWrite;\r
193   UINT32    PcAdjust = 0;\r
194 \r
195   CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"\n%a Exception PC at 0x%08x  CPSR 0x%08x ",\r
196          gExceptionTypeString[ExceptionType], SystemContext.SystemContextArm->PC, SystemContext.SystemContextArm->CPSR);\r
197   SerialPortWrite ((UINT8 *) Buffer, CharCount);\r
198 \r
199   DEBUG_CODE_BEGIN ();\r
200     CHAR8   *Pdb;\r
201     UINT32  ImageBase;\r
202     UINT32  PeCoffSizeOfHeader;\r
203     UINT32  Offset;\r
204     CHAR8   CpsrStr[CPSR_STRING_SIZE];  // char per bit. Lower 5-bits are mode\r
205                                         // that is a 3 char string\r
206     CHAR8   Buffer[80];\r
207     UINT8   *DisAsm;\r
208     UINT32  ItBlock;\r
209 \r
210     CpsrString (SystemContext.SystemContextArm->CPSR, CpsrStr);\r
211     DEBUG ((EFI_D_ERROR, "%a\n", CpsrStr));\r
212 \r
213     Pdb = GetImageName (SystemContext.SystemContextArm->PC, &ImageBase, &PeCoffSizeOfHeader);\r
214     Offset = SystemContext.SystemContextArm->PC - ImageBase;\r
215     if (Pdb != NULL) {\r
216       DEBUG ((EFI_D_ERROR, "%a\n", Pdb));\r
217 \r
218       //\r
219       // A PE/COFF image loads its headers into memory so the headers are\r
220       // included in the linked addresses. ELF and Mach-O images do not\r
221       // include the headers so the first byte of the image is usually\r
222       // text (code). If you look at link maps from ELF or Mach-O images\r
223       // you need to subtract out the size of the PE/COFF header to get\r
224       // get the offset that matches the link map.\r
225       //\r
226       DEBUG ((EFI_D_ERROR, "loaded at 0x%08x (PE/COFF offset) 0x%x (ELF or Mach-O offset) 0x%x", ImageBase, Offset, Offset - PeCoffSizeOfHeader));\r
227 \r
228       // If we come from an image it is safe to show the instruction. We know it should not fault\r
229       DisAsm = (UINT8 *)(UINTN)SystemContext.SystemContextArm->PC;\r
230       ItBlock = 0;\r
231       DisassembleInstruction (&DisAsm, (SystemContext.SystemContextArm->CPSR & BIT5) == BIT5, TRUE, &ItBlock, Buffer, sizeof (Buffer));\r
232       DEBUG ((EFI_D_ERROR, "\n%a", Buffer));\r
233 \r
234       switch (ExceptionType) {\r
235       case EXCEPT_ARM_UNDEFINED_INSTRUCTION:\r
236       case EXCEPT_ARM_SOFTWARE_INTERRUPT:\r
237       case EXCEPT_ARM_PREFETCH_ABORT:\r
238       case EXCEPT_ARM_DATA_ABORT:\r
239         // advance PC past the faulting instruction\r
240         PcAdjust = (UINTN)DisAsm - SystemContext.SystemContextArm->PC;\r
241         break;\r
242 \r
243       default:\r
244         break;\r
245       }\r
246 \r
247     }\r
248   DEBUG_CODE_END ();\r
249   DEBUG ((EFI_D_ERROR, "\n  R0 0x%08x   R1 0x%08x   R2 0x%08x   R3 0x%08x\n", SystemContext.SystemContextArm->R0, SystemContext.SystemContextArm->R1, SystemContext.SystemContextArm->R2, SystemContext.SystemContextArm->R3));\r
250   DEBUG ((EFI_D_ERROR, "  R4 0x%08x   R5 0x%08x   R6 0x%08x   R7 0x%08x\n", SystemContext.SystemContextArm->R4, SystemContext.SystemContextArm->R5, SystemContext.SystemContextArm->R6, SystemContext.SystemContextArm->R7));\r
251   DEBUG ((EFI_D_ERROR, "  R8 0x%08x   R9 0x%08x  R10 0x%08x  R11 0x%08x\n", SystemContext.SystemContextArm->R8, SystemContext.SystemContextArm->R9, SystemContext.SystemContextArm->R10, SystemContext.SystemContextArm->R11));\r
252   DEBUG ((EFI_D_ERROR, " R12 0x%08x   SP 0x%08x   LR 0x%08x   PC 0x%08x\n", SystemContext.SystemContextArm->R12, SystemContext.SystemContextArm->SP, SystemContext.SystemContextArm->LR, SystemContext.SystemContextArm->PC));\r
253   DEBUG ((EFI_D_ERROR, "DFSR 0x%08x DFAR 0x%08x IFSR 0x%08x IFAR 0x%08x\n", SystemContext.SystemContextArm->DFSR, SystemContext.SystemContextArm->DFAR, SystemContext.SystemContextArm->IFSR, SystemContext.SystemContextArm->IFAR));\r
254 \r
255   // Bit10 is Status[4] Bit3:0 is Status[3:0]\r
256   DfsrStatus = (SystemContext.SystemContextArm->DFSR & 0xf) | ((SystemContext.SystemContextArm->DFSR >> 6) & 0x10);\r
257   DfsrWrite = (SystemContext.SystemContextArm->DFSR & BIT11) != 0;\r
258   if (DfsrStatus != 0x00) {\r
259     DEBUG ((EFI_D_ERROR, " %a: %a 0x%08x\n", FaultStatusToString (DfsrStatus), DfsrWrite ? "write to" : "read from", SystemContext.SystemContextArm->DFAR));\r
260   }\r
261 \r
262   IfsrStatus = (SystemContext.SystemContextArm->IFSR & 0xf) | ((SystemContext.SystemContextArm->IFSR >> 6) & 0x10);\r
263   if (IfsrStatus != 0) {\r
264     DEBUG ((EFI_D_ERROR, " Instruction %a at 0x%08x\n", FaultStatusToString (SystemContext.SystemContextArm->IFSR & 0xf), SystemContext.SystemContextArm->IFAR));\r
265   }\r
266 \r
267   DEBUG ((EFI_D_ERROR, "\n"));\r
268   ASSERT (FALSE);\r
269 \r
270   CpuDeadLoop ();   // may return if executing under a debugger\r
271 \r
272   // Clear the error registers that we have already displayed incase some one wants to keep going\r
273   SystemContext.SystemContextArm->DFSR = 0;\r
274   SystemContext.SystemContextArm->IFSR = 0;\r
275 \r
276   // If some one is stepping past the exception handler adjust the PC to point to the next instruction\r
277   SystemContext.SystemContextArm->PC += PcAdjust;\r
278 }\r