* [PATCH 0/3] Add Pei/DxeCpuExceptionHandlerLibUnitTest @ 2022-10-11 6:03 duntan 2022-10-11 6:03 ` [PATCH 1/3] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib duntan ` (2 more replies) 0 siblings, 3 replies; 6+ messages in thread From: duntan @ 2022-10-11 6:03 UTC (permalink / raw) To: devel Add Pei/DxeCpuExceptionHandlerLibUnitTest Dun Tan (3): UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib UefiCpuPkg: Add Unit tests for PeiCpuExceptionHandlerLib UefiCpuPkg: Add Pei/DxeCpuExceptionHandlerLibUnitTest in dsc UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h | 353 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCommon.c | 856 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibUnitTest.inf | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnitTest.c | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTest.c | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTestAsm.nasm | 217 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerLibUnitTest.inf | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerUnitTest.c | 204 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTest.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTestAsm.nasm | 260 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ UefiCpuPkg/UefiCpuPkg.dsc | 7 +++++++ 11 files changed, 2512 insertions(+) create mode 100644 UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h create mode 100644 UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCommon.c create mode 100644 UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibUnitTest.inf create mode 100644 UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnitTest.c create mode 100644 UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTest.c create mode 100644 UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTestAsm.nasm create mode 100644 UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerLibUnitTest.inf create mode 100644 UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerUnitTest.c create mode 100644 UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTest.c create mode 100644 UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTestAsm.nasm -- 2.31.1.windows.1 ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/3] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib 2022-10-11 6:03 [PATCH 0/3] Add Pei/DxeCpuExceptionHandlerLibUnitTest duntan @ 2022-10-11 6:03 ` duntan 2022-10-11 9:37 ` Ni, Ray 2022-10-11 6:03 ` [PATCH 2/3] UefiCpuPkg: Add Unit tests for PeiCpuExceptionHandlerLib duntan 2022-10-11 6:03 ` [PATCH 3/3] UefiCpuPkg: Add Pei/DxeCpuExceptionHandlerLibUnitTest in dsc duntan 2 siblings, 1 reply; 6+ messages in thread From: duntan @ 2022-10-11 6:03 UTC (permalink / raw) To: devel; +Cc: Eric Dong, Ray Ni, Rahul Kumar Add target based unit tests for the DxeCpuExceptionHandlerLib. A DXE driver is created to test DxeCpuExceptionHandlerLib. Four kinds of test cases are created in this module: 1.Test if exception handler can be registered/unregistered for no error code exception. 2.Test if exception handler can be registered/unregistered for GP and PF. 3.Test if Cpu Context is consistent before and after exception. 4.Test if stack overflow can be captured by CpuStackGuard in both Bsp and AP. Signed-off-by: Dun Tan <dun.tan@intel.com> Cc: Eric Dong <eric.dong@intel.com> Cc: Ray Ni <ray.ni@intel.com> Cc: Rahul Kumar <rahul1.kumar@intel.com> --- UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h | 353 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCommon.c | 856 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibUnitTest.inf | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnitTest.c | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTest.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTestAsm.nasm | 260 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 1890 insertions(+) diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h new file mode 100644 index 0000000000..bfbc483075 --- /dev/null +++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h @@ -0,0 +1,353 @@ +/** @file + + Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef CPU_EXCEPTION_HANDLER_TEST_H_ +#define CPU_EXCEPTION_HANDLER_TEST_H_ + +#include <Uefi.h> +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/DebugLib.h> +#include <Library/UnitTestLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/UnitTestHostBaseLib.h> +#include <Library/CpuExceptionHandlerLib.h> +#include <Library/UefiLib.h> +#include <Library/SerialPortLib.h> +#include <Library/HobLib.h> +#include <Library/CpuPageTableLib.h> +#include <Guid/MemoryAllocationHob.h> +#include <Protocol/MpService.h> +#include <PiPei.h> +#include <Ppi/MpServices2.h> + +#define UNIT_TEST_APP_NAME "Cpu Exception Handler Lib Unit Tests" +#define UNIT_TEST_APP_VERSION "1.0" + +#define CPU_INTERRUPT_NUM 256 +#define SPEC_MAX_EXCEPTION_NUM 22 +#define CR4_RESERVED_BIT BIT15 + +#pragma pack (1) + +typedef union { + struct { + UINT32 LimitLow : 16; + UINT32 BaseLow : 16; + UINT32 BaseMid : 8; + UINT32 Type : 4; + UINT32 System : 1; + UINT32 Dpl : 2; + UINT32 Present : 1; + UINT32 LimitHigh : 4; + UINT32 Software : 1; + UINT32 Reserved : 1; + UINT32 DefaultSize : 1; + UINT32 Granularity : 1; + UINT32 BaseHigh : 8; + } Bits; + UINT64 Uint64; +} IA32_GDT; + +typedef struct { + UINT32 InitialApicId; + UINT32 ApicId; + UINT32 Health; + UINT64 ApTopOfStack; +} CPU_INFO_IN_HOB; +#pragma pack () + +typedef struct { + IA32_DESCRIPTOR OriginalGdtr; + IA32_DESCRIPTOR OriginalIdtr; + UINT16 Tr; +} CPU_REGISTER_BUFFER; + +typedef union { + EDKII_PEI_MP_SERVICES2_PPI *Ppi; + EFI_MP_SERVICES_PROTOCOL *Protocol; +} MP_SERVICES; + +typedef struct { + VOID *Buffer; + UINTN BufferSize; + EFI_STATUS Status; +} EXCEPTION_STACK_SWITCH_CONTEXT; + +typedef struct { + UINT64 Rdi; + UINT64 Rsi; + UINT64 Rbp; + UINT64 Rbx; + UINT64 Rdx; + UINT64 Rcx; + UINT64 Rax; + UINT64 R8Register; + UINT64 R9Register; + UINT64 R10Register; + UINT64 R11Register; + UINT64 R12Register; + UINT64 R13Register; + UINT64 R14Register; + UINT64 R15Register; +} GENERAL_REGISTER; + +typedef struct { + UINT32 Edi; + UINT32 Esi; + UINT32 Ebp; + UINT32 Ebx; + UINT32 Edx; + UINT32 Ecx; + UINT32 Eax; +} GENERAL_REGISTER_IA32; + +extern UINTN mFaultInstructionLength; +extern EFI_EXCEPTION_TYPE mExceptionType; +extern UINTN mRspAddress[]; +extern GENERAL_REGISTER mRegisterForCheck[]; +extern GENERAL_REGISTER mAdjustRegisterBeforeException; +extern GENERAL_REGISTER_IA32 mIa32RegisterForCheck[]; +extern GENERAL_REGISTER_IA32 mAdjustIa32RegisterBeforeException; + +/** + Initialize Bsp Idt with a new Idt table and return the IA32_DESCRIPTOR buffer. + In PEIM, store original PeiServicePointer before new Idt table. + + @return Pointer to the allocated IA32_DESCRIPTOR buffer. +**/ +VOID * +InitializeBspIdt ( + VOID + ); + +/** + Trigger no error code exception by INT n instruction. + + @param[in] ExceptionType No error code exception type. +**/ +VOID +EFIAPI +TriggerINTnException ( + IN EFI_EXCEPTION_TYPE ExceptionType + ); + +/** + Trigger GP exception. + + @param[in] Cr4ReservedBit Cr4 reserved bit. +**/ +VOID +EFIAPI +TriggerGPException ( + UINTN Cr4ReservedBit + ); + +/** + Trigger PF exception by write to not present or ReadOnly address. + + @param[in] PFAddress Not present or ReadOnly address in page table. +**/ +VOID +EFIAPI +TriggerPFException ( + UINTN PFAddress + ); + +/** + Special handler for fault exception. + Rip/Eip in SystemContext will be modified to the instruction after the exception instruction. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. +**/ +VOID +EFIAPI +AdjustRipForFaultHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ); + +/** + Test consistency of Cpu context. Four steps: + 1. Set Cpu register to mAdjustRegisterBeforeException before exception. + 2. Trigger exception specified by ExceptionType. + 3. Store SystemContext in mRegisterForCheck[0] and set SystemContext to mAdjustRegisterInsideException in handler. + 4. Store the Cpu register in mRegisterForCheck[1] + + Rcx/Ecx in mAdjustRegisterInsideException is decided by different exception type runtime since Rcx/Ecx is needed in assembly code. + For GP and PF, Rcx/Ecx is set to FaultParameter. For other exception triggered by INTn, Rcx/Ecx is set to ExceptionType. + + @param[in] ExceptionType Exception type. + @param[in] FaultParameter Parameter for GP and PF. OPTIONAL +**/ +VOID +EFIAPI +AsmTestConsistencyOfCpuContext ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN UINTN FaultParameter OPTIONAL + ); + +/** + Special handler for ConsistencyOfCpuContext test case. General register in SystemContext + is modified in this handler. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. +**/ +VOID +EFIAPI +AdjustCpuContextHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ); + +/** + Compare cpu context in ConsistencyOfCpuContext test case. + 1.Compare mRegisterForCheck[0] with mAdjustRegisterBeforeException. + 2.Compare mRegisterForCheck[1] with mAdjustRegisterAfterException. + + @retval UNIT_TEST_PASSED The Unit test has completed and it was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +CompareCpuContext ( + VOID + ); + +/** + Get EFI_MP_SERVICES_PROTOCOL/EDKII_PEI_MP_SERVICES2_PPI pointer. + + @param[out] MpServices Pointer to the MP_SERVICES buffer + + @retval EFI_SUCCESS EFI_MP_SERVICES_PROTOCOL/PPI interface is returned + @retval EFI_NOT_FOUND EFI_MP_SERVICES_PROTOCOL/PPI interface is not found +**/ +EFI_STATUS +GetMpServices ( + OUT MP_SERVICES *MpServices + ); + +/** + Create CpuExceptionLibUnitTestSuite and add test case. + + @param[in] FrameworkHandle Unit test framework. + + @return EFI_SUCCESS The unit test suite was created. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to + initialize the unit test suite. +**/ +EFI_STATUS +AddCommonTestCase ( + IN UNIT_TEST_FRAMEWORK_HANDLE Framework + ); + +/** + Execute a caller provided function on all enabled APs. + + @param[in] MpServices MP_SERVICES structure. + @param[in] Procedure Pointer to the function to be run on enabled APs of the system. + @param[in] SingleThread If TRUE, then all the enabled APs execute the function specified by Procedure + one by one, in ascending order of processor handle number. + If FALSE, then all the enabled APs execute the function specified by Procedure + simultaneously. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for APs to return from Procedure, + for blocking mode only. Zero means infinity. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + @retval EFI_SUCCESS Execute a caller provided function on all enabled APs successfully + @retval Others Execute a caller provided function on all enabled APs unsuccessfully +**/ +EFI_STATUS +MpServicesUnitTestStartupAllAPs ( + IN MP_SERVICES MpServices, + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN UINTN TimeoutInMicroSeconds, + IN VOID *ProcedureArgument + ); + +/** + Caller gets one enabled AP to execute a caller-provided function. + + @param[in] MpServices MP_SERVICES structure. + @param[in] Procedure Pointer to the function to be run on enabled APs of the system. + @param[in] ProcessorNumber The handle number of the AP. + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for APs to return from Procedure, + for blocking mode only. Zero means infinity. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + + @retval EFI_SUCCESS Caller gets one enabled AP to execute a caller-provided function successfully + @retval Others Caller gets one enabled AP to execute a caller-provided function unsuccessfully +**/ +EFI_STATUS +MpServicesUnitTestStartupThisAP ( + IN MP_SERVICES MpServices, + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN UINTN TimeoutInMicroSeconds, + IN VOID *ProcedureArgument + ); + +/** + Get the handle number for the calling processor. + + @param[in] MpServices Pointer to MP_SERVICES structure. + @param[out] ProcessorNumber The handle number for the calling processor. + + @retval EFI_SUCCESS Get the handle number for the calling processor successfully. + @retval Others Get the handle number for the calling processor unsuccessfully. +**/ +EFI_STATUS +MpServicesUnitTestWhoAmI ( + IN MP_SERVICES MpServices, + OUT UINTN *ProcessorNumber + ); + +/** + Retrieve the number of logical processor in the platform and the number of those logical processors that + are enabled on this boot. + + @param[in] MpServices Pointer to MP_SERVICES structure. + @param[out] NumberOfProcessors Pointer to the total number of logical processors in the system, including + the BSP and disabled APs. + @param[out] NumberOfEnabledProcessors Pointer to the number of processors in the system that are enabled. + + @retval EFI_SUCCESS Retrieve the number of logical processor successfully + @retval Others Retrieve the number of logical processor unsuccessfully +**/ +EFI_STATUS +MpServicesUnitTestGetNumberOfProcessors ( + IN MP_SERVICES MpServices, + OUT UINTN *NumberOfProcessors, + OUT UINTN *NumberOfEnabledProcessors + ); + +/** + Trigger stack overflow by CpuStackGuard. +**/ +VOID +EFIAPI +TriggerStackOverflowbyCpuStackGuard ( + VOID + ); + +/** + Special handler for CpuStackGuard test case. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. +**/ +VOID +EFIAPI +CpuStackGuardExceptionHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ); + +#endif diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCommon.c b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCommon.c new file mode 100644 index 0000000000..1c3d011c76 --- /dev/null +++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCommon.c @@ -0,0 +1,856 @@ +/** @file + Unit tests of the CpuExceptionHandlerLib. + + Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuExceptionHandlerTest.h" + +// +// Length of the assembly falut instruction. +// +UINTN mFaultInstructionLength = 0; +EFI_EXCEPTION_TYPE mExceptionType = 256; +UINTN mNumberOfProcessors = 1; +UINTN mRspAddress[2] = { 0 }; + +// +// Error code flag indicating whether or not an error code will be +// pushed on the stack if an exception occurs. +// +// 1 means an error code will be pushed, otherwise 0 +// +CONST UINT32 mErrorCodeExceptionFlag = 0x20227d00; + +/** + Special handler for trap exception. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. +**/ +VOID +EFIAPI +INTnExceptionHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + mExceptionType = ExceptionType; +} + +/** + Restore all cpu original register before test case. + + @param[in] Buffer Argument of the procedure. +**/ +VOID +EFIAPI +RestoreRegistersPerCpu ( + IN VOID *Buffer + ) +{ + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; + UINT16 Tr; + IA32_TSS_DESCRIPTOR *Tss; + + CpuOriginalRegisterBuffer = (CPU_REGISTER_BUFFER *)Buffer; + + AsmWriteGdtr (&(CpuOriginalRegisterBuffer->OriginalGdtr)); + AsmWriteIdtr (&(CpuOriginalRegisterBuffer->OriginalIdtr)); + Tr = CpuOriginalRegisterBuffer->Tr; + if ((Tr != 0) && (Tr < CpuOriginalRegisterBuffer->OriginalGdtr.Limit)) { + Tss = (IA32_TSS_DESCRIPTOR *)(CpuOriginalRegisterBuffer->OriginalGdtr.Base + Tr); + if (Tss->Bits.P == 1) { + // + // Clear busy bit of TSS before write Tr + // + Tss->Bits.Type &= 0xD; + AsmWriteTr (Tr); + } + } +} + +/** + Restore all cpu original register before test case. + + @param[in] MpServices MpServices. + @param[in] CpuOriginalRegisterBuffer Address of CpuOriginalRegisterBuffer. + @param[in] BspProcessorNum Bsp processor number. +**/ +VOID +RestoreAllCpuRegisters ( + MP_SERVICES *MpServices, OPTIONAL + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer, + UINTN BspProcessorNum + ) +{ + CPU_REGISTER_BUFFER *AllCpuOriginalRegisterBuffer; + UINTN Index; + EFI_STATUS Status; + + for (Index = 0; Index < mNumberOfProcessors; ++Index) { + AllCpuOriginalRegisterBuffer = CpuOriginalRegisterBuffer + Index; + if (Index == BspProcessorNum) { + RestoreRegistersPerCpu ((VOID *)AllCpuOriginalRegisterBuffer); + continue; + } + + ASSERT (MpServices != NULL); + Status = MpServicesUnitTestStartupThisAP ( + *MpServices, + (EFI_AP_PROCEDURE)RestoreRegistersPerCpu, + Index, + 0, + (VOID *)AllCpuOriginalRegisterBuffer + ); + ASSERT_EFI_ERROR (Status); + } +} + +/** + Store all cpu original register before test case. + + @param[in] Buffer Argument of the procedure. +**/ +VOID +EFIAPI +SaveRegisterPerCpu ( + IN VOID *Buffer + ) +{ + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; + IA32_DESCRIPTOR Gdtr; + IA32_DESCRIPTOR Idtr; + + CpuOriginalRegisterBuffer = (CPU_REGISTER_BUFFER *)Buffer; + + AsmReadGdtr (&Gdtr); + AsmReadIdtr (&Idtr); + CpuOriginalRegisterBuffer->OriginalGdtr.Base = Gdtr.Base; + CpuOriginalRegisterBuffer->OriginalGdtr.Limit = Gdtr.Limit; + CpuOriginalRegisterBuffer->OriginalIdtr.Base = Idtr.Base; + CpuOriginalRegisterBuffer->OriginalIdtr.Limit = Idtr.Limit; + CpuOriginalRegisterBuffer->Tr = AsmReadTr (); +} + +/** + Store all cpu original register before test case. + + @param[in] MpServices MpServices. + @param[in] BspProcessorNum Bsp processor number. + + @return Pointer to the allocated CPU_REGISTER_BUFFER. +**/ +CPU_REGISTER_BUFFER * +SaveAllCpuRegisters ( + MP_SERVICES *MpServices, OPTIONAL + UINTN BspProcessorNum + ) +{ + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; + CPU_REGISTER_BUFFER *AllCpuOriginalRegisterBuffer; + EFI_STATUS Status; + UINTN Index; + + CpuOriginalRegisterBuffer = AllocateZeroPool (mNumberOfProcessors * sizeof (CPU_REGISTER_BUFFER)); + ASSERT (CpuOriginalRegisterBuffer != NULL); + + for (Index = 0; Index < mNumberOfProcessors; ++Index) { + AllCpuOriginalRegisterBuffer = CpuOriginalRegisterBuffer + Index; + if (Index == BspProcessorNum) { + SaveRegisterPerCpu ((VOID *)AllCpuOriginalRegisterBuffer); + continue; + } + + ASSERT (MpServices != NULL); + Status = MpServicesUnitTestStartupThisAP ( + *MpServices, + (EFI_AP_PROCEDURE)SaveRegisterPerCpu, + Index, + 0, + (VOID *)AllCpuOriginalRegisterBuffer + ); + ASSERT_EFI_ERROR (Status); + } + + return CpuOriginalRegisterBuffer; +} + +/** + Initialize Ap Idt Procedure. + + @param[in] Buffer Argument of the procedure. +**/ +VOID +EFIAPI +InitializeIdtPerAp ( + IN VOID *Buffer + ) +{ + AsmWriteIdtr (Buffer); +} + +/** + Initialize all Ap Idt. + + @param[in] MpServices MpServices. + @param[in] BspIdtr Pointer to IA32_DESCRIPTOR allocated by Bsp. +**/ +VOID +InitializeApIdt ( + MP_SERVICES MpServices, + VOID *BspIdtr + ) +{ + EFI_STATUS Status; + + Status = MpServicesUnitTestStartupAllAPs ( + MpServices, + (EFI_AP_PROCEDURE)InitializeIdtPerAp, + FALSE, + 0, + BspIdtr + ); + ASSERT_EFI_ERROR (Status); +} + +/** + Check if exception handler can registered/unregistered for no error code exception. + + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that the + contents are well understood by all test cases that may + consume it. + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +TestRegisterHandlerForNoErrorCodeException ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + UINTN Index; + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; + VOID *NewIdtr; + + CpuOriginalRegisterBuffer = SaveAllCpuRegisters (NULL, 0); + NewIdtr = InitializeBspIdt (); + Status = InitializeCpuExceptionHandlers (NULL); + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); + + for (Index = 0; Index < SPEC_MAX_EXCEPTION_NUM; Index++) { + // + // Only test no error code exception by INT n instruction. + // + if ((mErrorCodeExceptionFlag & (1 << Index)) != 0) { + continue; + } + + DEBUG ((DEBUG_INFO, "TestCase1: ExceptionType is %d\n", Index)); + Status = RegisterCpuInterruptHandler (Index, INTnExceptionHandler); + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); + + TriggerINTnException (Index); + UT_ASSERT_EQUAL (mExceptionType, Index); + Status = RegisterCpuInterruptHandler (Index, NULL); + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); + } + + RestoreAllCpuRegisters (NULL, CpuOriginalRegisterBuffer, 0); + FreePool (CpuOriginalRegisterBuffer); + FreePool (NewIdtr); + return UNIT_TEST_PASSED; +} + +/** + Get Bsp stack base. + + @param[out] StackBase Pointer to stack base of BSP. +**/ +VOID +GetBspStackBase ( + OUT UINTN *StackBase + ) +{ + EFI_PEI_HOB_POINTERS Hob; + EFI_HOB_MEMORY_ALLOCATION *MemoryHob; + + // + // Get the base of stack from Hob. + // + ASSERT (StackBase != NULL); + Hob.Raw = GetHobList (); + while ((Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw)) != NULL) { + MemoryHob = Hob.MemoryAllocation; + if (CompareGuid (&gEfiHobMemoryAllocStackGuid, &MemoryHob->AllocDescriptor.Name)) { + DEBUG (( + DEBUG_INFO, + "%a: Bsp StackBase = 0x%016lx StackSize = 0x%016lx\n", + __FUNCTION__, + MemoryHob->AllocDescriptor.MemoryBaseAddress, + MemoryHob->AllocDescriptor.MemoryLength + )); + + *StackBase = (UINTN)MemoryHob->AllocDescriptor.MemoryBaseAddress; + // + // Ensure the base of the stack is page-size aligned. + // + ASSERT ((*StackBase & EFI_PAGE_MASK) == 0); + break; + } + + Hob.Raw = GET_NEXT_HOB (Hob); + } + + ASSERT (*StackBase != 0); +} + +/** + Initialize Ap Idt Procedure. + + @param[in] Buffer Argument of the procedure. +**/ +VOID +EFIAPI +GetStackBasePerAp ( + IN VOID *Buffer + ) +{ + UINTN ApTopOfStack; + + ApTopOfStack = ALIGN_VALUE ((UINTN)&ApTopOfStack, (UINTN)PcdGet32 (PcdCpuApStackSize)); + *(UINTN *)Buffer = ApTopOfStack - (UINTN)PcdGet32 (PcdCpuApStackSize); +} + +/** + Get Ap stack Info. + + @param[in] MpServices MpServices. + @param[in] BspProcessorNum Bsp processor number. + + @return Pointer to the allocated CpuStackBaseBuffer. +**/ +UINTN * +GetAllCpuStackBase ( + MP_SERVICES *MpServices, + UINTN BspProcessorNum + ) +{ + UINTN *CpuStackBaseBuffer; + UINTN *AllCpuStackBaseBuffer; + EFI_STATUS Status; + UINTN Index; + + CpuStackBaseBuffer = AllocateZeroPool (mNumberOfProcessors * sizeof (UINTN)); + ASSERT (CpuStackBaseBuffer != NULL); + + for (Index = 0; Index < mNumberOfProcessors; ++Index) { + AllCpuStackBaseBuffer = CpuStackBaseBuffer + Index; + if (Index == BspProcessorNum) { + GetBspStackBase (AllCpuStackBaseBuffer); + continue; + } + + ASSERT (MpServices != NULL); + Status = MpServicesUnitTestStartupThisAP ( + *MpServices, + (EFI_AP_PROCEDURE)GetStackBasePerAp, + Index, + 0, + (VOID *)AllCpuStackBaseBuffer + ); + ASSERT_EFI_ERROR (Status); + DEBUG ((DEBUG_INFO, "AP[%d] StackBase = 0x%x\n", Index, CpuStackBaseBuffer[Index])); + } + + return CpuStackBaseBuffer; +} + +/** + Return not present or ReadOnly address in page table. + + @param[out] PFAddress Access to the address which is not permitted will trigger PF exceptions. +**/ +VOID +PageFaultAddressInPageTable ( + UINTN *PFAddress + ) +{ + IA32_CR0 Cr0; + IA32_CR4 Cr4; + UINTN PageTable; + PAGING_MODE PagingMode; + BOOLEAN Enable5LevelPaging; + RETURN_STATUS Status; + IA32_MAP_ENTRY *Map; + UINTN MapCount; + UINTN Index; + + ASSERT (PFAddress != NULL); + *PFAddress = 0; + + Cr0.UintN = AsmReadCr0 (); + if (Cr0.Bits.PG == 0) { + return; + } + + PageTable = AsmReadCr3 (); + Cr4.UintN = AsmReadCr4 (); + if (sizeof (UINTN) == sizeof (UINT32)) { + ASSERT (Cr4.Bits.PAE == 1); + PagingMode = PagingPae; + } else { + Enable5LevelPaging = (BOOLEAN)(Cr4.Bits.LA57 == 1); + PagingMode = Enable5LevelPaging ? Paging5Level : Paging4Level; + } + + MapCount = 0; + Status = PageTableParse (PageTable, PagingMode, NULL, &MapCount); + ASSERT (Status == RETURN_BUFFER_TOO_SMALL); + Map = AllocatePages (EFI_SIZE_TO_PAGES (MapCount * sizeof (IA32_MAP_ENTRY))); + Status = PageTableParse (PageTable, PagingMode, Map, &MapCount); + ASSERT (Status == RETURN_SUCCESS); + + for (Index = 0; Index < MapCount; Index++) { + DEBUG (( + DEBUG_ERROR, + "%02d: %016lx - %016lx, %016lx\n", + Index, + Map[Index].LinearAddress, + Map[Index].LinearAddress + Map[Index].Length, + Map[Index].Attribute.Uint64 + )); + + // + // Not present address in page table. + // + if ((Index >= 1) && (Map[Index].LinearAddress > Map[Index - 1].LinearAddress + Map[Index - 1].Length)) { + *PFAddress = (UINTN)(Map[Index - 1].LinearAddress + Map[Index - 1].Length); + return; + } + + // + // ReadOnly address in page table. + // + if ((Cr0.Bits.WP != 0) && (Map[Index].Attribute.Bits.ReadWrite == 0)) { + *PFAddress = (UINTN)Map[Index].LinearAddress; + return; + } + } +} + +/** + Test if exception handler can registered/unregistered for GP and PF. + + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that the + contents are well understood by all test cases that may + consume it. + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +TestRegisterHandlerForGPAndPF ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; + UINTN PFAddress; + VOID *NewIdtr; + + PFAddress = 0; + CpuOriginalRegisterBuffer = SaveAllCpuRegisters (NULL, 0); + NewIdtr = InitializeBspIdt (); + Status = InitializeCpuExceptionHandlers (NULL); + + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); + + // + // GP exception. + // + DEBUG ((DEBUG_INFO, "TestCase2: ExceptionType is %d\n", EXCEPT_IA32_GP_FAULT)); + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_GP_FAULT, AdjustRipForFaultHandler); + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); + + TriggerGPException (CR4_RESERVED_BIT); + UT_ASSERT_EQUAL (mExceptionType, EXCEPT_IA32_GP_FAULT); + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_GP_FAULT, NULL); + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); + + // + // PF exception. + // + PageFaultAddressInPageTable (&PFAddress); + + if (PFAddress > 0) { + DEBUG ((DEBUG_INFO, "TestCase2: ExceptionType is %d\n", EXCEPT_IA32_PAGE_FAULT)); + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, AdjustRipForFaultHandler); + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); + TriggerPFException (PFAddress); + + UT_ASSERT_EQUAL (mExceptionType, EXCEPT_IA32_PAGE_FAULT); + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, NULL); + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); + } + + RestoreAllCpuRegisters (NULL, CpuOriginalRegisterBuffer, 0); + FreePool (CpuOriginalRegisterBuffer); + FreePool (NewIdtr); + return UNIT_TEST_PASSED; +} + +/** + Test if Cpu Context is consistent before and after exception. + + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that the + contents are well understood by all test cases that may + consume it. + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +TestCpuContextConsistency ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + UINTN Index; + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; + UINTN FaultParameter; + VOID *NewIdtr; + + FaultParameter = 0; + CpuOriginalRegisterBuffer = SaveAllCpuRegisters (NULL, 0); + NewIdtr = InitializeBspIdt (); + Status = InitializeCpuExceptionHandlers (NULL); + + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); + + for (Index = 0; Index < 22; Index++) { + if (Index == EXCEPT_IA32_PAGE_FAULT) { + PageFaultAddressInPageTable (&FaultParameter); + if (FaultParameter == 0) { + continue; + } + } else if (Index == EXCEPT_IA32_GP_FAULT) { + FaultParameter = CR4_RESERVED_BIT; + } else { + if ((mErrorCodeExceptionFlag & (1 << Index)) != 0) { + continue; + } + } + + DEBUG ((DEBUG_INFO, "TestCase3: ExceptionType is %d\n", Index)); + Status = RegisterCpuInterruptHandler (Index, AdjustCpuContextHandler); + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); + + // + // Trigger different type exception and compare different stage cpu context. + // + AsmTestConsistencyOfCpuContext (Index, FaultParameter); + CompareCpuContext (); + Status = RegisterCpuInterruptHandler (Index, NULL); + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); + } + + RestoreAllCpuRegisters (NULL, CpuOriginalRegisterBuffer, 0); + FreePool (CpuOriginalRegisterBuffer); + FreePool (NewIdtr); + return UNIT_TEST_PASSED; +} + +/** + Initializes CPU exceptions handlers for the sake of stack switch requirement. + + This function is a wrapper of InitializeSeparateExceptionStacks. It's mainly + for the sake of AP's init because of EFI_AP_PROCEDURE API requirement. + + @param[in,out] Buffer The pointer to private data buffer. + +**/ +VOID +EFIAPI +InitializeExceptionStackSwitchHandlersPerAp ( + IN OUT VOID *Buffer + ) +{ + EXCEPTION_STACK_SWITCH_CONTEXT *CpuSwitchStackData; + + CpuSwitchStackData = (EXCEPTION_STACK_SWITCH_CONTEXT *)Buffer; + + // + // This may be called twice for each Cpu. Only run InitializeSeparateExceptionStacks + // if this is the first call or the first call failed because of size too small. + // + if ((CpuSwitchStackData->Status == EFI_NOT_STARTED) || (CpuSwitchStackData->Status == EFI_BUFFER_TOO_SMALL)) { + CpuSwitchStackData->Status = InitializeSeparateExceptionStacks (CpuSwitchStackData->Buffer, &CpuSwitchStackData->BufferSize); + } +} + +/** + Initializes MP exceptions handlers for the sake of stack switch requirement. + + This function will allocate required resources required to setup stack switch + and pass them through SwitchStackData to each logic processor. + + @param[in, out] MpServices MpServices. + @param[in, out] BspProcessorNum Bsp processor number. + + @return Pointer to the allocated SwitchStackData. +**/ +EXCEPTION_STACK_SWITCH_CONTEXT * +InitializeMpExceptionStackSwitchHandlers ( + MP_SERVICES MpServices, + UINTN BspProcessorNum + ) +{ + UINTN Index; + EXCEPTION_STACK_SWITCH_CONTEXT *SwitchStackData; + EXCEPTION_STACK_SWITCH_CONTEXT *CpuSwitchStackData; + UINTN BufferSize; + EFI_STATUS Status; + UINT8 *Buffer; + + SwitchStackData = AllocateZeroPool (mNumberOfProcessors * sizeof (EXCEPTION_STACK_SWITCH_CONTEXT)); + ASSERT (SwitchStackData != NULL); + for (Index = 0; Index < mNumberOfProcessors; ++Index) { + CpuSwitchStackData = SwitchStackData + Index; + // + // Because the procedure may runs multiple times, use the status EFI_NOT_STARTED + // to indicate the procedure haven't been run yet. + // + SwitchStackData[Index].Status = EFI_NOT_STARTED; + if (Index == BspProcessorNum) { + InitializeExceptionStackSwitchHandlersPerAp ((VOID *)CpuSwitchStackData); + continue; + } + + Status = MpServicesUnitTestStartupThisAP ( + MpServices, + InitializeExceptionStackSwitchHandlersPerAp, + Index, + 0, + (VOID *)CpuSwitchStackData + ); + ASSERT_EFI_ERROR (Status); + } + + BufferSize = 0; + for (Index = 0; Index < mNumberOfProcessors; ++Index) { + if (SwitchStackData[Index].Status == EFI_BUFFER_TOO_SMALL) { + ASSERT (SwitchStackData[Index].BufferSize != 0); + BufferSize += SwitchStackData[Index].BufferSize; + } else { + ASSERT (SwitchStackData[Index].Status == EFI_SUCCESS); + ASSERT (SwitchStackData[Index].BufferSize == 0); + } + } + + if (BufferSize != 0) { + Buffer = AllocateZeroPool (BufferSize); + ASSERT (Buffer != NULL); + BufferSize = 0; + for (Index = 0; Index < mNumberOfProcessors; ++Index) { + if (SwitchStackData[Index].Status == EFI_BUFFER_TOO_SMALL) { + SwitchStackData[Index].Buffer = (VOID *)(&Buffer[BufferSize]); + BufferSize += SwitchStackData[Index].BufferSize; + DEBUG (( + DEBUG_INFO, + "Buffer[cpu%lu] for InitializeExceptionStackSwitchHandlersPerAp: 0x%lX with size 0x%lX\n", + (UINT64)(UINTN)Index, + (UINT64)(UINTN)SwitchStackData[Index].Buffer, + (UINT64)(UINTN)SwitchStackData[Index].BufferSize + )); + } + } + + for (Index = 0; Index < mNumberOfProcessors; ++Index) { + CpuSwitchStackData = SwitchStackData + Index; + if (Index == BspProcessorNum) { + InitializeExceptionStackSwitchHandlersPerAp ((VOID *)CpuSwitchStackData); + continue; + } + + Status = MpServicesUnitTestStartupThisAP ( + MpServices, + InitializeExceptionStackSwitchHandlersPerAp, + Index, + 0, + (VOID *)CpuSwitchStackData + ); + ASSERT_EFI_ERROR (Status); + } + + for (Index = 0; Index < mNumberOfProcessors; ++Index) { + ASSERT (SwitchStackData[Index].Status == EFI_SUCCESS); + } + } + + return SwitchStackData; +} + +/** + Test if stack overflow is captured by CpuStackGuard in Bsp and AP. + + @param[in] Context [Optional] An optional parameter that enables: + 1) test-case reuse with varied parameters and + 2) test-case re-entry for Target tests that need a + reboot. This parameter is a VOID* and it is the + responsibility of the test author to ensure that the + contents are well understood by all test cases that may + consume it. + + @retval UNIT_TEST_PASSED The Unit test has completed and the test + case was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +EFIAPI +TestCpuStackGuardInBspAndAp ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + UINTN OriginalStackBase; + UINTN NewStackTop; + UINTN NewStackBase; + EXCEPTION_STACK_SWITCH_CONTEXT *SwitchStackData; + MP_SERVICES MpServices; + UINTN ProcessorNumber; + UINTN EnabledProcessorNum; + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; + UINTN Index; + UINTN BspProcessorNum; + VOID *NewIdtr; + UINTN *CpuStackBaseBuffer; + + if (!PcdGetBool (PcdCpuStackGuard)) { + return UNIT_TEST_PASSED; + } + + // + // Get MP Service Protocol + // + Status = GetMpServices (&MpServices); + Status = MpServicesUnitTestGetNumberOfProcessors (MpServices, &ProcessorNumber, &EnabledProcessorNum); + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); + Status = MpServicesUnitTestWhoAmI (MpServices, &BspProcessorNum); + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); + mNumberOfProcessors = ProcessorNumber; + + CpuOriginalRegisterBuffer = SaveAllCpuRegisters (&MpServices, BspProcessorNum); + + // + // Initialize Bsp and AP Idt. + // Idt buffer should not be empty or it will hang in MP API. + // + NewIdtr = InitializeBspIdt (); + Status = InitializeCpuExceptionHandlers (NULL); + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); + InitializeApIdt (MpServices, NewIdtr); + + // + // Get BSP and AP original stack base. + // + CpuStackBaseBuffer = GetAllCpuStackBase (&MpServices, BspProcessorNum); + + // + // InitializeMpExceptionStackSwitchHandlers and register exception handler. + // + SwitchStackData = InitializeMpExceptionStackSwitchHandlers (MpServices, BspProcessorNum); + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, CpuStackGuardExceptionHandler); + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_DOUBLE_FAULT, AdjustRipForFaultHandler); + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); + + for (Index = 0; Index < mNumberOfProcessors; Index++) { + OriginalStackBase = CpuStackBaseBuffer[Index]; + NewStackTop = (UINTN)(SwitchStackData[Index].Buffer) + SwitchStackData[Index].BufferSize; + NewStackBase = (UINTN)(SwitchStackData[Index].Buffer); + if (Index == BspProcessorNum) { + TriggerStackOverflowbyCpuStackGuard (); + } else { + MpServicesUnitTestStartupThisAP ( + MpServices, + (EFI_AP_PROCEDURE)TriggerStackOverflowbyCpuStackGuard, + Index, + 0, + NULL + ); + } + + DEBUG ((DEBUG_INFO, "TestCase4: mRspAddress[0] is 0x%x, mRspAddress[1] is 0x%x\n", mRspAddress[0], mRspAddress[1])); + UT_ASSERT_TRUE ((mRspAddress[0] >= OriginalStackBase) && (mRspAddress[0] <= (OriginalStackBase + SIZE_4KB))); + UT_ASSERT_TRUE ((mRspAddress[1] >= NewStackBase) && (mRspAddress[1] < NewStackTop)); + } + + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, NULL); + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_DOUBLE_FAULT, NULL); + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); + RestoreAllCpuRegisters (&MpServices, CpuOriginalRegisterBuffer, BspProcessorNum); + FreePool (SwitchStackData); + FreePool (CpuOriginalRegisterBuffer); + FreePool (NewIdtr); + + return UNIT_TEST_PASSED; +} + +/** + Create CpuExceptionLibUnitTestSuite and add test case. + + @param[in] FrameworkHandle Unit test framework. + + @return EFI_SUCCESS The unit test suite was created. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to + initialize the unit test suite. +**/ +EFI_STATUS +AddCommonTestCase ( + IN UNIT_TEST_FRAMEWORK_HANDLE Framework + ) +{ + EFI_STATUS Status; + UNIT_TEST_SUITE_HANDLE CpuExceptionLibUnitTestSuite; + + // + // Populate the Manual Test Cases. + // + Status = CreateUnitTestSuite (&CpuExceptionLibUnitTestSuite, Framework, "Test CpuExceptionHandlerLib", "CpuExceptionHandlerLib.Manual", NULL, NULL); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for CpuExceptionHandlerLib Test Cases\n")); + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + AddTestCase (CpuExceptionLibUnitTestSuite, "Check if exception handler can be registered/unregistered for no error code exception", "TestRegisterHandlerForNoErrorCodeException", TestRegisterHandlerForNoErrorCodeException, NULL, NULL, NULL); + AddTestCase (CpuExceptionLibUnitTestSuite, "Check if exception handler can be registered/unregistered for GP and PF", "TestRegisterHandlerForGPAndPF", TestRegisterHandlerForGPAndPF, NULL, NULL, NULL); + + AddTestCase (CpuExceptionLibUnitTestSuite, "Check if Cpu Context is consistent before and after exception.", "TestCpuContextConsistency", TestCpuContextConsistency, NULL, NULL, NULL); + AddTestCase (CpuExceptionLibUnitTestSuite, "Check if stack overflow is captured by CpuStackGuard in Bsp and AP", "TestCpuStackGuardInBspAndAp", TestCpuStackGuardInBspAndAp, NULL, NULL, NULL); + + return EFI_SUCCESS; +} diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibUnitTest.inf b/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibUnitTest.inf new file mode 100644 index 0000000000..e3dbe7b9ab --- /dev/null +++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibUnitTest.inf @@ -0,0 +1,58 @@ +## @file +# Unit tests of the DxeCpuExceptionHandlerLib instance. +# +# Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CpuExceptionHandlerDxeTest + FILE_GUID = D76BFD9C-0B6D-46BD-AD66-2BBB6FA7031A + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = CpuExceptionHandlerTestEntry + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = X64 +# +[Sources.X64] + X64/ArchExceptionHandlerTestAsm.nasm + X64/ArchExceptionHandlerTest.c + +[Sources.common] + CpuExceptionHandlerTest.h + CpuExceptionHandlerTestCommon.c + DxeCpuExceptionHandlerUnitTest.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + UnitTestLib + MemoryAllocationLib + CpuExceptionHandlerLib + UefiDriverEntryPoint + HobLib + UefiBootServicesTableLib + CpuPageTableLib + +[Guids] + gEfiHobMemoryAllocStackGuid + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize ## CONSUMES + +[Protocols] + gEfiMpServiceProtocolGuid + +[Depex] + gEfiMpServiceProtocolGuid diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnitTest.c b/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnitTest.c new file mode 100644 index 0000000000..917fc549bf --- /dev/null +++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnitTest.c @@ -0,0 +1,196 @@ +/** @file + Unit tests of the CpuExceptionHandlerLib. + + Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuExceptionHandlerTest.h" +#include <Library/UefiBootServicesTableLib.h> + +/** + Initialize Bsp Idt with a new Idt table and return the IA32_DESCRIPTOR buffer. + In PEIM, store original PeiServicePointer before new Idt table. + + @return Pointer to the allocated IA32_DESCRIPTOR buffer. +**/ +VOID * +InitializeBspIdt ( + VOID + ) +{ + UINTN *NewIdtTable; + IA32_DESCRIPTOR *Idtr; + + Idtr = AllocateZeroPool (sizeof (IA32_DESCRIPTOR)); + ASSERT (Idtr != NULL); + NewIdtTable = AllocateZeroPool (sizeof (IA32_IDT_GATE_DESCRIPTOR) * CPU_INTERRUPT_NUM); + ASSERT (NewIdtTable != NULL); + Idtr->Base = (UINTN)NewIdtTable; + Idtr->Limit = (UINT16)(sizeof (IA32_IDT_GATE_DESCRIPTOR) * CPU_INTERRUPT_NUM - 1); + + AsmWriteIdtr (Idtr); + return Idtr; +} + +/** + Retrieve the number of logical processor in the platform and the number of those logical processors that + are enabled on this boot. + + @param[in] MpServices MP_SERVICES structure. + @param[out] NumberOfProcessors Pointer to the total number of logical processors in the system, including + the BSP and disabled APs. + @param[out] NumberOfEnabledProcessors Pointer to the number of processors in the system that are enabled. + + @retval EFI_SUCCESS Retrieve the number of logical processor successfully + @retval Others Retrieve the number of logical processor unsuccessfully +**/ +EFI_STATUS +MpServicesUnitTestGetNumberOfProcessors ( + IN MP_SERVICES MpServices, + OUT UINTN *NumberOfProcessors, + OUT UINTN *NumberOfEnabledProcessors + ) +{ + return MpServices.Protocol->GetNumberOfProcessors (MpServices.Protocol, NumberOfProcessors, NumberOfEnabledProcessors); +} + +/** + Get the handle number for the calling processor. + + @param[in] MpServices MP_SERVICES structure. + @param[out] ProcessorNumber The handle number for the calling processor. + + @retval EFI_SUCCESS Get the handle number for the calling processor successfully. + @retval Others Get the handle number for the calling processor unsuccessfully. +**/ +EFI_STATUS +MpServicesUnitTestWhoAmI ( + IN MP_SERVICES MpServices, + OUT UINTN *ProcessorNumber + ) +{ + return MpServices.Protocol->WhoAmI (MpServices.Protocol, ProcessorNumber); +} + +/** + Caller gets one enabled AP to execute a caller-provided function. + + @param[in] MpServices MP_SERVICES structure. + @param[in] Procedure Pointer to the function to be run on enabled APs of the system. + @param[in] ProcessorNumber The handle number of the AP. + @param[in] TimeoutInMicroSeconds Indicates the time limit in microseconds for APs to return from Procedure, + for blocking mode only. Zero means infinity. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + + @retval EFI_SUCCESS Caller gets one enabled AP to execute a caller-provided function successfully + @retval Others Caller gets one enabled AP to execute a caller-provided function unsuccessfully +**/ +EFI_STATUS +MpServicesUnitTestStartupThisAP ( + IN MP_SERVICES MpServices, + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN UINTN TimeoutInMicroSeconds, + IN VOID *ProcedureArgument + ) +{ + return MpServices.Protocol->StartupThisAP (MpServices.Protocol, Procedure, ProcessorNumber, NULL, TimeoutInMicroSeconds, ProcedureArgument, NULL); +} + +/** + Execute a caller provided function on all enabled APs. + + @param[in] MpServices MP_SERVICES structure. + @param[in] Procedure Pointer to the function to be run on enabled APs of the system. + @param[in] SingleThread If TRUE, then all the enabled APs execute the function specified by Procedure + one by one, in ascending order of processor handle number. + If FALSE, then all the enabled APs execute the function specified by Procedure + simultaneously. + @param[in] TimeoutInMicroSeconds Indicates the time limit in microseconds for APs to return from Procedure, + for blocking mode only. Zero means infinity. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + @retval EFI_SUCCESS Execute a caller provided function on all enabled APs successfully + @retval Others Execute a caller provided function on all enabled APs unsuccessfully +**/ +EFI_STATUS +MpServicesUnitTestStartupAllAPs ( + IN MP_SERVICES MpServices, + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN UINTN TimeoutInMicroSeconds, + IN VOID *ProcedureArgument + ) +{ + return MpServices.Protocol->StartupAllAPs (MpServices.Protocol, Procedure, SingleThread, NULL, TimeoutInMicroSeconds, ProcedureArgument, NULL); +} + +/** + Get EFI_MP_SERVICES_PROTOCOL pointer. + + @param[out] MpServices Pointer to the buffer where EFI_MP_SERVICES_PROTOCOL is stored + + @retval EFI_SUCCESS EFI_MP_SERVICES_PROTOCOL interface is returned + @retval EFI_NOT_FOUND EFI_MP_SERVICES_PROTOCOL interface is not found +**/ +EFI_STATUS +GetMpServices ( + OUT MP_SERVICES *MpServices + ) +{ + return gBS->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **)&MpServices->Protocol); +} + +/** + Entry for CpuExceptionHandlerDxeTest driver. + + @param ImageHandle Image handle this driver. + @param SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The driver executed normally. + +**/ +EFI_STATUS +EFIAPI +CpuExceptionHandlerTestEntry ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + UNIT_TEST_FRAMEWORK_HANDLE Framework; + + Framework = NULL; + + DEBUG ((DEBUG_INFO, "%a v%a\n", UNIT_TEST_APP_NAME, UNIT_TEST_APP_VERSION)); + + // + // Start setting up the test framework for running the tests. + // + Status = InitUnitTestFramework (&Framework, UNIT_TEST_APP_NAME, gEfiCallerBaseName, UNIT_TEST_APP_VERSION); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status)); + goto EXIT; + } + + Status = AddCommonTestCase (Framework); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in AddCommonTestCase. Status = %r\n", Status)); + goto EXIT; + } + + // + // Execute the tests. + // + Status = RunAllTestSuites (Framework); + +EXIT: + if (Framework) { + FreeUnitTestFramework (Framework); + } + + return Status; +} diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTest.c b/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTest.c new file mode 100644 index 0000000000..9b086622f2 --- /dev/null +++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTest.c @@ -0,0 +1,167 @@ +/** @file + Unit tests of the CpuExceptionHandlerLib. + + Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuExceptionHandlerTest.h" + +GENERAL_REGISTER mRegisterForCheck[2]; + +// +// In TestCpuContextConsistency, Cpu registers will be set to mAdjustRegisterBeforeException. +// Rcx in mAdjustRegisterInsideException is set runtime since Rcx is needed in assembly code. +// For GP and PF, Rcx is set to FaultParameter. For other exception triggered by INTn, Rcx is set to ExceptionType. +// +GENERAL_REGISTER mAdjustRegisterBeforeException = { 1, 2, 3, 4, 5, 0, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf }; +GENERAL_REGISTER mAdjustRegisterInsideException = { 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f }; + +/** + Special handler for fault exception. + Rip/Eip in SystemContext will be modified to the instruction after the exception instruction. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. +**/ +VOID +EFIAPI +AdjustRipForFaultHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + mExceptionType = ExceptionType; + SystemContext.SystemContextX64->Rip += mFaultInstructionLength; +} + +/** + Special handler for ConsistencyOfCpuContext test case. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. +**/ +VOID +EFIAPI +AdjustCpuContextHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + // + // Do not handle Esp and ebp. They will not be restored. + // Store SystemContext in mRegisterForCheck[0] modified before exception. + // + mRegisterForCheck[0].Rdi = SystemContext.SystemContextX64->Rdi; + mRegisterForCheck[0].Rsi = SystemContext.SystemContextX64->Rsi; + mRegisterForCheck[0].Rbx = SystemContext.SystemContextX64->Rbx; + mRegisterForCheck[0].Rdx = SystemContext.SystemContextX64->Rdx; + mRegisterForCheck[0].Rcx = SystemContext.SystemContextX64->Rcx; + mRegisterForCheck[0].Rax = SystemContext.SystemContextX64->Rax; + mRegisterForCheck[0].R8Register = SystemContext.SystemContextX64->R8; + mRegisterForCheck[0].R9Register = SystemContext.SystemContextX64->R9; + mRegisterForCheck[0].R10Register = SystemContext.SystemContextX64->R10; + mRegisterForCheck[0].R11Register = SystemContext.SystemContextX64->R11; + mRegisterForCheck[0].R12Register = SystemContext.SystemContextX64->R12; + mRegisterForCheck[0].R13Register = SystemContext.SystemContextX64->R13; + mRegisterForCheck[0].R14Register = SystemContext.SystemContextX64->R14; + mRegisterForCheck[0].R15Register = SystemContext.SystemContextX64->R15; + + // + // Modify cpu context which will be stored in mRegisterForCheck[1]. + // + SystemContext.SystemContextX64->Rdi = mAdjustRegisterInsideException.Rdi; + SystemContext.SystemContextX64->Rsi = mAdjustRegisterInsideException.Rsi; + SystemContext.SystemContextX64->Rbx = mAdjustRegisterInsideException.Rbx; + SystemContext.SystemContextX64->Rdx = mAdjustRegisterInsideException.Rdx; + SystemContext.SystemContextX64->Rcx = mAdjustRegisterInsideException.Rcx; + SystemContext.SystemContextX64->Rax = mAdjustRegisterInsideException.Rax; + SystemContext.SystemContextX64->R8 = mAdjustRegisterInsideException.R8Register; + SystemContext.SystemContextX64->R9 = mAdjustRegisterInsideException.R9Register; + SystemContext.SystemContextX64->R10 = mAdjustRegisterInsideException.R10Register; + SystemContext.SystemContextX64->R11 = mAdjustRegisterInsideException.R11Register; + SystemContext.SystemContextX64->R12 = mAdjustRegisterInsideException.R12Register; + SystemContext.SystemContextX64->R13 = mAdjustRegisterInsideException.R13Register; + SystemContext.SystemContextX64->R14 = mAdjustRegisterInsideException.R14Register; + SystemContext.SystemContextX64->R15 = mAdjustRegisterInsideException.R15Register; + + // + // When fault exception happens, eip/rip points to the faulting instruction. + // For now, olny GP and PF are tested in fault exception. + // + if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) || (ExceptionType == EXCEPT_IA32_GP_FAULT)) { + AdjustRipForFaultHandler (ExceptionType, SystemContext); + } +} + +/** + Compare cpu context in ConsistencyOfCpuContext test case. + 1.Compare mRegisterForCheck[0] with mAdjustRegisterBeforeException. + 2.Compare mRegisterForCheck[1] with mAdjustRegisterAfterException. + + @retval UNIT_TEST_PASSED The Unit test has completed and it was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +CompareCpuContext ( + VOID + ) +{ + // + // Do not handle Esp and ebp. They will not be restored. + // + UT_ASSERT_EQUAL (mRegisterForCheck[0].Rdi, mAdjustRegisterBeforeException.Rdi); + UT_ASSERT_EQUAL (mRegisterForCheck[0].Rsi, mAdjustRegisterBeforeException.Rsi); + UT_ASSERT_EQUAL (mRegisterForCheck[0].Rbx, mAdjustRegisterBeforeException.Rbx); + UT_ASSERT_EQUAL (mRegisterForCheck[0].Rdx, mAdjustRegisterBeforeException.Rdx); + UT_ASSERT_EQUAL (mRegisterForCheck[0].Rcx, mAdjustRegisterBeforeException.Rcx); + UT_ASSERT_EQUAL (mRegisterForCheck[0].Rax, mAdjustRegisterBeforeException.Rax); + UT_ASSERT_EQUAL (mRegisterForCheck[0].R8Register, mAdjustRegisterBeforeException.R8Register); + UT_ASSERT_EQUAL (mRegisterForCheck[0].R9Register, mAdjustRegisterBeforeException.R9Register); + UT_ASSERT_EQUAL (mRegisterForCheck[0].R10Register, mAdjustRegisterBeforeException.R10Register); + UT_ASSERT_EQUAL (mRegisterForCheck[0].R11Register, mAdjustRegisterBeforeException.R11Register); + UT_ASSERT_EQUAL (mRegisterForCheck[0].R12Register, mAdjustRegisterBeforeException.R12Register); + UT_ASSERT_EQUAL (mRegisterForCheck[0].R13Register, mAdjustRegisterBeforeException.R13Register); + UT_ASSERT_EQUAL (mRegisterForCheck[0].R14Register, mAdjustRegisterBeforeException.R14Register); + UT_ASSERT_EQUAL (mRegisterForCheck[0].R15Register, mAdjustRegisterBeforeException.R15Register); + + UT_ASSERT_EQUAL (mRegisterForCheck[1].Rdi, mAdjustRegisterInsideException.Rdi); + UT_ASSERT_EQUAL (mRegisterForCheck[1].Rsi, mAdjustRegisterInsideException.Rsi); + UT_ASSERT_EQUAL (mRegisterForCheck[1].Rbx, mAdjustRegisterInsideException.Rbx); + UT_ASSERT_EQUAL (mRegisterForCheck[1].Rdx, mAdjustRegisterInsideException.Rdx); + UT_ASSERT_EQUAL (mRegisterForCheck[1].Rcx, mAdjustRegisterInsideException.Rcx); + UT_ASSERT_EQUAL (mRegisterForCheck[1].Rax, mAdjustRegisterInsideException.Rax); + UT_ASSERT_EQUAL (mRegisterForCheck[1].R8Register, mAdjustRegisterInsideException.R8Register); + UT_ASSERT_EQUAL (mRegisterForCheck[1].R9Register, mAdjustRegisterInsideException.R9Register); + UT_ASSERT_EQUAL (mRegisterForCheck[1].R10Register, mAdjustRegisterInsideException.R10Register); + UT_ASSERT_EQUAL (mRegisterForCheck[1].R11Register, mAdjustRegisterInsideException.R11Register); + UT_ASSERT_EQUAL (mRegisterForCheck[1].R12Register, mAdjustRegisterInsideException.R12Register); + UT_ASSERT_EQUAL (mRegisterForCheck[1].R13Register, mAdjustRegisterInsideException.R13Register); + UT_ASSERT_EQUAL (mRegisterForCheck[1].R14Register, mAdjustRegisterInsideException.R14Register); + UT_ASSERT_EQUAL (mRegisterForCheck[1].R15Register, mAdjustRegisterInsideException.R15Register); + return UNIT_TEST_PASSED; +} + +/** + Special handler for CpuStackGuard test case. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. + +**/ +VOID +EFIAPI +CpuStackGuardExceptionHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINTN LocalVariable; + + AdjustRipForFaultHandler (ExceptionType, SystemContext); + mRspAddress[0] = (UINTN)SystemContext.SystemContextX64->Rsp; + mRspAddress[1] = (UINTN)(&LocalVariable); + + return; +} diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTestAsm.nasm b/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTestAsm.nasm new file mode 100644 index 0000000000..4838a12a67 --- /dev/null +++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTestAsm.nasm @@ -0,0 +1,260 @@ +;------------------------------------------------------------------------------ +; +; Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; ArchExceptionHandlerTestAsm.nasm +; +; Abstract: +; +; x64 CPU Exception Handler Lib Unit test +; +;------------------------------------------------------------------------------ + + DEFAULT REL + SECTION .text + +struc GENERAL_REGISTER + .Rdi: resq 1 + .Rsi: resq 1 + .Rbp: resq 1 + .Rbx: resq 1 + .Rdx: resq 1 + .Rcx: resq 1 + .Rax: resq 1 + .R8: resq 1 + .R9: resq 1 + .R10: resq 1 + .R11: resq 1 + .R12: resq 1 + .R13: resq 1 + .R14: resq 1 + .R15: resq 1 + +endstruc + +extern ASM_PFX(mRegisterForCheck) +extern ASM_PFX(mAdjustRegisterBeforeException) +extern ASM_PFX(mFaultInstructionLength) + +;------------------------------------------------------------------------------ +; VOID +; EFIAPI +; TriggerGPException ( +; UINTN Cr4ReservedBit +; ); +;------------------------------------------------------------------------------ +global ASM_PFX(TriggerGPException) +ASM_PFX(TriggerGPException): + ; + ; Set reserved bit 15 of cr4 to 1 + ; + push rcx + lea rcx, [ASM_PFX(mFaultInstructionLength)] + mov qword[rcx], TriggerGPExceptionAfter - TriggerGPExceptionBefore + pop rcx +TriggerGPExceptionBefore: + mov cr4, rcx +TriggerGPExceptionAfter: + ret + +;------------------------------------------------------------------------------ +; VOID +; EFIAPI +; TriggerPFException ( +; UINTN PFAddress +; ); +;------------------------------------------------------------------------------ +global ASM_PFX(TriggerPFException) +ASM_PFX(TriggerPFException): + push rcx + lea rcx, [ASM_PFX(mFaultInstructionLength)] + mov qword[rcx], TriggerPFExceptionAfter - TriggerPFExceptionBefore + pop rcx +TriggerPFExceptionBefore: + mov qword[rcx], 0x1 +TriggerPFExceptionAfter: + ret + +global ASM_PFX(ModifyRcxInGlobalBeforeException) +ASM_PFX(ModifyRcxInGlobalBeforeException): + push rax + lea rax, [ASM_PFX(mAdjustRegisterBeforeException)] + mov [rax + GENERAL_REGISTER.Rcx], rcx + pop rax + ret + +;------------------------------------------------------------------------------ +;VOID +;EFIAPI +;AsmTestConsistencyOfCpuContext ( +; IN EFI_EXCEPTION_TYPE ExceptionType +; IN UINTN FaultParameter OPTIONAL +; ); +;------------------------------------------------------------------------------ +global ASM_PFX(AsmTestConsistencyOfCpuContext) +ASM_PFX(AsmTestConsistencyOfCpuContext): + ; + ; push original register + ; + push r15 + push r14 + push r13 + push r12 + push r11 + push r10 + push r9 + push r8 + push rax + push rcx + push rbx + push rsi + push rdi + push rdx + + ; + ; Modify register to mAdjustRegisterBeforeException + ; + lea r15, [ASM_PFX(mAdjustRegisterBeforeException)] + mov rdi, [r15 + GENERAL_REGISTER.Rdi] + mov rsi, [r15 + GENERAL_REGISTER.Rsi] + mov rbx, [r15 + GENERAL_REGISTER.Rbx] + mov rdx, [r15 + GENERAL_REGISTER.Rdx] + mov rax, [r15 + GENERAL_REGISTER.Rax] + mov r8, [r15 + GENERAL_REGISTER.R8] + mov r9, [r15 + GENERAL_REGISTER.R9] + mov r10, [r15 + GENERAL_REGISTER.R10] + mov r11, [r15 + GENERAL_REGISTER.R11] + mov r12, [r15 + GENERAL_REGISTER.R12] + mov r13, [r15 + GENERAL_REGISTER.R13] + mov r14, [r15 + GENERAL_REGISTER.R14] + mov r15, [r15 + GENERAL_REGISTER.R15] + + cmp rcx, 0xd + jz GPException + cmp rcx, 0xe + jz PFException + jmp INTnException + +PFException: + ; + ; Pop rdx(Pei StackBase) to rcx and restore stack. + ; Modify Rcx in mAdjustRegisterBeforeException. + ; + pop rcx + push rcx + call ASM_PFX(ModifyRcxInGlobalBeforeException) + call ASM_PFX(TriggerPFException) + jmp AfterException + +GPException: + ; + ; Prepare rcx value for GP. + ; Modify Rcx in mAdjustRegisterBeforeException. + ; + pop rcx + push rcx + call ASM_PFX(ModifyRcxInGlobalBeforeException) + call ASM_PFX(TriggerGPException) + jmp AfterException + +INTnException: + ; + ; Modify Rcx in mAdjustRegisterBeforeException. + ; + call ASM_PFX(ModifyRcxInGlobalBeforeException) + call ASM_PFX(TriggerINTnException) + +AfterException: + ; + ; Save register in mRegisterForCheck[1] + ; + push rax + lea rax, [ASM_PFX(mRegisterForCheck)] + add rax, GENERAL_REGISTER_size + mov [rax + GENERAL_REGISTER.Rdi], rdi + mov [rax + GENERAL_REGISTER.Rsi], rsi + mov [rax + GENERAL_REGISTER.Rbx], rbx + mov [rax + GENERAL_REGISTER.Rdx], rdx + mov [rax + GENERAL_REGISTER.Rcx], rcx + pop rcx + mov [rax + GENERAL_REGISTER.Rax], rcx + mov [rax + GENERAL_REGISTER.R8], r8 + mov [rax + GENERAL_REGISTER.R9], r9 + mov [rax + GENERAL_REGISTER.R10], r10 + mov [rax + GENERAL_REGISTER.R11], r11 + mov [rax + GENERAL_REGISTER.R12], r12 + mov [rax + GENERAL_REGISTER.R13], r13 + mov [rax + GENERAL_REGISTER.R14], r14 + mov [rax + GENERAL_REGISTER.R15], r15 + + ; + ; restore original register + ; + pop rdx + pop rdi + pop rsi + pop rbx + pop rcx + pop rax + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + pop r13 + pop r14 + pop r15 + + ret + +;------------------------------------------------------------------------------ +; VOID +; EFIAPI +; TriggerStackOverflowbyCpuStackGuard ( +; VOID +; ); +;------------------------------------------------------------------------------ +global ASM_PFX(TriggerStackOverflowbyCpuStackGuard) +ASM_PFX(TriggerStackOverflowbyCpuStackGuard): + push rcx + lea rcx, [ASM_PFX(mFaultInstructionLength)] + mov qword[rcx], TriggerCpuStackGuardAfter - TriggerCpuStackGuardBefore + pop rcx +TriggerCpuStackGuardBefore: + call TriggerCpuStackGuardBefore +TriggerCpuStackGuardAfter: + ret + +;------------------------------------------------------------------------------ +; VOID +; EFIAPI +; TriggerINTnException ( +; IN EFI_EXCEPTION_TYPE ExceptionType +; ); +;------------------------------------------------------------------------------ +global ASM_PFX(TriggerINTnException) +ASM_PFX(TriggerINTnException): + push rax + push rdx + push rcx + lea rax, [AsmTriggerException1 - AsmTriggerException0] + mul rcx + mov rcx, AsmTriggerException0 + add rax, rcx + pop rcx + pop rdx + jmp rax + ; + ; rax = AsmTriggerException0 + (AsmTriggerException1 - AsmTriggerException0) * rcx + ; +%assign Vector 0 +%rep 22 +AsmTriggerException %+ Vector: + pop rax + INT Vector + ret +%assign Vector Vector+1 +%endrep -- 2.31.1.windows.1 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 1/3] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib 2022-10-11 6:03 ` [PATCH 1/3] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib duntan @ 2022-10-11 9:37 ` Ni, Ray 2022-10-12 1:31 ` duntan 0 siblings, 1 reply; 6+ messages in thread From: Ni, Ray @ 2022-10-11 9:37 UTC (permalink / raw) To: Tan, Dun, devel@edk2.groups.io; +Cc: Dong, Eric, Kumar, Rahul R Can you provide more details in the commit message? Especially explain what "consistent" means for #3 and what "CpuStackGuard in both BSP and AP" means in #4? Thanks, Ray > -----Original Message----- > From: Tan, Dun <dun.tan@intel.com> > Sent: Tuesday, October 11, 2022 2:04 PM > To: devel@edk2.groups.io > Cc: Dong, Eric <eric.dong@intel.com>; Ni, Ray <ray.ni@intel.com>; Kumar, > Rahul R <rahul.r.kumar@intel.com> > Subject: [PATCH 1/3] UefiCpuPkg: Add Unit tests for > DxeCpuExceptionHandlerLib > > Add target based unit tests for the DxeCpuExceptionHandlerLib. > A DXE driver is created to test DxeCpuExceptionHandlerLib. Four > kinds of test cases are created in this module: > 1.Test if exception handler can be registered/unregistered for > no error code exception. 2.Test if exception handler can be > registered/unregistered for GP and PF. 3.Test if Cpu Context > is consistent before and after exception. 4.Test if stack > overflow can be captured by CpuStackGuard in both Bsp and AP. > > Signed-off-by: Dun Tan <dun.tan@intel.com> > Cc: Eric Dong <eric.dong@intel.com> > Cc: Ray Ni <ray.ni@intel.com> > Cc: Rahul Kumar <rahul1.kumar@intel.com> > --- > UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h > | 353 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > +++++ > > UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestComm > on.c | 856 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++ > > UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibUnit > Test.inf | 58 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > > UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnitTe > st.c | 196 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++ > > UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTest.c > | 167 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > +++++++++++++++++++++++++++++++++++++++++++++++++++ > > UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTestA > sm.nasm | 260 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++ > 6 files changed, 1890 insertions(+) > > diff --git > a/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h > new file mode 100644 > index 0000000000..bfbc483075 > --- /dev/null > +++ > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h > @@ -0,0 +1,353 @@ > +/** @file > + > + Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#ifndef CPU_EXCEPTION_HANDLER_TEST_H_ > +#define CPU_EXCEPTION_HANDLER_TEST_H_ > + > +#include <Uefi.h> > +#include <Library/BaseLib.h> > +#include <Library/BaseMemoryLib.h> > +#include <Library/DebugLib.h> > +#include <Library/UnitTestLib.h> > +#include <Library/MemoryAllocationLib.h> > +#include <Library/UnitTestHostBaseLib.h> > +#include <Library/CpuExceptionHandlerLib.h> > +#include <Library/UefiLib.h> > +#include <Library/SerialPortLib.h> > +#include <Library/HobLib.h> > +#include <Library/CpuPageTableLib.h> > +#include <Guid/MemoryAllocationHob.h> > +#include <Protocol/MpService.h> > +#include <PiPei.h> > +#include <Ppi/MpServices2.h> > + > +#define UNIT_TEST_APP_NAME "Cpu Exception Handler Lib Unit Tests" > +#define UNIT_TEST_APP_VERSION "1.0" > + > +#define CPU_INTERRUPT_NUM 256 > +#define SPEC_MAX_EXCEPTION_NUM 22 > +#define CR4_RESERVED_BIT BIT15 > + > +#pragma pack (1) > + > +typedef union { > + struct { > + UINT32 LimitLow : 16; > + UINT32 BaseLow : 16; > + UINT32 BaseMid : 8; > + UINT32 Type : 4; > + UINT32 System : 1; > + UINT32 Dpl : 2; > + UINT32 Present : 1; > + UINT32 LimitHigh : 4; > + UINT32 Software : 1; > + UINT32 Reserved : 1; > + UINT32 DefaultSize : 1; > + UINT32 Granularity : 1; > + UINT32 BaseHigh : 8; > + } Bits; > + UINT64 Uint64; > +} IA32_GDT; > + > +typedef struct { > + UINT32 InitialApicId; > + UINT32 ApicId; > + UINT32 Health; > + UINT64 ApTopOfStack; > +} CPU_INFO_IN_HOB; > +#pragma pack () > + > +typedef struct { > + IA32_DESCRIPTOR OriginalGdtr; > + IA32_DESCRIPTOR OriginalIdtr; > + UINT16 Tr; > +} CPU_REGISTER_BUFFER; > + > +typedef union { > + EDKII_PEI_MP_SERVICES2_PPI *Ppi; > + EFI_MP_SERVICES_PROTOCOL *Protocol; > +} MP_SERVICES; > + > +typedef struct { > + VOID *Buffer; > + UINTN BufferSize; > + EFI_STATUS Status; > +} EXCEPTION_STACK_SWITCH_CONTEXT; > + > +typedef struct { > + UINT64 Rdi; > + UINT64 Rsi; > + UINT64 Rbp; > + UINT64 Rbx; > + UINT64 Rdx; > + UINT64 Rcx; > + UINT64 Rax; > + UINT64 R8Register; > + UINT64 R9Register; > + UINT64 R10Register; > + UINT64 R11Register; > + UINT64 R12Register; > + UINT64 R13Register; > + UINT64 R14Register; > + UINT64 R15Register; > +} GENERAL_REGISTER; > + > +typedef struct { > + UINT32 Edi; > + UINT32 Esi; > + UINT32 Ebp; > + UINT32 Ebx; > + UINT32 Edx; > + UINT32 Ecx; > + UINT32 Eax; > +} GENERAL_REGISTER_IA32; > + > +extern UINTN mFaultInstructionLength; > +extern EFI_EXCEPTION_TYPE mExceptionType; > +extern UINTN mRspAddress[]; > +extern GENERAL_REGISTER mRegisterForCheck[]; > +extern GENERAL_REGISTER mAdjustRegisterBeforeException; > +extern GENERAL_REGISTER_IA32 mIa32RegisterForCheck[]; > +extern GENERAL_REGISTER_IA32 mAdjustIa32RegisterBeforeException; > + > +/** > + Initialize Bsp Idt with a new Idt table and return the IA32_DESCRIPTOR > buffer. > + In PEIM, store original PeiServicePointer before new Idt table. > + > + @return Pointer to the allocated IA32_DESCRIPTOR buffer. > +**/ > +VOID * > +InitializeBspIdt ( > + VOID > + ); > + > +/** > + Trigger no error code exception by INT n instruction. > + > + @param[in] ExceptionType No error code exception type. > +**/ > +VOID > +EFIAPI > +TriggerINTnException ( > + IN EFI_EXCEPTION_TYPE ExceptionType > + ); > + > +/** > + Trigger GP exception. > + > + @param[in] Cr4ReservedBit Cr4 reserved bit. > +**/ > +VOID > +EFIAPI > +TriggerGPException ( > + UINTN Cr4ReservedBit > + ); > + > +/** > + Trigger PF exception by write to not present or ReadOnly address. > + > + @param[in] PFAddress Not present or ReadOnly address in page table. > +**/ > +VOID > +EFIAPI > +TriggerPFException ( > + UINTN PFAddress > + ); > + > +/** > + Special handler for fault exception. > + Rip/Eip in SystemContext will be modified to the instruction after the > exception instruction. > + > + @param ExceptionType Exception type. > + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. > +**/ > +VOID > +EFIAPI > +AdjustRipForFaultHandler ( > + IN EFI_EXCEPTION_TYPE ExceptionType, > + IN EFI_SYSTEM_CONTEXT SystemContext > + ); > + > +/** > + Test consistency of Cpu context. Four steps: > + 1. Set Cpu register to mAdjustRegisterBeforeException before exception. > + 2. Trigger exception specified by ExceptionType. > + 3. Store SystemContext in mRegisterForCheck[0] and set SystemContext > to mAdjustRegisterInsideException in handler. > + 4. Store the Cpu register in mRegisterForCheck[1] > + > + Rcx/Ecx in mAdjustRegisterInsideException is decided by different > exception type runtime since Rcx/Ecx is needed in assembly code. > + For GP and PF, Rcx/Ecx is set to FaultParameter. For other exception > triggered by INTn, Rcx/Ecx is set to ExceptionType. > + > + @param[in] ExceptionType Exception type. > + @param[in] FaultParameter Parameter for GP and PF. OPTIONAL > +**/ > +VOID > +EFIAPI > +AsmTestConsistencyOfCpuContext ( > + IN EFI_EXCEPTION_TYPE ExceptionType, > + IN UINTN FaultParameter OPTIONAL > + ); > + > +/** > + Special handler for ConsistencyOfCpuContext test case. General register in > SystemContext > + is modified in this handler. > + > + @param ExceptionType Exception type. > + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. > +**/ > +VOID > +EFIAPI > +AdjustCpuContextHandler ( > + IN EFI_EXCEPTION_TYPE ExceptionType, > + IN EFI_SYSTEM_CONTEXT SystemContext > + ); > + > +/** > + Compare cpu context in ConsistencyOfCpuContext test case. > + 1.Compare mRegisterForCheck[0] with mAdjustRegisterBeforeException. > + 2.Compare mRegisterForCheck[1] with mAdjustRegisterAfterException. > + > + @retval UNIT_TEST_PASSED The Unit test has completed and it was > successful. > + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. > +**/ > +UNIT_TEST_STATUS > +CompareCpuContext ( > + VOID > + ); > + > +/** > + Get EFI_MP_SERVICES_PROTOCOL/EDKII_PEI_MP_SERVICES2_PPI pointer. > + > + @param[out] MpServices Pointer to the MP_SERVICES buffer > + > + @retval EFI_SUCCESS EFI_MP_SERVICES_PROTOCOL/PPI interface is > returned > + @retval EFI_NOT_FOUND EFI_MP_SERVICES_PROTOCOL/PPI interface is > not found > +**/ > +EFI_STATUS > +GetMpServices ( > + OUT MP_SERVICES *MpServices > + ); > + > +/** > + Create CpuExceptionLibUnitTestSuite and add test case. > + > + @param[in] FrameworkHandle Unit test framework. > + > + @return EFI_SUCCESS The unit test suite was created. > + @retval EFI_OUT_OF_RESOURCES There are not enough resources > available to > + initialize the unit test suite. > +**/ > +EFI_STATUS > +AddCommonTestCase ( > + IN UNIT_TEST_FRAMEWORK_HANDLE Framework > + ); > + > +/** > + Execute a caller provided function on all enabled APs. > + > + @param[in] MpServices MP_SERVICES structure. > + @param[in] Procedure Pointer to the function to be run on enabled APs > of the system. > + @param[in] SingleThread If TRUE, then all the enabled APs execute the > function specified by Procedure > + one by one, in ascending order of processor handle number. > + If FALSE, then all the enabled APs execute the function > specified by Procedure > + simultaneously. > + @param[in] TimeoutInMicroseconds Indicates the time limit in > microseconds for APs to return from Procedure, > + for blocking mode only. Zero means infinity. > + @param[in] ProcedureArgument The parameter passed into Procedure > for all APs. > + > + @retval EFI_SUCCESS Execute a caller provided function on all enabled > APs successfully > + @retval Others Execute a caller provided function on all enabled APs > unsuccessfully > +**/ > +EFI_STATUS > +MpServicesUnitTestStartupAllAPs ( > + IN MP_SERVICES MpServices, > + IN EFI_AP_PROCEDURE Procedure, > + IN BOOLEAN SingleThread, > + IN UINTN TimeoutInMicroSeconds, > + IN VOID *ProcedureArgument > + ); > + > +/** > + Caller gets one enabled AP to execute a caller-provided function. > + > + @param[in] MpServices MP_SERVICES structure. > + @param[in] Procedure Pointer to the function to be run on enabled APs > of the system. > + @param[in] ProcessorNumber The handle number of the AP. > + @param[in] TimeoutInMicroseconds Indicates the time limit in > microseconds for APs to return from Procedure, > + for blocking mode only. Zero means infinity. > + @param[in] ProcedureArgument The parameter passed into Procedure > for all APs. > + > + > + @retval EFI_SUCCESS Caller gets one enabled AP to execute a caller- > provided function successfully > + @retval Others Caller gets one enabled AP to execute a caller- > provided function unsuccessfully > +**/ > +EFI_STATUS > +MpServicesUnitTestStartupThisAP ( > + IN MP_SERVICES MpServices, > + IN EFI_AP_PROCEDURE Procedure, > + IN UINTN ProcessorNumber, > + IN UINTN TimeoutInMicroSeconds, > + IN VOID *ProcedureArgument > + ); > + > +/** > + Get the handle number for the calling processor. > + > + @param[in] MpServices Pointer to MP_SERVICES structure. > + @param[out] ProcessorNumber The handle number for the calling > processor. > + > + @retval EFI_SUCCESS Get the handle number for the calling processor > successfully. > + @retval Others Get the handle number for the calling processor > unsuccessfully. > +**/ > +EFI_STATUS > +MpServicesUnitTestWhoAmI ( > + IN MP_SERVICES MpServices, > + OUT UINTN *ProcessorNumber > + ); > + > +/** > + Retrieve the number of logical processor in the platform and the number > of those logical processors that > + are enabled on this boot. > + > + @param[in] MpServices Pointer to MP_SERVICES structure. > + @param[out] NumberOfProcessors Pointer to the total number of logical > processors in the system, including > + the BSP and disabled APs. > + @param[out] NumberOfEnabledProcessors Pointer to the number of > processors in the system that are enabled. > + > + @retval EFI_SUCCESS Retrieve the number of logical processor > successfully > + @retval Others Retrieve the number of logical processor > unsuccessfully > +**/ > +EFI_STATUS > +MpServicesUnitTestGetNumberOfProcessors ( > + IN MP_SERVICES MpServices, > + OUT UINTN *NumberOfProcessors, > + OUT UINTN *NumberOfEnabledProcessors > + ); > + > +/** > + Trigger stack overflow by CpuStackGuard. > +**/ > +VOID > +EFIAPI > +TriggerStackOverflowbyCpuStackGuard ( > + VOID > + ); > + > +/** > + Special handler for CpuStackGuard test case. > + > + @param ExceptionType Exception type. > + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. > +**/ > +VOID > +EFIAPI > +CpuStackGuardExceptionHandler ( > + IN EFI_EXCEPTION_TYPE ExceptionType, > + IN EFI_SYSTEM_CONTEXT SystemContext > + ); > + > +#endif > diff --git > a/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCom > mon.c > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCom > mon.c > new file mode 100644 > index 0000000000..1c3d011c76 > --- /dev/null > +++ > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCom > mon.c > @@ -0,0 +1,856 @@ > +/** @file > + Unit tests of the CpuExceptionHandlerLib. > + > + Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#include "CpuExceptionHandlerTest.h" > + > +// > +// Length of the assembly falut instruction. > +// > +UINTN mFaultInstructionLength = 0; > +EFI_EXCEPTION_TYPE mExceptionType = 256; > +UINTN mNumberOfProcessors = 1; > +UINTN mRspAddress[2] = { 0 }; > + > +// > +// Error code flag indicating whether or not an error code will be > +// pushed on the stack if an exception occurs. > +// > +// 1 means an error code will be pushed, otherwise 0 > +// > +CONST UINT32 mErrorCodeExceptionFlag = 0x20227d00; > + > +/** > + Special handler for trap exception. > + > + @param ExceptionType Exception type. > + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. > +**/ > +VOID > +EFIAPI > +INTnExceptionHandler ( > + IN EFI_EXCEPTION_TYPE ExceptionType, > + IN EFI_SYSTEM_CONTEXT SystemContext > + ) > +{ > + mExceptionType = ExceptionType; > +} > + > +/** > + Restore all cpu original register before test case. > + > + @param[in] Buffer Argument of the procedure. > +**/ > +VOID > +EFIAPI > +RestoreRegistersPerCpu ( > + IN VOID *Buffer > + ) > +{ > + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; > + UINT16 Tr; > + IA32_TSS_DESCRIPTOR *Tss; > + > + CpuOriginalRegisterBuffer = (CPU_REGISTER_BUFFER *)Buffer; > + > + AsmWriteGdtr (&(CpuOriginalRegisterBuffer->OriginalGdtr)); > + AsmWriteIdtr (&(CpuOriginalRegisterBuffer->OriginalIdtr)); > + Tr = CpuOriginalRegisterBuffer->Tr; > + if ((Tr != 0) && (Tr < CpuOriginalRegisterBuffer->OriginalGdtr.Limit)) { > + Tss = (IA32_TSS_DESCRIPTOR *)(CpuOriginalRegisterBuffer- > >OriginalGdtr.Base + Tr); > + if (Tss->Bits.P == 1) { > + // > + // Clear busy bit of TSS before write Tr > + // > + Tss->Bits.Type &= 0xD; > + AsmWriteTr (Tr); > + } > + } > +} > + > +/** > + Restore all cpu original register before test case. > + > + @param[in] MpServices MpServices. > + @param[in] CpuOriginalRegisterBuffer Address of > CpuOriginalRegisterBuffer. > + @param[in] BspProcessorNum Bsp processor number. > +**/ > +VOID > +RestoreAllCpuRegisters ( > + MP_SERVICES *MpServices, OPTIONAL > + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer, > + UINTN BspProcessorNum > + ) > +{ > + CPU_REGISTER_BUFFER *AllCpuOriginalRegisterBuffer; > + UINTN Index; > + EFI_STATUS Status; > + > + for (Index = 0; Index < mNumberOfProcessors; ++Index) { > + AllCpuOriginalRegisterBuffer = CpuOriginalRegisterBuffer + Index; > + if (Index == BspProcessorNum) { > + RestoreRegistersPerCpu ((VOID *)AllCpuOriginalRegisterBuffer); > + continue; > + } > + > + ASSERT (MpServices != NULL); > + Status = MpServicesUnitTestStartupThisAP ( > + *MpServices, > + (EFI_AP_PROCEDURE)RestoreRegistersPerCpu, > + Index, > + 0, > + (VOID *)AllCpuOriginalRegisterBuffer > + ); > + ASSERT_EFI_ERROR (Status); > + } > +} > + > +/** > + Store all cpu original register before test case. > + > + @param[in] Buffer Argument of the procedure. > +**/ > +VOID > +EFIAPI > +SaveRegisterPerCpu ( > + IN VOID *Buffer > + ) > +{ > + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; > + IA32_DESCRIPTOR Gdtr; > + IA32_DESCRIPTOR Idtr; > + > + CpuOriginalRegisterBuffer = (CPU_REGISTER_BUFFER *)Buffer; > + > + AsmReadGdtr (&Gdtr); > + AsmReadIdtr (&Idtr); > + CpuOriginalRegisterBuffer->OriginalGdtr.Base = Gdtr.Base; > + CpuOriginalRegisterBuffer->OriginalGdtr.Limit = Gdtr.Limit; > + CpuOriginalRegisterBuffer->OriginalIdtr.Base = Idtr.Base; > + CpuOriginalRegisterBuffer->OriginalIdtr.Limit = Idtr.Limit; > + CpuOriginalRegisterBuffer->Tr = AsmReadTr (); > +} > + > +/** > + Store all cpu original register before test case. > + > + @param[in] MpServices MpServices. > + @param[in] BspProcessorNum Bsp processor number. > + > + @return Pointer to the allocated CPU_REGISTER_BUFFER. > +**/ > +CPU_REGISTER_BUFFER * > +SaveAllCpuRegisters ( > + MP_SERVICES *MpServices, OPTIONAL > + UINTN BspProcessorNum > + ) > +{ > + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; > + CPU_REGISTER_BUFFER *AllCpuOriginalRegisterBuffer; > + EFI_STATUS Status; > + UINTN Index; > + > + CpuOriginalRegisterBuffer = AllocateZeroPool (mNumberOfProcessors * > sizeof (CPU_REGISTER_BUFFER)); > + ASSERT (CpuOriginalRegisterBuffer != NULL); > + > + for (Index = 0; Index < mNumberOfProcessors; ++Index) { > + AllCpuOriginalRegisterBuffer = CpuOriginalRegisterBuffer + Index; > + if (Index == BspProcessorNum) { > + SaveRegisterPerCpu ((VOID *)AllCpuOriginalRegisterBuffer); > + continue; > + } > + > + ASSERT (MpServices != NULL); > + Status = MpServicesUnitTestStartupThisAP ( > + *MpServices, > + (EFI_AP_PROCEDURE)SaveRegisterPerCpu, > + Index, > + 0, > + (VOID *)AllCpuOriginalRegisterBuffer > + ); > + ASSERT_EFI_ERROR (Status); > + } > + > + return CpuOriginalRegisterBuffer; > +} > + > +/** > + Initialize Ap Idt Procedure. > + > + @param[in] Buffer Argument of the procedure. > +**/ > +VOID > +EFIAPI > +InitializeIdtPerAp ( > + IN VOID *Buffer > + ) > +{ > + AsmWriteIdtr (Buffer); > +} > + > +/** > + Initialize all Ap Idt. > + > + @param[in] MpServices MpServices. > + @param[in] BspIdtr Pointer to IA32_DESCRIPTOR allocated by Bsp. > +**/ > +VOID > +InitializeApIdt ( > + MP_SERVICES MpServices, > + VOID *BspIdtr > + ) > +{ > + EFI_STATUS Status; > + > + Status = MpServicesUnitTestStartupAllAPs ( > + MpServices, > + (EFI_AP_PROCEDURE)InitializeIdtPerAp, > + FALSE, > + 0, > + BspIdtr > + ); > + ASSERT_EFI_ERROR (Status); > +} > + > +/** > + Check if exception handler can registered/unregistered for no error code > exception. > + > + @param[in] Context [Optional] An optional parameter that enables: > + 1) test-case reuse with varied parameters and > + 2) test-case re-entry for Target tests that need a > + reboot. This parameter is a VOID* and it is the > + responsibility of the test author to ensure that the > + contents are well understood by all test cases that may > + consume it. > + > + @retval UNIT_TEST_PASSED The Unit test has completed and the > test > + case was successful. > + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. > +**/ > +UNIT_TEST_STATUS > +EFIAPI > +TestRegisterHandlerForNoErrorCodeException ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + EFI_STATUS Status; > + UINTN Index; > + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; > + VOID *NewIdtr; > + > + CpuOriginalRegisterBuffer = SaveAllCpuRegisters (NULL, 0); > + NewIdtr = InitializeBspIdt (); > + Status = InitializeCpuExceptionHandlers (NULL); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + > + for (Index = 0; Index < SPEC_MAX_EXCEPTION_NUM; Index++) { > + // > + // Only test no error code exception by INT n instruction. > + // > + if ((mErrorCodeExceptionFlag & (1 << Index)) != 0) { > + continue; > + } > + > + DEBUG ((DEBUG_INFO, "TestCase1: ExceptionType is %d\n", Index)); > + Status = RegisterCpuInterruptHandler (Index, INTnExceptionHandler); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + > + TriggerINTnException (Index); > + UT_ASSERT_EQUAL (mExceptionType, Index); > + Status = RegisterCpuInterruptHandler (Index, NULL); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + } > + > + RestoreAllCpuRegisters (NULL, CpuOriginalRegisterBuffer, 0); > + FreePool (CpuOriginalRegisterBuffer); > + FreePool (NewIdtr); > + return UNIT_TEST_PASSED; > +} > + > +/** > + Get Bsp stack base. > + > + @param[out] StackBase Pointer to stack base of BSP. > +**/ > +VOID > +GetBspStackBase ( > + OUT UINTN *StackBase > + ) > +{ > + EFI_PEI_HOB_POINTERS Hob; > + EFI_HOB_MEMORY_ALLOCATION *MemoryHob; > + > + // > + // Get the base of stack from Hob. > + // > + ASSERT (StackBase != NULL); > + Hob.Raw = GetHobList (); > + while ((Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, > Hob.Raw)) != NULL) { > + MemoryHob = Hob.MemoryAllocation; > + if (CompareGuid (&gEfiHobMemoryAllocStackGuid, &MemoryHob- > >AllocDescriptor.Name)) { > + DEBUG (( > + DEBUG_INFO, > + "%a: Bsp StackBase = 0x%016lx StackSize = 0x%016lx\n", > + __FUNCTION__, > + MemoryHob->AllocDescriptor.MemoryBaseAddress, > + MemoryHob->AllocDescriptor.MemoryLength > + )); > + > + *StackBase = (UINTN)MemoryHob- > >AllocDescriptor.MemoryBaseAddress; > + // > + // Ensure the base of the stack is page-size aligned. > + // > + ASSERT ((*StackBase & EFI_PAGE_MASK) == 0); > + break; > + } > + > + Hob.Raw = GET_NEXT_HOB (Hob); > + } > + > + ASSERT (*StackBase != 0); > +} > + > +/** > + Initialize Ap Idt Procedure. > + > + @param[in] Buffer Argument of the procedure. > +**/ > +VOID > +EFIAPI > +GetStackBasePerAp ( > + IN VOID *Buffer > + ) > +{ > + UINTN ApTopOfStack; > + > + ApTopOfStack = ALIGN_VALUE ((UINTN)&ApTopOfStack, > (UINTN)PcdGet32 (PcdCpuApStackSize)); > + *(UINTN *)Buffer = ApTopOfStack - (UINTN)PcdGet32 > (PcdCpuApStackSize); > +} > + > +/** > + Get Ap stack Info. > + > + @param[in] MpServices MpServices. > + @param[in] BspProcessorNum Bsp processor number. > + > + @return Pointer to the allocated CpuStackBaseBuffer. > +**/ > +UINTN * > +GetAllCpuStackBase ( > + MP_SERVICES *MpServices, > + UINTN BspProcessorNum > + ) > +{ > + UINTN *CpuStackBaseBuffer; > + UINTN *AllCpuStackBaseBuffer; > + EFI_STATUS Status; > + UINTN Index; > + > + CpuStackBaseBuffer = AllocateZeroPool (mNumberOfProcessors * sizeof > (UINTN)); > + ASSERT (CpuStackBaseBuffer != NULL); > + > + for (Index = 0; Index < mNumberOfProcessors; ++Index) { > + AllCpuStackBaseBuffer = CpuStackBaseBuffer + Index; > + if (Index == BspProcessorNum) { > + GetBspStackBase (AllCpuStackBaseBuffer); > + continue; > + } > + > + ASSERT (MpServices != NULL); > + Status = MpServicesUnitTestStartupThisAP ( > + *MpServices, > + (EFI_AP_PROCEDURE)GetStackBasePerAp, > + Index, > + 0, > + (VOID *)AllCpuStackBaseBuffer > + ); > + ASSERT_EFI_ERROR (Status); > + DEBUG ((DEBUG_INFO, "AP[%d] StackBase = 0x%x\n", Index, > CpuStackBaseBuffer[Index])); > + } > + > + return CpuStackBaseBuffer; > +} > + > +/** > + Return not present or ReadOnly address in page table. > + > + @param[out] PFAddress Access to the address which is not permitted will > trigger PF exceptions. > +**/ > +VOID > +PageFaultAddressInPageTable ( > + UINTN *PFAddress > + ) > +{ > + IA32_CR0 Cr0; > + IA32_CR4 Cr4; > + UINTN PageTable; > + PAGING_MODE PagingMode; > + BOOLEAN Enable5LevelPaging; > + RETURN_STATUS Status; > + IA32_MAP_ENTRY *Map; > + UINTN MapCount; > + UINTN Index; > + > + ASSERT (PFAddress != NULL); > + *PFAddress = 0; > + > + Cr0.UintN = AsmReadCr0 (); > + if (Cr0.Bits.PG == 0) { > + return; > + } > + > + PageTable = AsmReadCr3 (); > + Cr4.UintN = AsmReadCr4 (); > + if (sizeof (UINTN) == sizeof (UINT32)) { > + ASSERT (Cr4.Bits.PAE == 1); > + PagingMode = PagingPae; > + } else { > + Enable5LevelPaging = (BOOLEAN)(Cr4.Bits.LA57 == 1); > + PagingMode = Enable5LevelPaging ? Paging5Level : Paging4Level; > + } > + > + MapCount = 0; > + Status = PageTableParse (PageTable, PagingMode, NULL, &MapCount); > + ASSERT (Status == RETURN_BUFFER_TOO_SMALL); > + Map = AllocatePages (EFI_SIZE_TO_PAGES (MapCount * sizeof > (IA32_MAP_ENTRY))); > + Status = PageTableParse (PageTable, PagingMode, Map, &MapCount); > + ASSERT (Status == RETURN_SUCCESS); > + > + for (Index = 0; Index < MapCount; Index++) { > + DEBUG (( > + DEBUG_ERROR, > + "%02d: %016lx - %016lx, %016lx\n", > + Index, > + Map[Index].LinearAddress, > + Map[Index].LinearAddress + Map[Index].Length, > + Map[Index].Attribute.Uint64 > + )); > + > + // > + // Not present address in page table. > + // > + if ((Index >= 1) && (Map[Index].LinearAddress > Map[Index - > 1].LinearAddress + Map[Index - 1].Length)) { > + *PFAddress = (UINTN)(Map[Index - 1].LinearAddress + Map[Index - > 1].Length); > + return; > + } > + > + // > + // ReadOnly address in page table. > + // > + if ((Cr0.Bits.WP != 0) && (Map[Index].Attribute.Bits.ReadWrite == 0)) { > + *PFAddress = (UINTN)Map[Index].LinearAddress; > + return; > + } > + } > +} > + > +/** > + Test if exception handler can registered/unregistered for GP and PF. > + > + @param[in] Context [Optional] An optional parameter that enables: > + 1) test-case reuse with varied parameters and > + 2) test-case re-entry for Target tests that need a > + reboot. This parameter is a VOID* and it is the > + responsibility of the test author to ensure that the > + contents are well understood by all test cases that may > + consume it. > + > + @retval UNIT_TEST_PASSED The Unit test has completed and the > test > + case was successful. > + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. > +**/ > +UNIT_TEST_STATUS > +EFIAPI > +TestRegisterHandlerForGPAndPF ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + EFI_STATUS Status; > + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; > + UINTN PFAddress; > + VOID *NewIdtr; > + > + PFAddress = 0; > + CpuOriginalRegisterBuffer = SaveAllCpuRegisters (NULL, 0); > + NewIdtr = InitializeBspIdt (); > + Status = InitializeCpuExceptionHandlers (NULL); > + > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + > + // > + // GP exception. > + // > + DEBUG ((DEBUG_INFO, "TestCase2: ExceptionType is %d\n", > EXCEPT_IA32_GP_FAULT)); > + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_GP_FAULT, > AdjustRipForFaultHandler); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + > + TriggerGPException (CR4_RESERVED_BIT); > + UT_ASSERT_EQUAL (mExceptionType, EXCEPT_IA32_GP_FAULT); > + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_GP_FAULT, NULL); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + > + // > + // PF exception. > + // > + PageFaultAddressInPageTable (&PFAddress); > + > + if (PFAddress > 0) { > + DEBUG ((DEBUG_INFO, "TestCase2: ExceptionType is %d\n", > EXCEPT_IA32_PAGE_FAULT)); > + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, > AdjustRipForFaultHandler); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + TriggerPFException (PFAddress); > + > + UT_ASSERT_EQUAL (mExceptionType, EXCEPT_IA32_PAGE_FAULT); > + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, > NULL); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + } > + > + RestoreAllCpuRegisters (NULL, CpuOriginalRegisterBuffer, 0); > + FreePool (CpuOriginalRegisterBuffer); > + FreePool (NewIdtr); > + return UNIT_TEST_PASSED; > +} > + > +/** > + Test if Cpu Context is consistent before and after exception. > + > + @param[in] Context [Optional] An optional parameter that enables: > + 1) test-case reuse with varied parameters and > + 2) test-case re-entry for Target tests that need a > + reboot. This parameter is a VOID* and it is the > + responsibility of the test author to ensure that the > + contents are well understood by all test cases that may > + consume it. > + > + @retval UNIT_TEST_PASSED The Unit test has completed and the > test > + case was successful. > + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. > +**/ > +UNIT_TEST_STATUS > +EFIAPI > +TestCpuContextConsistency ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + EFI_STATUS Status; > + UINTN Index; > + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; > + UINTN FaultParameter; > + VOID *NewIdtr; > + > + FaultParameter = 0; > + CpuOriginalRegisterBuffer = SaveAllCpuRegisters (NULL, 0); > + NewIdtr = InitializeBspIdt (); > + Status = InitializeCpuExceptionHandlers (NULL); > + > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + > + for (Index = 0; Index < 22; Index++) { > + if (Index == EXCEPT_IA32_PAGE_FAULT) { > + PageFaultAddressInPageTable (&FaultParameter); > + if (FaultParameter == 0) { > + continue; > + } > + } else if (Index == EXCEPT_IA32_GP_FAULT) { > + FaultParameter = CR4_RESERVED_BIT; > + } else { > + if ((mErrorCodeExceptionFlag & (1 << Index)) != 0) { > + continue; > + } > + } > + > + DEBUG ((DEBUG_INFO, "TestCase3: ExceptionType is %d\n", Index)); > + Status = RegisterCpuInterruptHandler (Index, AdjustCpuContextHandler); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + > + // > + // Trigger different type exception and compare different stage cpu > context. > + // > + AsmTestConsistencyOfCpuContext (Index, FaultParameter); > + CompareCpuContext (); > + Status = RegisterCpuInterruptHandler (Index, NULL); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + } > + > + RestoreAllCpuRegisters (NULL, CpuOriginalRegisterBuffer, 0); > + FreePool (CpuOriginalRegisterBuffer); > + FreePool (NewIdtr); > + return UNIT_TEST_PASSED; > +} > + > +/** > + Initializes CPU exceptions handlers for the sake of stack switch > requirement. > + > + This function is a wrapper of InitializeSeparateExceptionStacks. It's mainly > + for the sake of AP's init because of EFI_AP_PROCEDURE API requirement. > + > + @param[in,out] Buffer The pointer to private data buffer. > + > +**/ > +VOID > +EFIAPI > +InitializeExceptionStackSwitchHandlersPerAp ( > + IN OUT VOID *Buffer > + ) > +{ > + EXCEPTION_STACK_SWITCH_CONTEXT *CpuSwitchStackData; > + > + CpuSwitchStackData = (EXCEPTION_STACK_SWITCH_CONTEXT *)Buffer; > + > + // > + // This may be called twice for each Cpu. Only run > InitializeSeparateExceptionStacks > + // if this is the first call or the first call failed because of size too small. > + // > + if ((CpuSwitchStackData->Status == EFI_NOT_STARTED) || > (CpuSwitchStackData->Status == EFI_BUFFER_TOO_SMALL)) { > + CpuSwitchStackData->Status = InitializeSeparateExceptionStacks > (CpuSwitchStackData->Buffer, &CpuSwitchStackData->BufferSize); > + } > +} > + > +/** > + Initializes MP exceptions handlers for the sake of stack switch requirement. > + > + This function will allocate required resources required to setup stack switch > + and pass them through SwitchStackData to each logic processor. > + > + @param[in, out] MpServices MpServices. > + @param[in, out] BspProcessorNum Bsp processor number. > + > + @return Pointer to the allocated SwitchStackData. > +**/ > +EXCEPTION_STACK_SWITCH_CONTEXT * > +InitializeMpExceptionStackSwitchHandlers ( > + MP_SERVICES MpServices, > + UINTN BspProcessorNum > + ) > +{ > + UINTN Index; > + EXCEPTION_STACK_SWITCH_CONTEXT *SwitchStackData; > + EXCEPTION_STACK_SWITCH_CONTEXT *CpuSwitchStackData; > + UINTN BufferSize; > + EFI_STATUS Status; > + UINT8 *Buffer; > + > + SwitchStackData = AllocateZeroPool (mNumberOfProcessors * sizeof > (EXCEPTION_STACK_SWITCH_CONTEXT)); > + ASSERT (SwitchStackData != NULL); > + for (Index = 0; Index < mNumberOfProcessors; ++Index) { > + CpuSwitchStackData = SwitchStackData + Index; > + // > + // Because the procedure may runs multiple times, use the status > EFI_NOT_STARTED > + // to indicate the procedure haven't been run yet. > + // > + SwitchStackData[Index].Status = EFI_NOT_STARTED; > + if (Index == BspProcessorNum) { > + InitializeExceptionStackSwitchHandlersPerAp ((VOID > *)CpuSwitchStackData); > + continue; > + } > + > + Status = MpServicesUnitTestStartupThisAP ( > + MpServices, > + InitializeExceptionStackSwitchHandlersPerAp, > + Index, > + 0, > + (VOID *)CpuSwitchStackData > + ); > + ASSERT_EFI_ERROR (Status); > + } > + > + BufferSize = 0; > + for (Index = 0; Index < mNumberOfProcessors; ++Index) { > + if (SwitchStackData[Index].Status == EFI_BUFFER_TOO_SMALL) { > + ASSERT (SwitchStackData[Index].BufferSize != 0); > + BufferSize += SwitchStackData[Index].BufferSize; > + } else { > + ASSERT (SwitchStackData[Index].Status == EFI_SUCCESS); > + ASSERT (SwitchStackData[Index].BufferSize == 0); > + } > + } > + > + if (BufferSize != 0) { > + Buffer = AllocateZeroPool (BufferSize); > + ASSERT (Buffer != NULL); > + BufferSize = 0; > + for (Index = 0; Index < mNumberOfProcessors; ++Index) { > + if (SwitchStackData[Index].Status == EFI_BUFFER_TOO_SMALL) { > + SwitchStackData[Index].Buffer = (VOID *)(&Buffer[BufferSize]); > + BufferSize += SwitchStackData[Index].BufferSize; > + DEBUG (( > + DEBUG_INFO, > + "Buffer[cpu%lu] for InitializeExceptionStackSwitchHandlersPerAp: > 0x%lX with size 0x%lX\n", > + (UINT64)(UINTN)Index, > + (UINT64)(UINTN)SwitchStackData[Index].Buffer, > + (UINT64)(UINTN)SwitchStackData[Index].BufferSize > + )); > + } > + } > + > + for (Index = 0; Index < mNumberOfProcessors; ++Index) { > + CpuSwitchStackData = SwitchStackData + Index; > + if (Index == BspProcessorNum) { > + InitializeExceptionStackSwitchHandlersPerAp ((VOID > *)CpuSwitchStackData); > + continue; > + } > + > + Status = MpServicesUnitTestStartupThisAP ( > + MpServices, > + InitializeExceptionStackSwitchHandlersPerAp, > + Index, > + 0, > + (VOID *)CpuSwitchStackData > + ); > + ASSERT_EFI_ERROR (Status); > + } > + > + for (Index = 0; Index < mNumberOfProcessors; ++Index) { > + ASSERT (SwitchStackData[Index].Status == EFI_SUCCESS); > + } > + } > + > + return SwitchStackData; > +} > + > +/** > + Test if stack overflow is captured by CpuStackGuard in Bsp and AP. > + > + @param[in] Context [Optional] An optional parameter that enables: > + 1) test-case reuse with varied parameters and > + 2) test-case re-entry for Target tests that need a > + reboot. This parameter is a VOID* and it is the > + responsibility of the test author to ensure that the > + contents are well understood by all test cases that may > + consume it. > + > + @retval UNIT_TEST_PASSED The Unit test has completed and the > test > + case was successful. > + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. > +**/ > +UNIT_TEST_STATUS > +EFIAPI > +TestCpuStackGuardInBspAndAp ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + EFI_STATUS Status; > + UINTN OriginalStackBase; > + UINTN NewStackTop; > + UINTN NewStackBase; > + EXCEPTION_STACK_SWITCH_CONTEXT *SwitchStackData; > + MP_SERVICES MpServices; > + UINTN ProcessorNumber; > + UINTN EnabledProcessorNum; > + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; > + UINTN Index; > + UINTN BspProcessorNum; > + VOID *NewIdtr; > + UINTN *CpuStackBaseBuffer; > + > + if (!PcdGetBool (PcdCpuStackGuard)) { > + return UNIT_TEST_PASSED; > + } > + > + // > + // Get MP Service Protocol > + // > + Status = GetMpServices (&MpServices); > + Status = MpServicesUnitTestGetNumberOfProcessors (MpServices, > &ProcessorNumber, &EnabledProcessorNum); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + Status = MpServicesUnitTestWhoAmI (MpServices, &BspProcessorNum); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + mNumberOfProcessors = ProcessorNumber; > + > + CpuOriginalRegisterBuffer = SaveAllCpuRegisters (&MpServices, > BspProcessorNum); > + > + // > + // Initialize Bsp and AP Idt. > + // Idt buffer should not be empty or it will hang in MP API. > + // > + NewIdtr = InitializeBspIdt (); > + Status = InitializeCpuExceptionHandlers (NULL); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + InitializeApIdt (MpServices, NewIdtr); > + > + // > + // Get BSP and AP original stack base. > + // > + CpuStackBaseBuffer = GetAllCpuStackBase (&MpServices, > BspProcessorNum); > + > + // > + // InitializeMpExceptionStackSwitchHandlers and register exception > handler. > + // > + SwitchStackData = InitializeMpExceptionStackSwitchHandlers (MpServices, > BspProcessorNum); > + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, > CpuStackGuardExceptionHandler); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_DOUBLE_FAULT, > AdjustRipForFaultHandler); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + > + for (Index = 0; Index < mNumberOfProcessors; Index++) { > + OriginalStackBase = CpuStackBaseBuffer[Index]; > + NewStackTop = (UINTN)(SwitchStackData[Index].Buffer) + > SwitchStackData[Index].BufferSize; > + NewStackBase = (UINTN)(SwitchStackData[Index].Buffer); > + if (Index == BspProcessorNum) { > + TriggerStackOverflowbyCpuStackGuard (); > + } else { > + MpServicesUnitTestStartupThisAP ( > + MpServices, > + (EFI_AP_PROCEDURE)TriggerStackOverflowbyCpuStackGuard, > + Index, > + 0, > + NULL > + ); > + } > + > + DEBUG ((DEBUG_INFO, "TestCase4: mRspAddress[0] is 0x%x, > mRspAddress[1] is 0x%x\n", mRspAddress[0], mRspAddress[1])); > + UT_ASSERT_TRUE ((mRspAddress[0] >= OriginalStackBase) && > (mRspAddress[0] <= (OriginalStackBase + SIZE_4KB))); > + UT_ASSERT_TRUE ((mRspAddress[1] >= NewStackBase) && > (mRspAddress[1] < NewStackTop)); > + } > + > + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, NULL); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_DOUBLE_FAULT, > NULL); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + RestoreAllCpuRegisters (&MpServices, CpuOriginalRegisterBuffer, > BspProcessorNum); > + FreePool (SwitchStackData); > + FreePool (CpuOriginalRegisterBuffer); > + FreePool (NewIdtr); > + > + return UNIT_TEST_PASSED; > +} > + > +/** > + Create CpuExceptionLibUnitTestSuite and add test case. > + > + @param[in] FrameworkHandle Unit test framework. > + > + @return EFI_SUCCESS The unit test suite was created. > + @retval EFI_OUT_OF_RESOURCES There are not enough resources > available to > + initialize the unit test suite. > +**/ > +EFI_STATUS > +AddCommonTestCase ( > + IN UNIT_TEST_FRAMEWORK_HANDLE Framework > + ) > +{ > + EFI_STATUS Status; > + UNIT_TEST_SUITE_HANDLE CpuExceptionLibUnitTestSuite; > + > + // > + // Populate the Manual Test Cases. > + // > + Status = CreateUnitTestSuite (&CpuExceptionLibUnitTestSuite, Framework, > "Test CpuExceptionHandlerLib", "CpuExceptionHandlerLib.Manual", NULL, > NULL); > + if (EFI_ERROR (Status)) { > + DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for > CpuExceptionHandlerLib Test Cases\n")); > + Status = EFI_OUT_OF_RESOURCES; > + return Status; > + } > + > + AddTestCase (CpuExceptionLibUnitTestSuite, "Check if exception handler > can be registered/unregistered for no error code exception", > "TestRegisterHandlerForNoErrorCodeException", > TestRegisterHandlerForNoErrorCodeException, NULL, NULL, NULL); > + AddTestCase (CpuExceptionLibUnitTestSuite, "Check if exception handler > can be registered/unregistered for GP and PF", > "TestRegisterHandlerForGPAndPF", TestRegisterHandlerForGPAndPF, NULL, > NULL, NULL); > + > + AddTestCase (CpuExceptionLibUnitTestSuite, "Check if Cpu Context is > consistent before and after exception.", "TestCpuContextConsistency", > TestCpuContextConsistency, NULL, NULL, NULL); > + AddTestCase (CpuExceptionLibUnitTestSuite, "Check if stack overflow is > captured by CpuStackGuard in Bsp and AP", > "TestCpuStackGuardInBspAndAp", TestCpuStackGuardInBspAndAp, NULL, > NULL, NULL); > + > + return EFI_SUCCESS; > +} > diff --git > a/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibU > nitTest.inf > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibU > nitTest.inf > new file mode 100644 > index 0000000000..e3dbe7b9ab > --- /dev/null > +++ > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibU > nitTest.inf > @@ -0,0 +1,58 @@ > +## @file > +# Unit tests of the DxeCpuExceptionHandlerLib instance. > +# > +# Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> > +# SPDX-License-Identifier: BSD-2-Clause-Patent > +## > + > +[Defines] > + INF_VERSION = 0x00010005 > + BASE_NAME = CpuExceptionHandlerDxeTest > + FILE_GUID = D76BFD9C-0B6D-46BD-AD66-2BBB6FA7031A > + MODULE_TYPE = DXE_DRIVER > + VERSION_STRING = 1.0 > + ENTRY_POINT = CpuExceptionHandlerTestEntry > + > +# > +# The following information is for reference only and not required by the > build tools. > +# > +# VALID_ARCHITECTURES = X64 > +# > +[Sources.X64] > + X64/ArchExceptionHandlerTestAsm.nasm > + X64/ArchExceptionHandlerTest.c > + > +[Sources.common] > + CpuExceptionHandlerTest.h > + CpuExceptionHandlerTestCommon.c > + DxeCpuExceptionHandlerUnitTest.c > + > +[Packages] > + MdePkg/MdePkg.dec > + MdeModulePkg/MdeModulePkg.dec > + UefiCpuPkg/UefiCpuPkg.dec > + > +[LibraryClasses] > + BaseLib > + BaseMemoryLib > + DebugLib > + UnitTestLib > + MemoryAllocationLib > + CpuExceptionHandlerLib > + UefiDriverEntryPoint > + HobLib > + UefiBootServicesTableLib > + CpuPageTableLib > + > +[Guids] > + gEfiHobMemoryAllocStackGuid > + > +[Pcd] > + gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard ## CONSUMES > + gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize ## CONSUMES > + > +[Protocols] > + gEfiMpServiceProtocolGuid > + > +[Depex] > + gEfiMpServiceProtocolGuid > diff --git > a/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnitT > est.c > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnit > Test.c > new file mode 100644 > index 0000000000..917fc549bf > --- /dev/null > +++ > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnit > Test.c > @@ -0,0 +1,196 @@ > +/** @file > + Unit tests of the CpuExceptionHandlerLib. > + > + Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#include "CpuExceptionHandlerTest.h" > +#include <Library/UefiBootServicesTableLib.h> > + > +/** > + Initialize Bsp Idt with a new Idt table and return the IA32_DESCRIPTOR > buffer. > + In PEIM, store original PeiServicePointer before new Idt table. > + > + @return Pointer to the allocated IA32_DESCRIPTOR buffer. > +**/ > +VOID * > +InitializeBspIdt ( > + VOID > + ) > +{ > + UINTN *NewIdtTable; > + IA32_DESCRIPTOR *Idtr; > + > + Idtr = AllocateZeroPool (sizeof (IA32_DESCRIPTOR)); > + ASSERT (Idtr != NULL); > + NewIdtTable = AllocateZeroPool (sizeof (IA32_IDT_GATE_DESCRIPTOR) * > CPU_INTERRUPT_NUM); > + ASSERT (NewIdtTable != NULL); > + Idtr->Base = (UINTN)NewIdtTable; > + Idtr->Limit = (UINT16)(sizeof (IA32_IDT_GATE_DESCRIPTOR) * > CPU_INTERRUPT_NUM - 1); > + > + AsmWriteIdtr (Idtr); > + return Idtr; > +} > + > +/** > + Retrieve the number of logical processor in the platform and the number > of those logical processors that > + are enabled on this boot. > + > + @param[in] MpServices MP_SERVICES structure. > + @param[out] NumberOfProcessors Pointer to the total number of logical > processors in the system, including > + the BSP and disabled APs. > + @param[out] NumberOfEnabledProcessors Pointer to the number of > processors in the system that are enabled. > + > + @retval EFI_SUCCESS Retrieve the number of logical processor > successfully > + @retval Others Retrieve the number of logical processor > unsuccessfully > +**/ > +EFI_STATUS > +MpServicesUnitTestGetNumberOfProcessors ( > + IN MP_SERVICES MpServices, > + OUT UINTN *NumberOfProcessors, > + OUT UINTN *NumberOfEnabledProcessors > + ) > +{ > + return MpServices.Protocol->GetNumberOfProcessors > (MpServices.Protocol, NumberOfProcessors, NumberOfEnabledProcessors); > +} > + > +/** > + Get the handle number for the calling processor. > + > + @param[in] MpServices MP_SERVICES structure. > + @param[out] ProcessorNumber The handle number for the calling > processor. > + > + @retval EFI_SUCCESS Get the handle number for the calling processor > successfully. > + @retval Others Get the handle number for the calling processor > unsuccessfully. > +**/ > +EFI_STATUS > +MpServicesUnitTestWhoAmI ( > + IN MP_SERVICES MpServices, > + OUT UINTN *ProcessorNumber > + ) > +{ > + return MpServices.Protocol->WhoAmI (MpServices.Protocol, > ProcessorNumber); > +} > + > +/** > + Caller gets one enabled AP to execute a caller-provided function. > + > + @param[in] MpServices MP_SERVICES structure. > + @param[in] Procedure Pointer to the function to be run on enabled APs > of the system. > + @param[in] ProcessorNumber The handle number of the AP. > + @param[in] TimeoutInMicroSeconds Indicates the time limit in > microseconds for APs to return from Procedure, > + for blocking mode only. Zero means infinity. > + @param[in] ProcedureArgument The parameter passed into Procedure > for all APs. > + > + > + @retval EFI_SUCCESS Caller gets one enabled AP to execute a caller- > provided function successfully > + @retval Others Caller gets one enabled AP to execute a caller- > provided function unsuccessfully > +**/ > +EFI_STATUS > +MpServicesUnitTestStartupThisAP ( > + IN MP_SERVICES MpServices, > + IN EFI_AP_PROCEDURE Procedure, > + IN UINTN ProcessorNumber, > + IN UINTN TimeoutInMicroSeconds, > + IN VOID *ProcedureArgument > + ) > +{ > + return MpServices.Protocol->StartupThisAP (MpServices.Protocol, > Procedure, ProcessorNumber, NULL, TimeoutInMicroSeconds, > ProcedureArgument, NULL); > +} > + > +/** > + Execute a caller provided function on all enabled APs. > + > + @param[in] MpServices MP_SERVICES structure. > + @param[in] Procedure Pointer to the function to be run on enabled APs > of the system. > + @param[in] SingleThread If TRUE, then all the enabled APs execute the > function specified by Procedure > + one by one, in ascending order of processor handle number. > + If FALSE, then all the enabled APs execute the function > specified by Procedure > + simultaneously. > + @param[in] TimeoutInMicroSeconds Indicates the time limit in > microseconds for APs to return from Procedure, > + for blocking mode only. Zero means infinity. > + @param[in] ProcedureArgument The parameter passed into Procedure > for all APs. > + > + @retval EFI_SUCCESS Execute a caller provided function on all enabled > APs successfully > + @retval Others Execute a caller provided function on all enabled APs > unsuccessfully > +**/ > +EFI_STATUS > +MpServicesUnitTestStartupAllAPs ( > + IN MP_SERVICES MpServices, > + IN EFI_AP_PROCEDURE Procedure, > + IN BOOLEAN SingleThread, > + IN UINTN TimeoutInMicroSeconds, > + IN VOID *ProcedureArgument > + ) > +{ > + return MpServices.Protocol->StartupAllAPs (MpServices.Protocol, > Procedure, SingleThread, NULL, TimeoutInMicroSeconds, > ProcedureArgument, NULL); > +} > + > +/** > + Get EFI_MP_SERVICES_PROTOCOL pointer. > + > + @param[out] MpServices Pointer to the buffer where > EFI_MP_SERVICES_PROTOCOL is stored > + > + @retval EFI_SUCCESS EFI_MP_SERVICES_PROTOCOL interface is > returned > + @retval EFI_NOT_FOUND EFI_MP_SERVICES_PROTOCOL interface is not > found > +**/ > +EFI_STATUS > +GetMpServices ( > + OUT MP_SERVICES *MpServices > + ) > +{ > + return gBS->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID > **)&MpServices->Protocol); > +} > + > +/** > + Entry for CpuExceptionHandlerDxeTest driver. > + > + @param ImageHandle Image handle this driver. > + @param SystemTable Pointer to the System Table. > + > + @retval EFI_SUCCESS The driver executed normally. > + > +**/ > +EFI_STATUS > +EFIAPI > +CpuExceptionHandlerTestEntry ( > + IN EFI_HANDLE ImageHandle, > + IN EFI_SYSTEM_TABLE *SystemTable > + ) > +{ > + EFI_STATUS Status; > + UNIT_TEST_FRAMEWORK_HANDLE Framework; > + > + Framework = NULL; > + > + DEBUG ((DEBUG_INFO, "%a v%a\n", UNIT_TEST_APP_NAME, > UNIT_TEST_APP_VERSION)); > + > + // > + // Start setting up the test framework for running the tests. > + // > + Status = InitUnitTestFramework (&Framework, UNIT_TEST_APP_NAME, > gEfiCallerBaseName, UNIT_TEST_APP_VERSION); > + if (EFI_ERROR (Status)) { > + DEBUG ((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status > = %r\n", Status)); > + goto EXIT; > + } > + > + Status = AddCommonTestCase (Framework); > + if (EFI_ERROR (Status)) { > + DEBUG ((DEBUG_ERROR, "Failed in AddCommonTestCase. Status = %r\n", > Status)); > + goto EXIT; > + } > + > + // > + // Execute the tests. > + // > + Status = RunAllTestSuites (Framework); > + > +EXIT: > + if (Framework) { > + FreeUnitTestFramework (Framework); > + } > + > + return Status; > +} > diff --git > a/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTes > t.c > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTes > t.c > new file mode 100644 > index 0000000000..9b086622f2 > --- /dev/null > +++ > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTes > t.c > @@ -0,0 +1,167 @@ > +/** @file > + Unit tests of the CpuExceptionHandlerLib. > + > + Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#include "CpuExceptionHandlerTest.h" > + > +GENERAL_REGISTER mRegisterForCheck[2]; > + > +// > +// In TestCpuContextConsistency, Cpu registers will be set to > mAdjustRegisterBeforeException. > +// Rcx in mAdjustRegisterInsideException is set runtime since Rcx is needed > in assembly code. > +// For GP and PF, Rcx is set to FaultParameter. For other exception > triggered by INTn, Rcx is set to ExceptionType. > +// > +GENERAL_REGISTER mAdjustRegisterBeforeException = { 1, 2, 3, 4, 5, 0, 7, 8, > 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf }; > +GENERAL_REGISTER mAdjustRegisterInsideException = { 0x11, 0x12, 0x13, > 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f }; > + > +/** > + Special handler for fault exception. > + Rip/Eip in SystemContext will be modified to the instruction after the > exception instruction. > + > + @param ExceptionType Exception type. > + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. > +**/ > +VOID > +EFIAPI > +AdjustRipForFaultHandler ( > + IN EFI_EXCEPTION_TYPE ExceptionType, > + IN EFI_SYSTEM_CONTEXT SystemContext > + ) > +{ > + mExceptionType = ExceptionType; > + SystemContext.SystemContextX64->Rip += mFaultInstructionLength; > +} > + > +/** > + Special handler for ConsistencyOfCpuContext test case. > + > + @param ExceptionType Exception type. > + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. > +**/ > +VOID > +EFIAPI > +AdjustCpuContextHandler ( > + IN EFI_EXCEPTION_TYPE ExceptionType, > + IN EFI_SYSTEM_CONTEXT SystemContext > + ) > +{ > + // > + // Do not handle Esp and ebp. They will not be restored. > + // Store SystemContext in mRegisterForCheck[0] modified before > exception. > + // > + mRegisterForCheck[0].Rdi = SystemContext.SystemContextX64->Rdi; > + mRegisterForCheck[0].Rsi = SystemContext.SystemContextX64->Rsi; > + mRegisterForCheck[0].Rbx = SystemContext.SystemContextX64->Rbx; > + mRegisterForCheck[0].Rdx = SystemContext.SystemContextX64->Rdx; > + mRegisterForCheck[0].Rcx = SystemContext.SystemContextX64->Rcx; > + mRegisterForCheck[0].Rax = SystemContext.SystemContextX64->Rax; > + mRegisterForCheck[0].R8Register = SystemContext.SystemContextX64- > >R8; > + mRegisterForCheck[0].R9Register = SystemContext.SystemContextX64- > >R9; > + mRegisterForCheck[0].R10Register = SystemContext.SystemContextX64- > >R10; > + mRegisterForCheck[0].R11Register = SystemContext.SystemContextX64- > >R11; > + mRegisterForCheck[0].R12Register = SystemContext.SystemContextX64- > >R12; > + mRegisterForCheck[0].R13Register = SystemContext.SystemContextX64- > >R13; > + mRegisterForCheck[0].R14Register = SystemContext.SystemContextX64- > >R14; > + mRegisterForCheck[0].R15Register = SystemContext.SystemContextX64- > >R15; > + > + // > + // Modify cpu context which will be stored in mRegisterForCheck[1]. > + // > + SystemContext.SystemContextX64->Rdi = > mAdjustRegisterInsideException.Rdi; > + SystemContext.SystemContextX64->Rsi = > mAdjustRegisterInsideException.Rsi; > + SystemContext.SystemContextX64->Rbx = > mAdjustRegisterInsideException.Rbx; > + SystemContext.SystemContextX64->Rdx = > mAdjustRegisterInsideException.Rdx; > + SystemContext.SystemContextX64->Rcx = > mAdjustRegisterInsideException.Rcx; > + SystemContext.SystemContextX64->Rax = > mAdjustRegisterInsideException.Rax; > + SystemContext.SystemContextX64->R8 = > mAdjustRegisterInsideException.R8Register; > + SystemContext.SystemContextX64->R9 = > mAdjustRegisterInsideException.R9Register; > + SystemContext.SystemContextX64->R10 = > mAdjustRegisterInsideException.R10Register; > + SystemContext.SystemContextX64->R11 = > mAdjustRegisterInsideException.R11Register; > + SystemContext.SystemContextX64->R12 = > mAdjustRegisterInsideException.R12Register; > + SystemContext.SystemContextX64->R13 = > mAdjustRegisterInsideException.R13Register; > + SystemContext.SystemContextX64->R14 = > mAdjustRegisterInsideException.R14Register; > + SystemContext.SystemContextX64->R15 = > mAdjustRegisterInsideException.R15Register; > + > + // > + // When fault exception happens, eip/rip points to the faulting instruction. > + // For now, olny GP and PF are tested in fault exception. > + // > + if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) || (ExceptionType == > EXCEPT_IA32_GP_FAULT)) { > + AdjustRipForFaultHandler (ExceptionType, SystemContext); > + } > +} > + > +/** > + Compare cpu context in ConsistencyOfCpuContext test case. > + 1.Compare mRegisterForCheck[0] with mAdjustRegisterBeforeException. > + 2.Compare mRegisterForCheck[1] with mAdjustRegisterAfterException. > + > + @retval UNIT_TEST_PASSED The Unit test has completed and it was > successful. > + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. > +**/ > +UNIT_TEST_STATUS > +CompareCpuContext ( > + VOID > + ) > +{ > + // > + // Do not handle Esp and ebp. They will not be restored. > + // > + UT_ASSERT_EQUAL (mRegisterForCheck[0].Rdi, > mAdjustRegisterBeforeException.Rdi); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].Rsi, > mAdjustRegisterBeforeException.Rsi); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].Rbx, > mAdjustRegisterBeforeException.Rbx); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].Rdx, > mAdjustRegisterBeforeException.Rdx); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].Rcx, > mAdjustRegisterBeforeException.Rcx); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].Rax, > mAdjustRegisterBeforeException.Rax); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].R8Register, > mAdjustRegisterBeforeException.R8Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].R9Register, > mAdjustRegisterBeforeException.R9Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].R10Register, > mAdjustRegisterBeforeException.R10Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].R11Register, > mAdjustRegisterBeforeException.R11Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].R12Register, > mAdjustRegisterBeforeException.R12Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].R13Register, > mAdjustRegisterBeforeException.R13Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].R14Register, > mAdjustRegisterBeforeException.R14Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].R15Register, > mAdjustRegisterBeforeException.R15Register); > + > + UT_ASSERT_EQUAL (mRegisterForCheck[1].Rdi, > mAdjustRegisterInsideException.Rdi); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].Rsi, > mAdjustRegisterInsideException.Rsi); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].Rbx, > mAdjustRegisterInsideException.Rbx); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].Rdx, > mAdjustRegisterInsideException.Rdx); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].Rcx, > mAdjustRegisterInsideException.Rcx); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].Rax, > mAdjustRegisterInsideException.Rax); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].R8Register, > mAdjustRegisterInsideException.R8Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].R9Register, > mAdjustRegisterInsideException.R9Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].R10Register, > mAdjustRegisterInsideException.R10Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].R11Register, > mAdjustRegisterInsideException.R11Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].R12Register, > mAdjustRegisterInsideException.R12Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].R13Register, > mAdjustRegisterInsideException.R13Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].R14Register, > mAdjustRegisterInsideException.R14Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].R15Register, > mAdjustRegisterInsideException.R15Register); > + return UNIT_TEST_PASSED; > +} > + > +/** > + Special handler for CpuStackGuard test case. > + > + @param ExceptionType Exception type. > + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. > + > +**/ > +VOID > +EFIAPI > +CpuStackGuardExceptionHandler ( > + IN EFI_EXCEPTION_TYPE ExceptionType, > + IN EFI_SYSTEM_CONTEXT SystemContext > + ) > +{ > + UINTN LocalVariable; > + > + AdjustRipForFaultHandler (ExceptionType, SystemContext); > + mRspAddress[0] = (UINTN)SystemContext.SystemContextX64->Rsp; > + mRspAddress[1] = (UINTN)(&LocalVariable); > + > + return; > +} > diff --git > a/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTes > tAsm.nasm > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTes > tAsm.nasm > new file mode 100644 > index 0000000000..4838a12a67 > --- /dev/null > +++ > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTes > tAsm.nasm > @@ -0,0 +1,260 @@ > +;------------------------------------------------------------------------------ > +; > +; Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> > +; SPDX-License-Identifier: BSD-2-Clause-Patent > +; > +; Module Name: > +; > +; ArchExceptionHandlerTestAsm.nasm > +; > +; Abstract: > +; > +; x64 CPU Exception Handler Lib Unit test > +; > +;------------------------------------------------------------------------------ > + > + DEFAULT REL > + SECTION .text > + > +struc GENERAL_REGISTER > + .Rdi: resq 1 > + .Rsi: resq 1 > + .Rbp: resq 1 > + .Rbx: resq 1 > + .Rdx: resq 1 > + .Rcx: resq 1 > + .Rax: resq 1 > + .R8: resq 1 > + .R9: resq 1 > + .R10: resq 1 > + .R11: resq 1 > + .R12: resq 1 > + .R13: resq 1 > + .R14: resq 1 > + .R15: resq 1 > + > +endstruc > + > +extern ASM_PFX(mRegisterForCheck) > +extern ASM_PFX(mAdjustRegisterBeforeException) > +extern ASM_PFX(mFaultInstructionLength) > + > +;------------------------------------------------------------------------------ > +; VOID > +; EFIAPI > +; TriggerGPException ( > +; UINTN Cr4ReservedBit > +; ); > +;------------------------------------------------------------------------------ > +global ASM_PFX(TriggerGPException) > +ASM_PFX(TriggerGPException): > + ; > + ; Set reserved bit 15 of cr4 to 1 > + ; > + push rcx > + lea rcx, [ASM_PFX(mFaultInstructionLength)] > + mov qword[rcx], TriggerGPExceptionAfter - TriggerGPExceptionBefore > + pop rcx > +TriggerGPExceptionBefore: > + mov cr4, rcx > +TriggerGPExceptionAfter: > + ret > + > +;------------------------------------------------------------------------------ > +; VOID > +; EFIAPI > +; TriggerPFException ( > +; UINTN PFAddress > +; ); > +;------------------------------------------------------------------------------ > +global ASM_PFX(TriggerPFException) > +ASM_PFX(TriggerPFException): > + push rcx > + lea rcx, [ASM_PFX(mFaultInstructionLength)] > + mov qword[rcx], TriggerPFExceptionAfter - TriggerPFExceptionBefore > + pop rcx > +TriggerPFExceptionBefore: > + mov qword[rcx], 0x1 > +TriggerPFExceptionAfter: > + ret > + > +global ASM_PFX(ModifyRcxInGlobalBeforeException) > +ASM_PFX(ModifyRcxInGlobalBeforeException): > + push rax > + lea rax, [ASM_PFX(mAdjustRegisterBeforeException)] > + mov [rax + GENERAL_REGISTER.Rcx], rcx > + pop rax > + ret > + > +;------------------------------------------------------------------------------ > +;VOID > +;EFIAPI > +;AsmTestConsistencyOfCpuContext ( > +; IN EFI_EXCEPTION_TYPE ExceptionType > +; IN UINTN FaultParameter OPTIONAL > +; ); > +;------------------------------------------------------------------------------ > +global ASM_PFX(AsmTestConsistencyOfCpuContext) > +ASM_PFX(AsmTestConsistencyOfCpuContext): > + ; > + ; push original register > + ; > + push r15 > + push r14 > + push r13 > + push r12 > + push r11 > + push r10 > + push r9 > + push r8 > + push rax > + push rcx > + push rbx > + push rsi > + push rdi > + push rdx > + > + ; > + ; Modify register to mAdjustRegisterBeforeException > + ; > + lea r15, [ASM_PFX(mAdjustRegisterBeforeException)] > + mov rdi, [r15 + GENERAL_REGISTER.Rdi] > + mov rsi, [r15 + GENERAL_REGISTER.Rsi] > + mov rbx, [r15 + GENERAL_REGISTER.Rbx] > + mov rdx, [r15 + GENERAL_REGISTER.Rdx] > + mov rax, [r15 + GENERAL_REGISTER.Rax] > + mov r8, [r15 + GENERAL_REGISTER.R8] > + mov r9, [r15 + GENERAL_REGISTER.R9] > + mov r10, [r15 + GENERAL_REGISTER.R10] > + mov r11, [r15 + GENERAL_REGISTER.R11] > + mov r12, [r15 + GENERAL_REGISTER.R12] > + mov r13, [r15 + GENERAL_REGISTER.R13] > + mov r14, [r15 + GENERAL_REGISTER.R14] > + mov r15, [r15 + GENERAL_REGISTER.R15] > + > + cmp rcx, 0xd > + jz GPException > + cmp rcx, 0xe > + jz PFException > + jmp INTnException > + > +PFException: > + ; > + ; Pop rdx(Pei StackBase) to rcx and restore stack. > + ; Modify Rcx in mAdjustRegisterBeforeException. > + ; > + pop rcx > + push rcx > + call ASM_PFX(ModifyRcxInGlobalBeforeException) > + call ASM_PFX(TriggerPFException) > + jmp AfterException > + > +GPException: > + ; > + ; Prepare rcx value for GP. > + ; Modify Rcx in mAdjustRegisterBeforeException. > + ; > + pop rcx > + push rcx > + call ASM_PFX(ModifyRcxInGlobalBeforeException) > + call ASM_PFX(TriggerGPException) > + jmp AfterException > + > +INTnException: > + ; > + ; Modify Rcx in mAdjustRegisterBeforeException. > + ; > + call ASM_PFX(ModifyRcxInGlobalBeforeException) > + call ASM_PFX(TriggerINTnException) > + > +AfterException: > + ; > + ; Save register in mRegisterForCheck[1] > + ; > + push rax > + lea rax, [ASM_PFX(mRegisterForCheck)] > + add rax, GENERAL_REGISTER_size > + mov [rax + GENERAL_REGISTER.Rdi], rdi > + mov [rax + GENERAL_REGISTER.Rsi], rsi > + mov [rax + GENERAL_REGISTER.Rbx], rbx > + mov [rax + GENERAL_REGISTER.Rdx], rdx > + mov [rax + GENERAL_REGISTER.Rcx], rcx > + pop rcx > + mov [rax + GENERAL_REGISTER.Rax], rcx > + mov [rax + GENERAL_REGISTER.R8], r8 > + mov [rax + GENERAL_REGISTER.R9], r9 > + mov [rax + GENERAL_REGISTER.R10], r10 > + mov [rax + GENERAL_REGISTER.R11], r11 > + mov [rax + GENERAL_REGISTER.R12], r12 > + mov [rax + GENERAL_REGISTER.R13], r13 > + mov [rax + GENERAL_REGISTER.R14], r14 > + mov [rax + GENERAL_REGISTER.R15], r15 > + > + ; > + ; restore original register > + ; > + pop rdx > + pop rdi > + pop rsi > + pop rbx > + pop rcx > + pop rax > + pop r8 > + pop r9 > + pop r10 > + pop r11 > + pop r12 > + pop r13 > + pop r14 > + pop r15 > + > + ret > + > +;------------------------------------------------------------------------------ > +; VOID > +; EFIAPI > +; TriggerStackOverflowbyCpuStackGuard ( > +; VOID > +; ); > +;------------------------------------------------------------------------------ > +global ASM_PFX(TriggerStackOverflowbyCpuStackGuard) > +ASM_PFX(TriggerStackOverflowbyCpuStackGuard): > + push rcx > + lea rcx, [ASM_PFX(mFaultInstructionLength)] > + mov qword[rcx], TriggerCpuStackGuardAfter - > TriggerCpuStackGuardBefore > + pop rcx > +TriggerCpuStackGuardBefore: > + call TriggerCpuStackGuardBefore > +TriggerCpuStackGuardAfter: > + ret > + > +;------------------------------------------------------------------------------ > +; VOID > +; EFIAPI > +; TriggerINTnException ( > +; IN EFI_EXCEPTION_TYPE ExceptionType > +; ); > +;------------------------------------------------------------------------------ > +global ASM_PFX(TriggerINTnException) > +ASM_PFX(TriggerINTnException): > + push rax > + push rdx > + push rcx > + lea rax, [AsmTriggerException1 - AsmTriggerException0] > + mul rcx > + mov rcx, AsmTriggerException0 > + add rax, rcx > + pop rcx > + pop rdx > + jmp rax > + ; > + ; rax = AsmTriggerException0 + (AsmTriggerException1 - > AsmTriggerException0) * rcx > + ; > +%assign Vector 0 > +%rep 22 > +AsmTriggerException %+ Vector: > + pop rax > + INT Vector > + ret > +%assign Vector Vector+1 > +%endrep > -- > 2.31.1.windows.1 ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/3] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib 2022-10-11 9:37 ` Ni, Ray @ 2022-10-12 1:31 ` duntan 0 siblings, 0 replies; 6+ messages in thread From: duntan @ 2022-10-12 1:31 UTC (permalink / raw) To: Ni, Ray, devel@edk2.groups.io; +Cc: Dong, Eric, Kumar, Rahul R Ok, I'll do this in V2 patch. Thanks for your comments. Thanks, Dun -----Original Message----- From: Ni, Ray <ray.ni@intel.com> Sent: Tuesday, October 11, 2022 5:38 PM To: Tan, Dun <dun.tan@intel.com>; devel@edk2.groups.io Cc: Dong, Eric <eric.dong@intel.com>; Kumar, Rahul R <rahul.r.kumar@intel.com> Subject: RE: [PATCH 1/3] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib Can you provide more details in the commit message? Especially explain what "consistent" means for #3 and what "CpuStackGuard in both BSP and AP" means in #4? Thanks, Ray > -----Original Message----- > From: Tan, Dun <dun.tan@intel.com> > Sent: Tuesday, October 11, 2022 2:04 PM > To: devel@edk2.groups.io > Cc: Dong, Eric <eric.dong@intel.com>; Ni, Ray <ray.ni@intel.com>; Kumar, > Rahul R <rahul.r.kumar@intel.com> > Subject: [PATCH 1/3] UefiCpuPkg: Add Unit tests for > DxeCpuExceptionHandlerLib > > Add target based unit tests for the DxeCpuExceptionHandlerLib. > A DXE driver is created to test DxeCpuExceptionHandlerLib. Four > kinds of test cases are created in this module: > 1.Test if exception handler can be registered/unregistered for > no error code exception. 2.Test if exception handler can be > registered/unregistered for GP and PF. 3.Test if Cpu Context > is consistent before and after exception. 4.Test if stack > overflow can be captured by CpuStackGuard in both Bsp and AP. > > Signed-off-by: Dun Tan <dun.tan@intel.com> > Cc: Eric Dong <eric.dong@intel.com> > Cc: Ray Ni <ray.ni@intel.com> > Cc: Rahul Kumar <rahul1.kumar@intel.com> > --- > UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h > | 353 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > +++++ > > UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestComm > on.c | 856 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++ > > UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibUnit > Test.inf | 58 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > > UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnitTe > st.c | 196 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++ > > UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTest.c > | 167 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > +++++++++++++++++++++++++++++++++++++++++++++++++++ > > UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTestA > sm.nasm | 260 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > ++++++++++++++++++++++++++++ > 6 files changed, 1890 insertions(+) > > diff --git > a/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h > new file mode 100644 > index 0000000000..bfbc483075 > --- /dev/null > +++ > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h > @@ -0,0 +1,353 @@ > +/** @file > + > + Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#ifndef CPU_EXCEPTION_HANDLER_TEST_H_ > +#define CPU_EXCEPTION_HANDLER_TEST_H_ > + > +#include <Uefi.h> > +#include <Library/BaseLib.h> > +#include <Library/BaseMemoryLib.h> > +#include <Library/DebugLib.h> > +#include <Library/UnitTestLib.h> > +#include <Library/MemoryAllocationLib.h> > +#include <Library/UnitTestHostBaseLib.h> > +#include <Library/CpuExceptionHandlerLib.h> > +#include <Library/UefiLib.h> > +#include <Library/SerialPortLib.h> > +#include <Library/HobLib.h> > +#include <Library/CpuPageTableLib.h> > +#include <Guid/MemoryAllocationHob.h> > +#include <Protocol/MpService.h> > +#include <PiPei.h> > +#include <Ppi/MpServices2.h> > + > +#define UNIT_TEST_APP_NAME "Cpu Exception Handler Lib Unit Tests" > +#define UNIT_TEST_APP_VERSION "1.0" > + > +#define CPU_INTERRUPT_NUM 256 > +#define SPEC_MAX_EXCEPTION_NUM 22 > +#define CR4_RESERVED_BIT BIT15 > + > +#pragma pack (1) > + > +typedef union { > + struct { > + UINT32 LimitLow : 16; > + UINT32 BaseLow : 16; > + UINT32 BaseMid : 8; > + UINT32 Type : 4; > + UINT32 System : 1; > + UINT32 Dpl : 2; > + UINT32 Present : 1; > + UINT32 LimitHigh : 4; > + UINT32 Software : 1; > + UINT32 Reserved : 1; > + UINT32 DefaultSize : 1; > + UINT32 Granularity : 1; > + UINT32 BaseHigh : 8; > + } Bits; > + UINT64 Uint64; > +} IA32_GDT; > + > +typedef struct { > + UINT32 InitialApicId; > + UINT32 ApicId; > + UINT32 Health; > + UINT64 ApTopOfStack; > +} CPU_INFO_IN_HOB; > +#pragma pack () > + > +typedef struct { > + IA32_DESCRIPTOR OriginalGdtr; > + IA32_DESCRIPTOR OriginalIdtr; > + UINT16 Tr; > +} CPU_REGISTER_BUFFER; > + > +typedef union { > + EDKII_PEI_MP_SERVICES2_PPI *Ppi; > + EFI_MP_SERVICES_PROTOCOL *Protocol; > +} MP_SERVICES; > + > +typedef struct { > + VOID *Buffer; > + UINTN BufferSize; > + EFI_STATUS Status; > +} EXCEPTION_STACK_SWITCH_CONTEXT; > + > +typedef struct { > + UINT64 Rdi; > + UINT64 Rsi; > + UINT64 Rbp; > + UINT64 Rbx; > + UINT64 Rdx; > + UINT64 Rcx; > + UINT64 Rax; > + UINT64 R8Register; > + UINT64 R9Register; > + UINT64 R10Register; > + UINT64 R11Register; > + UINT64 R12Register; > + UINT64 R13Register; > + UINT64 R14Register; > + UINT64 R15Register; > +} GENERAL_REGISTER; > + > +typedef struct { > + UINT32 Edi; > + UINT32 Esi; > + UINT32 Ebp; > + UINT32 Ebx; > + UINT32 Edx; > + UINT32 Ecx; > + UINT32 Eax; > +} GENERAL_REGISTER_IA32; > + > +extern UINTN mFaultInstructionLength; > +extern EFI_EXCEPTION_TYPE mExceptionType; > +extern UINTN mRspAddress[]; > +extern GENERAL_REGISTER mRegisterForCheck[]; > +extern GENERAL_REGISTER mAdjustRegisterBeforeException; > +extern GENERAL_REGISTER_IA32 mIa32RegisterForCheck[]; > +extern GENERAL_REGISTER_IA32 mAdjustIa32RegisterBeforeException; > + > +/** > + Initialize Bsp Idt with a new Idt table and return the IA32_DESCRIPTOR > buffer. > + In PEIM, store original PeiServicePointer before new Idt table. > + > + @return Pointer to the allocated IA32_DESCRIPTOR buffer. > +**/ > +VOID * > +InitializeBspIdt ( > + VOID > + ); > + > +/** > + Trigger no error code exception by INT n instruction. > + > + @param[in] ExceptionType No error code exception type. > +**/ > +VOID > +EFIAPI > +TriggerINTnException ( > + IN EFI_EXCEPTION_TYPE ExceptionType > + ); > + > +/** > + Trigger GP exception. > + > + @param[in] Cr4ReservedBit Cr4 reserved bit. > +**/ > +VOID > +EFIAPI > +TriggerGPException ( > + UINTN Cr4ReservedBit > + ); > + > +/** > + Trigger PF exception by write to not present or ReadOnly address. > + > + @param[in] PFAddress Not present or ReadOnly address in page table. > +**/ > +VOID > +EFIAPI > +TriggerPFException ( > + UINTN PFAddress > + ); > + > +/** > + Special handler for fault exception. > + Rip/Eip in SystemContext will be modified to the instruction after the > exception instruction. > + > + @param ExceptionType Exception type. > + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. > +**/ > +VOID > +EFIAPI > +AdjustRipForFaultHandler ( > + IN EFI_EXCEPTION_TYPE ExceptionType, > + IN EFI_SYSTEM_CONTEXT SystemContext > + ); > + > +/** > + Test consistency of Cpu context. Four steps: > + 1. Set Cpu register to mAdjustRegisterBeforeException before exception. > + 2. Trigger exception specified by ExceptionType. > + 3. Store SystemContext in mRegisterForCheck[0] and set SystemContext > to mAdjustRegisterInsideException in handler. > + 4. Store the Cpu register in mRegisterForCheck[1] > + > + Rcx/Ecx in mAdjustRegisterInsideException is decided by different > exception type runtime since Rcx/Ecx is needed in assembly code. > + For GP and PF, Rcx/Ecx is set to FaultParameter. For other exception > triggered by INTn, Rcx/Ecx is set to ExceptionType. > + > + @param[in] ExceptionType Exception type. > + @param[in] FaultParameter Parameter for GP and PF. OPTIONAL > +**/ > +VOID > +EFIAPI > +AsmTestConsistencyOfCpuContext ( > + IN EFI_EXCEPTION_TYPE ExceptionType, > + IN UINTN FaultParameter OPTIONAL > + ); > + > +/** > + Special handler for ConsistencyOfCpuContext test case. General register in > SystemContext > + is modified in this handler. > + > + @param ExceptionType Exception type. > + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. > +**/ > +VOID > +EFIAPI > +AdjustCpuContextHandler ( > + IN EFI_EXCEPTION_TYPE ExceptionType, > + IN EFI_SYSTEM_CONTEXT SystemContext > + ); > + > +/** > + Compare cpu context in ConsistencyOfCpuContext test case. > + 1.Compare mRegisterForCheck[0] with mAdjustRegisterBeforeException. > + 2.Compare mRegisterForCheck[1] with mAdjustRegisterAfterException. > + > + @retval UNIT_TEST_PASSED The Unit test has completed and it was > successful. > + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. > +**/ > +UNIT_TEST_STATUS > +CompareCpuContext ( > + VOID > + ); > + > +/** > + Get EFI_MP_SERVICES_PROTOCOL/EDKII_PEI_MP_SERVICES2_PPI pointer. > + > + @param[out] MpServices Pointer to the MP_SERVICES buffer > + > + @retval EFI_SUCCESS EFI_MP_SERVICES_PROTOCOL/PPI interface is > returned > + @retval EFI_NOT_FOUND EFI_MP_SERVICES_PROTOCOL/PPI interface is > not found > +**/ > +EFI_STATUS > +GetMpServices ( > + OUT MP_SERVICES *MpServices > + ); > + > +/** > + Create CpuExceptionLibUnitTestSuite and add test case. > + > + @param[in] FrameworkHandle Unit test framework. > + > + @return EFI_SUCCESS The unit test suite was created. > + @retval EFI_OUT_OF_RESOURCES There are not enough resources > available to > + initialize the unit test suite. > +**/ > +EFI_STATUS > +AddCommonTestCase ( > + IN UNIT_TEST_FRAMEWORK_HANDLE Framework > + ); > + > +/** > + Execute a caller provided function on all enabled APs. > + > + @param[in] MpServices MP_SERVICES structure. > + @param[in] Procedure Pointer to the function to be run on enabled APs > of the system. > + @param[in] SingleThread If TRUE, then all the enabled APs execute the > function specified by Procedure > + one by one, in ascending order of processor handle number. > + If FALSE, then all the enabled APs execute the function > specified by Procedure > + simultaneously. > + @param[in] TimeoutInMicroseconds Indicates the time limit in > microseconds for APs to return from Procedure, > + for blocking mode only. Zero means infinity. > + @param[in] ProcedureArgument The parameter passed into Procedure > for all APs. > + > + @retval EFI_SUCCESS Execute a caller provided function on all enabled > APs successfully > + @retval Others Execute a caller provided function on all enabled APs > unsuccessfully > +**/ > +EFI_STATUS > +MpServicesUnitTestStartupAllAPs ( > + IN MP_SERVICES MpServices, > + IN EFI_AP_PROCEDURE Procedure, > + IN BOOLEAN SingleThread, > + IN UINTN TimeoutInMicroSeconds, > + IN VOID *ProcedureArgument > + ); > + > +/** > + Caller gets one enabled AP to execute a caller-provided function. > + > + @param[in] MpServices MP_SERVICES structure. > + @param[in] Procedure Pointer to the function to be run on enabled APs > of the system. > + @param[in] ProcessorNumber The handle number of the AP. > + @param[in] TimeoutInMicroseconds Indicates the time limit in > microseconds for APs to return from Procedure, > + for blocking mode only. Zero means infinity. > + @param[in] ProcedureArgument The parameter passed into Procedure > for all APs. > + > + > + @retval EFI_SUCCESS Caller gets one enabled AP to execute a caller- > provided function successfully > + @retval Others Caller gets one enabled AP to execute a caller- > provided function unsuccessfully > +**/ > +EFI_STATUS > +MpServicesUnitTestStartupThisAP ( > + IN MP_SERVICES MpServices, > + IN EFI_AP_PROCEDURE Procedure, > + IN UINTN ProcessorNumber, > + IN UINTN TimeoutInMicroSeconds, > + IN VOID *ProcedureArgument > + ); > + > +/** > + Get the handle number for the calling processor. > + > + @param[in] MpServices Pointer to MP_SERVICES structure. > + @param[out] ProcessorNumber The handle number for the calling > processor. > + > + @retval EFI_SUCCESS Get the handle number for the calling processor > successfully. > + @retval Others Get the handle number for the calling processor > unsuccessfully. > +**/ > +EFI_STATUS > +MpServicesUnitTestWhoAmI ( > + IN MP_SERVICES MpServices, > + OUT UINTN *ProcessorNumber > + ); > + > +/** > + Retrieve the number of logical processor in the platform and the number > of those logical processors that > + are enabled on this boot. > + > + @param[in] MpServices Pointer to MP_SERVICES structure. > + @param[out] NumberOfProcessors Pointer to the total number of logical > processors in the system, including > + the BSP and disabled APs. > + @param[out] NumberOfEnabledProcessors Pointer to the number of > processors in the system that are enabled. > + > + @retval EFI_SUCCESS Retrieve the number of logical processor > successfully > + @retval Others Retrieve the number of logical processor > unsuccessfully > +**/ > +EFI_STATUS > +MpServicesUnitTestGetNumberOfProcessors ( > + IN MP_SERVICES MpServices, > + OUT UINTN *NumberOfProcessors, > + OUT UINTN *NumberOfEnabledProcessors > + ); > + > +/** > + Trigger stack overflow by CpuStackGuard. > +**/ > +VOID > +EFIAPI > +TriggerStackOverflowbyCpuStackGuard ( > + VOID > + ); > + > +/** > + Special handler for CpuStackGuard test case. > + > + @param ExceptionType Exception type. > + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. > +**/ > +VOID > +EFIAPI > +CpuStackGuardExceptionHandler ( > + IN EFI_EXCEPTION_TYPE ExceptionType, > + IN EFI_SYSTEM_CONTEXT SystemContext > + ); > + > +#endif > diff --git > a/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCom > mon.c > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCom > mon.c > new file mode 100644 > index 0000000000..1c3d011c76 > --- /dev/null > +++ > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCom > mon.c > @@ -0,0 +1,856 @@ > +/** @file > + Unit tests of the CpuExceptionHandlerLib. > + > + Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#include "CpuExceptionHandlerTest.h" > + > +// > +// Length of the assembly falut instruction. > +// > +UINTN mFaultInstructionLength = 0; > +EFI_EXCEPTION_TYPE mExceptionType = 256; > +UINTN mNumberOfProcessors = 1; > +UINTN mRspAddress[2] = { 0 }; > + > +// > +// Error code flag indicating whether or not an error code will be > +// pushed on the stack if an exception occurs. > +// > +// 1 means an error code will be pushed, otherwise 0 > +// > +CONST UINT32 mErrorCodeExceptionFlag = 0x20227d00; > + > +/** > + Special handler for trap exception. > + > + @param ExceptionType Exception type. > + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. > +**/ > +VOID > +EFIAPI > +INTnExceptionHandler ( > + IN EFI_EXCEPTION_TYPE ExceptionType, > + IN EFI_SYSTEM_CONTEXT SystemContext > + ) > +{ > + mExceptionType = ExceptionType; > +} > + > +/** > + Restore all cpu original register before test case. > + > + @param[in] Buffer Argument of the procedure. > +**/ > +VOID > +EFIAPI > +RestoreRegistersPerCpu ( > + IN VOID *Buffer > + ) > +{ > + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; > + UINT16 Tr; > + IA32_TSS_DESCRIPTOR *Tss; > + > + CpuOriginalRegisterBuffer = (CPU_REGISTER_BUFFER *)Buffer; > + > + AsmWriteGdtr (&(CpuOriginalRegisterBuffer->OriginalGdtr)); > + AsmWriteIdtr (&(CpuOriginalRegisterBuffer->OriginalIdtr)); > + Tr = CpuOriginalRegisterBuffer->Tr; > + if ((Tr != 0) && (Tr < CpuOriginalRegisterBuffer->OriginalGdtr.Limit)) { > + Tss = (IA32_TSS_DESCRIPTOR *)(CpuOriginalRegisterBuffer- > >OriginalGdtr.Base + Tr); > + if (Tss->Bits.P == 1) { > + // > + // Clear busy bit of TSS before write Tr > + // > + Tss->Bits.Type &= 0xD; > + AsmWriteTr (Tr); > + } > + } > +} > + > +/** > + Restore all cpu original register before test case. > + > + @param[in] MpServices MpServices. > + @param[in] CpuOriginalRegisterBuffer Address of > CpuOriginalRegisterBuffer. > + @param[in] BspProcessorNum Bsp processor number. > +**/ > +VOID > +RestoreAllCpuRegisters ( > + MP_SERVICES *MpServices, OPTIONAL > + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer, > + UINTN BspProcessorNum > + ) > +{ > + CPU_REGISTER_BUFFER *AllCpuOriginalRegisterBuffer; > + UINTN Index; > + EFI_STATUS Status; > + > + for (Index = 0; Index < mNumberOfProcessors; ++Index) { > + AllCpuOriginalRegisterBuffer = CpuOriginalRegisterBuffer + Index; > + if (Index == BspProcessorNum) { > + RestoreRegistersPerCpu ((VOID *)AllCpuOriginalRegisterBuffer); > + continue; > + } > + > + ASSERT (MpServices != NULL); > + Status = MpServicesUnitTestStartupThisAP ( > + *MpServices, > + (EFI_AP_PROCEDURE)RestoreRegistersPerCpu, > + Index, > + 0, > + (VOID *)AllCpuOriginalRegisterBuffer > + ); > + ASSERT_EFI_ERROR (Status); > + } > +} > + > +/** > + Store all cpu original register before test case. > + > + @param[in] Buffer Argument of the procedure. > +**/ > +VOID > +EFIAPI > +SaveRegisterPerCpu ( > + IN VOID *Buffer > + ) > +{ > + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; > + IA32_DESCRIPTOR Gdtr; > + IA32_DESCRIPTOR Idtr; > + > + CpuOriginalRegisterBuffer = (CPU_REGISTER_BUFFER *)Buffer; > + > + AsmReadGdtr (&Gdtr); > + AsmReadIdtr (&Idtr); > + CpuOriginalRegisterBuffer->OriginalGdtr.Base = Gdtr.Base; > + CpuOriginalRegisterBuffer->OriginalGdtr.Limit = Gdtr.Limit; > + CpuOriginalRegisterBuffer->OriginalIdtr.Base = Idtr.Base; > + CpuOriginalRegisterBuffer->OriginalIdtr.Limit = Idtr.Limit; > + CpuOriginalRegisterBuffer->Tr = AsmReadTr (); > +} > + > +/** > + Store all cpu original register before test case. > + > + @param[in] MpServices MpServices. > + @param[in] BspProcessorNum Bsp processor number. > + > + @return Pointer to the allocated CPU_REGISTER_BUFFER. > +**/ > +CPU_REGISTER_BUFFER * > +SaveAllCpuRegisters ( > + MP_SERVICES *MpServices, OPTIONAL > + UINTN BspProcessorNum > + ) > +{ > + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; > + CPU_REGISTER_BUFFER *AllCpuOriginalRegisterBuffer; > + EFI_STATUS Status; > + UINTN Index; > + > + CpuOriginalRegisterBuffer = AllocateZeroPool (mNumberOfProcessors * > sizeof (CPU_REGISTER_BUFFER)); > + ASSERT (CpuOriginalRegisterBuffer != NULL); > + > + for (Index = 0; Index < mNumberOfProcessors; ++Index) { > + AllCpuOriginalRegisterBuffer = CpuOriginalRegisterBuffer + Index; > + if (Index == BspProcessorNum) { > + SaveRegisterPerCpu ((VOID *)AllCpuOriginalRegisterBuffer); > + continue; > + } > + > + ASSERT (MpServices != NULL); > + Status = MpServicesUnitTestStartupThisAP ( > + *MpServices, > + (EFI_AP_PROCEDURE)SaveRegisterPerCpu, > + Index, > + 0, > + (VOID *)AllCpuOriginalRegisterBuffer > + ); > + ASSERT_EFI_ERROR (Status); > + } > + > + return CpuOriginalRegisterBuffer; > +} > + > +/** > + Initialize Ap Idt Procedure. > + > + @param[in] Buffer Argument of the procedure. > +**/ > +VOID > +EFIAPI > +InitializeIdtPerAp ( > + IN VOID *Buffer > + ) > +{ > + AsmWriteIdtr (Buffer); > +} > + > +/** > + Initialize all Ap Idt. > + > + @param[in] MpServices MpServices. > + @param[in] BspIdtr Pointer to IA32_DESCRIPTOR allocated by Bsp. > +**/ > +VOID > +InitializeApIdt ( > + MP_SERVICES MpServices, > + VOID *BspIdtr > + ) > +{ > + EFI_STATUS Status; > + > + Status = MpServicesUnitTestStartupAllAPs ( > + MpServices, > + (EFI_AP_PROCEDURE)InitializeIdtPerAp, > + FALSE, > + 0, > + BspIdtr > + ); > + ASSERT_EFI_ERROR (Status); > +} > + > +/** > + Check if exception handler can registered/unregistered for no error code > exception. > + > + @param[in] Context [Optional] An optional parameter that enables: > + 1) test-case reuse with varied parameters and > + 2) test-case re-entry for Target tests that need a > + reboot. This parameter is a VOID* and it is the > + responsibility of the test author to ensure that the > + contents are well understood by all test cases that may > + consume it. > + > + @retval UNIT_TEST_PASSED The Unit test has completed and the > test > + case was successful. > + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. > +**/ > +UNIT_TEST_STATUS > +EFIAPI > +TestRegisterHandlerForNoErrorCodeException ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + EFI_STATUS Status; > + UINTN Index; > + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; > + VOID *NewIdtr; > + > + CpuOriginalRegisterBuffer = SaveAllCpuRegisters (NULL, 0); > + NewIdtr = InitializeBspIdt (); > + Status = InitializeCpuExceptionHandlers (NULL); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + > + for (Index = 0; Index < SPEC_MAX_EXCEPTION_NUM; Index++) { > + // > + // Only test no error code exception by INT n instruction. > + // > + if ((mErrorCodeExceptionFlag & (1 << Index)) != 0) { > + continue; > + } > + > + DEBUG ((DEBUG_INFO, "TestCase1: ExceptionType is %d\n", Index)); > + Status = RegisterCpuInterruptHandler (Index, INTnExceptionHandler); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + > + TriggerINTnException (Index); > + UT_ASSERT_EQUAL (mExceptionType, Index); > + Status = RegisterCpuInterruptHandler (Index, NULL); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + } > + > + RestoreAllCpuRegisters (NULL, CpuOriginalRegisterBuffer, 0); > + FreePool (CpuOriginalRegisterBuffer); > + FreePool (NewIdtr); > + return UNIT_TEST_PASSED; > +} > + > +/** > + Get Bsp stack base. > + > + @param[out] StackBase Pointer to stack base of BSP. > +**/ > +VOID > +GetBspStackBase ( > + OUT UINTN *StackBase > + ) > +{ > + EFI_PEI_HOB_POINTERS Hob; > + EFI_HOB_MEMORY_ALLOCATION *MemoryHob; > + > + // > + // Get the base of stack from Hob. > + // > + ASSERT (StackBase != NULL); > + Hob.Raw = GetHobList (); > + while ((Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, > Hob.Raw)) != NULL) { > + MemoryHob = Hob.MemoryAllocation; > + if (CompareGuid (&gEfiHobMemoryAllocStackGuid, &MemoryHob- > >AllocDescriptor.Name)) { > + DEBUG (( > + DEBUG_INFO, > + "%a: Bsp StackBase = 0x%016lx StackSize = 0x%016lx\n", > + __FUNCTION__, > + MemoryHob->AllocDescriptor.MemoryBaseAddress, > + MemoryHob->AllocDescriptor.MemoryLength > + )); > + > + *StackBase = (UINTN)MemoryHob- > >AllocDescriptor.MemoryBaseAddress; > + // > + // Ensure the base of the stack is page-size aligned. > + // > + ASSERT ((*StackBase & EFI_PAGE_MASK) == 0); > + break; > + } > + > + Hob.Raw = GET_NEXT_HOB (Hob); > + } > + > + ASSERT (*StackBase != 0); > +} > + > +/** > + Initialize Ap Idt Procedure. > + > + @param[in] Buffer Argument of the procedure. > +**/ > +VOID > +EFIAPI > +GetStackBasePerAp ( > + IN VOID *Buffer > + ) > +{ > + UINTN ApTopOfStack; > + > + ApTopOfStack = ALIGN_VALUE ((UINTN)&ApTopOfStack, > (UINTN)PcdGet32 (PcdCpuApStackSize)); > + *(UINTN *)Buffer = ApTopOfStack - (UINTN)PcdGet32 > (PcdCpuApStackSize); > +} > + > +/** > + Get Ap stack Info. > + > + @param[in] MpServices MpServices. > + @param[in] BspProcessorNum Bsp processor number. > + > + @return Pointer to the allocated CpuStackBaseBuffer. > +**/ > +UINTN * > +GetAllCpuStackBase ( > + MP_SERVICES *MpServices, > + UINTN BspProcessorNum > + ) > +{ > + UINTN *CpuStackBaseBuffer; > + UINTN *AllCpuStackBaseBuffer; > + EFI_STATUS Status; > + UINTN Index; > + > + CpuStackBaseBuffer = AllocateZeroPool (mNumberOfProcessors * sizeof > (UINTN)); > + ASSERT (CpuStackBaseBuffer != NULL); > + > + for (Index = 0; Index < mNumberOfProcessors; ++Index) { > + AllCpuStackBaseBuffer = CpuStackBaseBuffer + Index; > + if (Index == BspProcessorNum) { > + GetBspStackBase (AllCpuStackBaseBuffer); > + continue; > + } > + > + ASSERT (MpServices != NULL); > + Status = MpServicesUnitTestStartupThisAP ( > + *MpServices, > + (EFI_AP_PROCEDURE)GetStackBasePerAp, > + Index, > + 0, > + (VOID *)AllCpuStackBaseBuffer > + ); > + ASSERT_EFI_ERROR (Status); > + DEBUG ((DEBUG_INFO, "AP[%d] StackBase = 0x%x\n", Index, > CpuStackBaseBuffer[Index])); > + } > + > + return CpuStackBaseBuffer; > +} > + > +/** > + Return not present or ReadOnly address in page table. > + > + @param[out] PFAddress Access to the address which is not permitted will > trigger PF exceptions. > +**/ > +VOID > +PageFaultAddressInPageTable ( > + UINTN *PFAddress > + ) > +{ > + IA32_CR0 Cr0; > + IA32_CR4 Cr4; > + UINTN PageTable; > + PAGING_MODE PagingMode; > + BOOLEAN Enable5LevelPaging; > + RETURN_STATUS Status; > + IA32_MAP_ENTRY *Map; > + UINTN MapCount; > + UINTN Index; > + > + ASSERT (PFAddress != NULL); > + *PFAddress = 0; > + > + Cr0.UintN = AsmReadCr0 (); > + if (Cr0.Bits.PG == 0) { > + return; > + } > + > + PageTable = AsmReadCr3 (); > + Cr4.UintN = AsmReadCr4 (); > + if (sizeof (UINTN) == sizeof (UINT32)) { > + ASSERT (Cr4.Bits.PAE == 1); > + PagingMode = PagingPae; > + } else { > + Enable5LevelPaging = (BOOLEAN)(Cr4.Bits.LA57 == 1); > + PagingMode = Enable5LevelPaging ? Paging5Level : Paging4Level; > + } > + > + MapCount = 0; > + Status = PageTableParse (PageTable, PagingMode, NULL, &MapCount); > + ASSERT (Status == RETURN_BUFFER_TOO_SMALL); > + Map = AllocatePages (EFI_SIZE_TO_PAGES (MapCount * sizeof > (IA32_MAP_ENTRY))); > + Status = PageTableParse (PageTable, PagingMode, Map, &MapCount); > + ASSERT (Status == RETURN_SUCCESS); > + > + for (Index = 0; Index < MapCount; Index++) { > + DEBUG (( > + DEBUG_ERROR, > + "%02d: %016lx - %016lx, %016lx\n", > + Index, > + Map[Index].LinearAddress, > + Map[Index].LinearAddress + Map[Index].Length, > + Map[Index].Attribute.Uint64 > + )); > + > + // > + // Not present address in page table. > + // > + if ((Index >= 1) && (Map[Index].LinearAddress > Map[Index - > 1].LinearAddress + Map[Index - 1].Length)) { > + *PFAddress = (UINTN)(Map[Index - 1].LinearAddress + Map[Index - > 1].Length); > + return; > + } > + > + // > + // ReadOnly address in page table. > + // > + if ((Cr0.Bits.WP != 0) && (Map[Index].Attribute.Bits.ReadWrite == 0)) { > + *PFAddress = (UINTN)Map[Index].LinearAddress; > + return; > + } > + } > +} > + > +/** > + Test if exception handler can registered/unregistered for GP and PF. > + > + @param[in] Context [Optional] An optional parameter that enables: > + 1) test-case reuse with varied parameters and > + 2) test-case re-entry for Target tests that need a > + reboot. This parameter is a VOID* and it is the > + responsibility of the test author to ensure that the > + contents are well understood by all test cases that may > + consume it. > + > + @retval UNIT_TEST_PASSED The Unit test has completed and the > test > + case was successful. > + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. > +**/ > +UNIT_TEST_STATUS > +EFIAPI > +TestRegisterHandlerForGPAndPF ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + EFI_STATUS Status; > + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; > + UINTN PFAddress; > + VOID *NewIdtr; > + > + PFAddress = 0; > + CpuOriginalRegisterBuffer = SaveAllCpuRegisters (NULL, 0); > + NewIdtr = InitializeBspIdt (); > + Status = InitializeCpuExceptionHandlers (NULL); > + > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + > + // > + // GP exception. > + // > + DEBUG ((DEBUG_INFO, "TestCase2: ExceptionType is %d\n", > EXCEPT_IA32_GP_FAULT)); > + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_GP_FAULT, > AdjustRipForFaultHandler); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + > + TriggerGPException (CR4_RESERVED_BIT); > + UT_ASSERT_EQUAL (mExceptionType, EXCEPT_IA32_GP_FAULT); > + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_GP_FAULT, NULL); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + > + // > + // PF exception. > + // > + PageFaultAddressInPageTable (&PFAddress); > + > + if (PFAddress > 0) { > + DEBUG ((DEBUG_INFO, "TestCase2: ExceptionType is %d\n", > EXCEPT_IA32_PAGE_FAULT)); > + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, > AdjustRipForFaultHandler); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + TriggerPFException (PFAddress); > + > + UT_ASSERT_EQUAL (mExceptionType, EXCEPT_IA32_PAGE_FAULT); > + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, > NULL); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + } > + > + RestoreAllCpuRegisters (NULL, CpuOriginalRegisterBuffer, 0); > + FreePool (CpuOriginalRegisterBuffer); > + FreePool (NewIdtr); > + return UNIT_TEST_PASSED; > +} > + > +/** > + Test if Cpu Context is consistent before and after exception. > + > + @param[in] Context [Optional] An optional parameter that enables: > + 1) test-case reuse with varied parameters and > + 2) test-case re-entry for Target tests that need a > + reboot. This parameter is a VOID* and it is the > + responsibility of the test author to ensure that the > + contents are well understood by all test cases that may > + consume it. > + > + @retval UNIT_TEST_PASSED The Unit test has completed and the > test > + case was successful. > + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. > +**/ > +UNIT_TEST_STATUS > +EFIAPI > +TestCpuContextConsistency ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + EFI_STATUS Status; > + UINTN Index; > + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; > + UINTN FaultParameter; > + VOID *NewIdtr; > + > + FaultParameter = 0; > + CpuOriginalRegisterBuffer = SaveAllCpuRegisters (NULL, 0); > + NewIdtr = InitializeBspIdt (); > + Status = InitializeCpuExceptionHandlers (NULL); > + > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + > + for (Index = 0; Index < 22; Index++) { > + if (Index == EXCEPT_IA32_PAGE_FAULT) { > + PageFaultAddressInPageTable (&FaultParameter); > + if (FaultParameter == 0) { > + continue; > + } > + } else if (Index == EXCEPT_IA32_GP_FAULT) { > + FaultParameter = CR4_RESERVED_BIT; > + } else { > + if ((mErrorCodeExceptionFlag & (1 << Index)) != 0) { > + continue; > + } > + } > + > + DEBUG ((DEBUG_INFO, "TestCase3: ExceptionType is %d\n", Index)); > + Status = RegisterCpuInterruptHandler (Index, AdjustCpuContextHandler); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + > + // > + // Trigger different type exception and compare different stage cpu > context. > + // > + AsmTestConsistencyOfCpuContext (Index, FaultParameter); > + CompareCpuContext (); > + Status = RegisterCpuInterruptHandler (Index, NULL); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + } > + > + RestoreAllCpuRegisters (NULL, CpuOriginalRegisterBuffer, 0); > + FreePool (CpuOriginalRegisterBuffer); > + FreePool (NewIdtr); > + return UNIT_TEST_PASSED; > +} > + > +/** > + Initializes CPU exceptions handlers for the sake of stack switch > requirement. > + > + This function is a wrapper of InitializeSeparateExceptionStacks. It's mainly > + for the sake of AP's init because of EFI_AP_PROCEDURE API requirement. > + > + @param[in,out] Buffer The pointer to private data buffer. > + > +**/ > +VOID > +EFIAPI > +InitializeExceptionStackSwitchHandlersPerAp ( > + IN OUT VOID *Buffer > + ) > +{ > + EXCEPTION_STACK_SWITCH_CONTEXT *CpuSwitchStackData; > + > + CpuSwitchStackData = (EXCEPTION_STACK_SWITCH_CONTEXT *)Buffer; > + > + // > + // This may be called twice for each Cpu. Only run > InitializeSeparateExceptionStacks > + // if this is the first call or the first call failed because of size too small. > + // > + if ((CpuSwitchStackData->Status == EFI_NOT_STARTED) || > (CpuSwitchStackData->Status == EFI_BUFFER_TOO_SMALL)) { > + CpuSwitchStackData->Status = InitializeSeparateExceptionStacks > (CpuSwitchStackData->Buffer, &CpuSwitchStackData->BufferSize); > + } > +} > + > +/** > + Initializes MP exceptions handlers for the sake of stack switch requirement. > + > + This function will allocate required resources required to setup stack switch > + and pass them through SwitchStackData to each logic processor. > + > + @param[in, out] MpServices MpServices. > + @param[in, out] BspProcessorNum Bsp processor number. > + > + @return Pointer to the allocated SwitchStackData. > +**/ > +EXCEPTION_STACK_SWITCH_CONTEXT * > +InitializeMpExceptionStackSwitchHandlers ( > + MP_SERVICES MpServices, > + UINTN BspProcessorNum > + ) > +{ > + UINTN Index; > + EXCEPTION_STACK_SWITCH_CONTEXT *SwitchStackData; > + EXCEPTION_STACK_SWITCH_CONTEXT *CpuSwitchStackData; > + UINTN BufferSize; > + EFI_STATUS Status; > + UINT8 *Buffer; > + > + SwitchStackData = AllocateZeroPool (mNumberOfProcessors * sizeof > (EXCEPTION_STACK_SWITCH_CONTEXT)); > + ASSERT (SwitchStackData != NULL); > + for (Index = 0; Index < mNumberOfProcessors; ++Index) { > + CpuSwitchStackData = SwitchStackData + Index; > + // > + // Because the procedure may runs multiple times, use the status > EFI_NOT_STARTED > + // to indicate the procedure haven't been run yet. > + // > + SwitchStackData[Index].Status = EFI_NOT_STARTED; > + if (Index == BspProcessorNum) { > + InitializeExceptionStackSwitchHandlersPerAp ((VOID > *)CpuSwitchStackData); > + continue; > + } > + > + Status = MpServicesUnitTestStartupThisAP ( > + MpServices, > + InitializeExceptionStackSwitchHandlersPerAp, > + Index, > + 0, > + (VOID *)CpuSwitchStackData > + ); > + ASSERT_EFI_ERROR (Status); > + } > + > + BufferSize = 0; > + for (Index = 0; Index < mNumberOfProcessors; ++Index) { > + if (SwitchStackData[Index].Status == EFI_BUFFER_TOO_SMALL) { > + ASSERT (SwitchStackData[Index].BufferSize != 0); > + BufferSize += SwitchStackData[Index].BufferSize; > + } else { > + ASSERT (SwitchStackData[Index].Status == EFI_SUCCESS); > + ASSERT (SwitchStackData[Index].BufferSize == 0); > + } > + } > + > + if (BufferSize != 0) { > + Buffer = AllocateZeroPool (BufferSize); > + ASSERT (Buffer != NULL); > + BufferSize = 0; > + for (Index = 0; Index < mNumberOfProcessors; ++Index) { > + if (SwitchStackData[Index].Status == EFI_BUFFER_TOO_SMALL) { > + SwitchStackData[Index].Buffer = (VOID *)(&Buffer[BufferSize]); > + BufferSize += SwitchStackData[Index].BufferSize; > + DEBUG (( > + DEBUG_INFO, > + "Buffer[cpu%lu] for InitializeExceptionStackSwitchHandlersPerAp: > 0x%lX with size 0x%lX\n", > + (UINT64)(UINTN)Index, > + (UINT64)(UINTN)SwitchStackData[Index].Buffer, > + (UINT64)(UINTN)SwitchStackData[Index].BufferSize > + )); > + } > + } > + > + for (Index = 0; Index < mNumberOfProcessors; ++Index) { > + CpuSwitchStackData = SwitchStackData + Index; > + if (Index == BspProcessorNum) { > + InitializeExceptionStackSwitchHandlersPerAp ((VOID > *)CpuSwitchStackData); > + continue; > + } > + > + Status = MpServicesUnitTestStartupThisAP ( > + MpServices, > + InitializeExceptionStackSwitchHandlersPerAp, > + Index, > + 0, > + (VOID *)CpuSwitchStackData > + ); > + ASSERT_EFI_ERROR (Status); > + } > + > + for (Index = 0; Index < mNumberOfProcessors; ++Index) { > + ASSERT (SwitchStackData[Index].Status == EFI_SUCCESS); > + } > + } > + > + return SwitchStackData; > +} > + > +/** > + Test if stack overflow is captured by CpuStackGuard in Bsp and AP. > + > + @param[in] Context [Optional] An optional parameter that enables: > + 1) test-case reuse with varied parameters and > + 2) test-case re-entry for Target tests that need a > + reboot. This parameter is a VOID* and it is the > + responsibility of the test author to ensure that the > + contents are well understood by all test cases that may > + consume it. > + > + @retval UNIT_TEST_PASSED The Unit test has completed and the > test > + case was successful. > + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. > +**/ > +UNIT_TEST_STATUS > +EFIAPI > +TestCpuStackGuardInBspAndAp ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + EFI_STATUS Status; > + UINTN OriginalStackBase; > + UINTN NewStackTop; > + UINTN NewStackBase; > + EXCEPTION_STACK_SWITCH_CONTEXT *SwitchStackData; > + MP_SERVICES MpServices; > + UINTN ProcessorNumber; > + UINTN EnabledProcessorNum; > + CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; > + UINTN Index; > + UINTN BspProcessorNum; > + VOID *NewIdtr; > + UINTN *CpuStackBaseBuffer; > + > + if (!PcdGetBool (PcdCpuStackGuard)) { > + return UNIT_TEST_PASSED; > + } > + > + // > + // Get MP Service Protocol > + // > + Status = GetMpServices (&MpServices); > + Status = MpServicesUnitTestGetNumberOfProcessors (MpServices, > &ProcessorNumber, &EnabledProcessorNum); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + Status = MpServicesUnitTestWhoAmI (MpServices, &BspProcessorNum); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + mNumberOfProcessors = ProcessorNumber; > + > + CpuOriginalRegisterBuffer = SaveAllCpuRegisters (&MpServices, > BspProcessorNum); > + > + // > + // Initialize Bsp and AP Idt. > + // Idt buffer should not be empty or it will hang in MP API. > + // > + NewIdtr = InitializeBspIdt (); > + Status = InitializeCpuExceptionHandlers (NULL); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + InitializeApIdt (MpServices, NewIdtr); > + > + // > + // Get BSP and AP original stack base. > + // > + CpuStackBaseBuffer = GetAllCpuStackBase (&MpServices, > BspProcessorNum); > + > + // > + // InitializeMpExceptionStackSwitchHandlers and register exception > handler. > + // > + SwitchStackData = InitializeMpExceptionStackSwitchHandlers (MpServices, > BspProcessorNum); > + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, > CpuStackGuardExceptionHandler); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_DOUBLE_FAULT, > AdjustRipForFaultHandler); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + > + for (Index = 0; Index < mNumberOfProcessors; Index++) { > + OriginalStackBase = CpuStackBaseBuffer[Index]; > + NewStackTop = (UINTN)(SwitchStackData[Index].Buffer) + > SwitchStackData[Index].BufferSize; > + NewStackBase = (UINTN)(SwitchStackData[Index].Buffer); > + if (Index == BspProcessorNum) { > + TriggerStackOverflowbyCpuStackGuard (); > + } else { > + MpServicesUnitTestStartupThisAP ( > + MpServices, > + (EFI_AP_PROCEDURE)TriggerStackOverflowbyCpuStackGuard, > + Index, > + 0, > + NULL > + ); > + } > + > + DEBUG ((DEBUG_INFO, "TestCase4: mRspAddress[0] is 0x%x, > mRspAddress[1] is 0x%x\n", mRspAddress[0], mRspAddress[1])); > + UT_ASSERT_TRUE ((mRspAddress[0] >= OriginalStackBase) && > (mRspAddress[0] <= (OriginalStackBase + SIZE_4KB))); > + UT_ASSERT_TRUE ((mRspAddress[1] >= NewStackBase) && > (mRspAddress[1] < NewStackTop)); > + } > + > + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, NULL); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + Status = RegisterCpuInterruptHandler (EXCEPT_IA32_DOUBLE_FAULT, > NULL); > + UT_ASSERT_EQUAL (Status, EFI_SUCCESS); > + RestoreAllCpuRegisters (&MpServices, CpuOriginalRegisterBuffer, > BspProcessorNum); > + FreePool (SwitchStackData); > + FreePool (CpuOriginalRegisterBuffer); > + FreePool (NewIdtr); > + > + return UNIT_TEST_PASSED; > +} > + > +/** > + Create CpuExceptionLibUnitTestSuite and add test case. > + > + @param[in] FrameworkHandle Unit test framework. > + > + @return EFI_SUCCESS The unit test suite was created. > + @retval EFI_OUT_OF_RESOURCES There are not enough resources > available to > + initialize the unit test suite. > +**/ > +EFI_STATUS > +AddCommonTestCase ( > + IN UNIT_TEST_FRAMEWORK_HANDLE Framework > + ) > +{ > + EFI_STATUS Status; > + UNIT_TEST_SUITE_HANDLE CpuExceptionLibUnitTestSuite; > + > + // > + // Populate the Manual Test Cases. > + // > + Status = CreateUnitTestSuite (&CpuExceptionLibUnitTestSuite, Framework, > "Test CpuExceptionHandlerLib", "CpuExceptionHandlerLib.Manual", NULL, > NULL); > + if (EFI_ERROR (Status)) { > + DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for > CpuExceptionHandlerLib Test Cases\n")); > + Status = EFI_OUT_OF_RESOURCES; > + return Status; > + } > + > + AddTestCase (CpuExceptionLibUnitTestSuite, "Check if exception handler > can be registered/unregistered for no error code exception", > "TestRegisterHandlerForNoErrorCodeException", > TestRegisterHandlerForNoErrorCodeException, NULL, NULL, NULL); > + AddTestCase (CpuExceptionLibUnitTestSuite, "Check if exception handler > can be registered/unregistered for GP and PF", > "TestRegisterHandlerForGPAndPF", TestRegisterHandlerForGPAndPF, NULL, > NULL, NULL); > + > + AddTestCase (CpuExceptionLibUnitTestSuite, "Check if Cpu Context is > consistent before and after exception.", "TestCpuContextConsistency", > TestCpuContextConsistency, NULL, NULL, NULL); > + AddTestCase (CpuExceptionLibUnitTestSuite, "Check if stack overflow is > captured by CpuStackGuard in Bsp and AP", > "TestCpuStackGuardInBspAndAp", TestCpuStackGuardInBspAndAp, NULL, > NULL, NULL); > + > + return EFI_SUCCESS; > +} > diff --git > a/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibU > nitTest.inf > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibU > nitTest.inf > new file mode 100644 > index 0000000000..e3dbe7b9ab > --- /dev/null > +++ > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibU > nitTest.inf > @@ -0,0 +1,58 @@ > +## @file > +# Unit tests of the DxeCpuExceptionHandlerLib instance. > +# > +# Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> > +# SPDX-License-Identifier: BSD-2-Clause-Patent > +## > + > +[Defines] > + INF_VERSION = 0x00010005 > + BASE_NAME = CpuExceptionHandlerDxeTest > + FILE_GUID = D76BFD9C-0B6D-46BD-AD66-2BBB6FA7031A > + MODULE_TYPE = DXE_DRIVER > + VERSION_STRING = 1.0 > + ENTRY_POINT = CpuExceptionHandlerTestEntry > + > +# > +# The following information is for reference only and not required by the > build tools. > +# > +# VALID_ARCHITECTURES = X64 > +# > +[Sources.X64] > + X64/ArchExceptionHandlerTestAsm.nasm > + X64/ArchExceptionHandlerTest.c > + > +[Sources.common] > + CpuExceptionHandlerTest.h > + CpuExceptionHandlerTestCommon.c > + DxeCpuExceptionHandlerUnitTest.c > + > +[Packages] > + MdePkg/MdePkg.dec > + MdeModulePkg/MdeModulePkg.dec > + UefiCpuPkg/UefiCpuPkg.dec > + > +[LibraryClasses] > + BaseLib > + BaseMemoryLib > + DebugLib > + UnitTestLib > + MemoryAllocationLib > + CpuExceptionHandlerLib > + UefiDriverEntryPoint > + HobLib > + UefiBootServicesTableLib > + CpuPageTableLib > + > +[Guids] > + gEfiHobMemoryAllocStackGuid > + > +[Pcd] > + gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard ## CONSUMES > + gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize ## CONSUMES > + > +[Protocols] > + gEfiMpServiceProtocolGuid > + > +[Depex] > + gEfiMpServiceProtocolGuid > diff --git > a/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnitT > est.c > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnit > Test.c > new file mode 100644 > index 0000000000..917fc549bf > --- /dev/null > +++ > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnit > Test.c > @@ -0,0 +1,196 @@ > +/** @file > + Unit tests of the CpuExceptionHandlerLib. > + > + Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#include "CpuExceptionHandlerTest.h" > +#include <Library/UefiBootServicesTableLib.h> > + > +/** > + Initialize Bsp Idt with a new Idt table and return the IA32_DESCRIPTOR > buffer. > + In PEIM, store original PeiServicePointer before new Idt table. > + > + @return Pointer to the allocated IA32_DESCRIPTOR buffer. > +**/ > +VOID * > +InitializeBspIdt ( > + VOID > + ) > +{ > + UINTN *NewIdtTable; > + IA32_DESCRIPTOR *Idtr; > + > + Idtr = AllocateZeroPool (sizeof (IA32_DESCRIPTOR)); > + ASSERT (Idtr != NULL); > + NewIdtTable = AllocateZeroPool (sizeof (IA32_IDT_GATE_DESCRIPTOR) * > CPU_INTERRUPT_NUM); > + ASSERT (NewIdtTable != NULL); > + Idtr->Base = (UINTN)NewIdtTable; > + Idtr->Limit = (UINT16)(sizeof (IA32_IDT_GATE_DESCRIPTOR) * > CPU_INTERRUPT_NUM - 1); > + > + AsmWriteIdtr (Idtr); > + return Idtr; > +} > + > +/** > + Retrieve the number of logical processor in the platform and the number > of those logical processors that > + are enabled on this boot. > + > + @param[in] MpServices MP_SERVICES structure. > + @param[out] NumberOfProcessors Pointer to the total number of logical > processors in the system, including > + the BSP and disabled APs. > + @param[out] NumberOfEnabledProcessors Pointer to the number of > processors in the system that are enabled. > + > + @retval EFI_SUCCESS Retrieve the number of logical processor > successfully > + @retval Others Retrieve the number of logical processor > unsuccessfully > +**/ > +EFI_STATUS > +MpServicesUnitTestGetNumberOfProcessors ( > + IN MP_SERVICES MpServices, > + OUT UINTN *NumberOfProcessors, > + OUT UINTN *NumberOfEnabledProcessors > + ) > +{ > + return MpServices.Protocol->GetNumberOfProcessors > (MpServices.Protocol, NumberOfProcessors, NumberOfEnabledProcessors); > +} > + > +/** > + Get the handle number for the calling processor. > + > + @param[in] MpServices MP_SERVICES structure. > + @param[out] ProcessorNumber The handle number for the calling > processor. > + > + @retval EFI_SUCCESS Get the handle number for the calling processor > successfully. > + @retval Others Get the handle number for the calling processor > unsuccessfully. > +**/ > +EFI_STATUS > +MpServicesUnitTestWhoAmI ( > + IN MP_SERVICES MpServices, > + OUT UINTN *ProcessorNumber > + ) > +{ > + return MpServices.Protocol->WhoAmI (MpServices.Protocol, > ProcessorNumber); > +} > + > +/** > + Caller gets one enabled AP to execute a caller-provided function. > + > + @param[in] MpServices MP_SERVICES structure. > + @param[in] Procedure Pointer to the function to be run on enabled APs > of the system. > + @param[in] ProcessorNumber The handle number of the AP. > + @param[in] TimeoutInMicroSeconds Indicates the time limit in > microseconds for APs to return from Procedure, > + for blocking mode only. Zero means infinity. > + @param[in] ProcedureArgument The parameter passed into Procedure > for all APs. > + > + > + @retval EFI_SUCCESS Caller gets one enabled AP to execute a caller- > provided function successfully > + @retval Others Caller gets one enabled AP to execute a caller- > provided function unsuccessfully > +**/ > +EFI_STATUS > +MpServicesUnitTestStartupThisAP ( > + IN MP_SERVICES MpServices, > + IN EFI_AP_PROCEDURE Procedure, > + IN UINTN ProcessorNumber, > + IN UINTN TimeoutInMicroSeconds, > + IN VOID *ProcedureArgument > + ) > +{ > + return MpServices.Protocol->StartupThisAP (MpServices.Protocol, > Procedure, ProcessorNumber, NULL, TimeoutInMicroSeconds, > ProcedureArgument, NULL); > +} > + > +/** > + Execute a caller provided function on all enabled APs. > + > + @param[in] MpServices MP_SERVICES structure. > + @param[in] Procedure Pointer to the function to be run on enabled APs > of the system. > + @param[in] SingleThread If TRUE, then all the enabled APs execute the > function specified by Procedure > + one by one, in ascending order of processor handle number. > + If FALSE, then all the enabled APs execute the function > specified by Procedure > + simultaneously. > + @param[in] TimeoutInMicroSeconds Indicates the time limit in > microseconds for APs to return from Procedure, > + for blocking mode only. Zero means infinity. > + @param[in] ProcedureArgument The parameter passed into Procedure > for all APs. > + > + @retval EFI_SUCCESS Execute a caller provided function on all enabled > APs successfully > + @retval Others Execute a caller provided function on all enabled APs > unsuccessfully > +**/ > +EFI_STATUS > +MpServicesUnitTestStartupAllAPs ( > + IN MP_SERVICES MpServices, > + IN EFI_AP_PROCEDURE Procedure, > + IN BOOLEAN SingleThread, > + IN UINTN TimeoutInMicroSeconds, > + IN VOID *ProcedureArgument > + ) > +{ > + return MpServices.Protocol->StartupAllAPs (MpServices.Protocol, > Procedure, SingleThread, NULL, TimeoutInMicroSeconds, > ProcedureArgument, NULL); > +} > + > +/** > + Get EFI_MP_SERVICES_PROTOCOL pointer. > + > + @param[out] MpServices Pointer to the buffer where > EFI_MP_SERVICES_PROTOCOL is stored > + > + @retval EFI_SUCCESS EFI_MP_SERVICES_PROTOCOL interface is > returned > + @retval EFI_NOT_FOUND EFI_MP_SERVICES_PROTOCOL interface is not > found > +**/ > +EFI_STATUS > +GetMpServices ( > + OUT MP_SERVICES *MpServices > + ) > +{ > + return gBS->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID > **)&MpServices->Protocol); > +} > + > +/** > + Entry for CpuExceptionHandlerDxeTest driver. > + > + @param ImageHandle Image handle this driver. > + @param SystemTable Pointer to the System Table. > + > + @retval EFI_SUCCESS The driver executed normally. > + > +**/ > +EFI_STATUS > +EFIAPI > +CpuExceptionHandlerTestEntry ( > + IN EFI_HANDLE ImageHandle, > + IN EFI_SYSTEM_TABLE *SystemTable > + ) > +{ > + EFI_STATUS Status; > + UNIT_TEST_FRAMEWORK_HANDLE Framework; > + > + Framework = NULL; > + > + DEBUG ((DEBUG_INFO, "%a v%a\n", UNIT_TEST_APP_NAME, > UNIT_TEST_APP_VERSION)); > + > + // > + // Start setting up the test framework for running the tests. > + // > + Status = InitUnitTestFramework (&Framework, UNIT_TEST_APP_NAME, > gEfiCallerBaseName, UNIT_TEST_APP_VERSION); > + if (EFI_ERROR (Status)) { > + DEBUG ((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status > = %r\n", Status)); > + goto EXIT; > + } > + > + Status = AddCommonTestCase (Framework); > + if (EFI_ERROR (Status)) { > + DEBUG ((DEBUG_ERROR, "Failed in AddCommonTestCase. Status = %r\n", > Status)); > + goto EXIT; > + } > + > + // > + // Execute the tests. > + // > + Status = RunAllTestSuites (Framework); > + > +EXIT: > + if (Framework) { > + FreeUnitTestFramework (Framework); > + } > + > + return Status; > +} > diff --git > a/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTes > t.c > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTes > t.c > new file mode 100644 > index 0000000000..9b086622f2 > --- /dev/null > +++ > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTes > t.c > @@ -0,0 +1,167 @@ > +/** @file > + Unit tests of the CpuExceptionHandlerLib. > + > + Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#include "CpuExceptionHandlerTest.h" > + > +GENERAL_REGISTER mRegisterForCheck[2]; > + > +// > +// In TestCpuContextConsistency, Cpu registers will be set to > mAdjustRegisterBeforeException. > +// Rcx in mAdjustRegisterInsideException is set runtime since Rcx is needed > in assembly code. > +// For GP and PF, Rcx is set to FaultParameter. For other exception > triggered by INTn, Rcx is set to ExceptionType. > +// > +GENERAL_REGISTER mAdjustRegisterBeforeException = { 1, 2, 3, 4, 5, 0, 7, 8, > 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf }; > +GENERAL_REGISTER mAdjustRegisterInsideException = { 0x11, 0x12, 0x13, > 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f }; > + > +/** > + Special handler for fault exception. > + Rip/Eip in SystemContext will be modified to the instruction after the > exception instruction. > + > + @param ExceptionType Exception type. > + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. > +**/ > +VOID > +EFIAPI > +AdjustRipForFaultHandler ( > + IN EFI_EXCEPTION_TYPE ExceptionType, > + IN EFI_SYSTEM_CONTEXT SystemContext > + ) > +{ > + mExceptionType = ExceptionType; > + SystemContext.SystemContextX64->Rip += mFaultInstructionLength; > +} > + > +/** > + Special handler for ConsistencyOfCpuContext test case. > + > + @param ExceptionType Exception type. > + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. > +**/ > +VOID > +EFIAPI > +AdjustCpuContextHandler ( > + IN EFI_EXCEPTION_TYPE ExceptionType, > + IN EFI_SYSTEM_CONTEXT SystemContext > + ) > +{ > + // > + // Do not handle Esp and ebp. They will not be restored. > + // Store SystemContext in mRegisterForCheck[0] modified before > exception. > + // > + mRegisterForCheck[0].Rdi = SystemContext.SystemContextX64->Rdi; > + mRegisterForCheck[0].Rsi = SystemContext.SystemContextX64->Rsi; > + mRegisterForCheck[0].Rbx = SystemContext.SystemContextX64->Rbx; > + mRegisterForCheck[0].Rdx = SystemContext.SystemContextX64->Rdx; > + mRegisterForCheck[0].Rcx = SystemContext.SystemContextX64->Rcx; > + mRegisterForCheck[0].Rax = SystemContext.SystemContextX64->Rax; > + mRegisterForCheck[0].R8Register = SystemContext.SystemContextX64- > >R8; > + mRegisterForCheck[0].R9Register = SystemContext.SystemContextX64- > >R9; > + mRegisterForCheck[0].R10Register = SystemContext.SystemContextX64- > >R10; > + mRegisterForCheck[0].R11Register = SystemContext.SystemContextX64- > >R11; > + mRegisterForCheck[0].R12Register = SystemContext.SystemContextX64- > >R12; > + mRegisterForCheck[0].R13Register = SystemContext.SystemContextX64- > >R13; > + mRegisterForCheck[0].R14Register = SystemContext.SystemContextX64- > >R14; > + mRegisterForCheck[0].R15Register = SystemContext.SystemContextX64- > >R15; > + > + // > + // Modify cpu context which will be stored in mRegisterForCheck[1]. > + // > + SystemContext.SystemContextX64->Rdi = > mAdjustRegisterInsideException.Rdi; > + SystemContext.SystemContextX64->Rsi = > mAdjustRegisterInsideException.Rsi; > + SystemContext.SystemContextX64->Rbx = > mAdjustRegisterInsideException.Rbx; > + SystemContext.SystemContextX64->Rdx = > mAdjustRegisterInsideException.Rdx; > + SystemContext.SystemContextX64->Rcx = > mAdjustRegisterInsideException.Rcx; > + SystemContext.SystemContextX64->Rax = > mAdjustRegisterInsideException.Rax; > + SystemContext.SystemContextX64->R8 = > mAdjustRegisterInsideException.R8Register; > + SystemContext.SystemContextX64->R9 = > mAdjustRegisterInsideException.R9Register; > + SystemContext.SystemContextX64->R10 = > mAdjustRegisterInsideException.R10Register; > + SystemContext.SystemContextX64->R11 = > mAdjustRegisterInsideException.R11Register; > + SystemContext.SystemContextX64->R12 = > mAdjustRegisterInsideException.R12Register; > + SystemContext.SystemContextX64->R13 = > mAdjustRegisterInsideException.R13Register; > + SystemContext.SystemContextX64->R14 = > mAdjustRegisterInsideException.R14Register; > + SystemContext.SystemContextX64->R15 = > mAdjustRegisterInsideException.R15Register; > + > + // > + // When fault exception happens, eip/rip points to the faulting instruction. > + // For now, olny GP and PF are tested in fault exception. > + // > + if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) || (ExceptionType == > EXCEPT_IA32_GP_FAULT)) { > + AdjustRipForFaultHandler (ExceptionType, SystemContext); > + } > +} > + > +/** > + Compare cpu context in ConsistencyOfCpuContext test case. > + 1.Compare mRegisterForCheck[0] with mAdjustRegisterBeforeException. > + 2.Compare mRegisterForCheck[1] with mAdjustRegisterAfterException. > + > + @retval UNIT_TEST_PASSED The Unit test has completed and it was > successful. > + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. > +**/ > +UNIT_TEST_STATUS > +CompareCpuContext ( > + VOID > + ) > +{ > + // > + // Do not handle Esp and ebp. They will not be restored. > + // > + UT_ASSERT_EQUAL (mRegisterForCheck[0].Rdi, > mAdjustRegisterBeforeException.Rdi); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].Rsi, > mAdjustRegisterBeforeException.Rsi); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].Rbx, > mAdjustRegisterBeforeException.Rbx); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].Rdx, > mAdjustRegisterBeforeException.Rdx); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].Rcx, > mAdjustRegisterBeforeException.Rcx); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].Rax, > mAdjustRegisterBeforeException.Rax); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].R8Register, > mAdjustRegisterBeforeException.R8Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].R9Register, > mAdjustRegisterBeforeException.R9Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].R10Register, > mAdjustRegisterBeforeException.R10Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].R11Register, > mAdjustRegisterBeforeException.R11Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].R12Register, > mAdjustRegisterBeforeException.R12Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].R13Register, > mAdjustRegisterBeforeException.R13Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].R14Register, > mAdjustRegisterBeforeException.R14Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[0].R15Register, > mAdjustRegisterBeforeException.R15Register); > + > + UT_ASSERT_EQUAL (mRegisterForCheck[1].Rdi, > mAdjustRegisterInsideException.Rdi); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].Rsi, > mAdjustRegisterInsideException.Rsi); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].Rbx, > mAdjustRegisterInsideException.Rbx); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].Rdx, > mAdjustRegisterInsideException.Rdx); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].Rcx, > mAdjustRegisterInsideException.Rcx); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].Rax, > mAdjustRegisterInsideException.Rax); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].R8Register, > mAdjustRegisterInsideException.R8Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].R9Register, > mAdjustRegisterInsideException.R9Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].R10Register, > mAdjustRegisterInsideException.R10Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].R11Register, > mAdjustRegisterInsideException.R11Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].R12Register, > mAdjustRegisterInsideException.R12Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].R13Register, > mAdjustRegisterInsideException.R13Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].R14Register, > mAdjustRegisterInsideException.R14Register); > + UT_ASSERT_EQUAL (mRegisterForCheck[1].R15Register, > mAdjustRegisterInsideException.R15Register); > + return UNIT_TEST_PASSED; > +} > + > +/** > + Special handler for CpuStackGuard test case. > + > + @param ExceptionType Exception type. > + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. > + > +**/ > +VOID > +EFIAPI > +CpuStackGuardExceptionHandler ( > + IN EFI_EXCEPTION_TYPE ExceptionType, > + IN EFI_SYSTEM_CONTEXT SystemContext > + ) > +{ > + UINTN LocalVariable; > + > + AdjustRipForFaultHandler (ExceptionType, SystemContext); > + mRspAddress[0] = (UINTN)SystemContext.SystemContextX64->Rsp; > + mRspAddress[1] = (UINTN)(&LocalVariable); > + > + return; > +} > diff --git > a/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTes > tAsm.nasm > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTes > tAsm.nasm > new file mode 100644 > index 0000000000..4838a12a67 > --- /dev/null > +++ > b/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTes > tAsm.nasm > @@ -0,0 +1,260 @@ > +;------------------------------------------------------------------------------ > +; > +; Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> > +; SPDX-License-Identifier: BSD-2-Clause-Patent > +; > +; Module Name: > +; > +; ArchExceptionHandlerTestAsm.nasm > +; > +; Abstract: > +; > +; x64 CPU Exception Handler Lib Unit test > +; > +;------------------------------------------------------------------------------ > + > + DEFAULT REL > + SECTION .text > + > +struc GENERAL_REGISTER > + .Rdi: resq 1 > + .Rsi: resq 1 > + .Rbp: resq 1 > + .Rbx: resq 1 > + .Rdx: resq 1 > + .Rcx: resq 1 > + .Rax: resq 1 > + .R8: resq 1 > + .R9: resq 1 > + .R10: resq 1 > + .R11: resq 1 > + .R12: resq 1 > + .R13: resq 1 > + .R14: resq 1 > + .R15: resq 1 > + > +endstruc > + > +extern ASM_PFX(mRegisterForCheck) > +extern ASM_PFX(mAdjustRegisterBeforeException) > +extern ASM_PFX(mFaultInstructionLength) > + > +;------------------------------------------------------------------------------ > +; VOID > +; EFIAPI > +; TriggerGPException ( > +; UINTN Cr4ReservedBit > +; ); > +;------------------------------------------------------------------------------ > +global ASM_PFX(TriggerGPException) > +ASM_PFX(TriggerGPException): > + ; > + ; Set reserved bit 15 of cr4 to 1 > + ; > + push rcx > + lea rcx, [ASM_PFX(mFaultInstructionLength)] > + mov qword[rcx], TriggerGPExceptionAfter - TriggerGPExceptionBefore > + pop rcx > +TriggerGPExceptionBefore: > + mov cr4, rcx > +TriggerGPExceptionAfter: > + ret > + > +;------------------------------------------------------------------------------ > +; VOID > +; EFIAPI > +; TriggerPFException ( > +; UINTN PFAddress > +; ); > +;------------------------------------------------------------------------------ > +global ASM_PFX(TriggerPFException) > +ASM_PFX(TriggerPFException): > + push rcx > + lea rcx, [ASM_PFX(mFaultInstructionLength)] > + mov qword[rcx], TriggerPFExceptionAfter - TriggerPFExceptionBefore > + pop rcx > +TriggerPFExceptionBefore: > + mov qword[rcx], 0x1 > +TriggerPFExceptionAfter: > + ret > + > +global ASM_PFX(ModifyRcxInGlobalBeforeException) > +ASM_PFX(ModifyRcxInGlobalBeforeException): > + push rax > + lea rax, [ASM_PFX(mAdjustRegisterBeforeException)] > + mov [rax + GENERAL_REGISTER.Rcx], rcx > + pop rax > + ret > + > +;------------------------------------------------------------------------------ > +;VOID > +;EFIAPI > +;AsmTestConsistencyOfCpuContext ( > +; IN EFI_EXCEPTION_TYPE ExceptionType > +; IN UINTN FaultParameter OPTIONAL > +; ); > +;------------------------------------------------------------------------------ > +global ASM_PFX(AsmTestConsistencyOfCpuContext) > +ASM_PFX(AsmTestConsistencyOfCpuContext): > + ; > + ; push original register > + ; > + push r15 > + push r14 > + push r13 > + push r12 > + push r11 > + push r10 > + push r9 > + push r8 > + push rax > + push rcx > + push rbx > + push rsi > + push rdi > + push rdx > + > + ; > + ; Modify register to mAdjustRegisterBeforeException > + ; > + lea r15, [ASM_PFX(mAdjustRegisterBeforeException)] > + mov rdi, [r15 + GENERAL_REGISTER.Rdi] > + mov rsi, [r15 + GENERAL_REGISTER.Rsi] > + mov rbx, [r15 + GENERAL_REGISTER.Rbx] > + mov rdx, [r15 + GENERAL_REGISTER.Rdx] > + mov rax, [r15 + GENERAL_REGISTER.Rax] > + mov r8, [r15 + GENERAL_REGISTER.R8] > + mov r9, [r15 + GENERAL_REGISTER.R9] > + mov r10, [r15 + GENERAL_REGISTER.R10] > + mov r11, [r15 + GENERAL_REGISTER.R11] > + mov r12, [r15 + GENERAL_REGISTER.R12] > + mov r13, [r15 + GENERAL_REGISTER.R13] > + mov r14, [r15 + GENERAL_REGISTER.R14] > + mov r15, [r15 + GENERAL_REGISTER.R15] > + > + cmp rcx, 0xd > + jz GPException > + cmp rcx, 0xe > + jz PFException > + jmp INTnException > + > +PFException: > + ; > + ; Pop rdx(Pei StackBase) to rcx and restore stack. > + ; Modify Rcx in mAdjustRegisterBeforeException. > + ; > + pop rcx > + push rcx > + call ASM_PFX(ModifyRcxInGlobalBeforeException) > + call ASM_PFX(TriggerPFException) > + jmp AfterException > + > +GPException: > + ; > + ; Prepare rcx value for GP. > + ; Modify Rcx in mAdjustRegisterBeforeException. > + ; > + pop rcx > + push rcx > + call ASM_PFX(ModifyRcxInGlobalBeforeException) > + call ASM_PFX(TriggerGPException) > + jmp AfterException > + > +INTnException: > + ; > + ; Modify Rcx in mAdjustRegisterBeforeException. > + ; > + call ASM_PFX(ModifyRcxInGlobalBeforeException) > + call ASM_PFX(TriggerINTnException) > + > +AfterException: > + ; > + ; Save register in mRegisterForCheck[1] > + ; > + push rax > + lea rax, [ASM_PFX(mRegisterForCheck)] > + add rax, GENERAL_REGISTER_size > + mov [rax + GENERAL_REGISTER.Rdi], rdi > + mov [rax + GENERAL_REGISTER.Rsi], rsi > + mov [rax + GENERAL_REGISTER.Rbx], rbx > + mov [rax + GENERAL_REGISTER.Rdx], rdx > + mov [rax + GENERAL_REGISTER.Rcx], rcx > + pop rcx > + mov [rax + GENERAL_REGISTER.Rax], rcx > + mov [rax + GENERAL_REGISTER.R8], r8 > + mov [rax + GENERAL_REGISTER.R9], r9 > + mov [rax + GENERAL_REGISTER.R10], r10 > + mov [rax + GENERAL_REGISTER.R11], r11 > + mov [rax + GENERAL_REGISTER.R12], r12 > + mov [rax + GENERAL_REGISTER.R13], r13 > + mov [rax + GENERAL_REGISTER.R14], r14 > + mov [rax + GENERAL_REGISTER.R15], r15 > + > + ; > + ; restore original register > + ; > + pop rdx > + pop rdi > + pop rsi > + pop rbx > + pop rcx > + pop rax > + pop r8 > + pop r9 > + pop r10 > + pop r11 > + pop r12 > + pop r13 > + pop r14 > + pop r15 > + > + ret > + > +;------------------------------------------------------------------------------ > +; VOID > +; EFIAPI > +; TriggerStackOverflowbyCpuStackGuard ( > +; VOID > +; ); > +;------------------------------------------------------------------------------ > +global ASM_PFX(TriggerStackOverflowbyCpuStackGuard) > +ASM_PFX(TriggerStackOverflowbyCpuStackGuard): > + push rcx > + lea rcx, [ASM_PFX(mFaultInstructionLength)] > + mov qword[rcx], TriggerCpuStackGuardAfter - > TriggerCpuStackGuardBefore > + pop rcx > +TriggerCpuStackGuardBefore: > + call TriggerCpuStackGuardBefore > +TriggerCpuStackGuardAfter: > + ret > + > +;------------------------------------------------------------------------------ > +; VOID > +; EFIAPI > +; TriggerINTnException ( > +; IN EFI_EXCEPTION_TYPE ExceptionType > +; ); > +;------------------------------------------------------------------------------ > +global ASM_PFX(TriggerINTnException) > +ASM_PFX(TriggerINTnException): > + push rax > + push rdx > + push rcx > + lea rax, [AsmTriggerException1 - AsmTriggerException0] > + mul rcx > + mov rcx, AsmTriggerException0 > + add rax, rcx > + pop rcx > + pop rdx > + jmp rax > + ; > + ; rax = AsmTriggerException0 + (AsmTriggerException1 - > AsmTriggerException0) * rcx > + ; > +%assign Vector 0 > +%rep 22 > +AsmTriggerException %+ Vector: > + pop rax > + INT Vector > + ret > +%assign Vector Vector+1 > +%endrep > -- > 2.31.1.windows.1 ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 2/3] UefiCpuPkg: Add Unit tests for PeiCpuExceptionHandlerLib 2022-10-11 6:03 [PATCH 0/3] Add Pei/DxeCpuExceptionHandlerLibUnitTest duntan 2022-10-11 6:03 ` [PATCH 1/3] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib duntan @ 2022-10-11 6:03 ` duntan 2022-10-11 6:03 ` [PATCH 3/3] UefiCpuPkg: Add Pei/DxeCpuExceptionHandlerLibUnitTest in dsc duntan 2 siblings, 0 replies; 6+ messages in thread From: duntan @ 2022-10-11 6:03 UTC (permalink / raw) To: devel; +Cc: Eric Dong, Ray Ni, Rahul Kumar Add target based unit tests for the PeiCpuExceptionHandlerLib. A PEIM is created to test PeiCpuExceptionHandlerLib. Four kinds of test cases are created in this module: 1.Test if exception handler can be registered/unregistered for no error code exception. 2.Test if exception handler can be registered/unregistered for GP and PF. 3.Test if Cpu Context is consistent before and after exception. 4.Test if stack overflow can be captured by CpuStackGuard in both Bsp and AP. Signed-off-by: Dun Tan <dun.tan@intel.com> Cc: Eric Dong <eric.dong@intel.com> Cc: Ray Ni <ray.ni@intel.com> Cc: Rahul Kumar <rahul1.kumar@intel.com> --- UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTest.c | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTestAsm.nasm | 217 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerLibUnitTest.inf | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerUnitTest.c | 204 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 615 insertions(+) diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTest.c b/UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTest.c new file mode 100644 index 0000000000..32188b9a81 --- /dev/null +++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTest.c @@ -0,0 +1,133 @@ +/** @file + Unit tests of the CpuExceptionHandlerLib. + + Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuExceptionHandlerTest.h" + +GENERAL_REGISTER_IA32 mIa32RegisterForCheck[2]; + +// +// In TestCpuContextConsistency, Cpu registers will be set to mAdjustRegisterBeforeException/mAdjustRegisterInsideException. +// Ecx in mAdjustRegisterInsideException is set runtime since Ecx is needed in assembly code. +// For GP and PF, Ecx is set to FaultParameter. For other exception triggered by INTn, Ecx is set to ExceptionType. +// +GENERAL_REGISTER_IA32 mAdjustIa32RegisterBeforeException = { 1, 2, 3, 4, 5, 0, 7 }; +GENERAL_REGISTER_IA32 mAdjustIa32RegisterInsideException = { 11, 12, 13, 14, 15, 16, 17 }; + +/** + Special handler for fault exception. + Rip/Eip in SystemContext will be modified to the instruction after the exception instruction. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. + +**/ +VOID +EFIAPI +AdjustRipForFaultHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + mExceptionType = ExceptionType; + SystemContext.SystemContextIa32->Eip += mFaultInstructionLength; +} + +/** + Special handler for ConsistencyOfCpuContext test case. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. +**/ +VOID +EFIAPI +AdjustCpuContextHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + // + // Do not handle Esp and ebp. They will not be restored. + // Store SystemContext modified before exception. + // + mIa32RegisterForCheck[0].Edi = SystemContext.SystemContextIa32->Edi; + mIa32RegisterForCheck[0].Esi = SystemContext.SystemContextIa32->Esi; + mIa32RegisterForCheck[0].Ebx = SystemContext.SystemContextIa32->Ebx; + mIa32RegisterForCheck[0].Edx = SystemContext.SystemContextIa32->Edx; + mIa32RegisterForCheck[0].Ecx = SystemContext.SystemContextIa32->Ecx; + mIa32RegisterForCheck[0].Eax = SystemContext.SystemContextIa32->Eax; + + // + // Modify cpu context which will be stored in mIa32RegisterForCheck[1]. + // + SystemContext.SystemContextIa32->Edi = mAdjustIa32RegisterInsideException.Edi; + SystemContext.SystemContextIa32->Esi = mAdjustIa32RegisterInsideException.Esi; + SystemContext.SystemContextIa32->Ebx = mAdjustIa32RegisterInsideException.Ebx; + SystemContext.SystemContextIa32->Edx = mAdjustIa32RegisterInsideException.Edx; + SystemContext.SystemContextIa32->Ecx = mAdjustIa32RegisterInsideException.Ecx; + SystemContext.SystemContextIa32->Eax = mAdjustIa32RegisterInsideException.Eax; + + // + // When fault exception happens, eip/rip points to the faulting instruction. + // For now, olny GP and PF are tested in fault exception. + // + if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) || (ExceptionType == EXCEPT_IA32_GP_FAULT)) { + AdjustRipForFaultHandler (ExceptionType, SystemContext); + } +} + +/** + Compare cpu context in ConsistencyOfCpuContext test case. + 1.Compare mRegisterForCheck[0] with mAdjustRegisterBeforeException. + 2.Compare mRegisterForCheck[1] with mAdjustRegisterAfterException. + + @retval UNIT_TEST_PASSED The Unit test has completed and it was successful. + @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. +**/ +UNIT_TEST_STATUS +CompareCpuContext ( + VOID + ) +{ + UT_ASSERT_EQUAL (mIa32RegisterForCheck[0].Edi, mAdjustIa32RegisterBeforeException.Edi); + UT_ASSERT_EQUAL (mIa32RegisterForCheck[0].Esi, mAdjustIa32RegisterBeforeException.Esi); + UT_ASSERT_EQUAL (mIa32RegisterForCheck[0].Ebx, mAdjustIa32RegisterBeforeException.Ebx); + UT_ASSERT_EQUAL (mIa32RegisterForCheck[0].Edx, mAdjustIa32RegisterBeforeException.Edx); + UT_ASSERT_EQUAL (mIa32RegisterForCheck[0].Ecx, mAdjustIa32RegisterBeforeException.Ecx); + UT_ASSERT_EQUAL (mIa32RegisterForCheck[0].Eax, mAdjustIa32RegisterBeforeException.Eax); + + UT_ASSERT_EQUAL (mIa32RegisterForCheck[1].Edi, mAdjustIa32RegisterInsideException.Edi); + UT_ASSERT_EQUAL (mIa32RegisterForCheck[1].Esi, mAdjustIa32RegisterInsideException.Esi); + UT_ASSERT_EQUAL (mIa32RegisterForCheck[1].Ebx, mAdjustIa32RegisterInsideException.Ebx); + UT_ASSERT_EQUAL (mIa32RegisterForCheck[1].Edx, mAdjustIa32RegisterInsideException.Edx); + UT_ASSERT_EQUAL (mIa32RegisterForCheck[1].Ecx, mAdjustIa32RegisterInsideException.Ecx); + UT_ASSERT_EQUAL (mIa32RegisterForCheck[1].Eax, mAdjustIa32RegisterInsideException.Eax); + return UNIT_TEST_PASSED; +} + +/** + Special handler for CpuStackGuard test case. + + @param ExceptionType Exception type. + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. + +**/ +VOID +EFIAPI +CpuStackGuardExceptionHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINTN LocalVariable; + + AdjustRipForFaultHandler (ExceptionType, SystemContext); + mRspAddress[0] = (UINTN)SystemContext.SystemContextIa32->Esp; + mRspAddress[1] = (UINTN)(&LocalVariable); + + return; +} diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTestAsm.nasm b/UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTestAsm.nasm new file mode 100644 index 0000000000..ccadd8765f --- /dev/null +++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTestAsm.nasm @@ -0,0 +1,217 @@ +;------------------------------------------------------------------------------ +; +; Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +; Module Name: +; +; ArchExceptionHandlerTestAsm.nasm +; +; Abstract: +; +; ia32 CPU Exception Handler Lib Unit test +; +;------------------------------------------------------------------------------ + + SECTION .text + +struc GENERAL_REGISTER_IA32 + .Edi: resd 1 + .Esi: resd 1 + .Ebp: resd 1 + .Ebx: resd 1 + .Edx: resd 1 + .Ecx: resd 1 + .Eax: resd 1 + +endstruc + +extern ASM_PFX(mIa32RegisterForCheck) +extern ASM_PFX(mAdjustIa32RegisterBeforeException) +extern ASM_PFX(mFaultInstructionLength) + +;------------------------------------------------------------------------------ +; VOID +; EFIAPI +; TriggerGPException ( +; UINTN Cr4ReservedBit +; ); +;------------------------------------------------------------------------------ +global ASM_PFX(TriggerGPException) +ASM_PFX(TriggerGPException): + ; + ; Set reserved bit 15 of cr4 to 1 + ; + lea ecx, [ASM_PFX(mFaultInstructionLength)] + mov dword[ecx], TriggerGPExceptionAfter - TriggerGPExceptionBefore + mov ecx, dword [esp + 0x4] +TriggerGPExceptionBefore: + mov cr4, ecx +TriggerGPExceptionAfter: + ret + +;------------------------------------------------------------------------------ +; VOID +; EFIAPI +; TriggerPFException ( +; UINTN PfAddress +; ); +;------------------------------------------------------------------------------ +global ASM_PFX(TriggerPFException) +ASM_PFX(TriggerPFException): + lea ecx, [ASM_PFX(mFaultInstructionLength)] + mov dword[ecx], TriggerPFExceptionAfter - TriggerPFExceptionBefore + mov ecx, dword [esp + 0x4] +TriggerPFExceptionBefore: + mov dword[ecx], 0x1 +TriggerPFExceptionAfter: + ret + +global ASM_PFX(ModifyEcxInGlobalBeforeException) +ASM_PFX(ModifyEcxInGlobalBeforeException): + push eax + lea eax, [ASM_PFX(mAdjustIa32RegisterBeforeException)] + mov [eax + GENERAL_REGISTER_IA32.Ecx], ecx + pop eax + ret + +;------------------------------------------------------------------------------ +;VOID +;EFIAPI +;AsmTestConsistencyOfCpuContext ( +; IN EFI_EXCEPTION_TYPE ExceptionType +; IN UINTN FaultParameter OPTIONAL +; ); +;------------------------------------------------------------------------------ +global ASM_PFX(AsmTestConsistencyOfCpuContext) +ASM_PFX(AsmTestConsistencyOfCpuContext): + ; + ; push 7 general register plus 4 bytes + ; + pushad + + ; + ; modify Modify register to mAdjustRegisterBeforeException + ; + lea eax, [ASM_PFX(mAdjustIa32RegisterBeforeException)] + mov edi, [eax + GENERAL_REGISTER_IA32.Edi] + mov esi, [eax + GENERAL_REGISTER_IA32.Esi] + mov ebx, [eax + GENERAL_REGISTER_IA32.Ebx] + mov edx, [eax + GENERAL_REGISTER_IA32.Edx] + ; + ; Set ecx to ExceptionType + ; + mov ecx, dword [esp + 0x24] + mov eax, [eax + GENERAL_REGISTER_IA32.Eax] + + cmp ecx, 0xd + jz GPException + cmp ecx, 0xe + jz PFException + jmp INTnException + +PFException: + ; + ; Set ecx to PFAddress and Modify Ecx in mAdjustRegisterBeforeException. + ; Push PfAddress into stack. + ; + mov ecx, dword [esp + 0x28] + call ASM_PFX(ModifyEcxInGlobalBeforeException) + push ecx + call ASM_PFX(TriggerPFException) + jmp AfterException + +GPException: + ; + ; Set ecx to CR4_RESERVED_BIT and Modify Ecx in mAdjustRegisterBeforeException. + ; Push CR4_RESERVED_BIT into stack + ; + mov ecx, dword [esp + 0x28] + call ASM_PFX(ModifyEcxInGlobalBeforeException) + push ecx + call ASM_PFX(TriggerGPException) + jmp AfterException + +INTnException: + ; + ; Modify Ecx in mAdjustRegisterBeforeException. + ; Push exception index into stack + ; + call ASM_PFX(ModifyEcxInGlobalBeforeException) + push ecx + call ASM_PFX(TriggerINTnException) + +AfterException: + ; + ; Save register in mRegisterForCheck[1]. + ; Esp and ebp will not be synced to the value in SystemContext. Do not test esp and ebp. + ; + push eax + lea eax, [ASM_PFX(mIa32RegisterForCheck)] + add eax, GENERAL_REGISTER_IA32_size + mov [eax + GENERAL_REGISTER_IA32.Edi], edi + mov [eax + GENERAL_REGISTER_IA32.Esi], esi + mov [eax + GENERAL_REGISTER_IA32.Ebx], ebx + mov [eax + GENERAL_REGISTER_IA32.Edx], edx + mov [eax + GENERAL_REGISTER_IA32.Ecx], ecx + pop ecx + mov [eax + GENERAL_REGISTER_IA32.Eax], ecx + add esp, 4 + + ; + ; restore original register + ; + popad + ret + +;------------------------------------------------------------------------------ +; VOID +; EFIAPI +; TriggerStackOverflowbyCpuStackGuard ( +; VOID +; ); +;------------------------------------------------------------------------------ +global ASM_PFX(TriggerStackOverflowbyCpuStackGuard) +ASM_PFX(TriggerStackOverflowbyCpuStackGuard): + lea ecx, [ASM_PFX(mFaultInstructionLength)] + mov dword[ecx], TriggerCpuStackGuardAfter - TriggerCpuStackGuardBefore +TriggerCpuStackGuardBefore: + ; + ; Clear CR0.TS since it is set after return from a nested DF + ; + call TriggerCpuStackGuardBefore + clts +TriggerCpuStackGuardAfter: + ret + +;------------------------------------------------------------------------------ +; VOID +; EFIAPI +; TriggerINTnException ( +; IN EFI_EXCEPTION_TYPE ExceptionType +; ); +;------------------------------------------------------------------------------ +global ASM_PFX(TriggerINTnException) +ASM_PFX(TriggerINTnException): + push eax + push edx + lea eax, [AsmTriggerException1 - AsmTriggerException0] + mov ecx, dword [esp + 0xc] + push ecx + mul ecx + mov ecx, AsmTriggerException0 + add eax, ecx + pop ecx + pop edx + jmp eax + ; + ; eax = AsmTriggerException0 + (AsmTriggerException1 - AsmTriggerException0) * ecx + ; +%assign Vector 0 +%rep 22 +AsmTriggerException %+ Vector: + pop eax + INT Vector + ret +%assign Vector Vector+1 +%endrep diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerLibUnitTest.inf b/UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerLibUnitTest.inf new file mode 100644 index 0000000000..25f8f8dbe0 --- /dev/null +++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerLibUnitTest.inf @@ -0,0 +1,61 @@ +## @file +# Unit tests of the PeiCpuExceptionHandlerLib instance. +# +# Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = CpuExceptionHandlerPeiTest + FILE_GUID = 39A96CF7-F369-4357-9234-4B52F98A007F + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = PeiEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# +[Sources.Ia32] + Ia32/ArchExceptionHandlerTestAsm.nasm + Ia32/ArchExceptionHandlerTest.c + +[Sources.X64] + X64/ArchExceptionHandlerTestAsm.nasm + X64/ArchExceptionHandlerTest.c + +[Sources.common] + CpuExceptionHandlerTest.h + CpuExceptionHandlerTestCommon.c + PeiCpuExceptionHandlerUnitTest.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + UnitTestLib + MemoryAllocationLib + CpuExceptionHandlerLib + PeimEntryPoint + HobLib + PeiServicesLib + CpuPageTableLib + PeiServicesTablePointerLib + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize ## CONSUMES + +[Ppis] + gEdkiiPeiMpServices2PpiGuid ## CONSUMES + +[Depex] + gEdkiiPeiMpServices2PpiGuid AND + gEfiPeiMemoryDiscoveredPpiGuid diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerUnitTest.c b/UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerUnitTest.c new file mode 100644 index 0000000000..d9408d2f5e --- /dev/null +++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerUnitTest.c @@ -0,0 +1,204 @@ +/** @file + Unit tests of the CpuExceptionHandlerLib. + + Copyright (c) 2022, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuExceptionHandlerTest.h" +#include <Library/PeimEntryPoint.h> +#include <Library/PeiServicesLib.h> +#include <Library/PeiServicesTablePointerLib.h> + +/** + Initialize Bsp Idt with a new Idt table and return the IA32_DESCRIPTOR buffer. + In PEIM, store original PeiServicePointer before new Idt table. + + @return Pointer to the allocated IA32_DESCRIPTOR buffer. +**/ +VOID * +InitializeBspIdt ( + VOID + ) +{ + UINTN *NewIdtTable; + IA32_DESCRIPTOR *Idtr; + + Idtr = AllocateZeroPool (sizeof (IA32_DESCRIPTOR)); + ASSERT (Idtr != NULL); + NewIdtTable = AllocateZeroPool (sizeof (IA32_IDT_GATE_DESCRIPTOR) * CPU_INTERRUPT_NUM + sizeof (UINTN)); + ASSERT (NewIdtTable != NULL); + // + // Store original PeiServicePointer before new Idt table + // + *NewIdtTable = (UINTN)GetPeiServicesTablePointer (); + NewIdtTable = (UINTN *)((UINTN)NewIdtTable + sizeof (UINTN)); + + Idtr->Base = (UINTN)NewIdtTable; + Idtr->Limit = (UINT16)(sizeof (IA32_IDT_GATE_DESCRIPTOR) * CPU_INTERRUPT_NUM - 1); + + AsmWriteIdtr (Idtr); + return Idtr; +} + +/** + Retrieve the number of logical processor in the platform and the number of those logical processors that + are enabled on this boot. + + @param[in] MpServices MP_SERVICES structure. + @param[out] NumberOfProcessors Pointer to the total number of logical processors in the system, including + the BSP and disabled APs. + @param[out] NumberOfEnabledProcessors Pointer to the number of processors in the system that are enabled. + + @retval EFI_SUCCESS Retrieve the number of logical processor successfully + @retval Others Retrieve the number of logical processor unsuccessfully +**/ +EFI_STATUS +MpServicesUnitTestGetNumberOfProcessors ( + IN MP_SERVICES MpServices, + OUT UINTN *NumberOfProcessors, + OUT UINTN *NumberOfEnabledProcessors + ) +{ + return MpServices.Ppi->GetNumberOfProcessors (MpServices.Ppi, NumberOfProcessors, NumberOfEnabledProcessors); +} + +/** + Caller gets one enabled AP to execute a caller-provided function. + + @param[in] MpServices MP_SERVICES structure. + @param[in] Procedure Pointer to the function to be run on enabled APs of the system. + @param[in] ProcessorNumber The handle number of the AP. + @param[in] TimeoutInMicroSeconds Indicates the time limit in microseconds for APs to return from Procedure, + for blocking mode only. Zero means infinity. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + + @retval EFI_SUCCESS Caller gets one enabled AP to execute a caller-provided function successfully + @retval Others Caller gets one enabled AP to execute a caller-provided function unsuccessfully +**/ +EFI_STATUS +MpServicesUnitTestStartupThisAP ( + IN MP_SERVICES MpServices, + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN UINTN TimeoutInMicroSeconds, + IN VOID *ProcedureArgument + ) +{ + return MpServices.Ppi->StartupThisAP (MpServices.Ppi, Procedure, ProcessorNumber, TimeoutInMicroSeconds, ProcedureArgument); +} + +/** + Execute a caller provided function on all enabled APs. + + @param[in] MpServices MP_SERVICES structure. + @param[in] Procedure Pointer to the function to be run on enabled APs of the system. + @param[in] SingleThread If TRUE, then all the enabled APs execute the function specified by Procedure + one by one, in ascending order of processor handle number. + If FALSE, then all the enabled APs execute the function specified by Procedure + simultaneously. + @param[in] TimeoutInMicroSeconds Indicates the time limit in microseconds for APs to return from Procedure, + for blocking mode only. Zero means infinity. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + @retval EFI_SUCCESS Execute a caller provided function on all enabled APs successfully + @retval Others Execute a caller provided function on all enabled APs unsuccessfully +**/ +EFI_STATUS +MpServicesUnitTestStartupAllAPs ( + IN MP_SERVICES MpServices, + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN UINTN TimeoutInMicroSeconds, + IN VOID *ProcedureArgument + ) +{ + return MpServices.Ppi->StartupAllAPs (MpServices.Ppi, Procedure, SingleThread, TimeoutInMicroSeconds, ProcedureArgument); +} + +/** + Get the handle number for the calling processor. + + @param[in] MpServices MP_SERVICES structure. + @param[out] ProcessorNumber The handle number for the calling processor. + + @retval EFI_SUCCESS Get the handle number for the calling processor successfully. + @retval Others Get the handle number for the calling processor unsuccessfully. +**/ +EFI_STATUS +MpServicesUnitTestWhoAmI ( + IN MP_SERVICES MpServices, + OUT UINTN *ProcessorNumber + ) +{ + return MpServices.Ppi->WhoAmI (MpServices.Ppi, ProcessorNumber); +} + +/** + Get EDKII_PEI_MP_SERVICES2_PPI pointer. + + @param[out] MpServices Pointer to the buffer where EDKII_PEI_MP_SERVICES2_PPI is stored + + @retval EFI_SUCCESS EDKII_PEI_MP_SERVICES2_PPI interface is returned + @retval EFI_NOT_FOUND EDKII_PEI_MP_SERVICES2_PPI interface is not found +**/ +EFI_STATUS +GetMpServices ( + OUT MP_SERVICES *MpServices + ) +{ + return PeiServicesLocatePpi (&gEdkiiPeiMpServices2PpiGuid, 0, NULL, (VOID **)&MpServices->Ppi); +} + +/** + Entry point of CpuExceptionHandlerPeiTest PEIM. + + @param[in] FileHandle Handle of the file being invoked. + @param[in] PeiServices Describes the list of possible PEI Services. + + @retval EFI_SUCCESS The PEIM executed normally. + +**/ +EFI_STATUS +EFIAPI +PeiEntryPoint ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + UNIT_TEST_FRAMEWORK_HANDLE Framework; + + Framework = NULL; + + DEBUG ((DEBUG_INFO, "%a v%a\n", UNIT_TEST_APP_NAME, UNIT_TEST_APP_VERSION)); + + // + // Start setting up the test framework for running the tests. + // + Status = InitUnitTestFramework (&Framework, UNIT_TEST_APP_NAME, gEfiCallerBaseName, UNIT_TEST_APP_VERSION); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status)); + goto EXIT; + } + + Status = AddCommonTestCase (Framework); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed in AddCommonTestCase. Status = %r\n", Status)); + goto EXIT; + } + + // + // Execute the tests. + // + Status = RunAllTestSuites (Framework); + +EXIT: + if (Framework) { + FreeUnitTestFramework (Framework); + } + + return Status; +} -- 2.31.1.windows.1 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 3/3] UefiCpuPkg: Add Pei/DxeCpuExceptionHandlerLibUnitTest in dsc 2022-10-11 6:03 [PATCH 0/3] Add Pei/DxeCpuExceptionHandlerLibUnitTest duntan 2022-10-11 6:03 ` [PATCH 1/3] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib duntan 2022-10-11 6:03 ` [PATCH 2/3] UefiCpuPkg: Add Unit tests for PeiCpuExceptionHandlerLib duntan @ 2022-10-11 6:03 ` duntan 2 siblings, 0 replies; 6+ messages in thread From: duntan @ 2022-10-11 6:03 UTC (permalink / raw) To: devel; +Cc: Eric Dong, Ray Ni, Rahul Kumar Add Pei/DxeCpuExceptionHandlerLibUnitTest module in UefiCpuPkg.dsc Signed-off-by: Dun Tan <dun.tan@intel.com> Cc: Eric Dong <eric.dong@intel.com> Cc: Ray Ni <ray.ni@intel.com> Cc: Rahul Kumar <rahul1.kumar@intel.com> --- UefiCpuPkg/UefiCpuPkg.dsc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc index f694b3a77c..31145dbe7e 100644 --- a/UefiCpuPkg/UefiCpuPkg.dsc +++ b/UefiCpuPkg/UefiCpuPkg.dsc @@ -63,6 +63,9 @@ MicrocodeLib|UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.inf SmmCpuRendezvousLib|UefiCpuPkg/Library/SmmCpuRendezvousLib/SmmCpuRendezvousLib.inf CpuPageTableLib|UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf + UnitTestLib|UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.inf + UnitTestPersistenceLib|UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.inf + UnitTestResultReportLib|UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.inf [LibraryClasses.common.SEC] PlatformSecLib|UefiCpuPkg/Library/PlatformSecLibNull/PlatformSecLibNull.inf @@ -177,6 +180,10 @@ UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.inf UefiCpuPkg/Library/SmmCpuRendezvousLib/SmmCpuRendezvousLib.inf UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf + UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerLibUnitTest.inf + +[Components.X64] + UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibUnitTest.inf [BuildOptions] *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES -- 2.31.1.windows.1 ^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2022-10-12 1:31 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2022-10-11 6:03 [PATCH 0/3] Add Pei/DxeCpuExceptionHandlerLibUnitTest duntan 2022-10-11 6:03 ` [PATCH 1/3] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib duntan 2022-10-11 9:37 ` Ni, Ray 2022-10-12 1:31 ` duntan 2022-10-11 6:03 ` [PATCH 2/3] UefiCpuPkg: Add Unit tests for PeiCpuExceptionHandlerLib duntan 2022-10-11 6:03 ` [PATCH 3/3] UefiCpuPkg: Add Pei/DxeCpuExceptionHandlerLibUnitTest in dsc duntan
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox