Minor update to move array into function to dead strip better on release builds
[mirror_edk2.git] / ArmPkg / Drivers / CpuDxe / Exception.c
CommitLineData
2ef2b01e
A
1/** @file\r
2\r
3 Copyright (c) 2008-2009, Apple Inc. All rights reserved.\r
4 \r
5 All rights reserved. This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution. The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9\r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
15#include "CpuDxe.h" \r
f659880b 16\r
62e797a4 17EFI_DEBUG_IMAGE_INFO_TABLE_HEADER *gDebugImageTableHeader = NULL;\r
2ef2b01e
A
18\r
19VOID\r
20ExceptionHandlersStart (\r
21 VOID\r
22 );\r
23\r
24VOID\r
25ExceptionHandlersEnd (\r
26 VOID\r
27 );\r
28\r
29VOID\r
30CommonExceptionEntry (\r
31 VOID\r
32 );\r
33\r
34VOID\r
35AsmCommonExceptionEntry (\r
36 VOID\r
37 );\r
38\r
39\r
40EFI_EXCEPTION_CALLBACK gExceptionHandlers[MAX_ARM_EXCEPTION + 1];\r
41EFI_EXCEPTION_CALLBACK gDebuggerExceptionHandlers[MAX_ARM_EXCEPTION + 1];\r
42\r
43\r
44\r
45/**\r
46 This function registers and enables the handler specified by InterruptHandler for a processor \r
47 interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the \r
48 handler for the processor interrupt or exception type specified by InterruptType is uninstalled. \r
49 The installed handler is called once for each processor interrupt or exception.\r
50\r
51 @param InterruptType A pointer to the processor's current interrupt state. Set to TRUE if interrupts\r
52 are enabled and FALSE if interrupts are disabled.\r
53 @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called\r
54 when a processor interrupt occurs. If this parameter is NULL, then the handler\r
55 will be uninstalled.\r
56\r
57 @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.\r
58 @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was\r
59 previously installed.\r
60 @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not\r
61 previously installed.\r
62 @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported.\r
63\r
64**/\r
65EFI_STATUS\r
66RegisterInterruptHandler (\r
67 IN EFI_EXCEPTION_TYPE InterruptType,\r
68 IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler\r
69 )\r
70{\r
71 if (InterruptType > MAX_ARM_EXCEPTION) {\r
72 return EFI_UNSUPPORTED;\r
73 }\r
74\r
75 if ((InterruptHandler != NULL) && (gExceptionHandlers[InterruptType] != NULL)) {\r
76 return EFI_ALREADY_STARTED;\r
77 }\r
78\r
79 gExceptionHandlers[InterruptType] = InterruptHandler;\r
80\r
81 return EFI_SUCCESS;\r
82}\r
83\r
84\r
85/**\r
86 This function registers and enables the handler specified by InterruptHandler for a processor \r
87 interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the \r
88 handler for the processor interrupt or exception type specified by InterruptType is uninstalled. \r
89 The installed handler is called once for each processor interrupt or exception.\r
90\r
91 @param InterruptType A pointer to the processor's current interrupt state. Set to TRUE if interrupts\r
92 are enabled and FALSE if interrupts are disabled.\r
93 @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called\r
94 when a processor interrupt occurs. If this parameter is NULL, then the handler\r
95 will be uninstalled.\r
96\r
97 @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.\r
98 @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was\r
99 previously installed.\r
100 @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not\r
101 previously installed.\r
102 @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported.\r
103\r
104**/\r
105EFI_STATUS\r
106RegisterDebuggerInterruptHandler (\r
107 IN EFI_EXCEPTION_TYPE InterruptType,\r
108 IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler\r
109 )\r
110{\r
111 if (InterruptType > MAX_ARM_EXCEPTION) {\r
112 return EFI_UNSUPPORTED;\r
113 }\r
114\r
115 if ((InterruptHandler != NULL) && (gDebuggerExceptionHandlers[InterruptType] != NULL)) {\r
116 return EFI_ALREADY_STARTED;\r
117 }\r
118\r
119 gDebuggerExceptionHandlers[InterruptType] = InterruptHandler;\r
120\r
121 return EFI_SUCCESS;\r
122}\r
123\r
f659880b 124\r
f659880b
A
125\r
126\r
127CHAR8 *\r
128GetImageName (\r
129 IN UINT32 FaultAddress,\r
130 OUT UINT32 *ImageBase,\r
131 OUT UINT32 *PeCoffSizeOfHeaders\r
132 )\r
133{\r
62e797a4
A
134 EFI_DEBUG_IMAGE_INFO *DebugTable;\r
135 UINTN Entry;\r
136 CHAR8 *Address;\r
137\r
f659880b
A
138 \r
139 DebugTable = gDebugImageTableHeader->EfiDebugImageInfoTable;\r
140 if (DebugTable == NULL) {\r
141 return NULL;\r
142 }\r
143\r
144 Address = (CHAR8 *)(UINTN)FaultAddress;\r
62e797a4
A
145 for (Entry = 0; Entry < gDebugImageTableHeader->TableSize; Entry++, DebugTable++) {\r
146 if (DebugTable->NormalImage != NULL) {\r
147 if ((DebugTable->NormalImage->ImageInfoType == EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL) && \r
148 (DebugTable->NormalImage->LoadedImageProtocolInstance != NULL)) {\r
149 if ((Address >= (CHAR8 *)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase) &&\r
150 (Address <= ((CHAR8 *)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase + DebugTable->NormalImage->LoadedImageProtocolInstance->ImageSize))) {\r
151 *ImageBase = (UINT32)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase;\r
152 *PeCoffSizeOfHeaders = PeCoffGetSizeOfHeaders ((VOID *)(UINTN)*ImageBase);\r
153 return PeCoffLoaderGetPdbPointer (DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase);\r
154 } \r
155 }\r
156 } \r
f659880b
A
157 }\r
158\r
159 return NULL;\r
160}\r
161\r
235605b5
A
162typedef struct {\r
163 UINT32 Bit;\r
164 CHAR8 Char;\r
165} CPSR_CHAR;\r
166\r
f659880b 167\r
235605b5
A
168VOID\r
169CpsrString (\r
170 IN UINT32 Cpsr,\r
171 OUT CHAR8 *ReturnStr\r
172 )\r
173{\r
174 UINTN Index;\r
175 CHAR8 *Str = ReturnStr;\r
176 CHAR8 *ModeStr;\r
79b9790f
A
177 CPSR_CHAR CpsrChar[] = {\r
178 { 31, 'n' },\r
179 { 30, 'z' },\r
180 { 29, 'c' },\r
181 { 28, 'v' },\r
182\r
183 { 9, 'e' },\r
184 { 8, 'a' },\r
185 { 7, 'i' },\r
186 { 6, 'f' },\r
187 { 5, 't' },\r
188 { 0, '?' }\r
189 };\r
235605b5 190 \r
79b9790f
A
191 for (Index = 0; CpsrChar[Index].Bit != 0; Index++, Str++) {\r
192 *Str = CpsrChar[Index].Char;\r
193 if ((Cpsr & (1 << CpsrChar[Index].Bit)) != 0) {\r
235605b5
A
194 // Concert to upper case if bit is set\r
195 *Str &= ~0x20;\r
196 }\r
197 }\r
198 \r
199 *Str++ = '_';\r
200 *Str = '\0';\r
201 \r
202 switch (Cpsr & 0x1f) {\r
203 case 0x17:\r
204 ModeStr = "abt";\r
205 break;\r
206 case 0x011:\r
207 ModeStr = "fiq";\r
208 break;\r
209 case 0x12:\r
210 ModeStr = "irq";\r
211 break;\r
212 case 0x13:\r
213 ModeStr = "svc";\r
214 break;\r
215 case 0x1f:\r
216 ModeStr = "sys";\r
217 break;\r
218 case 0x1b:\r
219 ModeStr = "und";\r
220 break;\r
221 case 0x10:\r
222 ModeStr = "usr";\r
223 break;\r
224 \r
225 default:\r
226 ModeStr = "???";\r
227 break;\r
228 }\r
229 \r
230 AsciiStrCat (Str, ModeStr);\r
231 return;\r
232} \r
233 \r
8a4d81e6
A
234CHAR8 *gExceptionTypeString[] = {\r
235 "Reset",\r
236 "Undefined Instruction",\r
237 "SWI",\r
238 "Prefetch Abort",\r
239 "Data Abort",\r
240 "Undefined",\r
241 "IRQ",\r
242 "FIQ"\r
243};\r
2ef2b01e
A
244\r
245VOID\r
246EFIAPI\r
247CommonCExceptionHandler (\r
248 IN EFI_EXCEPTION_TYPE ExceptionType,\r
249 IN OUT EFI_SYSTEM_CONTEXT SystemContext\r
250 )\r
251{\r
252 BOOLEAN Dispatched = FALSE;\r
8a4d81e6 253 \r
d213712d 254 \r
2ef2b01e
A
255 if (ExceptionType <= MAX_ARM_EXCEPTION) {\r
256 if (gDebuggerExceptionHandlers[ExceptionType]) {\r
257 //\r
258 // If DebugSupport hooked the interrupt call the handler. This does not disable \r
259 // the normal handler.\r
260 //\r
261 gDebuggerExceptionHandlers[ExceptionType] (ExceptionType, SystemContext);\r
262 Dispatched = TRUE;\r
263 }\r
264 if (gExceptionHandlers[ExceptionType]) {\r
265 gExceptionHandlers[ExceptionType] (ExceptionType, SystemContext);\r
266 Dispatched = TRUE;\r
267 }\r
8a4d81e6
A
268 } else {\r
269 DEBUG ((EFI_D_ERROR, "Unknown exception type %d from %08x\n", ExceptionType, SystemContext.SystemContextArm->PC));\r
270 ASSERT (FALSE);\r
2ef2b01e
A
271 }\r
272\r
273 if (Dispatched) {\r
274 //\r
275 // We did work so this was an expected ExceptionType\r
276 //\r
277 return;\r
278 }\r
279 \r
280 if (ExceptionType == EXCEPT_ARM_SOFTWARE_INTERRUPT) {\r
281 //\r
282 // ARM JTAG debuggers some times use this vector, so it is not an error to get one\r
283 //\r
284 return;\r
285 }\r
286\r
287 //\r
f659880b 288 // Code after here is the default exception handler... Dump the context\r
2ef2b01e 289 //\r
235605b5 290 DEBUG ((EFI_D_ERROR, "\n%a Exception PC at 0x%08x CPSR 0x%08x ", gExceptionTypeString[ExceptionType], SystemContext.SystemContextArm->PC, SystemContext.SystemContextArm->CPSR));\r
f659880b 291 DEBUG_CODE_BEGIN ();\r
62e797a4
A
292 CHAR8 *Pdb;\r
293 UINT32 ImageBase;\r
294 UINT32 PeCoffSizeOfHeader;\r
295 UINT32 Offset;\r
235605b5
A
296 CHAR8 CpsrStr[32]; // char per bit. Lower 5-bits are mode that is a 3 char string\r
297 \r
298 CpsrString (SystemContext.SystemContextArm->CPSR, CpsrStr);\r
299 DEBUG ((EFI_D_ERROR, "%a\n", CpsrStr));\r
f659880b
A
300 \r
301 Pdb = GetImageName (SystemContext.SystemContextArm->PC, &ImageBase, &PeCoffSizeOfHeader);\r
302 Offset = SystemContext.SystemContextArm->PC - ImageBase;\r
303 if (Pdb != NULL) {\r
304 DEBUG ((EFI_D_ERROR, "%a\n", Pdb));\r
2ef2b01e 305\r
f659880b
A
306 //\r
307 // A PE/COFF image loads its headers into memory so the headers are \r
308 // included in the linked addressess. ELF and Mach-O images do not\r
309 // include the headers so the first byte of the image is usually\r
310 // text (code). If you look at link maps from ELF or Mach-O images\r
311 // you need to subtact out the size of the PE/COFF header to get\r
312 // get the offset that matches the link map. \r
313 //\r
235605b5 314 DEBUG ((EFI_D_ERROR, "loadded at 0x%08x (PE/COFF offset) 0x%x (ELF or Mach-O offset) 0x%x", ImageBase, Offset, Offset - PeCoffSizeOfHeader));\r
f659880b
A
315 }\r
316 DEBUG_CODE_END ();\r
235605b5
A
317 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
318 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
319 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
320 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
321 DEBUG ((EFI_D_ERROR, "DFSR 0x%08x DFAR 0x%08x IFSR 0x%08x IFAR 0x%08x\n\n", SystemContext.SystemContextArm->DFSR, SystemContext.SystemContextArm->DFAR, SystemContext.SystemContextArm->IFSR, SystemContext.SystemContextArm->IFAR));\r
f659880b
A
322\r
323 ASSERT (FALSE);\r
2ef2b01e
A
324}\r
325\r
326\r
327\r
328EFI_STATUS\r
329InitializeExceptions (\r
330 IN EFI_CPU_ARCH_PROTOCOL *Cpu\r
331 )\r
332{\r
333 EFI_STATUS Status;\r
334 UINTN Offset;\r
335 UINTN Length;\r
336 UINTN Index;\r
337 BOOLEAN Enabled;\r
338 EFI_PHYSICAL_ADDRESS Base;\r
339\r
62e797a4
A
340 Status = EfiGetSystemConfigurationTable (&gEfiDebugImageInfoTableGuid, (VOID **)&gDebugImageTableHeader);\r
341 if (EFI_ERROR (Status)) {\r
342 gDebugImageTableHeader = NULL;\r
343 }\r
f659880b 344\r
2ef2b01e
A
345 //\r
346 // Disable interrupts\r
347 //\r
348 Cpu->GetInterruptState (Cpu, &Enabled);\r
349 Cpu->DisableInterrupt (Cpu);\r
350\r
351 //\r
352 // Initialize the C entry points for interrupts\r
353 //\r
354 for (Index = 0; Index <= MAX_ARM_EXCEPTION; Index++) {\r
355 Status = RegisterInterruptHandler (Index, NULL);\r
356 ASSERT_EFI_ERROR (Status);\r
357 \r
358 Status = RegisterDebuggerInterruptHandler (Index, NULL);\r
359 ASSERT_EFI_ERROR (Status);\r
360 }\r
361 \r
362 //\r
8a4d81e6 363 // Copy an implementation of the ARM exception vectors to PcdCpuVectorBaseAddress.\r
2ef2b01e
A
364 //\r
365 Length = (UINTN)ExceptionHandlersEnd - (UINTN)ExceptionHandlersStart;\r
366\r
367 //\r
368 // Reserve space for the exception handlers\r
369 //\r
370 Base = (EFI_PHYSICAL_ADDRESS)PcdGet32 (PcdCpuVectorBaseAddress);\r
371 Status = gBS->AllocatePages (AllocateAddress, EfiBootServicesCode, EFI_SIZE_TO_PAGES (Length), &Base);\r
372 // If the request was for memory that's not in the memory map (which is often the case for 0x00000000\r
373 // on embedded systems, for example, we don't want to hang up. So we'll check here for a status of \r
374 // EFI_NOT_FOUND, and continue in that case.\r
375 if (EFI_ERROR(Status) && (Status != EFI_NOT_FOUND)) {\r
8a4d81e6 376 ASSERT_EFI_ERROR (Status);\r
2ef2b01e
A
377 }\r
378\r
379 CopyMem ((VOID *)(UINTN)PcdGet32 (PcdCpuVectorBaseAddress), (VOID *)ExceptionHandlersStart, Length);\r
380\r
381 //\r
382 // Patch in the common Assembly exception handler\r
383 //\r
384 Offset = (UINTN)CommonExceptionEntry - (UINTN)ExceptionHandlersStart;\r
385 *(UINTN *) ((UINT8 *)(UINTN)PcdGet32 (PcdCpuVectorBaseAddress) + Offset) = (UINTN)AsmCommonExceptionEntry;\r
386\r
387 // Flush Caches since we updated executable stuff\r
8a4d81e6 388 InvalidateInstructionCacheRange ((VOID *)PcdGet32(PcdCpuVectorBaseAddress), Length);\r
2ef2b01e
A
389\r
390 if (Enabled) {\r
391 // \r
392 // Restore interrupt state\r
393 //\r
394 Status = Cpu->EnableInterrupt (Cpu);\r
395 }\r
396\r
397 return Status;\r
398}\r