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