* [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
* [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
* 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
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