]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPkg/Drivers/CpuDxe/Exception.c
Fix GCC build bug and add a debug library to dump load and unload commands into the...
[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
62e797a4
A
125UINT32\r
126EFIAPI\r
127PeCoffGetSizeOfHeaders (\r
128 IN VOID *Pe32Data\r
129 )\r
130{\r
131 EFI_IMAGE_DOS_HEADER *DosHdr;\r
132 EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;\r
133 UINTN SizeOfHeaders;\r
134\r
135 DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;\r
136 if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {\r
137 //\r
138 // DOS image header is present, so read the PE header after the DOS image header.\r
139 //\r
140 Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));\r
141 } else {\r
142 //\r
143 // DOS image header is not present, so PE header is at the image base.\r
144 //\r
145 Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;\r
146 }\r
147\r
148 if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {\r
149 SizeOfHeaders = sizeof (EFI_TE_IMAGE_HEADER) + (UINTN)Hdr.Te->BaseOfCode - (UINTN)Hdr.Te->StrippedSize;\r
150 } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {\r
151 SizeOfHeaders = Hdr.Pe32->OptionalHeader.SizeOfHeaders;\r
152 } else {\r
153 SizeOfHeaders = 0;\r
154 }\r
155\r
156 return SizeOfHeaders;\r
f659880b
A
157}\r
158\r
159\r
160CHAR8 *\r
161GetImageName (\r
162 IN UINT32 FaultAddress,\r
163 OUT UINT32 *ImageBase,\r
164 OUT UINT32 *PeCoffSizeOfHeaders\r
165 )\r
166{\r
62e797a4
A
167 EFI_DEBUG_IMAGE_INFO *DebugTable;\r
168 UINTN Entry;\r
169 CHAR8 *Address;\r
170\r
f659880b
A
171 \r
172 DebugTable = gDebugImageTableHeader->EfiDebugImageInfoTable;\r
173 if (DebugTable == NULL) {\r
174 return NULL;\r
175 }\r
176\r
177 Address = (CHAR8 *)(UINTN)FaultAddress;\r
62e797a4
A
178 for (Entry = 0; Entry < gDebugImageTableHeader->TableSize; Entry++, DebugTable++) {\r
179 if (DebugTable->NormalImage != NULL) {\r
180 if ((DebugTable->NormalImage->ImageInfoType == EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL) && \r
181 (DebugTable->NormalImage->LoadedImageProtocolInstance != NULL)) {\r
182 if ((Address >= (CHAR8 *)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase) &&\r
183 (Address <= ((CHAR8 *)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase + DebugTable->NormalImage->LoadedImageProtocolInstance->ImageSize))) {\r
184 *ImageBase = (UINT32)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase;\r
185 *PeCoffSizeOfHeaders = PeCoffGetSizeOfHeaders ((VOID *)(UINTN)*ImageBase);\r
186 return PeCoffLoaderGetPdbPointer (DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase);\r
187 } \r
188 }\r
189 } \r
f659880b
A
190 }\r
191\r
192 return NULL;\r
193}\r
194\r
195\r
8a4d81e6
A
196CHAR8 *gExceptionTypeString[] = {\r
197 "Reset",\r
198 "Undefined Instruction",\r
199 "SWI",\r
200 "Prefetch Abort",\r
201 "Data Abort",\r
202 "Undefined",\r
203 "IRQ",\r
204 "FIQ"\r
205};\r
2ef2b01e
A
206\r
207VOID\r
208EFIAPI\r
209CommonCExceptionHandler (\r
210 IN EFI_EXCEPTION_TYPE ExceptionType,\r
211 IN OUT EFI_SYSTEM_CONTEXT SystemContext\r
212 )\r
213{\r
214 BOOLEAN Dispatched = FALSE;\r
8a4d81e6 215 \r
d213712d 216 \r
2ef2b01e
A
217 if (ExceptionType <= MAX_ARM_EXCEPTION) {\r
218 if (gDebuggerExceptionHandlers[ExceptionType]) {\r
219 //\r
220 // If DebugSupport hooked the interrupt call the handler. This does not disable \r
221 // the normal handler.\r
222 //\r
223 gDebuggerExceptionHandlers[ExceptionType] (ExceptionType, SystemContext);\r
224 Dispatched = TRUE;\r
225 }\r
226 if (gExceptionHandlers[ExceptionType]) {\r
227 gExceptionHandlers[ExceptionType] (ExceptionType, SystemContext);\r
228 Dispatched = TRUE;\r
229 }\r
8a4d81e6
A
230 } else {\r
231 DEBUG ((EFI_D_ERROR, "Unknown exception type %d from %08x\n", ExceptionType, SystemContext.SystemContextArm->PC));\r
232 ASSERT (FALSE);\r
2ef2b01e
A
233 }\r
234\r
235 if (Dispatched) {\r
236 //\r
237 // We did work so this was an expected ExceptionType\r
238 //\r
239 return;\r
240 }\r
241 \r
242 if (ExceptionType == EXCEPT_ARM_SOFTWARE_INTERRUPT) {\r
243 //\r
244 // ARM JTAG debuggers some times use this vector, so it is not an error to get one\r
245 //\r
246 return;\r
247 }\r
248\r
249 //\r
f659880b 250 // Code after here is the default exception handler... Dump the context\r
2ef2b01e 251 //\r
f659880b
A
252 DEBUG ((EFI_D_ERROR, "\n%a Exception from instruction at 0x%08x CPSR 0x%08x\n", gExceptionTypeString[ExceptionType], SystemContext.SystemContextArm->PC, SystemContext.SystemContextArm->CPSR));\r
253 DEBUG_CODE_BEGIN ();\r
62e797a4
A
254 CHAR8 *Pdb;\r
255 UINT32 ImageBase;\r
256 UINT32 PeCoffSizeOfHeader;\r
257 UINT32 Offset;\r
f659880b
A
258 \r
259 Pdb = GetImageName (SystemContext.SystemContextArm->PC, &ImageBase, &PeCoffSizeOfHeader);\r
260 Offset = SystemContext.SystemContextArm->PC - ImageBase;\r
261 if (Pdb != NULL) {\r
262 DEBUG ((EFI_D_ERROR, "%a\n", Pdb));\r
2ef2b01e 263\r
f659880b
A
264 //\r
265 // A PE/COFF image loads its headers into memory so the headers are \r
266 // included in the linked addressess. ELF and Mach-O images do not\r
267 // include the headers so the first byte of the image is usually\r
268 // text (code). If you look at link maps from ELF or Mach-O images\r
269 // you need to subtact out the size of the PE/COFF header to get\r
270 // get the offset that matches the link map. \r
271 //\r
272 DEBUG ((EFI_D_ERROR, "loadded at 0x%08x (PE/COFF offset) 0x%08x (ELF or Mach-O offset) 0x%08x\n", ImageBase, Offset, Offset - PeCoffSizeOfHeader));\r
273 }\r
274 DEBUG_CODE_END ();\r
275 DEBUG ((EFI_D_ERROR, " 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
276 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
277 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
278 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
279 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
280\r
281 ASSERT (FALSE);\r
282// while (TRUE) {\r
283// CpuSleep ();\r
284// }\r
2ef2b01e
A
285}\r
286\r
287\r
288\r
289EFI_STATUS\r
290InitializeExceptions (\r
291 IN EFI_CPU_ARCH_PROTOCOL *Cpu\r
292 )\r
293{\r
294 EFI_STATUS Status;\r
295 UINTN Offset;\r
296 UINTN Length;\r
297 UINTN Index;\r
298 BOOLEAN Enabled;\r
299 EFI_PHYSICAL_ADDRESS Base;\r
300\r
62e797a4
A
301 Status = EfiGetSystemConfigurationTable (&gEfiDebugImageInfoTableGuid, (VOID **)&gDebugImageTableHeader);\r
302 if (EFI_ERROR (Status)) {\r
303 gDebugImageTableHeader = NULL;\r
304 }\r
f659880b 305\r
2ef2b01e
A
306 //\r
307 // Disable interrupts\r
308 //\r
309 Cpu->GetInterruptState (Cpu, &Enabled);\r
310 Cpu->DisableInterrupt (Cpu);\r
311\r
312 //\r
313 // Initialize the C entry points for interrupts\r
314 //\r
315 for (Index = 0; Index <= MAX_ARM_EXCEPTION; Index++) {\r
316 Status = RegisterInterruptHandler (Index, NULL);\r
317 ASSERT_EFI_ERROR (Status);\r
318 \r
319 Status = RegisterDebuggerInterruptHandler (Index, NULL);\r
320 ASSERT_EFI_ERROR (Status);\r
321 }\r
322 \r
323 //\r
8a4d81e6 324 // Copy an implementation of the ARM exception vectors to PcdCpuVectorBaseAddress.\r
2ef2b01e
A
325 //\r
326 Length = (UINTN)ExceptionHandlersEnd - (UINTN)ExceptionHandlersStart;\r
327\r
328 //\r
329 // Reserve space for the exception handlers\r
330 //\r
331 Base = (EFI_PHYSICAL_ADDRESS)PcdGet32 (PcdCpuVectorBaseAddress);\r
332 Status = gBS->AllocatePages (AllocateAddress, EfiBootServicesCode, EFI_SIZE_TO_PAGES (Length), &Base);\r
333 // If the request was for memory that's not in the memory map (which is often the case for 0x00000000\r
334 // on embedded systems, for example, we don't want to hang up. So we'll check here for a status of \r
335 // EFI_NOT_FOUND, and continue in that case.\r
336 if (EFI_ERROR(Status) && (Status != EFI_NOT_FOUND)) {\r
8a4d81e6 337 ASSERT_EFI_ERROR (Status);\r
2ef2b01e
A
338 }\r
339\r
340 CopyMem ((VOID *)(UINTN)PcdGet32 (PcdCpuVectorBaseAddress), (VOID *)ExceptionHandlersStart, Length);\r
341\r
342 //\r
343 // Patch in the common Assembly exception handler\r
344 //\r
345 Offset = (UINTN)CommonExceptionEntry - (UINTN)ExceptionHandlersStart;\r
346 *(UINTN *) ((UINT8 *)(UINTN)PcdGet32 (PcdCpuVectorBaseAddress) + Offset) = (UINTN)AsmCommonExceptionEntry;\r
347\r
348 // Flush Caches since we updated executable stuff\r
8a4d81e6 349 InvalidateInstructionCacheRange ((VOID *)PcdGet32(PcdCpuVectorBaseAddress), Length);\r
2ef2b01e
A
350\r
351 if (Enabled) {\r
352 // \r
353 // Restore interrupt state\r
354 //\r
355 Status = Cpu->EnableInterrupt (Cpu);\r
356 }\r
357\r
358 return Status;\r
359}\r