/** @file\r
\r
- Copyright (c) 2008-2009, Apple Inc. All rights reserved.\r
- \r
- All rights reserved. This program and the accompanying materials\r
- are licensed and made available under the terms and conditions of the BSD License\r
- which accompanies this distribution. The full text of the license may be found at\r
- http://opensource.org/licenses/bsd-license.php\r
+ Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>\r
+ Portions Copyright (c) 2011 - 2021, Arm Limited. All rights reserved.<BR>\r
\r
- THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
- WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\r
-#include "CpuDxe.h" \r
+#include "CpuDxe.h"\r
+#include <Library/CpuExceptionHandlerLib.h>\r
+#include <Guid/VectorHandoffTable.h>\r
\r
-EFI_DEBUG_IMAGE_INFO_TABLE_HEADER *gDebugImageTableHeader = NULL;
-\r
-VOID\r
-ExceptionHandlersStart (\r
- VOID\r
- );\r
-\r
-VOID\r
-ExceptionHandlersEnd (\r
- VOID\r
- );\r
-\r
-VOID\r
-CommonExceptionEntry (\r
- VOID\r
- );\r
-\r
-VOID\r
-AsmCommonExceptionEntry (\r
- VOID\r
- );\r
-\r
-\r
-EFI_EXCEPTION_CALLBACK gExceptionHandlers[MAX_ARM_EXCEPTION + 1];\r
-EFI_EXCEPTION_CALLBACK gDebuggerExceptionHandlers[MAX_ARM_EXCEPTION + 1];\r
-\r
-\r
-\r
-/**\r
- This function registers and enables the handler specified by InterruptHandler for a processor \r
- interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the \r
- handler for the processor interrupt or exception type specified by InterruptType is uninstalled. \r
- The installed handler is called once for each processor interrupt or exception.\r
-\r
- @param InterruptType A pointer to the processor's current interrupt state. Set to TRUE if interrupts\r
- are enabled and FALSE if interrupts are disabled.\r
- @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called\r
- when a processor interrupt occurs. If this parameter is NULL, then the handler\r
- will be uninstalled.\r
-\r
- @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.\r
- @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was\r
- previously installed.\r
- @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not\r
- previously installed.\r
- @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported.\r
-\r
-**/\r
-EFI_STATUS\r
-RegisterInterruptHandler (\r
- IN EFI_EXCEPTION_TYPE InterruptType,\r
- IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler\r
- )\r
-{\r
- if (InterruptType > MAX_ARM_EXCEPTION) {\r
- return EFI_UNSUPPORTED;\r
- }\r
-\r
- if ((InterruptHandler != NULL) && (gExceptionHandlers[InterruptType] != NULL)) {\r
- return EFI_ALREADY_STARTED;\r
- }\r
-\r
- gExceptionHandlers[InterruptType] = InterruptHandler;\r
-\r
- return EFI_SUCCESS;\r
-}\r
-\r
-\r
-/**\r
- This function registers and enables the handler specified by InterruptHandler for a processor \r
- interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the \r
- handler for the processor interrupt or exception type specified by InterruptType is uninstalled. \r
- The installed handler is called once for each processor interrupt or exception.\r
-\r
- @param InterruptType A pointer to the processor's current interrupt state. Set to TRUE if interrupts\r
- are enabled and FALSE if interrupts are disabled.\r
- @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called\r
- when a processor interrupt occurs. If this parameter is NULL, then the handler\r
- will be uninstalled.\r
-\r
- @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.\r
- @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was\r
- previously installed.\r
- @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not\r
- previously installed.\r
- @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported.\r
-\r
-**/\r
EFI_STATUS\r
-RegisterDebuggerInterruptHandler (\r
- IN EFI_EXCEPTION_TYPE InterruptType,\r
- IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler\r
- )\r
-{\r
- if (InterruptType > MAX_ARM_EXCEPTION) {\r
- return EFI_UNSUPPORTED;\r
- }\r
-\r
- if ((InterruptHandler != NULL) && (gDebuggerExceptionHandlers[InterruptType] != NULL)) {\r
- return EFI_ALREADY_STARTED;\r
- }\r
-\r
- gDebuggerExceptionHandlers[InterruptType] = InterruptHandler;\r
-\r
- return EFI_SUCCESS;\r
-}\r
-\r
-\r
-UINT32
-EFIAPI
-PeCoffGetSizeOfHeaders (
- IN VOID *Pe32Data
- )
-{
- EFI_IMAGE_DOS_HEADER *DosHdr;
- EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
- UINTN SizeOfHeaders;
-
- DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;
- if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
- //
- // DOS image header is present, so read the PE header after the DOS image header.
- //
- Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));
- } else {
- //
- // DOS image header is not present, so PE header is at the image base.
- //
- Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;
- }
-
- if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {
- SizeOfHeaders = sizeof (EFI_TE_IMAGE_HEADER) + (UINTN)Hdr.Te->BaseOfCode - (UINTN)Hdr.Te->StrippedSize;
- } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {
- SizeOfHeaders = Hdr.Pe32->OptionalHeader.SizeOfHeaders;
- }
-
- return SizeOfHeaders;
-}\r
-\r
-\r
-CHAR8 *\r
-GetImageName (\r
- IN UINT32 FaultAddress,\r
- OUT UINT32 *ImageBase,\r
- OUT UINT32 *PeCoffSizeOfHeaders\r
+InitializeExceptions (\r
+ IN EFI_CPU_ARCH_PROTOCOL *Cpu\r
)\r
{\r
- EFI_DEBUG_IMAGE_INFO *DebugTable;
- UINTN Entry;
- CHAR8 *Address;
-
- \r
- DebugTable = gDebugImageTableHeader->EfiDebugImageInfoTable;\r
- if (DebugTable == NULL) {\r
- return NULL;\r
+ EFI_STATUS Status;\r
+ EFI_VECTOR_HANDOFF_INFO *VectorInfoList;\r
+ EFI_VECTOR_HANDOFF_INFO *VectorInfo;\r
+ BOOLEAN IrqEnabled;\r
+ BOOLEAN FiqEnabled;\r
+\r
+ VectorInfo = (EFI_VECTOR_HANDOFF_INFO *)NULL;\r
+ Status = EfiGetSystemConfigurationTable (&gEfiVectorHandoffTableGuid, (VOID **)&VectorInfoList);\r
+ if ((Status == EFI_SUCCESS) && (VectorInfoList != NULL)) {\r
+ VectorInfo = VectorInfoList;\r
}\r
\r
- Address = (CHAR8 *)(UINTN)FaultAddress;\r
- for (Entry = 0; Entry < gDebugImageTableHeader->TableSize; Entry++, DebugTable++) {
- if (DebugTable->NormalImage != NULL) {
- if ((DebugTable->NormalImage->ImageInfoType == EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL) &&
- (DebugTable->NormalImage->LoadedImageProtocolInstance != NULL)) {
- if ((Address >= DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase) &&
- (Address <= ((CHAR8 *)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase + DebugTable->NormalImage->LoadedImageProtocolInstance->ImageSize))) {
- *ImageBase = (UINT32)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase;
- *PeCoffSizeOfHeaders = PeCoffGetSizeOfHeaders ((VOID *)(UINTN)*ImageBase);
- return PeCoffLoaderGetPdbPointer (DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase);
- }
- }
- }
- }\r
+ // initialize the CpuExceptionHandlerLib so we take over the exception vector table from the DXE Core\r
+ InitializeCpuExceptionHandlers (VectorInfo);\r
\r
- return NULL;\r
-}\r
+ Status = EFI_SUCCESS;\r
\r
+ //\r
+ // Disable interrupts\r
+ //\r
+ Cpu->GetInterruptState (Cpu, &IrqEnabled);\r
+ Cpu->DisableInterrupt (Cpu);\r
\r
-CHAR8 *gExceptionTypeString[] = {\r
- "Reset",\r
- "Undefined Instruction",\r
- "SWI",\r
- "Prefetch Abort",\r
- "Data Abort",\r
- "Undefined",\r
- "IRQ",\r
- "FIQ"\r
-};\r
+ //\r
+ // EFI does not use the FIQ, but a debugger might so we must disable\r
+ // as we take over the exception vectors.\r
+ //\r
+ FiqEnabled = ArmGetFiqState ();\r
+ ArmDisableFiq ();\r
\r
-VOID\r
-EFIAPI\r
-CommonCExceptionHandler (\r
- IN EFI_EXCEPTION_TYPE ExceptionType,\r
- IN OUT EFI_SYSTEM_CONTEXT SystemContext\r
- )\r
-{\r
- BOOLEAN Dispatched = FALSE;\r
- \r
- \r
- if (ExceptionType <= MAX_ARM_EXCEPTION) {\r
- if (gDebuggerExceptionHandlers[ExceptionType]) {\r
- //\r
- // If DebugSupport hooked the interrupt call the handler. This does not disable \r
- // the normal handler.\r
- //\r
- gDebuggerExceptionHandlers[ExceptionType] (ExceptionType, SystemContext);\r
- Dispatched = TRUE;\r
- }\r
- if (gExceptionHandlers[ExceptionType]) {\r
- gExceptionHandlers[ExceptionType] (ExceptionType, SystemContext);\r
- Dispatched = TRUE;\r
- }\r
- } else {\r
- DEBUG ((EFI_D_ERROR, "Unknown exception type %d from %08x\n", ExceptionType, SystemContext.SystemContextArm->PC));\r
- ASSERT (FALSE);\r
+ if (FiqEnabled) {\r
+ ArmEnableFiq ();\r
}\r
\r
- if (Dispatched) {\r
- //\r
- // We did work so this was an expected ExceptionType\r
- //\r
- return;\r
- }\r
- \r
- if (ExceptionType == EXCEPT_ARM_SOFTWARE_INTERRUPT) {\r
+ if (IrqEnabled) {\r
//\r
- // ARM JTAG debuggers some times use this vector, so it is not an error to get one\r
+ // Restore interrupt state\r
//\r
- return;\r
+ Status = Cpu->EnableInterrupt (Cpu);\r
}\r
\r
//\r
- // Code after here is the default exception handler... Dump the context\r
+ // On a DEBUG build, unmask SErrors so they are delivered right away rather\r
+ // than when the OS unmasks them. This gives us a better chance of figuring\r
+ // out the cause.\r
//\r
- 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
- DEBUG_CODE_BEGIN ();\r
- CHAR8 *Pdb;
- UINT32 ImageBase;
- UINT32 PeCoffSizeOfHeader;
- UINT32 Offset;
- \r
- Pdb = GetImageName (SystemContext.SystemContextArm->PC, &ImageBase, &PeCoffSizeOfHeader);\r
- Offset = SystemContext.SystemContextArm->PC - ImageBase;\r
- if (Pdb != NULL) {\r
- DEBUG ((EFI_D_ERROR, "%a\n", Pdb));\r
+ DEBUG_CODE (\r
+ ArmEnableAsynchronousAbort ();\r
+ );\r
\r
- //\r
- // A PE/COFF image loads its headers into memory so the headers are \r
- // included in the linked addressess. ELF and Mach-O images do not\r
- // include the headers so the first byte of the image is usually\r
- // text (code). If you look at link maps from ELF or Mach-O images\r
- // you need to subtact out the size of the PE/COFF header to get\r
- // get the offset that matches the link map. \r
- //\r
- 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
- }\r
- DEBUG_CODE_END ();\r
- 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
- 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
- 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
- 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
- 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
-\r
- ASSERT (FALSE);\r
-// while (TRUE) {\r
-// CpuSleep ();\r
-// }\r
+ return Status;\r
}\r
\r
+/**\r
+This function registers and enables the handler specified by InterruptHandler for a processor\r
+interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the\r
+handler for the processor interrupt or exception type specified by InterruptType is uninstalled.\r
+The installed handler is called once for each processor interrupt or exception.\r
+\r
+@param InterruptType A pointer to the processor's current interrupt state. Set to TRUE if interrupts\r
+are enabled and FALSE if interrupts are disabled.\r
+@param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called\r
+when a processor interrupt occurs. If this parameter is NULL, then the handler\r
+will be uninstalled.\r
+\r
+@retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.\r
+@retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was\r
+previously installed.\r
+@retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not\r
+previously installed.\r
+@retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported.\r
\r
-\r
+**/\r
EFI_STATUS\r
-InitializeExceptions (\r
- IN EFI_CPU_ARCH_PROTOCOL *Cpu\r
+RegisterInterruptHandler (\r
+ IN EFI_EXCEPTION_TYPE InterruptType,\r
+ IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler\r
)\r
{\r
- EFI_STATUS Status;\r
- UINTN Offset;\r
- UINTN Length;\r
- UINTN Index;\r
- BOOLEAN Enabled;\r
- EFI_PHYSICAL_ADDRESS Base;\r
-\r
- Status = EfiGetSystemConfigurationTable (&gEfiDebugImageInfoTableGuid, (VOID **)&gDebugImageTableHeader);
- if (EFI_ERROR (Status)) {
- gDebugImageTableHeader = NULL;
- }
-\r
- //\r
- // Disable interrupts\r
- //\r
- Cpu->GetInterruptState (Cpu, &Enabled);\r
- Cpu->DisableInterrupt (Cpu);\r
-\r
- //\r
- // Initialize the C entry points for interrupts\r
- //\r
- for (Index = 0; Index <= MAX_ARM_EXCEPTION; Index++) {\r
- Status = RegisterInterruptHandler (Index, NULL);\r
- ASSERT_EFI_ERROR (Status);\r
- \r
- Status = RegisterDebuggerInterruptHandler (Index, NULL);\r
- ASSERT_EFI_ERROR (Status);\r
- }\r
- \r
- //\r
- // Copy an implementation of the ARM exception vectors to PcdCpuVectorBaseAddress.\r
- //\r
- Length = (UINTN)ExceptionHandlersEnd - (UINTN)ExceptionHandlersStart;\r
-\r
- //\r
- // Reserve space for the exception handlers\r
- //\r
- Base = (EFI_PHYSICAL_ADDRESS)PcdGet32 (PcdCpuVectorBaseAddress);\r
- Status = gBS->AllocatePages (AllocateAddress, EfiBootServicesCode, EFI_SIZE_TO_PAGES (Length), &Base);\r
- // If the request was for memory that's not in the memory map (which is often the case for 0x00000000\r
- // on embedded systems, for example, we don't want to hang up. So we'll check here for a status of \r
- // EFI_NOT_FOUND, and continue in that case.\r
- if (EFI_ERROR(Status) && (Status != EFI_NOT_FOUND)) {\r
- ASSERT_EFI_ERROR (Status);\r
- }\r
-\r
- CopyMem ((VOID *)(UINTN)PcdGet32 (PcdCpuVectorBaseAddress), (VOID *)ExceptionHandlersStart, Length);\r
-\r
- //\r
- // Patch in the common Assembly exception handler\r
- //\r
- Offset = (UINTN)CommonExceptionEntry - (UINTN)ExceptionHandlersStart;\r
- *(UINTN *) ((UINT8 *)(UINTN)PcdGet32 (PcdCpuVectorBaseAddress) + Offset) = (UINTN)AsmCommonExceptionEntry;\r
-\r
- // Flush Caches since we updated executable stuff\r
- InvalidateInstructionCacheRange ((VOID *)PcdGet32(PcdCpuVectorBaseAddress), Length);\r
-\r
- if (Enabled) {\r
- // \r
- // Restore interrupt state\r
- //\r
- Status = Cpu->EnableInterrupt (Cpu);\r
- }\r
-\r
- return Status;\r
+ // pass down to CpuExceptionHandlerLib\r
+ return (EFI_STATUS)RegisterCpuInterruptHandler (InterruptType, InterruptHandler);\r
}\r