diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c new file mode 100644 index 00000000..a0a0860e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/Library/CpuExceptionHandlerLib/PeiDxeSmmCpuException.c @@ -0,0 +1,314 @@ +/** @file + CPU Exception Library provides PEI/DXE/SMM CPU common exception handler. + +Copyright (c) 2012 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Library/DebugLib.h> +#include <Library/VmgExitLib.h> +#include "CpuExceptionCommon.h" + +/** + Internal worker function for common exception handler. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. + @param ExceptionHandlerData Pointer to exception handler data. +**/ +VOID +CommonExceptionHandlerWorker ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext, + IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData + ) +{ + EXCEPTION_HANDLER_CONTEXT *ExceptionHandlerContext; + RESERVED_VECTORS_DATA *ReservedVectors; + EFI_CPU_INTERRUPT_HANDLER *ExternalInterruptHandler; + + if (ExceptionType == VC_EXCEPTION) { + EFI_STATUS Status; + // + // #VC needs to be handled immediately upon enabling exception handling + // and therefore can't use the RegisterCpuInterruptHandler() interface. + // + // Handle the #VC: + // On EFI_SUCCESS - Exception has been handled, return + // On other - ExceptionType contains (possibly new) exception + // value + // + Status = VmgExitHandleVc (&ExceptionType, SystemContext); + if (!EFI_ERROR (Status)) { + return; + } + } + + ExceptionHandlerContext = (EXCEPTION_HANDLER_CONTEXT *) (UINTN) (SystemContext.SystemContextIa32); + ReservedVectors = ExceptionHandlerData->ReservedVectors; + ExternalInterruptHandler = ExceptionHandlerData->ExternalInterruptHandler; + + switch (ReservedVectors[ExceptionType].Attribute) { + case EFI_VECTOR_HANDOFF_HOOK_BEFORE: + // + // The new exception handler registered by RegisterCpuInterruptHandler() is executed BEFORE original handler. + // Save the original handler to stack so the assembly code can jump to it instead of returning from handler. + // + ExceptionHandlerContext->ExceptionDataFlag = (mErrorCodeFlag & (1 << ExceptionType)) ? TRUE : FALSE; + ExceptionHandlerContext->OldIdtHandler = ReservedVectors[ExceptionType].ExceptonHandler; + break; + case EFI_VECTOR_HANDOFF_HOOK_AFTER: + while (TRUE) { + // + // If spin-lock can be acquired, it's the first time entering here. + // + if (AcquireSpinLockOrFail (&ReservedVectors[ExceptionType].SpinLock)) { + // + // The new exception handler registered by RegisterCpuInterruptHandler() is executed AFTER original handler. + // Save the original handler to stack but skip running the new handler so the original handler is executed + // firstly. + // + ReservedVectors[ExceptionType].ApicId = GetApicId (); + ArchSaveExceptionContext (ExceptionType, SystemContext, ExceptionHandlerData); + ExceptionHandlerContext->ExceptionDataFlag = (mErrorCodeFlag & (1 << ExceptionType)) ? TRUE : FALSE; + ExceptionHandlerContext->OldIdtHandler = ReservedVectors[ExceptionType].ExceptonHandler; + return; + } + // + // If spin-lock cannot be acquired, it's the second time entering here. + // 'break' instead of 'return' is used so the new exception handler can be executed. + // + if (ReservedVectors[ExceptionType].ApicId == GetApicId ()) { + // + // Old IDT handler has been executed, then restore CPU exception content to + // run new exception handler. + // + ArchRestoreExceptionContext (ExceptionType, SystemContext, ExceptionHandlerData); + // + // Release spin lock for ApicId + // + ReleaseSpinLock (&ReservedVectors[ExceptionType].SpinLock); + break; + } + CpuPause (); + } + break; + case 0xffffffff: + break; + default: + // + // It should never reach here + // + CpuDeadLoop (); + break; + } + + if (ExternalInterruptHandler != NULL && + ExternalInterruptHandler[ExceptionType] != NULL) { + (ExternalInterruptHandler[ExceptionType]) (ExceptionType, SystemContext); + } else if (ExceptionType < CPU_EXCEPTION_NUM) { + // + // Get Spinlock to display CPU information + // + while (!AcquireSpinLockOrFail (&ExceptionHandlerData->DisplayMessageSpinLock)) { + CpuPause (); + } + // + // Initialize the serial port before dumping. + // + SerialPortInitialize (); + // + // Display ExceptionType, CPU information and Image information + // + DumpImageAndCpuContent (ExceptionType, SystemContext); + // + // Release Spinlock of output message + // + ReleaseSpinLock (&ExceptionHandlerData->DisplayMessageSpinLock); + // + // Enter a dead loop if needn't to execute old IDT handler further + // + if (ReservedVectors[ExceptionType].Attribute != EFI_VECTOR_HANDOFF_HOOK_BEFORE) { + CpuDeadLoop (); + } + } +} + +/** + Internal worker function to update IDT entries accordling to vector attributes. + + @param[in] IdtTable Pointer to IDT table. + @param[in] TemplateMap Pointer to a buffer where the address map is + returned. + @param[in] ExceptionHandlerData Pointer to exception handler data. + +**/ +VOID +UpdateIdtTable ( + IN IA32_IDT_GATE_DESCRIPTOR *IdtTable, + IN EXCEPTION_HANDLER_TEMPLATE_MAP *TemplateMap, + IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData + ) +{ + UINT16 CodeSegment; + UINTN Index; + UINTN InterruptHandler; + RESERVED_VECTORS_DATA *ReservedVectors; + + ReservedVectors = ExceptionHandlerData->ReservedVectors; + // + // Use current CS as the segment selector of interrupt gate in IDT + // + CodeSegment = AsmReadCs (); + + for (Index = 0; Index < ExceptionHandlerData->IdtEntryCount; Index ++) { + IdtTable[Index].Bits.Selector = CodeSegment; + // + // Check reserved vectors attributes + // + switch (ReservedVectors[Index].Attribute) { + case EFI_VECTOR_HANDOFF_DO_NOT_HOOK: + // + // Keep original IDT entry + // + continue; + case EFI_VECTOR_HANDOFF_HOOK_AFTER: + InitializeSpinLock (&ReservedVectors[Index].SpinLock); + CopyMem ( + (VOID *) ReservedVectors[Index].HookAfterStubHeaderCode, + (VOID *) TemplateMap->HookAfterStubHeaderStart, + TemplateMap->ExceptionStubHeaderSize + ); + AsmVectorNumFixup ( + (VOID *) ReservedVectors[Index].HookAfterStubHeaderCode, + (UINT8) Index, + (VOID *) TemplateMap->HookAfterStubHeaderStart + ); + // + // Go on the following code + // + case EFI_VECTOR_HANDOFF_HOOK_BEFORE: + // + // Save original IDT handler address + // + ReservedVectors[Index].ExceptonHandler = ArchGetIdtHandler (&IdtTable[Index]); + // + // Go on the following code + // + default: + // + // Update new IDT entry + // + InterruptHandler = TemplateMap->ExceptionStart + Index * TemplateMap->ExceptionStubHeaderSize; + ArchUpdateIdtEntry (&IdtTable[Index], InterruptHandler); + break; + } + } +} + +/** + Internal worker function to initialize exception handler. + + @param[in] VectorInfo Pointer to reserved vector list. + @param[in, out] ExceptionHandlerData Pointer to exception handler data. + + @retval EFI_SUCCESS CPU Exception Entries have been successfully initialized + with default exception handlers. + @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL. + @retval EFI_UNSUPPORTED This function is not supported. + +**/ +EFI_STATUS +InitializeCpuExceptionHandlersWorker ( + IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL, + IN OUT EXCEPTION_HANDLER_DATA *ExceptionHandlerData + ) +{ + EFI_STATUS Status; + IA32_DESCRIPTOR IdtDescriptor; + UINTN IdtEntryCount; + EXCEPTION_HANDLER_TEMPLATE_MAP TemplateMap; + IA32_IDT_GATE_DESCRIPTOR *IdtTable; + RESERVED_VECTORS_DATA *ReservedVectors; + + ReservedVectors = ExceptionHandlerData->ReservedVectors; + SetMem ((VOID *) ReservedVectors, sizeof (RESERVED_VECTORS_DATA) * CPU_EXCEPTION_NUM, 0xff); + if (VectorInfo != NULL) { + Status = ReadAndVerifyVectorInfo (VectorInfo, ReservedVectors, CPU_EXCEPTION_NUM); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + } + + // + // Read IDT descriptor and calculate IDT size + // + AsmReadIdtr (&IdtDescriptor); + IdtEntryCount = (IdtDescriptor.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR); + if (IdtEntryCount > CPU_EXCEPTION_NUM) { + // + // CPU exception library only setup CPU_EXCEPTION_NUM exception handler at most + // + IdtEntryCount = CPU_EXCEPTION_NUM; + } + + IdtTable = (IA32_IDT_GATE_DESCRIPTOR *) IdtDescriptor.Base; + AsmGetTemplateAddressMap (&TemplateMap); + ASSERT (TemplateMap.ExceptionStubHeaderSize <= HOOKAFTER_STUB_SIZE); + + ExceptionHandlerData->IdtEntryCount = IdtEntryCount; + UpdateIdtTable (IdtTable, &TemplateMap, ExceptionHandlerData); + + return EFI_SUCCESS; +} + +/** + Registers a function to be called from the processor interrupt handler. + + @param[in] InterruptType Defines which interrupt or exception to hook. + @param[in] InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called + when a processor interrupt occurs. If this parameter is NULL, then the handler + will be uninstalled + @param[in] ExceptionHandlerData Pointer to exception handler data. + + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled. + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was + previously installed. + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not + previously installed. + @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported, + or this function is not supported. +**/ +EFI_STATUS +RegisterCpuInterruptHandlerWorker ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler, + IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData + ) +{ + UINTN EnabledInterruptNum; + RESERVED_VECTORS_DATA *ReservedVectors; + EFI_CPU_INTERRUPT_HANDLER *ExternalInterruptHandler; + + EnabledInterruptNum = ExceptionHandlerData->IdtEntryCount; + ReservedVectors = ExceptionHandlerData->ReservedVectors; + ExternalInterruptHandler = ExceptionHandlerData->ExternalInterruptHandler; + + if (InterruptType < 0 || InterruptType >= (EFI_EXCEPTION_TYPE)EnabledInterruptNum || + ReservedVectors[InterruptType].Attribute == EFI_VECTOR_HANDOFF_DO_NOT_HOOK) { + return EFI_UNSUPPORTED; + } + + if (InterruptHandler == NULL && ExternalInterruptHandler[InterruptType] == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (InterruptHandler != NULL && ExternalInterruptHandler[InterruptType] != NULL) { + return EFI_ALREADY_STARTED; + } + + ExternalInterruptHandler[InterruptType] = InterruptHandler; + return EFI_SUCCESS; +} + |