| /** @file | |
| IA32/x64 generic functions to support Debug Support protocol. | |
| Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "DebugSupport.h" | |
| // | |
| // This the global main table to keep track of the interrupts | |
| // | |
| IDT_ENTRY *IdtEntryTable = NULL; | |
| /** | |
| Read IDT Gate Descriptor from IDT Table. | |
| @param Vector Specifies vector number. | |
| @param IdtGateDescriptor Pointer to IDT Gate Descriptor read from IDT Table. | |
| **/ | |
| VOID | |
| ReadIdtGateDescriptor ( | |
| IN EFI_EXCEPTION_TYPE Vector, | |
| OUT IA32_IDT_GATE_DESCRIPTOR *IdtGateDescriptor | |
| ) | |
| { | |
| IA32_DESCRIPTOR IdtrValue; | |
| IA32_IDT_GATE_DESCRIPTOR *IdtTable; | |
| AsmReadIdtr (&IdtrValue); | |
| IdtTable = (IA32_IDT_GATE_DESCRIPTOR *)IdtrValue.Base; | |
| CopyMem ((VOID *)IdtGateDescriptor, (VOID *)&(IdtTable)[Vector], sizeof (IA32_IDT_GATE_DESCRIPTOR)); | |
| } | |
| /** | |
| Write IDT Gate Descriptor into IDT Table. | |
| @param Vector Specifies vector number. | |
| @param IdtGateDescriptor Pointer to IDT Gate Descriptor written into IDT Table. | |
| **/ | |
| VOID | |
| WriteIdtGateDescriptor ( | |
| EFI_EXCEPTION_TYPE Vector, | |
| IA32_IDT_GATE_DESCRIPTOR *IdtGateDescriptor | |
| ) | |
| { | |
| IA32_DESCRIPTOR IdtrValue; | |
| IA32_IDT_GATE_DESCRIPTOR *IdtTable; | |
| AsmReadIdtr (&IdtrValue); | |
| IdtTable = (IA32_IDT_GATE_DESCRIPTOR *)IdtrValue.Base; | |
| CopyMem ((VOID *)&(IdtTable)[Vector], (VOID *)IdtGateDescriptor, sizeof (IA32_IDT_GATE_DESCRIPTOR)); | |
| } | |
| /** | |
| Creates a nes entry stub. Then saves the current IDT entry and replaces it | |
| with an interrupt gate for the new entry point. The IdtEntryTable is updated | |
| with the new registered function. | |
| This code executes in boot services context. The stub entry executes in interrupt | |
| context. | |
| @param ExceptionType Specifies which vector to hook. | |
| @param NewCallback A pointer to the new function to be registered. | |
| **/ | |
| VOID | |
| HookEntry ( | |
| IN EFI_EXCEPTION_TYPE ExceptionType, | |
| IN CALLBACK_FUNC NewCallback | |
| ) | |
| { | |
| BOOLEAN OldIntFlagState; | |
| CreateEntryStub (ExceptionType, (VOID **)&IdtEntryTable[ExceptionType].StubEntry); | |
| // | |
| // Disables CPU interrupts and returns the previous interrupt state | |
| // | |
| OldIntFlagState = SaveAndDisableInterrupts (); | |
| // | |
| // gets IDT Gate descriptor by index | |
| // | |
| ReadIdtGateDescriptor (ExceptionType, &(IdtEntryTable[ExceptionType].OrigDesc)); | |
| // | |
| // stores orignal interrupt handle | |
| // | |
| IdtEntryTable[ExceptionType].OrigVector = (DEBUG_PROC)GetInterruptHandleFromIdt (&(IdtEntryTable[ExceptionType].OrigDesc)); | |
| // | |
| // encodes new IDT Gate descriptor by stub entry | |
| // | |
| Vect2Desc (&IdtEntryTable[ExceptionType].NewDesc, IdtEntryTable[ExceptionType].StubEntry); | |
| // | |
| // stores NewCallback | |
| // | |
| IdtEntryTable[ExceptionType].RegisteredCallback = NewCallback; | |
| // | |
| // writes back new IDT Gate descriptor | |
| // | |
| WriteIdtGateDescriptor (ExceptionType, &(IdtEntryTable[ExceptionType].NewDesc)); | |
| // | |
| // restore interrupt state | |
| // | |
| SetInterruptState (OldIntFlagState); | |
| return; | |
| } | |
| /** | |
| Undoes HookEntry. This code executes in boot services context. | |
| @param ExceptionType Specifies which entry to unhook | |
| **/ | |
| VOID | |
| UnhookEntry ( | |
| IN EFI_EXCEPTION_TYPE ExceptionType | |
| ) | |
| { | |
| BOOLEAN OldIntFlagState; | |
| // | |
| // Disables CPU interrupts and returns the previous interrupt state | |
| // | |
| OldIntFlagState = SaveAndDisableInterrupts (); | |
| // | |
| // restore the default IDT Date Descriptor | |
| // | |
| WriteIdtGateDescriptor (ExceptionType, &(IdtEntryTable[ExceptionType].OrigDesc)); | |
| // | |
| // restore interrupt state | |
| // | |
| SetInterruptState (OldIntFlagState); | |
| return; | |
| } | |
| /** | |
| Returns the maximum value that may be used for the ProcessorIndex parameter in | |
| RegisterPeriodicCallback() and RegisterExceptionCallback(). | |
| Hard coded to support only 1 processor for now. | |
| @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. | |
| @param MaxProcessorIndex Pointer to a caller-allocated UINTN in which the maximum supported | |
| processor index is returned. Always 0 returned. | |
| @retval EFI_SUCCESS Always returned with **MaxProcessorIndex set to 0. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| GetMaximumProcessorIndex ( | |
| IN EFI_DEBUG_SUPPORT_PROTOCOL *This, | |
| OUT UINTN *MaxProcessorIndex | |
| ) | |
| { | |
| *MaxProcessorIndex = 0; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Registers a function to be called back periodically in interrupt context. | |
| @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. | |
| @param ProcessorIndex Specifies which processor the callback function applies to. | |
| @param PeriodicCallback A pointer to a function of type PERIODIC_CALLBACK that is the main | |
| periodic entry point of the debug agent. | |
| @retval EFI_SUCCESS The function completed successfully. | |
| @retval EFI_ALREADY_STARTED Non-NULL PeriodicCallback parameter when a callback | |
| function was previously registered. | |
| @retval EFI_OUT_OF_RESOURCES System has insufficient memory resources to register new callback | |
| function. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| RegisterPeriodicCallback ( | |
| IN EFI_DEBUG_SUPPORT_PROTOCOL *This, | |
| IN UINTN ProcessorIndex, | |
| IN EFI_PERIODIC_CALLBACK PeriodicCallback | |
| ) | |
| { | |
| return ManageIdtEntryTable ((CALLBACK_FUNC)PeriodicCallback, SYSTEM_TIMER_VECTOR); | |
| } | |
| /** | |
| Registers a function to be called when a given processor exception occurs. | |
| This code executes in boot services context. | |
| @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. | |
| @param ProcessorIndex Specifies which processor the callback function applies to. | |
| @param ExceptionCallback A pointer to a function of type EXCEPTION_CALLBACK that is called | |
| when the processor exception specified by ExceptionType occurs. | |
| @param ExceptionType Specifies which processor exception to hook. | |
| @retval EFI_SUCCESS The function completed successfully. | |
| @retval EFI_ALREADY_STARTED Non-NULL PeriodicCallback parameter when a callback | |
| function was previously registered. | |
| @retval EFI_OUT_OF_RESOURCES System has insufficient memory resources to register new callback | |
| function. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| RegisterExceptionCallback ( | |
| IN EFI_DEBUG_SUPPORT_PROTOCOL *This, | |
| IN UINTN ProcessorIndex, | |
| IN EFI_EXCEPTION_CALLBACK ExceptionCallback, | |
| IN EFI_EXCEPTION_TYPE ExceptionType | |
| ) | |
| { | |
| return ManageIdtEntryTable ((CALLBACK_FUNC)ExceptionCallback, ExceptionType); | |
| } | |
| /** | |
| Invalidates processor instruction cache for a memory range. Subsequent execution in this range | |
| causes a fresh memory fetch to retrieve code to be executed. | |
| @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. | |
| @param ProcessorIndex Specifies which processor's instruction cache is to be invalidated. | |
| @param Start Specifies the physical base of the memory range to be invalidated. | |
| @param Length Specifies the minimum number of bytes in the processor's instruction | |
| cache to invalidate. | |
| @retval EFI_SUCCESS Always returned. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| InvalidateInstructionCache ( | |
| IN EFI_DEBUG_SUPPORT_PROTOCOL *This, | |
| IN UINTN ProcessorIndex, | |
| IN VOID *Start, | |
| IN UINT64 Length | |
| ) | |
| { | |
| AsmWbinvd (); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Common piece of code that invokes the registered handlers. | |
| This code executes in exception context so no efi calls are allowed. | |
| This code is called from assembly file. | |
| @param ExceptionType Exception type | |
| @param ContextRecord System context | |
| **/ | |
| VOID | |
| InterruptDistrubutionHub ( | |
| IN EFI_EXCEPTION_TYPE ExceptionType, | |
| IN EFI_SYSTEM_CONTEXT ContextRecord | |
| ) | |
| { | |
| EFI_EXCEPTION_CALLBACK ExceptionCallback; | |
| EFI_PERIODIC_CALLBACK PeriodicCallback; | |
| if (IdtEntryTable[ExceptionType].RegisteredCallback == NULL) { | |
| return; | |
| } | |
| if (ExceptionType == SYSTEM_TIMER_VECTOR) { | |
| OrigVector = IdtEntryTable[ExceptionType].OrigVector; | |
| PeriodicCallback = (EFI_PERIODIC_CALLBACK)IdtEntryTable[ExceptionType].RegisteredCallback; | |
| PeriodicCallback (ContextRecord); | |
| } else { | |
| ExceptionCallback = (EFI_EXCEPTION_CALLBACK)IdtEntryTable[ExceptionType].RegisteredCallback; | |
| ExceptionCallback (ExceptionType, ContextRecord); | |
| } | |
| } | |
| /** | |
| This is the callback that is written to the Loaded Image protocol instance | |
| on the image handle. It uninstalls all registered handlers and frees all entry | |
| stub memory. | |
| @param ImageHandle The firmware allocated handle for the EFI image. | |
| @retval EFI_SUCCESS Always. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| PlUnloadDebugSupportDriver ( | |
| IN EFI_HANDLE ImageHandle | |
| ) | |
| { | |
| EFI_EXCEPTION_TYPE ExceptionType; | |
| for (ExceptionType = 0; ExceptionType < NUM_IDT_ENTRIES; ExceptionType++) { | |
| ManageIdtEntryTable (NULL, ExceptionType); | |
| // | |
| // Free space for each Interrupt Stub precedure. | |
| // | |
| if (IdtEntryTable[ExceptionType].StubEntry != NULL) { | |
| FreePool ((VOID *)(UINTN)IdtEntryTable[ExceptionType].StubEntry); | |
| } | |
| } | |
| FreePool (IdtEntryTable); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Initializes driver's handler registration database. | |
| This code executes in boot services context. | |
| Must be public because it's referenced from DebugSupport.c | |
| @retval EFI_UNSUPPORTED If IA32/x64 processor does not support FXSTOR/FXRSTOR instructions, | |
| the context save will fail, so these processors are not supported. | |
| @retval EFI_OUT_OF_RESOURCES Fails to allocate memory. | |
| @retval EFI_SUCCESS Initializes successfully. | |
| **/ | |
| EFI_STATUS | |
| PlInitializeDebugSupportDriver ( | |
| VOID | |
| ) | |
| { | |
| EFI_EXCEPTION_TYPE ExceptionType; | |
| // | |
| // Check whether FxStor instructions are supported. | |
| // | |
| if (!FxStorSupport ()) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| IdtEntryTable = AllocateZeroPool (sizeof (IDT_ENTRY) * NUM_IDT_ENTRIES); | |
| if (IdtEntryTable == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| for (ExceptionType = 0; ExceptionType < NUM_IDT_ENTRIES; ExceptionType++) { | |
| IdtEntryTable[ExceptionType].StubEntry = (DEBUG_PROC)(UINTN)AllocatePool (StubSize); | |
| if (IdtEntryTable[ExceptionType].StubEntry == NULL) { | |
| goto ErrorCleanup; | |
| } | |
| // | |
| // Copy Interrupt stub code. | |
| // | |
| CopyMem ((VOID *)(UINTN)IdtEntryTable[ExceptionType].StubEntry, InterruptEntryStub, StubSize); | |
| } | |
| return EFI_SUCCESS; | |
| ErrorCleanup: | |
| for (ExceptionType = 0; ExceptionType < NUM_IDT_ENTRIES; ExceptionType++) { | |
| if (IdtEntryTable[ExceptionType].StubEntry != NULL) { | |
| FreePool ((VOID *)(UINTN)IdtEntryTable[ExceptionType].StubEntry); | |
| } | |
| } | |
| FreePool (IdtEntryTable); | |
| return EFI_OUT_OF_RESOURCES; | |
| } |