]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Drivers/CpuDxe/Exception.c
Updated ARM Exception handler to print out text values for CPSR register
[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 CPSR_CHAR gCpsrChar[] = {
168 { 31, 'n' },
169 { 30, 'z' },
170 { 29, 'c' },
171 { 28, 'v' },
172 { 27, 'q' },
173
174 { 8, 'a' },
175 { 7, 'i' },
176 { 6, 'f' },
177 { 5, 't' },
178 { 0, '?' }
179 };
180
181
182 VOID
183 CpsrString (
184 IN UINT32 Cpsr,
185 OUT CHAR8 *ReturnStr
186 )
187 {
188 UINTN Index;
189 CHAR8 *Str = ReturnStr;
190 CHAR8 *ModeStr;
191
192 for (Index = 0; gCpsrChar[Index].Bit != 0; Index++, Str++) {
193 *Str = gCpsrChar[Index].Char;
194 if ((Cpsr & (1 << gCpsrChar[Index].Bit)) != 0) {
195 // Concert to upper case if bit is set
196 *Str &= ~0x20;
197 }
198 }
199
200 *Str++ = '_';
201 *Str = '\0';
202
203 switch (Cpsr & 0x1f) {
204 case 0x17:
205 ModeStr = "abt";
206 break;
207 case 0x011:
208 ModeStr = "fiq";
209 break;
210 case 0x12:
211 ModeStr = "irq";
212 break;
213 case 0x13:
214 ModeStr = "svc";
215 break;
216 case 0x1f:
217 ModeStr = "sys";
218 break;
219 case 0x1b:
220 ModeStr = "und";
221 break;
222 case 0x10:
223 ModeStr = "usr";
224 break;
225
226 default:
227 ModeStr = "???";
228 break;
229 }
230
231 AsciiStrCat (Str, ModeStr);
232 return;
233 }
234
235 CHAR8 *gExceptionTypeString[] = {
236 "Reset",
237 "Undefined Instruction",
238 "SWI",
239 "Prefetch Abort",
240 "Data Abort",
241 "Undefined",
242 "IRQ",
243 "FIQ"
244 };
245
246 VOID
247 EFIAPI
248 CommonCExceptionHandler (
249 IN EFI_EXCEPTION_TYPE ExceptionType,
250 IN OUT EFI_SYSTEM_CONTEXT SystemContext
251 )
252 {
253 BOOLEAN Dispatched = FALSE;
254
255
256 if (ExceptionType <= MAX_ARM_EXCEPTION) {
257 if (gDebuggerExceptionHandlers[ExceptionType]) {
258 //
259 // If DebugSupport hooked the interrupt call the handler. This does not disable
260 // the normal handler.
261 //
262 gDebuggerExceptionHandlers[ExceptionType] (ExceptionType, SystemContext);
263 Dispatched = TRUE;
264 }
265 if (gExceptionHandlers[ExceptionType]) {
266 gExceptionHandlers[ExceptionType] (ExceptionType, SystemContext);
267 Dispatched = TRUE;
268 }
269 } else {
270 DEBUG ((EFI_D_ERROR, "Unknown exception type %d from %08x\n", ExceptionType, SystemContext.SystemContextArm->PC));
271 ASSERT (FALSE);
272 }
273
274 if (Dispatched) {
275 //
276 // We did work so this was an expected ExceptionType
277 //
278 return;
279 }
280
281 if (ExceptionType == EXCEPT_ARM_SOFTWARE_INTERRUPT) {
282 //
283 // ARM JTAG debuggers some times use this vector, so it is not an error to get one
284 //
285 return;
286 }
287
288 //
289 // Code after here is the default exception handler... Dump the context
290 //
291 DEBUG ((EFI_D_ERROR, "\n%a Exception PC at 0x%08x CPSR 0x%08x ", gExceptionTypeString[ExceptionType], SystemContext.SystemContextArm->PC, SystemContext.SystemContextArm->CPSR));
292 DEBUG_CODE_BEGIN ();
293 CHAR8 *Pdb;
294 UINT32 ImageBase;
295 UINT32 PeCoffSizeOfHeader;
296 UINT32 Offset;
297 CHAR8 CpsrStr[32]; // char per bit. Lower 5-bits are mode that is a 3 char string
298
299 CpsrString (SystemContext.SystemContextArm->CPSR, CpsrStr);
300 DEBUG ((EFI_D_ERROR, "%a\n", CpsrStr));
301
302 Pdb = GetImageName (SystemContext.SystemContextArm->PC, &ImageBase, &PeCoffSizeOfHeader);
303 Offset = SystemContext.SystemContextArm->PC - ImageBase;
304 if (Pdb != NULL) {
305 DEBUG ((EFI_D_ERROR, "%a\n", Pdb));
306
307 //
308 // A PE/COFF image loads its headers into memory so the headers are
309 // included in the linked addressess. ELF and Mach-O images do not
310 // include the headers so the first byte of the image is usually
311 // text (code). If you look at link maps from ELF or Mach-O images
312 // you need to subtact out the size of the PE/COFF header to get
313 // get the offset that matches the link map.
314 //
315 DEBUG ((EFI_D_ERROR, "loadded at 0x%08x (PE/COFF offset) 0x%x (ELF or Mach-O offset) 0x%x", ImageBase, Offset, Offset - PeCoffSizeOfHeader));
316 }
317 DEBUG_CODE_END ();
318 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));
319 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));
320 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));
321 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));
322 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));
323
324 ASSERT (FALSE);
325 }
326
327
328
329 EFI_STATUS
330 InitializeExceptions (
331 IN EFI_CPU_ARCH_PROTOCOL *Cpu
332 )
333 {
334 EFI_STATUS Status;
335 UINTN Offset;
336 UINTN Length;
337 UINTN Index;
338 BOOLEAN Enabled;
339 EFI_PHYSICAL_ADDRESS Base;
340
341 Status = EfiGetSystemConfigurationTable (&gEfiDebugImageInfoTableGuid, (VOID **)&gDebugImageTableHeader);
342 if (EFI_ERROR (Status)) {
343 gDebugImageTableHeader = NULL;
344 }
345
346 //
347 // Disable interrupts
348 //
349 Cpu->GetInterruptState (Cpu, &Enabled);
350 Cpu->DisableInterrupt (Cpu);
351
352 //
353 // Initialize the C entry points for interrupts
354 //
355 for (Index = 0; Index <= MAX_ARM_EXCEPTION; Index++) {
356 Status = RegisterInterruptHandler (Index, NULL);
357 ASSERT_EFI_ERROR (Status);
358
359 Status = RegisterDebuggerInterruptHandler (Index, NULL);
360 ASSERT_EFI_ERROR (Status);
361 }
362
363 //
364 // Copy an implementation of the ARM exception vectors to PcdCpuVectorBaseAddress.
365 //
366 Length = (UINTN)ExceptionHandlersEnd - (UINTN)ExceptionHandlersStart;
367
368 //
369 // Reserve space for the exception handlers
370 //
371 Base = (EFI_PHYSICAL_ADDRESS)PcdGet32 (PcdCpuVectorBaseAddress);
372 Status = gBS->AllocatePages (AllocateAddress, EfiBootServicesCode, EFI_SIZE_TO_PAGES (Length), &Base);
373 // If the request was for memory that's not in the memory map (which is often the case for 0x00000000
374 // on embedded systems, for example, we don't want to hang up. So we'll check here for a status of
375 // EFI_NOT_FOUND, and continue in that case.
376 if (EFI_ERROR(Status) && (Status != EFI_NOT_FOUND)) {
377 ASSERT_EFI_ERROR (Status);
378 }
379
380 CopyMem ((VOID *)(UINTN)PcdGet32 (PcdCpuVectorBaseAddress), (VOID *)ExceptionHandlersStart, Length);
381
382 //
383 // Patch in the common Assembly exception handler
384 //
385 Offset = (UINTN)CommonExceptionEntry - (UINTN)ExceptionHandlersStart;
386 *(UINTN *) ((UINT8 *)(UINTN)PcdGet32 (PcdCpuVectorBaseAddress) + Offset) = (UINTN)AsmCommonExceptionEntry;
387
388 // Flush Caches since we updated executable stuff
389 InvalidateInstructionCacheRange ((VOID *)PcdGet32(PcdCpuVectorBaseAddress), Length);
390
391 if (Enabled) {
392 //
393 // Restore interrupt state
394 //
395 Status = Cpu->EnableInterrupt (Cpu);
396 }
397
398 return Status;
399 }