public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [Patch V3 0/4] Add Unit tests for Pei/DxeCpuExceptionHandlerLib
@ 2022-10-14  9:19 duntan
  2022-10-14  9:19 ` [Patch V3 1/4] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib duntan
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: duntan @ 2022-10-14  9:19 UTC (permalink / raw)
  To: devel

In V3 1/4 patch:
Remove uunneeded CPU_INFO_IN_HOB and IA32_GDT structure.
Use R8/R9 as the register name instead of R8Register.
Return a Boolean flag for FindPFAddressInPageTable.

In V3 2/4 patch:
Use same global variable name for IA32 mExpectedContextInHandler/AfterException.

In the new 4/4 patch:
Add CpuExceptionHandlerTest.h in EccCheck IgnoreFiles of
UefiCpuPkg/UefiCpuPkg.ci.yaml to pass ECC check.

Dun Tan (4):
  UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib
  UefiCpuPkg: Add Unit tests for PeiCpuExceptionHandlerLib
  UefiCpuPkg: Add Pei/DxeCpuExceptionHandlerLibUnitTest in dsc
  UefiCpuPkg: Add CpuExceptionHandlerTest.h in ECC IgnoreFile

 UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h             | 345 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCommon.c       | 852 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibUnitTest.inf |  58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnitTest.c      | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTest.c       | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTestAsm.nasm | 208 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerLibUnitTest.inf |  61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerUnitTest.c      | 204 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTest.c        | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTestAsm.nasm  | 256 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/UefiCpuPkg.ci.yaml                                                |   1 +
 UefiCpuPkg/UefiCpuPkg.dsc                                                    |   7 +++++++
 12 files changed, 2489 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] 9+ messages in thread

* [Patch V3 1/4] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib
  2022-10-14  9:19 [Patch V3 0/4] Add Unit tests for Pei/DxeCpuExceptionHandlerLib duntan
@ 2022-10-14  9:19 ` duntan
  2022-10-14 11:57   ` Ni, Ray
  2022-10-14  9:19 ` [Patch V3 2/4] UefiCpuPkg: Add Unit tests for PeiCpuExceptionHandlerLib duntan
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 9+ messages in thread
From: duntan @ 2022-10-14  9:19 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 test cases are created in this Unit Test module:
a.Test if exception handler can be registered/unregistered
for no error code exception.In the test case, only no error
code exception is triggered and tested by INTn instruction.

b.Test if exception handler can be registered/unregistered
for GP and PF. In the test case, GP exception is triggered
and tested by setting CR4_RESERVED_BIT to 1. PF exception
is triggered by writting to not-present or RO address.

c.Test if CpuContext is consistent before and after exception.
In this test case:
1.Set Cpu register to mExpectedContextInHandler before
exception. 2.Trigger exception specified by ExceptionType.
3.Store SystemContext in mActualContextInHandler and set
SystemContext to mExpectedContextAfterException in handler.
4.After return from exception, store Cpu registers in
mActualContextAfterException.
The expectation is:
1.Register values in mActualContextInHandler are the same
with register values in mExpectedContextInHandler.
2.Register values in mActualContextAfterException are the
same with register values mActualContextAfterException.

d.Test if stack overflow can be captured by CpuStackGuard
in both Bsp and AP. In this test case, stack overflow is
triggered by a funtion which calls itself continuously.
This test case triggers stack overflow in both BSP and AP.
All AP use same Idt with Bsp. The expectation is:
1. PF exception is triggered (leading to a DF if sepereated
stack is not prepared for PF) when Rsp<=StackBase+SIZE_4KB
since [StackBase, StackBase + SIZE_4KB] is marked as not
present in page table when PcdCpuStackGuard is TRUE.
2. Stack for PF/DF exception handler in both Bsp and AP is
succussfully switched by InitializeSeparateExceptionStacks.

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             | 336 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCommon.c       | 852 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibUnitTest.inf |  58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnitTest.c      | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTest.c        | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTestAsm.nasm  | 256 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 1864 insertions(+)

diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h
new file mode 100644
index 0000000000..936098fde8
--- /dev/null
+++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h
@@ -0,0 +1,336 @@
+/** @file
+
+  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  Four test cases are created in this Unit Test module.
+  a.Test if exception handler can be registered/unregistered for no error code exception
+    In this test case, only no error code exception is triggered and tested by INTn instruction.
+    The special hanlder for these exception will modify a global variable for check.
+
+  b.Test if exception handler can be registered/unregistered for GP and PF.
+    In this test case, GP exception is triggered and tested by setting CR4_RESERVED_BIT to 1.
+    PF exception is triggered and tested by writting to not-present or RO addres.
+    The special hanlder for these exceptions will set a global vartiable for check and adjust Rip to return from fault exception.
+
+  c.Test if Cpu Context is consistent before and after exception.
+    In this test case:
+      1. Set Cpu register to mExpectedContextInHandler before exception.
+      2. Trigger exception specified by ExceptionType.
+      3. Store SystemContext in mActualContextInHandler and set SystemContext to mExpectedContextAfterException in handler.
+      4. After return from exception, store Cpu registers in mActualContextAfterException.
+    The expectation is:
+      1. Register values in mActualContextInHandler are the same with register values in mExpectedContextInHandler.
+      2. Register values in mActualContextAfterException are the same with register values mActualContextAfterException.
+
+  d.Test if stack overflow can be captured by CpuStackGuard in both Bsp and AP.
+    In this test case, stack overflow is triggered by a funtion which calls itself continuously. This test case triggers stack
+    overflow in both BSP and AP. All AP use same Idt with Bsp. The expectation is:
+      1. PF exception is triggered (leading to a DF if sepereated stack is not prepared for PF) when Rsp <= StackBase + SIZE_4KB
+         since [StackBase, StackBase + SIZE_4KB] is marked as not present in page table when PcdCpuStackGuard is TRUE.
+      2. Stack for PF/DF exception handler in both Bsp and AP is succussfully switched by InitializeSeparateExceptionStacks.
+
+**/
+
+#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
+
+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    Rbx;
+  UINT64    Rdx;
+  UINT64    Rcx;
+  UINT64    Rax;
+  UINT64    R8;
+  UINT64    R9;
+  UINT64    R10;
+  UINT64    R11;
+  UINT64    R12;
+  UINT64    R13;
+  UINT64    R14;
+  UINT64    R15;
+} GENERAL_REGISTER;
+
+extern UINTN               mFaultInstructionLength;
+extern EFI_EXCEPTION_TYPE  mExceptionType;
+extern UINTN               mRspAddress[];
+
+/**
+  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 by setting CR4_RESERVED_BIT to 1.
+
+  @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.
+  This handler sets Rip/Eip in SystemContext to the instruction address 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 mExpectedContextInHandler before exception.
+  2. Trigger exception specified by ExceptionType.
+  3. Store SystemContext in mActualContextInHandler and set SystemContext to mExpectedContextAfterException in handler.
+  4. After return from exception, store Cpu registers in mActualContextAfterException.
+
+  Rcx/Ecx in mExpectedContextInHandler 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 to mExpectedContextInHandler 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 mActualContextInHandler with mExpectedContextInHandler.
+  2.Compare mActualContextAfterException with mActualContextAfterException.
+
+  @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      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          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 calling itself continuously.
+**/
+VOID
+EFIAPI
+TriggerStackOverflow (
+  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..17afb592d3
--- /dev/null
+++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCommon.c
@@ -0,0 +1,852 @@
+/** @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 exception triggered by INTn instruction.
+  This hanlder only modifies a global variable for check.
+
+  @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 cpu original registers before exit 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 cpu original registers before exit 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
+  )
+{
+  UINTN       Index;
+  EFI_STATUS  Status;
+
+  for (Index = 0; Index < mNumberOfProcessors; ++Index) {
+    if (Index == BspProcessorNum) {
+      RestoreRegistersPerCpu ((VOID *)&CpuOriginalRegisterBuffer[Index]);
+      continue;
+    }
+
+    ASSERT (MpServices != NULL);
+    Status = MpServicesUnitTestStartupThisAP (
+               *MpServices,
+               (EFI_AP_PROCEDURE)RestoreRegistersPerCpu,
+               Index,
+               0,
+               (VOID *)&CpuOriginalRegisterBuffer[Index]
+               );
+    ASSERT_EFI_ERROR (Status);
+  }
+}
+
+/**
+  Store cpu registers before the test case starts.
+
+  @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 cpu registers before the test case starts.
+
+  @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;
+  EFI_STATUS           Status;
+  UINTN                Index;
+
+  CpuOriginalRegisterBuffer = AllocateZeroPool (mNumberOfProcessors * sizeof (CPU_REGISTER_BUFFER));
+  ASSERT (CpuOriginalRegisterBuffer != NULL);
+
+  for (Index = 0; Index < mNumberOfProcessors; ++Index) {
+    if (Index == BspProcessorNum) {
+      SaveRegisterPerCpu ((VOID *)&CpuOriginalRegisterBuffer[Index]);
+      continue;
+    }
+
+    ASSERT (MpServices != NULL);
+    Status = MpServicesUnitTestStartupThisAP (
+               *MpServices,
+               (EFI_AP_PROCEDURE)SaveRegisterPerCpu,
+               Index,
+               0,
+               (VOID *)&CpuOriginalRegisterBuffer[Index]
+               );
+    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);
+}
+
+/**
+  Get Ap stack base procedure.
+
+  @param[out] ApStackBase  Pointer to Ap stack base.
+**/
+VOID
+EFIAPI
+GetStackBasePerAp (
+  OUT VOID  *ApStackBase
+  )
+{
+  UINTN  ApTopOfStack;
+
+  ApTopOfStack          = ALIGN_VALUE ((UINTN)&ApTopOfStack, (UINTN)PcdGet32 (PcdCpuApStackSize));
+  *(UINTN *)ApStackBase = ApTopOfStack - (UINTN)PcdGet32 (PcdCpuApStackSize);
+}
+
+/**
+  Get all Cpu stack base.
+
+  @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;
+  EFI_STATUS  Status;
+  UINTN       Index;
+
+  CpuStackBaseBuffer = AllocateZeroPool (mNumberOfProcessors * sizeof (UINTN));
+  ASSERT (CpuStackBaseBuffer != NULL);
+
+  for (Index = 0; Index < mNumberOfProcessors; ++Index) {
+    if (Index == BspProcessorNum) {
+      GetBspStackBase (&CpuStackBaseBuffer[Index]);
+      continue;
+    }
+
+    ASSERT (MpServices != NULL);
+    Status = MpServicesUnitTestStartupThisAP (
+               *MpServices,
+               (EFI_AP_PROCEDURE)GetStackBasePerAp,
+               Index,
+               0,
+               (VOID *)&CpuStackBaseBuffer[Index]
+               );
+    ASSERT_EFI_ERROR (Status);
+    DEBUG ((DEBUG_INFO, "AP[%d] StackBase = 0x%x\n", Index, CpuStackBaseBuffer[Index]));
+  }
+
+  return CpuStackBaseBuffer;
+}
+
+/**
+  Find not present or ReadOnly address in page table.
+
+  @param[out] PFAddress  Access to the address which is not permitted will trigger PF exceptions.
+
+  @retval TRUE   Found not present or ReadOnly address in page table.
+  @retval FALSE  Failed to found PFAddress in page table.
+**/
+BOOLEAN
+FindPFAddressInPageTable (
+  OUT 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;
+  UINTN           PreviousAddress;
+
+  ASSERT (PFAddress != NULL);
+
+  Cr0.UintN = AsmReadCr0 ();
+  if (Cr0.Bits.PG == 0) {
+    return FALSE;
+  }
+
+  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);
+
+  PreviousAddress = 0;
+  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 (Map[Index].LinearAddress > PreviousAddress) {
+      *PFAddress = PreviousAddress;
+      return TRUE;
+    }
+
+    PreviousAddress = (UINTN)(Map[Index].LinearAddress + Map[Index].Length);
+
+    //
+    // ReadOnly address in page table.
+    //
+    if ((Cr0.Bits.WP != 0) && (Map[Index].Attribute.Bits.ReadWrite == 0)) {
+      *PFAddress = (UINTN)Map[Index].LinearAddress;
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+/**
+  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.
+  //
+  if (FindPFAddressInPageTable (&PFAddress)) {
+    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) {
+      if (!FindPFAddressInPageTable (&FaultParameter)) {
+        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;
+  UINTN                           BufferSize;
+  EFI_STATUS                      Status;
+  UINT8                           *Buffer;
+
+  SwitchStackData = AllocateZeroPool (mNumberOfProcessors * sizeof (EXCEPTION_STACK_SWITCH_CONTEXT));
+  ASSERT (SwitchStackData != NULL);
+  for (Index = 0; Index < mNumberOfProcessors; ++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 *)&SwitchStackData[Index]);
+      continue;
+    }
+
+    Status = MpServicesUnitTestStartupThisAP (
+               MpServices,
+               InitializeExceptionStackSwitchHandlersPerAp,
+               Index,
+               0,
+               (VOID *)&SwitchStackData[Index]
+               );
+    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) {
+      if (Index == BspProcessorNum) {
+        InitializeExceptionStackSwitchHandlersPerAp ((VOID *)&SwitchStackData[Index]);
+        continue;
+      }
+
+      Status = MpServicesUnitTestStartupThisAP (
+                 MpServices,
+                 InitializeExceptionStackSwitchHandlersPerAp,
+                 Index,
+                 0,
+                 (VOID *)&SwitchStackData[Index]
+                 );
+      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 both 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) {
+      TriggerStackOverflow ();
+    } else {
+      MpServicesUnitTestStartupThisAP (
+        MpServices,
+        (EFI_AP_PROCEDURE)TriggerStackOverflow,
+        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..c0d962f26d
--- /dev/null
+++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTest.c
@@ -0,0 +1,166 @@
+/** @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  mActualContextInHandler;
+GENERAL_REGISTER  mActualContextAfterException;
+
+//
+// In TestCpuContextConsistency, Cpu registers will be set to mExpectedContextInHandler/mExpectedContextAfterException.
+// Rcx in mExpectedContextInHandler 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  mExpectedContextInHandler      = { 1, 2, 3, 4, 5, 0, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe };
+GENERAL_REGISTER  mExpectedContextAfterException = { 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e };
+
+/**
+  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
+  )
+{
+  //
+  // Store SystemContext in mActualContextInHandler.
+  //
+  mActualContextInHandler.Rdi = SystemContext.SystemContextX64->Rdi;
+  mActualContextInHandler.Rsi = SystemContext.SystemContextX64->Rsi;
+  mActualContextInHandler.Rbx = SystemContext.SystemContextX64->Rbx;
+  mActualContextInHandler.Rdx = SystemContext.SystemContextX64->Rdx;
+  mActualContextInHandler.Rcx = SystemContext.SystemContextX64->Rcx;
+  mActualContextInHandler.Rax = SystemContext.SystemContextX64->Rax;
+  mActualContextInHandler.R8  = SystemContext.SystemContextX64->R8;
+  mActualContextInHandler.R9  = SystemContext.SystemContextX64->R9;
+  mActualContextInHandler.R10 = SystemContext.SystemContextX64->R10;
+  mActualContextInHandler.R11 = SystemContext.SystemContextX64->R11;
+  mActualContextInHandler.R12 = SystemContext.SystemContextX64->R12;
+  mActualContextInHandler.R13 = SystemContext.SystemContextX64->R13;
+  mActualContextInHandler.R14 = SystemContext.SystemContextX64->R14;
+  mActualContextInHandler.R15 = SystemContext.SystemContextX64->R15;
+
+  //
+  // Modify cpu context. These registers will be stored in mActualContextAfterException.
+  // Do not handle Rsp and Rbp. CpuExceptionHandlerLib doesn't set Rsp and Rbp register
+  // to the value in SystemContext.
+  //
+  SystemContext.SystemContextX64->Rdi = mExpectedContextAfterException.Rdi;
+  SystemContext.SystemContextX64->Rsi = mExpectedContextAfterException.Rsi;
+  SystemContext.SystemContextX64->Rbx = mExpectedContextAfterException.Rbx;
+  SystemContext.SystemContextX64->Rdx = mExpectedContextAfterException.Rdx;
+  SystemContext.SystemContextX64->Rcx = mExpectedContextAfterException.Rcx;
+  SystemContext.SystemContextX64->Rax = mExpectedContextAfterException.Rax;
+  SystemContext.SystemContextX64->R8  = mExpectedContextAfterException.R8;
+  SystemContext.SystemContextX64->R9  = mExpectedContextAfterException.R9;
+  SystemContext.SystemContextX64->R10 = mExpectedContextAfterException.R10;
+  SystemContext.SystemContextX64->R11 = mExpectedContextAfterException.R11;
+  SystemContext.SystemContextX64->R12 = mExpectedContextAfterException.R12;
+  SystemContext.SystemContextX64->R13 = mExpectedContextAfterException.R13;
+  SystemContext.SystemContextX64->R14 = mExpectedContextAfterException.R14;
+  SystemContext.SystemContextX64->R15 = mExpectedContextAfterException.R15;
+
+  //
+  // 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 mActualContextInHandler with mExpectedContextInHandler.
+  2.Compare mActualContextAfterException with mActualContextAfterException.
+
+  @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 (mActualContextInHandler.Rdi, mExpectedContextInHandler.Rdi);
+  UT_ASSERT_EQUAL (mActualContextInHandler.Rsi, mExpectedContextInHandler.Rsi);
+  UT_ASSERT_EQUAL (mActualContextInHandler.Rbx, mExpectedContextInHandler.Rbx);
+  UT_ASSERT_EQUAL (mActualContextInHandler.Rdx, mExpectedContextInHandler.Rdx);
+  UT_ASSERT_EQUAL (mActualContextInHandler.Rcx, mExpectedContextInHandler.Rcx);
+  UT_ASSERT_EQUAL (mActualContextInHandler.Rax, mExpectedContextInHandler.Rax);
+  UT_ASSERT_EQUAL (mActualContextInHandler.R8, mExpectedContextInHandler.R8);
+  UT_ASSERT_EQUAL (mActualContextInHandler.R9, mExpectedContextInHandler.R9);
+  UT_ASSERT_EQUAL (mActualContextInHandler.R10, mExpectedContextInHandler.R10);
+  UT_ASSERT_EQUAL (mActualContextInHandler.R11, mExpectedContextInHandler.R11);
+  UT_ASSERT_EQUAL (mActualContextInHandler.R12, mExpectedContextInHandler.R12);
+  UT_ASSERT_EQUAL (mActualContextInHandler.R13, mExpectedContextInHandler.R13);
+  UT_ASSERT_EQUAL (mActualContextInHandler.R14, mExpectedContextInHandler.R14);
+  UT_ASSERT_EQUAL (mActualContextInHandler.R15, mExpectedContextInHandler.R15);
+
+  UT_ASSERT_EQUAL (mActualContextAfterException.Rdi, mExpectedContextAfterException.Rdi);
+  UT_ASSERT_EQUAL (mActualContextAfterException.Rsi, mExpectedContextAfterException.Rsi);
+  UT_ASSERT_EQUAL (mActualContextAfterException.Rbx, mExpectedContextAfterException.Rbx);
+  UT_ASSERT_EQUAL (mActualContextAfterException.Rdx, mExpectedContextAfterException.Rdx);
+  UT_ASSERT_EQUAL (mActualContextAfterException.Rcx, mExpectedContextAfterException.Rcx);
+  UT_ASSERT_EQUAL (mActualContextAfterException.Rax, mExpectedContextAfterException.Rax);
+  UT_ASSERT_EQUAL (mActualContextAfterException.R8, mExpectedContextAfterException.R8);
+  UT_ASSERT_EQUAL (mActualContextAfterException.R9, mExpectedContextAfterException.R9);
+  UT_ASSERT_EQUAL (mActualContextAfterException.R10, mExpectedContextAfterException.R10);
+  UT_ASSERT_EQUAL (mActualContextAfterException.R11, mExpectedContextAfterException.R11);
+  UT_ASSERT_EQUAL (mActualContextAfterException.R12, mExpectedContextAfterException.R12);
+  UT_ASSERT_EQUAL (mActualContextAfterException.R13, mExpectedContextAfterException.R13);
+  UT_ASSERT_EQUAL (mActualContextAfterException.R14, mExpectedContextAfterException.R14);
+  UT_ASSERT_EQUAL (mActualContextAfterException.R15, mExpectedContextAfterException.R15);
+  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..e229dbed00
--- /dev/null
+++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTestAsm.nasm
@@ -0,0 +1,256 @@
+;------------------------------------------------------------------------------
+;
+; 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
+  .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(mExpectedContextInHandler)
+extern ASM_PFX(mActualContextAfterException)
+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
+
+;------------------------------------------------------------------------------
+; ModifyRcxInGlobalBeforeException;
+; This function is writed by assebly code because it's only called in this file.
+; It's used to set Rcx in mExpectedContextInHandler for different exception.
+;------------------------------------------------------------------------------
+global ASM_PFX(ModifyRcxInGlobalBeforeException)
+ASM_PFX(ModifyRcxInGlobalBeforeException):
+    push rax
+    lea  rax, [ASM_PFX(mExpectedContextInHandler)]
+    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
+    push rdx
+
+    ;
+    ; Modify registers to mExpectedContextInHandler. Do not handle Rsp and Rbp.
+    ; CpuExceptionHandlerLib doesn't set Rsp and Rsp register to the value in SystemContext.
+    ;
+    lea r15, [ASM_PFX(mExpectedContextInHandler)]
+    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  rcx                                       ; Pop rdx(PFAddress) to rcx.
+    call ASM_PFX(ModifyRcxInGlobalBeforeException) ; Set mExpectedContextInHandler.Rcx to PFAddress.
+    call ASM_PFX(TriggerPFException)
+    jmp  AfterException
+
+GPException:
+    pop  rcx                                       ; Pop rdx(Cr4ReservedBit) to rcx.
+    call ASM_PFX(ModifyRcxInGlobalBeforeException) ; Set mExpectedContextInHandler.Rcx to Cr4ReservedBit.
+    call ASM_PFX(TriggerGPException)
+    jmp  AfterException
+
+INTnException:
+    ;
+    ; Modify Rcx in mExpectedContextInHandler.
+    ;
+    add  Rsp, 8                                    ; Discard the extra Rdx in stack. Rcx is ExceptionType now.
+    call ASM_PFX(ModifyRcxInGlobalBeforeException) ; Set mExpectedContextInHandler.Rcx to ExceptionType.
+    call ASM_PFX(TriggerINTnException)
+
+AfterException:
+    ;
+    ; Save registers in mActualContextAfterException
+    ;
+    push rax
+    lea  rax, [ASM_PFX(mActualContextAfterException)]
+    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
+; TriggerStackOverflow (
+;  VOID
+;  );
+;------------------------------------------------------------------------------
+global ASM_PFX(TriggerStackOverflow)
+ASM_PFX(TriggerStackOverflow):
+    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] 9+ messages in thread

* [Patch V3 2/4] UefiCpuPkg: Add Unit tests for PeiCpuExceptionHandlerLib
  2022-10-14  9:19 [Patch V3 0/4] Add Unit tests for Pei/DxeCpuExceptionHandlerLib duntan
  2022-10-14  9:19 ` [Patch V3 1/4] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib duntan
@ 2022-10-14  9:19 ` duntan
  2022-10-14 11:58   ` Ni, Ray
  2022-10-14  9:19 ` [Patch V3 3/4] UefiCpuPkg: Add Pei/DxeCpuExceptionHandlerLibUnitTest in dsc duntan
  2022-10-14  9:19 ` [Patch V3 4/4] UefiCpuPkg: Add CpuExceptionHandlerTest.h in ECC IgnoreFile duntan
  3 siblings, 1 reply; 9+ messages in thread
From: duntan @ 2022-10-14  9:19 UTC (permalink / raw)
  To: devel; +Cc: Eric Dong, Ray Ni, Rahul Kumar

The previous change adds unit test for DxeCpuExeptionHandlerLib
in 64bit mode. This change create a PEIM to add unit test for
PeiCpuExceptionHandlerLib based on previous change.It can run
in both 32bit and 64bit modes.

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             |   9 +++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTest.c       | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTestAsm.nasm | 208 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerLibUnitTest.inf |  61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerUnitTest.c      | 204 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 617 insertions(+)

diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h
index 936098fde8..bad3387db5 100644
--- a/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h
+++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h
@@ -93,6 +93,15 @@ typedef struct {
   UINT64    R15;
 } GENERAL_REGISTER;
 
+typedef struct {
+  UINT32    Edi;
+  UINT32    Esi;
+  UINT32    Ebx;
+  UINT32    Edx;
+  UINT32    Ecx;
+  UINT32    Eax;
+} GENERAL_REGISTER_IA32;
+
 extern UINTN               mFaultInstructionLength;
 extern EFI_EXCEPTION_TYPE  mExceptionType;
 extern UINTN               mRspAddress[];
diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTest.c b/UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTest.c
new file mode 100644
index 0000000000..8bb27249dc
--- /dev/null
+++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTest.c
@@ -0,0 +1,135 @@
+/** @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  mActualContextInHandler;
+GENERAL_REGISTER_IA32  mActualContextAfterException;
+
+//
+// In TestCpuContextConsistency, Cpu registers will be set to mExpectedContextInHandler/mExpectedContextAfterException.
+// Ecx in mExpectedContextInHandler 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  mExpectedContextInHandler      = { 1, 2, 3, 4, 5, 0 };
+GENERAL_REGISTER_IA32  mExpectedContextAfterException = { 11, 12, 13, 14, 15, 16 };
+
+/**
+  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
+  )
+{
+  //
+  // Store SystemContext in exception handler.
+  //
+  mActualContextInHandler.Edi = SystemContext.SystemContextIa32->Edi;
+  mActualContextInHandler.Esi = SystemContext.SystemContextIa32->Esi;
+  mActualContextInHandler.Ebx = SystemContext.SystemContextIa32->Ebx;
+  mActualContextInHandler.Edx = SystemContext.SystemContextIa32->Edx;
+  mActualContextInHandler.Ecx = SystemContext.SystemContextIa32->Ecx;
+  mActualContextInHandler.Eax = SystemContext.SystemContextIa32->Eax;
+
+  //
+  // Modify cpu context. These registers will be stored in mActualContextAfterException.
+  // Do not handle Esp and Ebp in SystemContext. CpuExceptionHandlerLib doesn't set Esp and
+  // Esp register to the value in SystemContext.
+  //
+  SystemContext.SystemContextIa32->Edi = mExpectedContextAfterException.Edi;
+  SystemContext.SystemContextIa32->Esi = mExpectedContextAfterException.Esi;
+  SystemContext.SystemContextIa32->Ebx = mExpectedContextAfterException.Ebx;
+  SystemContext.SystemContextIa32->Edx = mExpectedContextAfterException.Edx;
+  SystemContext.SystemContextIa32->Ecx = mExpectedContextAfterException.Ecx;
+  SystemContext.SystemContextIa32->Eax = mExpectedContextAfterException.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 mActualContextInHandler with mExpectedContextInHandler.
+  2.Compare mActualContextAfterException with mExpectedContextAfterException.
+
+  @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 (mActualContextInHandler.Edi, mExpectedContextInHandler.Edi);
+  UT_ASSERT_EQUAL (mActualContextInHandler.Esi, mExpectedContextInHandler.Esi);
+  UT_ASSERT_EQUAL (mActualContextInHandler.Ebx, mExpectedContextInHandler.Ebx);
+  UT_ASSERT_EQUAL (mActualContextInHandler.Edx, mExpectedContextInHandler.Edx);
+  UT_ASSERT_EQUAL (mActualContextInHandler.Ecx, mExpectedContextInHandler.Ecx);
+  UT_ASSERT_EQUAL (mActualContextInHandler.Eax, mExpectedContextInHandler.Eax);
+
+  UT_ASSERT_EQUAL (mActualContextAfterException.Edi, mExpectedContextAfterException.Edi);
+  UT_ASSERT_EQUAL (mActualContextAfterException.Esi, mExpectedContextAfterException.Esi);
+  UT_ASSERT_EQUAL (mActualContextAfterException.Ebx, mExpectedContextAfterException.Ebx);
+  UT_ASSERT_EQUAL (mActualContextAfterException.Edx, mExpectedContextAfterException.Edx);
+  UT_ASSERT_EQUAL (mActualContextAfterException.Ecx, mExpectedContextAfterException.Ecx);
+  UT_ASSERT_EQUAL (mActualContextAfterException.Eax, mExpectedContextAfterException.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..48031a5109
--- /dev/null
+++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTestAsm.nasm
@@ -0,0 +1,208 @@
+;------------------------------------------------------------------------------
+;
+; 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
+  .Ebx:    resd    1
+  .Edx:    resd    1
+  .Ecx:    resd    1
+  .Eax:    resd    1
+
+endstruc
+
+extern ASM_PFX(mExpectedContextInHandler)
+extern ASM_PFX(mActualContextAfterException)
+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
+
+;------------------------------------------------------------------------------
+; ModifyEcxInGlobalBeforeException;
+; This function is writed by assebly code because it's only called in this file.
+; It's used to set Ecx in mExpectedContextInHandler for different exception.
+;------------------------------------------------------------------------------
+global ASM_PFX(ModifyEcxInGlobalBeforeException)
+ASM_PFX(ModifyEcxInGlobalBeforeException):
+    push eax
+    lea  eax, [ASM_PFX(mExpectedContextInHandler)]
+    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 register to mExpectedContextInHandler. Do not handle Esp and Ebp.
+    ; CpuExceptionHandlerLib doesn't set Esp and Esp register to the value in SystemContext.
+    ;
+    lea eax, [ASM_PFX(mExpectedContextInHandler)]
+    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:
+    mov  ecx, dword [esp + 0x28]                    ; Set ecx to PFAddress.
+    call ASM_PFX(ModifyEcxInGlobalBeforeException)  ; Set mExpectedContextInHandler.Ecx to PFAddress.
+    push ecx                                        ; Push PfAddress into stack.
+    call ASM_PFX(TriggerPFException)
+    jmp  AfterException
+
+GPException:
+    mov  ecx, dword [esp + 0x28]                    ; Set ecx to CR4_RESERVED_BIT.
+    call ASM_PFX(ModifyEcxInGlobalBeforeException)  ; Set mExpectedContextInHandler.Ecx to CR4_RESERVED_BIT.
+    push ecx                                        ; Push CR4_RESERVED_BIT into stack.
+    call ASM_PFX(TriggerGPException)
+    jmp  AfterException
+
+INTnException:
+    call ASM_PFX(ModifyEcxInGlobalBeforeException)  ; Set mExpectedContextInHandler.Ecx to ExceptionType.
+    push ecx                                        ; Push ExceptionType into stack.
+    call ASM_PFX(TriggerINTnException)
+
+AfterException:
+    ;
+    ; Save register in mActualContextAfterException.
+    ;
+    push eax
+    lea  eax, [ASM_PFX(mActualContextAfterException)]
+    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
+; TriggerStackOverflow (
+;  VOID
+;  );
+;------------------------------------------------------------------------------
+global ASM_PFX(TriggerStackOverflow)
+ASM_PFX(TriggerStackOverflow):
+    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] 9+ messages in thread

* [Patch V3 3/4] UefiCpuPkg: Add Pei/DxeCpuExceptionHandlerLibUnitTest in dsc
  2022-10-14  9:19 [Patch V3 0/4] Add Unit tests for Pei/DxeCpuExceptionHandlerLib duntan
  2022-10-14  9:19 ` [Patch V3 1/4] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib duntan
  2022-10-14  9:19 ` [Patch V3 2/4] UefiCpuPkg: Add Unit tests for PeiCpuExceptionHandlerLib duntan
@ 2022-10-14  9:19 ` duntan
  2022-10-14  9:19 ` [Patch V3 4/4] UefiCpuPkg: Add CpuExceptionHandlerTest.h in ECC IgnoreFile duntan
  3 siblings, 0 replies; 9+ messages in thread
From: duntan @ 2022-10-14  9:19 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>
Reviewed-by: 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] 9+ messages in thread

* [Patch V3 4/4] UefiCpuPkg: Add CpuExceptionHandlerTest.h in ECC IgnoreFile
  2022-10-14  9:19 [Patch V3 0/4] Add Unit tests for Pei/DxeCpuExceptionHandlerLib duntan
                   ` (2 preceding siblings ...)
  2022-10-14  9:19 ` [Patch V3 3/4] UefiCpuPkg: Add Pei/DxeCpuExceptionHandlerLibUnitTest in dsc duntan
@ 2022-10-14  9:19 ` duntan
  2022-10-14  9:39   ` Ni, Ray
  3 siblings, 1 reply; 9+ messages in thread
From: duntan @ 2022-10-14  9:19 UTC (permalink / raw)
  To: devel; +Cc: Eric Dong, Ray Ni, Rahul Kumar

Add CpuExceptionHandlerTest.h in EccCheck IgnoreFiles of
UefiCpuPkg/UefiCpuPkg.ci.yaml to pass ECC check. The
GENERAL_REGISTER.R8 in this file triggers EccCheck failure
since no lower case characters in R8/R9/R10.

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.ci.yaml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/UefiCpuPkg/UefiCpuPkg.ci.yaml b/UefiCpuPkg/UefiCpuPkg.ci.yaml
index bbdc44a45b..1acc6daa76 100644
--- a/UefiCpuPkg/UefiCpuPkg.ci.yaml
+++ b/UefiCpuPkg/UefiCpuPkg.ci.yaml
@@ -19,6 +19,7 @@
         ],
         ## Both file path and directory path are accepted.
         "IgnoreFiles": [
+            "CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h"
         ]
     },
     "CompilerPlugin": {
-- 
2.31.1.windows.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [Patch V3 4/4] UefiCpuPkg: Add CpuExceptionHandlerTest.h in ECC IgnoreFile
  2022-10-14  9:19 ` [Patch V3 4/4] UefiCpuPkg: Add CpuExceptionHandlerTest.h in ECC IgnoreFile duntan
@ 2022-10-14  9:39   ` Ni, Ray
  2022-10-14 10:01     ` duntan
  0 siblings, 1 reply; 9+ messages in thread
From: Ni, Ray @ 2022-10-14  9:39 UTC (permalink / raw)
  To: Tan, Dun, devel@edk2.groups.io; +Cc: Dong, Eric, Kumar, Rahul R

Can we use BaseTools/Source/Python/Ecc/exception.xml to mark exceptions of R8/R9/etc.
instead of ignoring the entire H file?

Thanks,
ray

> -----Original Message-----
> From: Tan, Dun <dun.tan@intel.com>
> Sent: Friday, October 14, 2022 5:20 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 V3 4/4] UefiCpuPkg: Add CpuExceptionHandlerTest.h in ECC IgnoreFile
> 
> Add CpuExceptionHandlerTest.h in EccCheck IgnoreFiles of
> UefiCpuPkg/UefiCpuPkg.ci.yaml to pass ECC check. The
> GENERAL_REGISTER.R8 in this file triggers EccCheck failure
> since no lower case characters in R8/R9/R10.
> 
> 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.ci.yaml | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/UefiCpuPkg/UefiCpuPkg.ci.yaml b/UefiCpuPkg/UefiCpuPkg.ci.yaml
> index bbdc44a45b..1acc6daa76 100644
> --- a/UefiCpuPkg/UefiCpuPkg.ci.yaml
> +++ b/UefiCpuPkg/UefiCpuPkg.ci.yaml
> @@ -19,6 +19,7 @@
>          ],
>          ## Both file path and directory path are accepted.
>          "IgnoreFiles": [
> +            "CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h"
>          ]
>      },
>      "CompilerPlugin": {
> --
> 2.31.1.windows.1


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Patch V3 4/4] UefiCpuPkg: Add CpuExceptionHandlerTest.h in ECC IgnoreFile
  2022-10-14  9:39   ` Ni, Ray
@ 2022-10-14 10:01     ` duntan
  0 siblings, 0 replies; 9+ messages in thread
From: duntan @ 2022-10-14 10:01 UTC (permalink / raw)
  To: Ni, Ray, devel@edk2.groups.io; +Cc: Dong, Eric, Kumar, Rahul R

Ok, I'll try to add this format code for R8/R9/etc.
"ExceptionList": [
          "<ErrorID>", "<KeyWord>"
        ],

Thanks,
Dun
-----Original Message-----
From: Ni, Ray <ray.ni@intel.com> 
Sent: Friday, October 14, 2022 5:40 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 V3 4/4] UefiCpuPkg: Add CpuExceptionHandlerTest.h in ECC IgnoreFile

Can we use BaseTools/Source/Python/Ecc/exception.xml to mark exceptions of R8/R9/etc.
instead of ignoring the entire H file?

Thanks,
ray

> -----Original Message-----
> From: Tan, Dun <dun.tan@intel.com>
> Sent: Friday, October 14, 2022 5:20 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 V3 4/4] UefiCpuPkg: Add CpuExceptionHandlerTest.h in 
> ECC IgnoreFile
> 
> Add CpuExceptionHandlerTest.h in EccCheck IgnoreFiles of 
> UefiCpuPkg/UefiCpuPkg.ci.yaml to pass ECC check. The
> GENERAL_REGISTER.R8 in this file triggers EccCheck failure since no 
> lower case characters in R8/R9/R10.
> 
> 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.ci.yaml | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/UefiCpuPkg/UefiCpuPkg.ci.yaml 
> b/UefiCpuPkg/UefiCpuPkg.ci.yaml index bbdc44a45b..1acc6daa76 100644
> --- a/UefiCpuPkg/UefiCpuPkg.ci.yaml
> +++ b/UefiCpuPkg/UefiCpuPkg.ci.yaml
> @@ -19,6 +19,7 @@
>          ],
>          ## Both file path and directory path are accepted.
>          "IgnoreFiles": [
> +            "CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h"
>          ]
>      },
>      "CompilerPlugin": {
> --
> 2.31.1.windows.1


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Patch V3 1/4] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib
  2022-10-14  9:19 ` [Patch V3 1/4] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib duntan
@ 2022-10-14 11:57   ` Ni, Ray
  0 siblings, 0 replies; 9+ messages in thread
From: Ni, Ray @ 2022-10-14 11:57 UTC (permalink / raw)
  To: Tan, Dun, devel@edk2.groups.io; +Cc: Dong, Eric, Kumar, Rahul R

Reviewed-by: Ray Ni <ray.ni@intel.com>

> -----Original Message-----
> From: Tan, Dun <dun.tan@intel.com>
> Sent: Friday, October 14, 2022 5:19 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 V3 1/4] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib
> 
> Add target based unit tests for the DxeCpuExceptionHandlerLib.
> A DXE driver is created to test DxeCpuExceptionHandlerLib.
> 
> Four test cases are created in this Unit Test module:
> a.Test if exception handler can be registered/unregistered
> for no error code exception.In the test case, only no error
> code exception is triggered and tested by INTn instruction.
> 
> b.Test if exception handler can be registered/unregistered
> for GP and PF. In the test case, GP exception is triggered
> and tested by setting CR4_RESERVED_BIT to 1. PF exception
> is triggered by writting to not-present or RO address.
> 
> c.Test if CpuContext is consistent before and after exception.
> In this test case:
> 1.Set Cpu register to mExpectedContextInHandler before
> exception. 2.Trigger exception specified by ExceptionType.
> 3.Store SystemContext in mActualContextInHandler and set
> SystemContext to mExpectedContextAfterException in handler.
> 4.After return from exception, store Cpu registers in
> mActualContextAfterException.
> The expectation is:
> 1.Register values in mActualContextInHandler are the same
> with register values in mExpectedContextInHandler.
> 2.Register values in mActualContextAfterException are the
> same with register values mActualContextAfterException.
> 
> d.Test if stack overflow can be captured by CpuStackGuard
> in both Bsp and AP. In this test case, stack overflow is
> triggered by a funtion which calls itself continuously.
> This test case triggers stack overflow in both BSP and AP.
> All AP use same Idt with Bsp. The expectation is:
> 1. PF exception is triggered (leading to a DF if sepereated
> stack is not prepared for PF) when Rsp<=StackBase+SIZE_4KB
> since [StackBase, StackBase + SIZE_4KB] is marked as not
> present in page table when PcdCpuStackGuard is TRUE.
> 2. Stack for PF/DF exception handler in both Bsp and AP is
> succussfully switched by InitializeSeparateExceptionStacks.
> 
> 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             | 336
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCommon.c       | 852
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> +++++++++++++++
>  UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibUnitTest.inf |  58
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnitTest.c      | 196
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> ++++++++++
>  UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTest.c        | 166
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTestAsm.nasm  | 256
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  6 files changed, 1864 insertions(+)
> 
> diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h
> b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h
> new file mode 100644
> index 0000000000..936098fde8
> --- /dev/null
> +++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h
> @@ -0,0 +1,336 @@
> +/** @file
> +
> +  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +  Four test cases are created in this Unit Test module.
> +  a.Test if exception handler can be registered/unregistered for no error code exception
> +    In this test case, only no error code exception is triggered and tested by INTn instruction.
> +    The special hanlder for these exception will modify a global variable for check.
> +
> +  b.Test if exception handler can be registered/unregistered for GP and PF.
> +    In this test case, GP exception is triggered and tested by setting CR4_RESERVED_BIT to 1.
> +    PF exception is triggered and tested by writting to not-present or RO addres.
> +    The special hanlder for these exceptions will set a global vartiable for check and adjust Rip to return from fault exception.
> +
> +  c.Test if Cpu Context is consistent before and after exception.
> +    In this test case:
> +      1. Set Cpu register to mExpectedContextInHandler before exception.
> +      2. Trigger exception specified by ExceptionType.
> +      3. Store SystemContext in mActualContextInHandler and set SystemContext to mExpectedContextAfterException in
> handler.
> +      4. After return from exception, store Cpu registers in mActualContextAfterException.
> +    The expectation is:
> +      1. Register values in mActualContextInHandler are the same with register values in mExpectedContextInHandler.
> +      2. Register values in mActualContextAfterException are the same with register values mActualContextAfterException.
> +
> +  d.Test if stack overflow can be captured by CpuStackGuard in both Bsp and AP.
> +    In this test case, stack overflow is triggered by a funtion which calls itself continuously. This test case triggers stack
> +    overflow in both BSP and AP. All AP use same Idt with Bsp. The expectation is:
> +      1. PF exception is triggered (leading to a DF if sepereated stack is not prepared for PF) when Rsp <= StackBase +
> SIZE_4KB
> +         since [StackBase, StackBase + SIZE_4KB] is marked as not present in page table when PcdCpuStackGuard is TRUE.
> +      2. Stack for PF/DF exception handler in both Bsp and AP is succussfully switched by InitializeSeparateExceptionStacks.
> +
> +**/
> +
> +#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
> +
> +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    Rbx;
> +  UINT64    Rdx;
> +  UINT64    Rcx;
> +  UINT64    Rax;
> +  UINT64    R8;
> +  UINT64    R9;
> +  UINT64    R10;
> +  UINT64    R11;
> +  UINT64    R12;
> +  UINT64    R13;
> +  UINT64    R14;
> +  UINT64    R15;
> +} GENERAL_REGISTER;
> +
> +extern UINTN               mFaultInstructionLength;
> +extern EFI_EXCEPTION_TYPE  mExceptionType;
> +extern UINTN               mRspAddress[];
> +
> +/**
> +  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 by setting CR4_RESERVED_BIT to 1.
> +
> +  @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.
> +  This handler sets Rip/Eip in SystemContext to the instruction address 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 mExpectedContextInHandler before exception.
> +  2. Trigger exception specified by ExceptionType.
> +  3. Store SystemContext in mActualContextInHandler and set SystemContext to mExpectedContextAfterException in
> handler.
> +  4. After return from exception, store Cpu registers in mActualContextAfterException.
> +
> +  Rcx/Ecx in mExpectedContextInHandler 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 to mExpectedContextInHandler 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 mActualContextInHandler with mExpectedContextInHandler.
> +  2.Compare mActualContextAfterException with mActualContextAfterException.
> +
> +  @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      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          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 calling itself continuously.
> +**/
> +VOID
> +EFIAPI
> +TriggerStackOverflow (
> +  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..17afb592d3
> --- /dev/null
> +++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCommon.c
> @@ -0,0 +1,852 @@
> +/** @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 exception triggered by INTn instruction.
> +  This hanlder only modifies a global variable for check.
> +
> +  @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 cpu original registers before exit 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 cpu original registers before exit 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
> +  )
> +{
> +  UINTN       Index;
> +  EFI_STATUS  Status;
> +
> +  for (Index = 0; Index < mNumberOfProcessors; ++Index) {
> +    if (Index == BspProcessorNum) {
> +      RestoreRegistersPerCpu ((VOID *)&CpuOriginalRegisterBuffer[Index]);
> +      continue;
> +    }
> +
> +    ASSERT (MpServices != NULL);
> +    Status = MpServicesUnitTestStartupThisAP (
> +               *MpServices,
> +               (EFI_AP_PROCEDURE)RestoreRegistersPerCpu,
> +               Index,
> +               0,
> +               (VOID *)&CpuOriginalRegisterBuffer[Index]
> +               );
> +    ASSERT_EFI_ERROR (Status);
> +  }
> +}
> +
> +/**
> +  Store cpu registers before the test case starts.
> +
> +  @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 cpu registers before the test case starts.
> +
> +  @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;
> +  EFI_STATUS           Status;
> +  UINTN                Index;
> +
> +  CpuOriginalRegisterBuffer = AllocateZeroPool (mNumberOfProcessors * sizeof (CPU_REGISTER_BUFFER));
> +  ASSERT (CpuOriginalRegisterBuffer != NULL);
> +
> +  for (Index = 0; Index < mNumberOfProcessors; ++Index) {
> +    if (Index == BspProcessorNum) {
> +      SaveRegisterPerCpu ((VOID *)&CpuOriginalRegisterBuffer[Index]);
> +      continue;
> +    }
> +
> +    ASSERT (MpServices != NULL);
> +    Status = MpServicesUnitTestStartupThisAP (
> +               *MpServices,
> +               (EFI_AP_PROCEDURE)SaveRegisterPerCpu,
> +               Index,
> +               0,
> +               (VOID *)&CpuOriginalRegisterBuffer[Index]
> +               );
> +    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);
> +}
> +
> +/**
> +  Get Ap stack base procedure.
> +
> +  @param[out] ApStackBase  Pointer to Ap stack base.
> +**/
> +VOID
> +EFIAPI
> +GetStackBasePerAp (
> +  OUT VOID  *ApStackBase
> +  )
> +{
> +  UINTN  ApTopOfStack;
> +
> +  ApTopOfStack          = ALIGN_VALUE ((UINTN)&ApTopOfStack, (UINTN)PcdGet32 (PcdCpuApStackSize));
> +  *(UINTN *)ApStackBase = ApTopOfStack - (UINTN)PcdGet32 (PcdCpuApStackSize);
> +}
> +
> +/**
> +  Get all Cpu stack base.
> +
> +  @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;
> +  EFI_STATUS  Status;
> +  UINTN       Index;
> +
> +  CpuStackBaseBuffer = AllocateZeroPool (mNumberOfProcessors * sizeof (UINTN));
> +  ASSERT (CpuStackBaseBuffer != NULL);
> +
> +  for (Index = 0; Index < mNumberOfProcessors; ++Index) {
> +    if (Index == BspProcessorNum) {
> +      GetBspStackBase (&CpuStackBaseBuffer[Index]);
> +      continue;
> +    }
> +
> +    ASSERT (MpServices != NULL);
> +    Status = MpServicesUnitTestStartupThisAP (
> +               *MpServices,
> +               (EFI_AP_PROCEDURE)GetStackBasePerAp,
> +               Index,
> +               0,
> +               (VOID *)&CpuStackBaseBuffer[Index]
> +               );
> +    ASSERT_EFI_ERROR (Status);
> +    DEBUG ((DEBUG_INFO, "AP[%d] StackBase = 0x%x\n", Index, CpuStackBaseBuffer[Index]));
> +  }
> +
> +  return CpuStackBaseBuffer;
> +}
> +
> +/**
> +  Find not present or ReadOnly address in page table.
> +
> +  @param[out] PFAddress  Access to the address which is not permitted will trigger PF exceptions.
> +
> +  @retval TRUE   Found not present or ReadOnly address in page table.
> +  @retval FALSE  Failed to found PFAddress in page table.
> +**/
> +BOOLEAN
> +FindPFAddressInPageTable (
> +  OUT 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;
> +  UINTN           PreviousAddress;
> +
> +  ASSERT (PFAddress != NULL);
> +
> +  Cr0.UintN = AsmReadCr0 ();
> +  if (Cr0.Bits.PG == 0) {
> +    return FALSE;
> +  }
> +
> +  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);
> +
> +  PreviousAddress = 0;
> +  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 (Map[Index].LinearAddress > PreviousAddress) {
> +      *PFAddress = PreviousAddress;
> +      return TRUE;
> +    }
> +
> +    PreviousAddress = (UINTN)(Map[Index].LinearAddress + Map[Index].Length);
> +
> +    //
> +    // ReadOnly address in page table.
> +    //
> +    if ((Cr0.Bits.WP != 0) && (Map[Index].Attribute.Bits.ReadWrite == 0)) {
> +      *PFAddress = (UINTN)Map[Index].LinearAddress;
> +      return TRUE;
> +    }
> +  }
> +
> +  return FALSE;
> +}
> +
> +/**
> +  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.
> +  //
> +  if (FindPFAddressInPageTable (&PFAddress)) {
> +    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) {
> +      if (!FindPFAddressInPageTable (&FaultParameter)) {
> +        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;
> +  UINTN                           BufferSize;
> +  EFI_STATUS                      Status;
> +  UINT8                           *Buffer;
> +
> +  SwitchStackData = AllocateZeroPool (mNumberOfProcessors * sizeof (EXCEPTION_STACK_SWITCH_CONTEXT));
> +  ASSERT (SwitchStackData != NULL);
> +  for (Index = 0; Index < mNumberOfProcessors; ++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 *)&SwitchStackData[Index]);
> +      continue;
> +    }
> +
> +    Status = MpServicesUnitTestStartupThisAP (
> +               MpServices,
> +               InitializeExceptionStackSwitchHandlersPerAp,
> +               Index,
> +               0,
> +               (VOID *)&SwitchStackData[Index]
> +               );
> +    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) {
> +      if (Index == BspProcessorNum) {
> +        InitializeExceptionStackSwitchHandlersPerAp ((VOID *)&SwitchStackData[Index]);
> +        continue;
> +      }
> +
> +      Status = MpServicesUnitTestStartupThisAP (
> +                 MpServices,
> +                 InitializeExceptionStackSwitchHandlersPerAp,
> +                 Index,
> +                 0,
> +                 (VOID *)&SwitchStackData[Index]
> +                 );
> +      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 both 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) {
> +      TriggerStackOverflow ();
> +    } else {
> +      MpServicesUnitTestStartupThisAP (
> +        MpServices,
> +        (EFI_AP_PROCEDURE)TriggerStackOverflow,
> +        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..c0d962f26d
> --- /dev/null
> +++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTest.c
> @@ -0,0 +1,166 @@
> +/** @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  mActualContextInHandler;
> +GENERAL_REGISTER  mActualContextAfterException;
> +
> +//
> +// In TestCpuContextConsistency, Cpu registers will be set to
> mExpectedContextInHandler/mExpectedContextAfterException.
> +// Rcx in mExpectedContextInHandler 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  mExpectedContextInHandler      = { 1, 2, 3, 4, 5, 0, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe };
> +GENERAL_REGISTER  mExpectedContextAfterException = { 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
> 0x1c, 0x1d, 0x1e };
> +
> +/**
> +  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
> +  )
> +{
> +  //
> +  // Store SystemContext in mActualContextInHandler.
> +  //
> +  mActualContextInHandler.Rdi = SystemContext.SystemContextX64->Rdi;
> +  mActualContextInHandler.Rsi = SystemContext.SystemContextX64->Rsi;
> +  mActualContextInHandler.Rbx = SystemContext.SystemContextX64->Rbx;
> +  mActualContextInHandler.Rdx = SystemContext.SystemContextX64->Rdx;
> +  mActualContextInHandler.Rcx = SystemContext.SystemContextX64->Rcx;
> +  mActualContextInHandler.Rax = SystemContext.SystemContextX64->Rax;
> +  mActualContextInHandler.R8  = SystemContext.SystemContextX64->R8;
> +  mActualContextInHandler.R9  = SystemContext.SystemContextX64->R9;
> +  mActualContextInHandler.R10 = SystemContext.SystemContextX64->R10;
> +  mActualContextInHandler.R11 = SystemContext.SystemContextX64->R11;
> +  mActualContextInHandler.R12 = SystemContext.SystemContextX64->R12;
> +  mActualContextInHandler.R13 = SystemContext.SystemContextX64->R13;
> +  mActualContextInHandler.R14 = SystemContext.SystemContextX64->R14;
> +  mActualContextInHandler.R15 = SystemContext.SystemContextX64->R15;
> +
> +  //
> +  // Modify cpu context. These registers will be stored in mActualContextAfterException.
> +  // Do not handle Rsp and Rbp. CpuExceptionHandlerLib doesn't set Rsp and Rbp register
> +  // to the value in SystemContext.
> +  //
> +  SystemContext.SystemContextX64->Rdi = mExpectedContextAfterException.Rdi;
> +  SystemContext.SystemContextX64->Rsi = mExpectedContextAfterException.Rsi;
> +  SystemContext.SystemContextX64->Rbx = mExpectedContextAfterException.Rbx;
> +  SystemContext.SystemContextX64->Rdx = mExpectedContextAfterException.Rdx;
> +  SystemContext.SystemContextX64->Rcx = mExpectedContextAfterException.Rcx;
> +  SystemContext.SystemContextX64->Rax = mExpectedContextAfterException.Rax;
> +  SystemContext.SystemContextX64->R8  = mExpectedContextAfterException.R8;
> +  SystemContext.SystemContextX64->R9  = mExpectedContextAfterException.R9;
> +  SystemContext.SystemContextX64->R10 = mExpectedContextAfterException.R10;
> +  SystemContext.SystemContextX64->R11 = mExpectedContextAfterException.R11;
> +  SystemContext.SystemContextX64->R12 = mExpectedContextAfterException.R12;
> +  SystemContext.SystemContextX64->R13 = mExpectedContextAfterException.R13;
> +  SystemContext.SystemContextX64->R14 = mExpectedContextAfterException.R14;
> +  SystemContext.SystemContextX64->R15 = mExpectedContextAfterException.R15;
> +
> +  //
> +  // 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 mActualContextInHandler with mExpectedContextInHandler.
> +  2.Compare mActualContextAfterException with mActualContextAfterException.
> +
> +  @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 (mActualContextInHandler.Rdi, mExpectedContextInHandler.Rdi);
> +  UT_ASSERT_EQUAL (mActualContextInHandler.Rsi, mExpectedContextInHandler.Rsi);
> +  UT_ASSERT_EQUAL (mActualContextInHandler.Rbx, mExpectedContextInHandler.Rbx);
> +  UT_ASSERT_EQUAL (mActualContextInHandler.Rdx, mExpectedContextInHandler.Rdx);
> +  UT_ASSERT_EQUAL (mActualContextInHandler.Rcx, mExpectedContextInHandler.Rcx);
> +  UT_ASSERT_EQUAL (mActualContextInHandler.Rax, mExpectedContextInHandler.Rax);
> +  UT_ASSERT_EQUAL (mActualContextInHandler.R8, mExpectedContextInHandler.R8);
> +  UT_ASSERT_EQUAL (mActualContextInHandler.R9, mExpectedContextInHandler.R9);
> +  UT_ASSERT_EQUAL (mActualContextInHandler.R10, mExpectedContextInHandler.R10);
> +  UT_ASSERT_EQUAL (mActualContextInHandler.R11, mExpectedContextInHandler.R11);
> +  UT_ASSERT_EQUAL (mActualContextInHandler.R12, mExpectedContextInHandler.R12);
> +  UT_ASSERT_EQUAL (mActualContextInHandler.R13, mExpectedContextInHandler.R13);
> +  UT_ASSERT_EQUAL (mActualContextInHandler.R14, mExpectedContextInHandler.R14);
> +  UT_ASSERT_EQUAL (mActualContextInHandler.R15, mExpectedContextInHandler.R15);
> +
> +  UT_ASSERT_EQUAL (mActualContextAfterException.Rdi, mExpectedContextAfterException.Rdi);
> +  UT_ASSERT_EQUAL (mActualContextAfterException.Rsi, mExpectedContextAfterException.Rsi);
> +  UT_ASSERT_EQUAL (mActualContextAfterException.Rbx, mExpectedContextAfterException.Rbx);
> +  UT_ASSERT_EQUAL (mActualContextAfterException.Rdx, mExpectedContextAfterException.Rdx);
> +  UT_ASSERT_EQUAL (mActualContextAfterException.Rcx, mExpectedContextAfterException.Rcx);
> +  UT_ASSERT_EQUAL (mActualContextAfterException.Rax, mExpectedContextAfterException.Rax);
> +  UT_ASSERT_EQUAL (mActualContextAfterException.R8, mExpectedContextAfterException.R8);
> +  UT_ASSERT_EQUAL (mActualContextAfterException.R9, mExpectedContextAfterException.R9);
> +  UT_ASSERT_EQUAL (mActualContextAfterException.R10, mExpectedContextAfterException.R10);
> +  UT_ASSERT_EQUAL (mActualContextAfterException.R11, mExpectedContextAfterException.R11);
> +  UT_ASSERT_EQUAL (mActualContextAfterException.R12, mExpectedContextAfterException.R12);
> +  UT_ASSERT_EQUAL (mActualContextAfterException.R13, mExpectedContextAfterException.R13);
> +  UT_ASSERT_EQUAL (mActualContextAfterException.R14, mExpectedContextAfterException.R14);
> +  UT_ASSERT_EQUAL (mActualContextAfterException.R15, mExpectedContextAfterException.R15);
> +  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..e229dbed00
> --- /dev/null
> +++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTestAsm.nasm
> @@ -0,0 +1,256 @@
> +;------------------------------------------------------------------------------
> +;
> +; 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
> +  .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(mExpectedContextInHandler)
> +extern ASM_PFX(mActualContextAfterException)
> +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
> +
> +;------------------------------------------------------------------------------
> +; ModifyRcxInGlobalBeforeException;
> +; This function is writed by assebly code because it's only called in this file.
> +; It's used to set Rcx in mExpectedContextInHandler for different exception.
> +;------------------------------------------------------------------------------
> +global ASM_PFX(ModifyRcxInGlobalBeforeException)
> +ASM_PFX(ModifyRcxInGlobalBeforeException):
> +    push rax
> +    lea  rax, [ASM_PFX(mExpectedContextInHandler)]
> +    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
> +    push rdx
> +
> +    ;
> +    ; Modify registers to mExpectedContextInHandler. Do not handle Rsp and Rbp.
> +    ; CpuExceptionHandlerLib doesn't set Rsp and Rsp register to the value in SystemContext.
> +    ;
> +    lea r15, [ASM_PFX(mExpectedContextInHandler)]
> +    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  rcx                                       ; Pop rdx(PFAddress) to rcx.
> +    call ASM_PFX(ModifyRcxInGlobalBeforeException) ; Set mExpectedContextInHandler.Rcx to PFAddress.
> +    call ASM_PFX(TriggerPFException)
> +    jmp  AfterException
> +
> +GPException:
> +    pop  rcx                                       ; Pop rdx(Cr4ReservedBit) to rcx.
> +    call ASM_PFX(ModifyRcxInGlobalBeforeException) ; Set mExpectedContextInHandler.Rcx to Cr4ReservedBit.
> +    call ASM_PFX(TriggerGPException)
> +    jmp  AfterException
> +
> +INTnException:
> +    ;
> +    ; Modify Rcx in mExpectedContextInHandler.
> +    ;
> +    add  Rsp, 8                                    ; Discard the extra Rdx in stack. Rcx is ExceptionType now.
> +    call ASM_PFX(ModifyRcxInGlobalBeforeException) ; Set mExpectedContextInHandler.Rcx to ExceptionType.
> +    call ASM_PFX(TriggerINTnException)
> +
> +AfterException:
> +    ;
> +    ; Save registers in mActualContextAfterException
> +    ;
> +    push rax
> +    lea  rax, [ASM_PFX(mActualContextAfterException)]
> +    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
> +; TriggerStackOverflow (
> +;  VOID
> +;  );
> +;------------------------------------------------------------------------------
> +global ASM_PFX(TriggerStackOverflow)
> +ASM_PFX(TriggerStackOverflow):
> +    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] 9+ messages in thread

* Re: [Patch V3 2/4] UefiCpuPkg: Add Unit tests for PeiCpuExceptionHandlerLib
  2022-10-14  9:19 ` [Patch V3 2/4] UefiCpuPkg: Add Unit tests for PeiCpuExceptionHandlerLib duntan
@ 2022-10-14 11:58   ` Ni, Ray
  0 siblings, 0 replies; 9+ messages in thread
From: Ni, Ray @ 2022-10-14 11:58 UTC (permalink / raw)
  To: Tan, Dun, devel@edk2.groups.io; +Cc: Dong, Eric, Kumar, Rahul R

Reviewed-by: Ray Ni <ray.ni@intel.com>

> -----Original Message-----
> From: Tan, Dun <dun.tan@intel.com>
> Sent: Friday, October 14, 2022 5:19 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 V3 2/4] UefiCpuPkg: Add Unit tests for PeiCpuExceptionHandlerLib
> 
> The previous change adds unit test for DxeCpuExeptionHandlerLib
> in 64bit mode. This change create a PEIM to add unit test for
> PeiCpuExceptionHandlerLib based on previous change.It can run
> in both 32bit and 64bit modes.
> 
> 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             |   9 +++++++++
>  UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTest.c       | 135
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> ++++++++++++++++++++++++++++++++++++++++++
>  UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTestAsm.nasm | 208
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> ++++++++++++++++++++++
>  UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerLibUnitTest.inf |  61
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerUnitTest.c      | 204
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> ++++++++++++++++++
>  5 files changed, 617 insertions(+)
> 
> diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h
> b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h
> index 936098fde8..bad3387db5 100644
> --- a/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h
> +++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h
> @@ -93,6 +93,15 @@ typedef struct {
>    UINT64    R15;
>  } GENERAL_REGISTER;
> 
> +typedef struct {
> +  UINT32    Edi;
> +  UINT32    Esi;
> +  UINT32    Ebx;
> +  UINT32    Edx;
> +  UINT32    Ecx;
> +  UINT32    Eax;
> +} GENERAL_REGISTER_IA32;
> +
>  extern UINTN               mFaultInstructionLength;
>  extern EFI_EXCEPTION_TYPE  mExceptionType;
>  extern UINTN               mRspAddress[];
> diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTest.c
> b/UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTest.c
> new file mode 100644
> index 0000000000..8bb27249dc
> --- /dev/null
> +++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTest.c
> @@ -0,0 +1,135 @@
> +/** @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  mActualContextInHandler;
> +GENERAL_REGISTER_IA32  mActualContextAfterException;
> +
> +//
> +// In TestCpuContextConsistency, Cpu registers will be set to
> mExpectedContextInHandler/mExpectedContextAfterException.
> +// Ecx in mExpectedContextInHandler 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  mExpectedContextInHandler      = { 1, 2, 3, 4, 5, 0 };
> +GENERAL_REGISTER_IA32  mExpectedContextAfterException = { 11, 12, 13, 14, 15, 16 };
> +
> +/**
> +  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
> +  )
> +{
> +  //
> +  // Store SystemContext in exception handler.
> +  //
> +  mActualContextInHandler.Edi = SystemContext.SystemContextIa32->Edi;
> +  mActualContextInHandler.Esi = SystemContext.SystemContextIa32->Esi;
> +  mActualContextInHandler.Ebx = SystemContext.SystemContextIa32->Ebx;
> +  mActualContextInHandler.Edx = SystemContext.SystemContextIa32->Edx;
> +  mActualContextInHandler.Ecx = SystemContext.SystemContextIa32->Ecx;
> +  mActualContextInHandler.Eax = SystemContext.SystemContextIa32->Eax;
> +
> +  //
> +  // Modify cpu context. These registers will be stored in mActualContextAfterException.
> +  // Do not handle Esp and Ebp in SystemContext. CpuExceptionHandlerLib doesn't set Esp and
> +  // Esp register to the value in SystemContext.
> +  //
> +  SystemContext.SystemContextIa32->Edi = mExpectedContextAfterException.Edi;
> +  SystemContext.SystemContextIa32->Esi = mExpectedContextAfterException.Esi;
> +  SystemContext.SystemContextIa32->Ebx = mExpectedContextAfterException.Ebx;
> +  SystemContext.SystemContextIa32->Edx = mExpectedContextAfterException.Edx;
> +  SystemContext.SystemContextIa32->Ecx = mExpectedContextAfterException.Ecx;
> +  SystemContext.SystemContextIa32->Eax = mExpectedContextAfterException.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 mActualContextInHandler with mExpectedContextInHandler.
> +  2.Compare mActualContextAfterException with mExpectedContextAfterException.
> +
> +  @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 (mActualContextInHandler.Edi, mExpectedContextInHandler.Edi);
> +  UT_ASSERT_EQUAL (mActualContextInHandler.Esi, mExpectedContextInHandler.Esi);
> +  UT_ASSERT_EQUAL (mActualContextInHandler.Ebx, mExpectedContextInHandler.Ebx);
> +  UT_ASSERT_EQUAL (mActualContextInHandler.Edx, mExpectedContextInHandler.Edx);
> +  UT_ASSERT_EQUAL (mActualContextInHandler.Ecx, mExpectedContextInHandler.Ecx);
> +  UT_ASSERT_EQUAL (mActualContextInHandler.Eax, mExpectedContextInHandler.Eax);
> +
> +  UT_ASSERT_EQUAL (mActualContextAfterException.Edi, mExpectedContextAfterException.Edi);
> +  UT_ASSERT_EQUAL (mActualContextAfterException.Esi, mExpectedContextAfterException.Esi);
> +  UT_ASSERT_EQUAL (mActualContextAfterException.Ebx, mExpectedContextAfterException.Ebx);
> +  UT_ASSERT_EQUAL (mActualContextAfterException.Edx, mExpectedContextAfterException.Edx);
> +  UT_ASSERT_EQUAL (mActualContextAfterException.Ecx, mExpectedContextAfterException.Ecx);
> +  UT_ASSERT_EQUAL (mActualContextAfterException.Eax, mExpectedContextAfterException.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..48031a5109
> --- /dev/null
> +++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTestAsm.nasm
> @@ -0,0 +1,208 @@
> +;------------------------------------------------------------------------------
> +;
> +; 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
> +  .Ebx:    resd    1
> +  .Edx:    resd    1
> +  .Ecx:    resd    1
> +  .Eax:    resd    1
> +
> +endstruc
> +
> +extern ASM_PFX(mExpectedContextInHandler)
> +extern ASM_PFX(mActualContextAfterException)
> +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
> +
> +;------------------------------------------------------------------------------
> +; ModifyEcxInGlobalBeforeException;
> +; This function is writed by assebly code because it's only called in this file.
> +; It's used to set Ecx in mExpectedContextInHandler for different exception.
> +;------------------------------------------------------------------------------
> +global ASM_PFX(ModifyEcxInGlobalBeforeException)
> +ASM_PFX(ModifyEcxInGlobalBeforeException):
> +    push eax
> +    lea  eax, [ASM_PFX(mExpectedContextInHandler)]
> +    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 register to mExpectedContextInHandler. Do not handle Esp and Ebp.
> +    ; CpuExceptionHandlerLib doesn't set Esp and Esp register to the value in SystemContext.
> +    ;
> +    lea eax, [ASM_PFX(mExpectedContextInHandler)]
> +    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:
> +    mov  ecx, dword [esp + 0x28]                    ; Set ecx to PFAddress.
> +    call ASM_PFX(ModifyEcxInGlobalBeforeException)  ; Set mExpectedContextInHandler.Ecx to PFAddress.
> +    push ecx                                        ; Push PfAddress into stack.
> +    call ASM_PFX(TriggerPFException)
> +    jmp  AfterException
> +
> +GPException:
> +    mov  ecx, dword [esp + 0x28]                    ; Set ecx to CR4_RESERVED_BIT.
> +    call ASM_PFX(ModifyEcxInGlobalBeforeException)  ; Set mExpectedContextInHandler.Ecx to CR4_RESERVED_BIT.
> +    push ecx                                        ; Push CR4_RESERVED_BIT into stack.
> +    call ASM_PFX(TriggerGPException)
> +    jmp  AfterException
> +
> +INTnException:
> +    call ASM_PFX(ModifyEcxInGlobalBeforeException)  ; Set mExpectedContextInHandler.Ecx to ExceptionType.
> +    push ecx                                        ; Push ExceptionType into stack.
> +    call ASM_PFX(TriggerINTnException)
> +
> +AfterException:
> +    ;
> +    ; Save register in mActualContextAfterException.
> +    ;
> +    push eax
> +    lea  eax, [ASM_PFX(mActualContextAfterException)]
> +    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
> +; TriggerStackOverflow (
> +;  VOID
> +;  );
> +;------------------------------------------------------------------------------
> +global ASM_PFX(TriggerStackOverflow)
> +ASM_PFX(TriggerStackOverflow):
> +    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	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2022-10-14 11:58 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-10-14  9:19 [Patch V3 0/4] Add Unit tests for Pei/DxeCpuExceptionHandlerLib duntan
2022-10-14  9:19 ` [Patch V3 1/4] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib duntan
2022-10-14 11:57   ` Ni, Ray
2022-10-14  9:19 ` [Patch V3 2/4] UefiCpuPkg: Add Unit tests for PeiCpuExceptionHandlerLib duntan
2022-10-14 11:58   ` Ni, Ray
2022-10-14  9:19 ` [Patch V3 3/4] UefiCpuPkg: Add Pei/DxeCpuExceptionHandlerLibUnitTest in dsc duntan
2022-10-14  9:19 ` [Patch V3 4/4] UefiCpuPkg: Add CpuExceptionHandlerTest.h in ECC IgnoreFile duntan
2022-10-14  9:39   ` Ni, Ray
2022-10-14 10:01     ` duntan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox