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