+/** @file\r
+ CPU Exception Library provides PEI/DXE/SMM CPU common exception handler.\r
+\r
+Copyright (c) 2012 - 2016, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials are licensed and made available under\r
+the terms and conditions of the BSD License that accompanies this distribution.\r
+The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php.\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
+\r
+**/\r
+\r
+#include "CpuExceptionCommon.h"\r
+#include <Library/DebugLib.h>\r
+\r
+//\r
+// Spinlock for CPU information display\r
+//\r
+SPIN_LOCK mDisplayMessageSpinLock;\r
+\r
+//\r
+// Image align size for DXE/SMM\r
+//\r
+CONST UINTN mImageAlignSize = SIZE_4KB;\r
+\r
+RESERVED_VECTORS_DATA mReservedVectorsData[CPU_EXCEPTION_NUM];\r
+EFI_CPU_INTERRUPT_HANDLER mExternalInterruptHandlerTable[CPU_EXCEPTION_NUM];\r
+EFI_CPU_INTERRUPT_HANDLER *mExternalInterruptHandler = NULL;\r
+UINTN mEnabledInterruptNum = 0;\r
+\r
+/**\r
+ Common exception handler.\r
+\r
+ @param ExceptionType Exception type.\r
+ @param SystemContext Pointer to EFI_SYSTEM_CONTEXT.\r
+**/\r
+VOID\r
+EFIAPI\r
+CommonExceptionHandler (\r
+ IN EFI_EXCEPTION_TYPE ExceptionType, \r
+ IN EFI_SYSTEM_CONTEXT SystemContext\r
+ )\r
+{\r
+ EXCEPTION_HANDLER_CONTEXT *ExceptionHandlerContext;\r
+\r
+ ExceptionHandlerContext = (EXCEPTION_HANDLER_CONTEXT *) (UINTN) (SystemContext.SystemContextIa32);\r
+\r
+ switch (mReservedVectors[ExceptionType].Attribute) {\r
+ case EFI_VECTOR_HANDOFF_HOOK_BEFORE:\r
+ //\r
+ // Need to jmp to old IDT handler after this exception handler\r
+ //\r
+ ExceptionHandlerContext->ExceptionDataFlag = (mErrorCodeFlag & (1 << ExceptionType)) ? TRUE : FALSE;\r
+ ExceptionHandlerContext->OldIdtHandler = mReservedVectors[ExceptionType].ExceptonHandler;\r
+ break;\r
+ case EFI_VECTOR_HANDOFF_HOOK_AFTER:\r
+ while (TRUE) {\r
+ //\r
+ // If if anyone has gotten SPIN_LOCK for owner running hook after\r
+ //\r
+ if (AcquireSpinLockOrFail (&mReservedVectors[ExceptionType].SpinLock)) {\r
+ //\r
+ // Need to execute old IDT handler before running this exception handler\r
+ //\r
+ mReservedVectors[ExceptionType].ApicId = GetApicId ();\r
+ ArchSaveExceptionContext (ExceptionType, SystemContext);\r
+ ExceptionHandlerContext->ExceptionDataFlag = (mErrorCodeFlag & (1 << ExceptionType)) ? TRUE : FALSE;\r
+ ExceptionHandlerContext->OldIdtHandler = mReservedVectors[ExceptionType].ExceptonHandler;\r
+ return;\r
+ }\r
+ //\r
+ // If failed to acquire SPIN_LOCK, check if it was locked by processor itself\r
+ //\r
+ if (mReservedVectors[ExceptionType].ApicId == GetApicId ()) {\r
+ //\r
+ // Old IDT handler has been executed, then retore CPU exception content to\r
+ // run new exception handler.\r
+ //\r
+ ArchRestoreExceptionContext (ExceptionType, SystemContext);\r
+ //\r
+ // Rlease spin lock for ApicId\r
+ //\r
+ ReleaseSpinLock (&mReservedVectors[ExceptionType].SpinLock);\r
+ break;\r
+ }\r
+ CpuPause ();\r
+ }\r
+ break;\r
+ case 0xffffffff:\r
+ break;\r
+ default:\r
+ //\r
+ // It should never reach here\r
+ //\r
+ CpuDeadLoop ();\r
+ break;\r
+ }\r
+ \r
+ if (mExternalInterruptHandler[ExceptionType] != NULL) {\r
+ (mExternalInterruptHandler[ExceptionType]) (ExceptionType, SystemContext);\r
+ } else if (ExceptionType < CPU_EXCEPTION_NUM) {\r
+ //\r
+ // Get Spinlock to display CPU information\r
+ //\r
+ while (!AcquireSpinLockOrFail (&mDisplayMessageSpinLock)) {\r
+ CpuPause ();\r
+ }\r
+ //\r
+ // Display ExceptionType, CPU information and Image information\r
+ // \r
+ DumpCpuContent (ExceptionType, SystemContext);\r
+ //\r
+ // Release Spinlock of output message\r
+ //\r
+ ReleaseSpinLock (&mDisplayMessageSpinLock);\r
+ //\r
+ // Enter a dead loop if needn't to execute old IDT handler further\r
+ //\r
+ if (mReservedVectors[ExceptionType].Attribute != EFI_VECTOR_HANDOFF_HOOK_BEFORE) {\r
+ CpuDeadLoop ();\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Internal worker function to update IDT entries accordling to vector attributes.\r
+\r
+ @param[in] IdtTable Pointer to IDT table.\r
+ @param[in] TemplateMap Pointer to a buffer where the address map is returned.\r
+ @param[in] IdtEntryCount IDT entries number to be updated.\r
+\r
+**/\r
+VOID\r
+UpdateIdtTable (\r
+ IN IA32_IDT_GATE_DESCRIPTOR *IdtTable,\r
+ IN EXCEPTION_HANDLER_TEMPLATE_MAP *TemplateMap,\r
+ IN UINTN IdtEntryCount\r
+ )\r
+{\r
+ UINT16 CodeSegment;\r
+ UINTN Index;\r
+ UINTN InterruptHandler;\r
+\r
+ //\r
+ // Use current CS as the segment selector of interrupt gate in IDT\r
+ //\r
+ CodeSegment = AsmReadCs ();\r
+\r
+ for (Index = 0; Index < IdtEntryCount; Index ++) {\r
+ IdtTable[Index].Bits.Selector = CodeSegment;\r
+ //\r
+ // Check reserved vectors attributes\r
+ //\r
+ switch (mReservedVectors[Index].Attribute) {\r
+ case EFI_VECTOR_HANDOFF_DO_NOT_HOOK:\r
+ //\r
+ // Keep original IDT entry\r
+ //\r
+ continue;\r
+ case EFI_VECTOR_HANDOFF_HOOK_AFTER:\r
+ InitializeSpinLock (&mReservedVectors[Index].SpinLock);\r
+ CopyMem (\r
+ (VOID *) mReservedVectors[Index].HookAfterStubHeaderCode,\r
+ (VOID *) TemplateMap->HookAfterStubHeaderStart,\r
+ TemplateMap->ExceptionStubHeaderSize\r
+ );\r
+ AsmVectorNumFixup (\r
+ (VOID *) mReservedVectors[Index].HookAfterStubHeaderCode,\r
+ (UINT8) Index,\r
+ (VOID *) TemplateMap->HookAfterStubHeaderStart\r
+ );\r
+ //\r
+ // Go on the following code\r
+ //\r
+ case EFI_VECTOR_HANDOFF_HOOK_BEFORE:\r
+ //\r
+ // Save original IDT handler address\r
+ //\r
+ mReservedVectors[Index].ExceptonHandler = ArchGetIdtHandler (&IdtTable[Index]);\r
+ //\r
+ // Go on the following code\r
+ //\r
+ default:\r
+ //\r
+ // Update new IDT entry\r
+ //\r
+ InterruptHandler = TemplateMap->ExceptionStart + Index * TemplateMap->ExceptionStubHeaderSize;\r
+ ArchUpdateIdtEntry (&IdtTable[Index], InterruptHandler);\r
+ break;\r
+ }\r
+ }\r
+ \r
+ //\r
+ // Save Interrupt number to global variable used for RegisterCpuInterruptHandler ()\r
+ //\r
+ mEnabledInterruptNum = IdtEntryCount;\r
+}\r
+\r
+/**\r
+ Internal worker function to initialize exception handler.\r
+\r
+ @param[in] VectorInfo Pointer to reserved vector list.\r
+ \r
+ @retval EFI_SUCCESS CPU Exception Entries have been successfully initialized \r
+ with default exception handlers.\r
+ @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL.\r
+ @retval EFI_UNSUPPORTED This function is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+InitializeCpuExceptionHandlersWorker (\r
+ IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ IA32_DESCRIPTOR IdtDescriptor;\r
+ UINTN IdtEntryCount;\r
+ EXCEPTION_HANDLER_TEMPLATE_MAP TemplateMap;\r
+ IA32_IDT_GATE_DESCRIPTOR *IdtTable;\r
+\r
+ mReservedVectors = mReservedVectorsData;\r
+ SetMem ((VOID *) mReservedVectors, sizeof (RESERVED_VECTORS_DATA) * CPU_EXCEPTION_NUM, 0xff);\r
+ if (VectorInfo != NULL) {\r
+ Status = ReadAndVerifyVectorInfo (VectorInfo, mReservedVectors, CPU_EXCEPTION_NUM);\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+ InitializeSpinLock (&mDisplayMessageSpinLock);\r
+\r
+ mExternalInterruptHandler = mExternalInterruptHandlerTable;\r
+ //\r
+ // Read IDT descriptor and calculate IDT size\r
+ //\r
+ AsmReadIdtr (&IdtDescriptor);\r
+ IdtEntryCount = (IdtDescriptor.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR);\r
+ if (IdtEntryCount > CPU_EXCEPTION_NUM) {\r
+ //\r
+ // CPU exeption library only setup CPU_EXCEPTION_NUM exception handler at most\r
+ //\r
+ IdtEntryCount = CPU_EXCEPTION_NUM;\r
+ }\r
+\r
+ IdtTable = (IA32_IDT_GATE_DESCRIPTOR *) IdtDescriptor.Base;\r
+ AsmGetTemplateAddressMap (&TemplateMap);\r
+ ASSERT (TemplateMap.ExceptionStubHeaderSize <= HOOKAFTER_STUB_SIZE);\r
+ UpdateIdtTable (IdtTable, &TemplateMap, IdtEntryCount);\r
+ mEnabledInterruptNum = IdtEntryCount;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Registers a function to be called from the processor interrupt handler.\r
+\r
+ @param[in] InterruptType Defines which interrupt or exception to hook.\r
+ @param[in] 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
+ or this function is not supported.\r
+**/\r
+EFI_STATUS\r
+RegisterCpuInterruptHandlerWorker (\r
+ IN EFI_EXCEPTION_TYPE InterruptType,\r
+ IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler\r
+ )\r
+{\r
+ if (InterruptType < 0 || InterruptType >= (EFI_EXCEPTION_TYPE)mEnabledInterruptNum ||\r
+ mReservedVectors[InterruptType].Attribute == EFI_VECTOR_HANDOFF_DO_NOT_HOOK) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ if (InterruptHandler == NULL && mExternalInterruptHandler[InterruptType] == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (InterruptHandler != NULL && mExternalInterruptHandler[InterruptType] != NULL) {\r
+ return EFI_ALREADY_STARTED;\r
+ }\r
+\r
+ mExternalInterruptHandler[InterruptType] = InterruptHandler;\r
+ return EFI_SUCCESS;\r
+}\r
+\r