/** @file
CPU Exception Library provides PEI/DXE/SMM CPU common exception handler.
Copyright (c) 2012 - 2022, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#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
)
{
EFI_STATUS Status;
EXCEPTION_HANDLER_CONTEXT *ExceptionHandlerContext;
RESERVED_VECTORS_DATA *ReservedVectors;
EFI_CPU_INTERRUPT_HANDLER *ExternalInterruptHandler;
switch (ExceptionType) {
case VC_EXCEPTION:
//
// #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 = CcExitHandleVc (&ExceptionType, SystemContext);
if (!EFI_ERROR (Status)) {
return;
}
break;
case VE_EXCEPTION:
//
// #VE needs to be handled immediately upon enabling exception handling
// and therefore can't use the RegisterCpuInterruptHandler() interface.
//
// Handle the #VE:
// On EFI_SUCCESS - Exception has been handled, return
// On other - ExceptionType contains (possibly new) exception
// value
//
Status = CcExitHandleVe (&ExceptionType, SystemContext);
if (!EFI_ERROR (Status)) {
return;
}
break;
default:
break;
}
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) * ExceptionHandlerData->IdtEntryCount, 0xff);
if (VectorInfo != NULL) {
Status = ReadAndVerifyVectorInfo (VectorInfo, ReservedVectors, ExceptionHandlerData->IdtEntryCount);
if (EFI_ERROR (Status)) {
return EFI_INVALID_PARAMETER;
}
}
//
// Setup the exception handlers according to IDT size, but no more than
// ExceptionHandlerData->IdtEntryCount (32 in PEI and SMM, 256 in DXE) handlers.
//
AsmReadIdtr (&IdtDescriptor);
IdtEntryCount = (IdtDescriptor.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR);
ExceptionHandlerData->IdtEntryCount = MIN (IdtEntryCount, ExceptionHandlerData->IdtEntryCount);
IdtTable = (IA32_IDT_GATE_DESCRIPTOR *)IdtDescriptor.Base;
AsmGetTemplateAddressMap (&TemplateMap);
ASSERT (TemplateMap.ExceptionStubHeaderSize <= HOOKAFTER_STUB_SIZE);
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;
}