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

Add Pei/DxeCpuExceptionHandlerLibUnitTest

Dun Tan (3):
  UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib
  UefiCpuPkg: Add Unit tests for PeiCpuExceptionHandlerLib
  UefiCpuPkg: Add Pei/DxeCpuExceptionHandlerLibUnitTest in dsc

 UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h             | 353 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCommon.c       | 856 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibUnitTest.inf |  58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnitTest.c      | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTest.c       | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTestAsm.nasm | 217 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerLibUnitTest.inf |  61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerUnitTest.c      | 204 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTest.c        | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTestAsm.nasm  | 260 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/UefiCpuPkg.dsc                                                    |   7 +++++++
 11 files changed, 2512 insertions(+)
 create mode 100644 UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h
 create mode 100644 UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCommon.c
 create mode 100644 UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibUnitTest.inf
 create mode 100644 UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnitTest.c
 create mode 100644 UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTest.c
 create mode 100644 UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTestAsm.nasm
 create mode 100644 UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerLibUnitTest.inf
 create mode 100644 UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerUnitTest.c
 create mode 100644 UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTest.c
 create mode 100644 UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTestAsm.nasm

-- 
2.31.1.windows.1


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

* [PATCH 1/3] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib
  2022-10-11  6:03 [PATCH 0/3] Add Pei/DxeCpuExceptionHandlerLibUnitTest duntan
@ 2022-10-11  6:03 ` duntan
  2022-10-11  9:37   ` Ni, Ray
  2022-10-11  6:03 ` [PATCH 2/3] UefiCpuPkg: Add Unit tests for PeiCpuExceptionHandlerLib duntan
  2022-10-11  6:03 ` [PATCH 3/3] UefiCpuPkg: Add Pei/DxeCpuExceptionHandlerLibUnitTest in dsc duntan
  2 siblings, 1 reply; 6+ messages in thread
From: duntan @ 2022-10-11  6:03 UTC (permalink / raw)
  To: devel; +Cc: Eric Dong, Ray Ni, Rahul Kumar

Add target based unit tests for the DxeCpuExceptionHandlerLib.
A DXE driver is created to test DxeCpuExceptionHandlerLib. Four
kinds of test cases are created in this module:
1.Test if exception handler can be registered/unregistered for
no error code exception. 2.Test if exception handler can be
registered/unregistered for GP and PF. 3.Test if Cpu Context
is consistent before and after exception. 4.Test if stack
overflow can be captured by CpuStackGuard in both Bsp and AP.

Signed-off-by: Dun Tan <dun.tan@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
---
 UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h             | 353 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCommon.c       | 856 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibUnitTest.inf |  58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnitTest.c      | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTest.c        | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTestAsm.nasm  | 260 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 1890 insertions(+)

diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h
new file mode 100644
index 0000000000..bfbc483075
--- /dev/null
+++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTest.h
@@ -0,0 +1,353 @@
+/** @file
+
+  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef CPU_EXCEPTION_HANDLER_TEST_H_
+#define CPU_EXCEPTION_HANDLER_TEST_H_
+
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UnitTestLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UnitTestHostBaseLib.h>
+#include <Library/CpuExceptionHandlerLib.h>
+#include <Library/UefiLib.h>
+#include <Library/SerialPortLib.h>
+#include <Library/HobLib.h>
+#include <Library/CpuPageTableLib.h>
+#include <Guid/MemoryAllocationHob.h>
+#include <Protocol/MpService.h>
+#include <PiPei.h>
+#include <Ppi/MpServices2.h>
+
+#define UNIT_TEST_APP_NAME     "Cpu Exception Handler Lib Unit Tests"
+#define UNIT_TEST_APP_VERSION  "1.0"
+
+#define  CPU_INTERRUPT_NUM       256
+#define  SPEC_MAX_EXCEPTION_NUM  22
+#define  CR4_RESERVED_BIT        BIT15
+
+#pragma pack (1)
+
+typedef union {
+  struct {
+    UINT32    LimitLow    : 16;
+    UINT32    BaseLow     : 16;
+    UINT32    BaseMid     : 8;
+    UINT32    Type        : 4;
+    UINT32    System      : 1;
+    UINT32    Dpl         : 2;
+    UINT32    Present     : 1;
+    UINT32    LimitHigh   : 4;
+    UINT32    Software    : 1;
+    UINT32    Reserved    : 1;
+    UINT32    DefaultSize : 1;
+    UINT32    Granularity : 1;
+    UINT32    BaseHigh    : 8;
+  } Bits;
+  UINT64    Uint64;
+} IA32_GDT;
+
+typedef struct {
+  UINT32    InitialApicId;
+  UINT32    ApicId;
+  UINT32    Health;
+  UINT64    ApTopOfStack;
+} CPU_INFO_IN_HOB;
+#pragma pack ()
+
+typedef struct {
+  IA32_DESCRIPTOR    OriginalGdtr;
+  IA32_DESCRIPTOR    OriginalIdtr;
+  UINT16             Tr;
+} CPU_REGISTER_BUFFER;
+
+typedef union {
+  EDKII_PEI_MP_SERVICES2_PPI    *Ppi;
+  EFI_MP_SERVICES_PROTOCOL      *Protocol;
+} MP_SERVICES;
+
+typedef struct {
+  VOID          *Buffer;
+  UINTN         BufferSize;
+  EFI_STATUS    Status;
+} EXCEPTION_STACK_SWITCH_CONTEXT;
+
+typedef struct {
+  UINT64    Rdi;
+  UINT64    Rsi;
+  UINT64    Rbp;
+  UINT64    Rbx;
+  UINT64    Rdx;
+  UINT64    Rcx;
+  UINT64    Rax;
+  UINT64    R8Register;
+  UINT64    R9Register;
+  UINT64    R10Register;
+  UINT64    R11Register;
+  UINT64    R12Register;
+  UINT64    R13Register;
+  UINT64    R14Register;
+  UINT64    R15Register;
+} GENERAL_REGISTER;
+
+typedef struct {
+  UINT32    Edi;
+  UINT32    Esi;
+  UINT32    Ebp;
+  UINT32    Ebx;
+  UINT32    Edx;
+  UINT32    Ecx;
+  UINT32    Eax;
+} GENERAL_REGISTER_IA32;
+
+extern UINTN                  mFaultInstructionLength;
+extern EFI_EXCEPTION_TYPE     mExceptionType;
+extern UINTN                  mRspAddress[];
+extern GENERAL_REGISTER       mRegisterForCheck[];
+extern GENERAL_REGISTER       mAdjustRegisterBeforeException;
+extern GENERAL_REGISTER_IA32  mIa32RegisterForCheck[];
+extern GENERAL_REGISTER_IA32  mAdjustIa32RegisterBeforeException;
+
+/**
+  Initialize Bsp Idt with a new Idt table and return the IA32_DESCRIPTOR buffer.
+  In PEIM, store original PeiServicePointer before new Idt table.
+
+  @return Pointer to the allocated IA32_DESCRIPTOR buffer.
+**/
+VOID *
+InitializeBspIdt (
+  VOID
+  );
+
+/**
+  Trigger no error code exception by INT n instruction.
+
+  @param[in]  ExceptionType  No error code exception type.
+**/
+VOID
+EFIAPI
+TriggerINTnException (
+  IN  EFI_EXCEPTION_TYPE  ExceptionType
+  );
+
+/**
+  Trigger GP exception.
+
+  @param[in]  Cr4ReservedBit  Cr4 reserved bit.
+**/
+VOID
+EFIAPI
+TriggerGPException (
+  UINTN  Cr4ReservedBit
+  );
+
+/**
+  Trigger PF exception by write to not present or ReadOnly address.
+
+  @param[in]  PFAddress  Not present or ReadOnly address in page table.
+**/
+VOID
+EFIAPI
+TriggerPFException (
+  UINTN  PFAddress
+  );
+
+/**
+  Special handler for fault exception.
+  Rip/Eip in SystemContext will be modified to the instruction after the exception instruction.
+
+  @param ExceptionType  Exception type.
+  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+VOID
+EFIAPI
+AdjustRipForFaultHandler (
+  IN EFI_EXCEPTION_TYPE  ExceptionType,
+  IN EFI_SYSTEM_CONTEXT  SystemContext
+  );
+
+/**
+  Test consistency of Cpu context. Four steps:
+  1. Set Cpu register to mAdjustRegisterBeforeException before exception.
+  2. Trigger exception specified by ExceptionType.
+  3. Store SystemContext in mRegisterForCheck[0] and set SystemContext to mAdjustRegisterInsideException in handler.
+  4. Store the Cpu register in mRegisterForCheck[1]
+
+  Rcx/Ecx in mAdjustRegisterInsideException is decided by different exception type runtime since Rcx/Ecx is needed in assembly code.
+  For GP and PF, Rcx/Ecx is set to FaultParameter. For other exception triggered by INTn, Rcx/Ecx is set to ExceptionType.
+
+  @param[in] ExceptionType   Exception type.
+  @param[in] FaultParameter  Parameter for GP and PF. OPTIONAL
+**/
+VOID
+EFIAPI
+AsmTestConsistencyOfCpuContext (
+  IN  EFI_EXCEPTION_TYPE  ExceptionType,
+  IN  UINTN               FaultParameter   OPTIONAL
+  );
+
+/**
+  Special handler for ConsistencyOfCpuContext test case. General register in SystemContext
+  is modified in this handler.
+
+  @param ExceptionType  Exception type.
+  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+VOID
+EFIAPI
+AdjustCpuContextHandler (
+  IN EFI_EXCEPTION_TYPE  ExceptionType,
+  IN EFI_SYSTEM_CONTEXT  SystemContext
+  );
+
+/**
+  Compare cpu context in ConsistencyOfCpuContext test case.
+  1.Compare mRegisterForCheck[0] with mAdjustRegisterBeforeException.
+  2.Compare mRegisterForCheck[1] with mAdjustRegisterAfterException.
+
+  @retval  UNIT_TEST_PASSED             The Unit test has completed and it was successful.
+  @retval  UNIT_TEST_ERROR_TEST_FAILED  A test case assertion has failed.
+**/
+UNIT_TEST_STATUS
+CompareCpuContext (
+  VOID
+  );
+
+/**
+  Get EFI_MP_SERVICES_PROTOCOL/EDKII_PEI_MP_SERVICES2_PPI pointer.
+
+  @param[out] MpServices    Pointer to the MP_SERVICES buffer
+
+  @retval EFI_SUCCESS       EFI_MP_SERVICES_PROTOCOL/PPI interface is returned
+  @retval EFI_NOT_FOUND     EFI_MP_SERVICES_PROTOCOL/PPI interface is not found
+**/
+EFI_STATUS
+GetMpServices (
+  OUT MP_SERVICES  *MpServices
+  );
+
+/**
+  Create CpuExceptionLibUnitTestSuite and add test case.
+
+  @param[in]  FrameworkHandle    Unit test framework.
+
+  @return  EFI_SUCCESS           The unit test suite was created.
+  @retval  EFI_OUT_OF_RESOURCES  There are not enough resources available to
+                                 initialize the unit test suite.
+**/
+EFI_STATUS
+AddCommonTestCase (
+  IN  UNIT_TEST_FRAMEWORK_HANDLE  Framework
+  );
+
+/**
+  Execute a caller provided function on all enabled APs.
+
+  @param[in]  MpServices    MP_SERVICES structure.
+  @param[in]  Procedure     Pointer to the function to be run on enabled APs of the system.
+  @param[in]  SingleThread  If TRUE, then all the enabled APs execute the function specified by Procedure
+                            one by one, in ascending order of processor handle number.
+                            If FALSE, then all the enabled APs execute the function specified by Procedure
+                            simultaneously.
+  @param[in]  TimeoutInMicroseconds Indicates the time limit in microseconds for APs to return from Procedure,
+                                    for blocking mode only. Zero means infinity.
+  @param[in]  ProcedureArgument     The parameter passed into Procedure for all APs.
+
+  @retval EFI_SUCCESS       Execute a caller provided function on all enabled APs successfully
+  @retval Others            Execute a caller provided function on all enabled APs unsuccessfully
+**/
+EFI_STATUS
+MpServicesUnitTestStartupAllAPs (
+  IN MP_SERVICES       MpServices,
+  IN EFI_AP_PROCEDURE  Procedure,
+  IN BOOLEAN           SingleThread,
+  IN UINTN             TimeoutInMicroSeconds,
+  IN VOID              *ProcedureArgument
+  );
+
+/**
+  Caller gets one enabled AP to execute a caller-provided function.
+
+  @param[in]  MpServices    MP_SERVICES structure.
+  @param[in]  Procedure     Pointer to the function to be run on enabled APs of the system.
+  @param[in]  ProcessorNumber       The handle number of the AP.
+  @param[in]  TimeoutInMicroseconds Indicates the time limit in microseconds for APs to return from Procedure,
+                                    for blocking mode only. Zero means infinity.
+  @param[in]  ProcedureArgument     The parameter passed into Procedure for all APs.
+
+
+  @retval EFI_SUCCESS       Caller gets one enabled AP to execute a caller-provided function successfully
+  @retval Others            Caller gets one enabled AP to execute a caller-provided function unsuccessfully
+**/
+EFI_STATUS
+MpServicesUnitTestStartupThisAP (
+  IN MP_SERVICES       MpServices,
+  IN EFI_AP_PROCEDURE  Procedure,
+  IN UINTN             ProcessorNumber,
+  IN UINTN             TimeoutInMicroSeconds,
+  IN VOID              *ProcedureArgument
+  );
+
+/**
+  Get the handle number for the calling processor.
+
+  @param[in]  MpServices      Pointer to MP_SERVICES structure.
+  @param[out] ProcessorNumber The handle number for the calling processor.
+
+  @retval EFI_SUCCESS       Get the handle number for the calling processor successfully.
+  @retval Others            Get the handle number for the calling processor unsuccessfully.
+**/
+EFI_STATUS
+MpServicesUnitTestWhoAmI (
+  IN MP_SERVICES  MpServices,
+  OUT UINTN       *ProcessorNumber
+  );
+
+/**
+  Retrieve the number of logical processor in the platform and the number of those logical processors that
+  are enabled on this boot.
+
+  @param[in]  MpServices          Pointer to MP_SERVICES structure.
+  @param[out] NumberOfProcessors  Pointer to the total number of logical processors in the system, including
+                                  the BSP and disabled APs.
+  @param[out] NumberOfEnabledProcessors Pointer to the number of processors in the system that are enabled.
+
+  @retval EFI_SUCCESS       Retrieve the number of logical processor successfully
+  @retval Others            Retrieve the number of logical processor unsuccessfully
+**/
+EFI_STATUS
+MpServicesUnitTestGetNumberOfProcessors (
+  IN MP_SERVICES  MpServices,
+  OUT UINTN       *NumberOfProcessors,
+  OUT UINTN       *NumberOfEnabledProcessors
+  );
+
+/**
+  Trigger stack overflow by CpuStackGuard.
+**/
+VOID
+EFIAPI
+TriggerStackOverflowbyCpuStackGuard (
+  VOID
+  );
+
+/**
+  Special handler for CpuStackGuard test case.
+
+  @param ExceptionType  Exception type.
+  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+VOID
+EFIAPI
+CpuStackGuardExceptionHandler (
+  IN EFI_EXCEPTION_TYPE  ExceptionType,
+  IN EFI_SYSTEM_CONTEXT  SystemContext
+  );
+
+#endif
diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCommon.c b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCommon.c
new file mode 100644
index 0000000000..1c3d011c76
--- /dev/null
+++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/CpuExceptionHandlerTestCommon.c
@@ -0,0 +1,856 @@
+/** @file
+  Unit tests of the CpuExceptionHandlerLib.
+
+  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "CpuExceptionHandlerTest.h"
+
+//
+// Length of the assembly falut instruction.
+//
+UINTN               mFaultInstructionLength = 0;
+EFI_EXCEPTION_TYPE  mExceptionType          = 256;
+UINTN               mNumberOfProcessors     = 1;
+UINTN               mRspAddress[2]          = { 0 };
+
+//
+// Error code flag indicating whether or not an error code will be
+// pushed on the stack if an exception occurs.
+//
+// 1 means an error code will be pushed, otherwise 0
+//
+CONST UINT32  mErrorCodeExceptionFlag = 0x20227d00;
+
+/**
+  Special handler for trap exception.
+
+  @param ExceptionType  Exception type.
+  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+VOID
+EFIAPI
+INTnExceptionHandler (
+  IN EFI_EXCEPTION_TYPE  ExceptionType,
+  IN EFI_SYSTEM_CONTEXT  SystemContext
+  )
+{
+  mExceptionType = ExceptionType;
+}
+
+/**
+  Restore all cpu original register before test case.
+
+  @param[in] Buffer  Argument of the procedure.
+**/
+VOID
+EFIAPI
+RestoreRegistersPerCpu (
+  IN VOID  *Buffer
+  )
+{
+  CPU_REGISTER_BUFFER  *CpuOriginalRegisterBuffer;
+  UINT16               Tr;
+  IA32_TSS_DESCRIPTOR  *Tss;
+
+  CpuOriginalRegisterBuffer = (CPU_REGISTER_BUFFER *)Buffer;
+
+  AsmWriteGdtr (&(CpuOriginalRegisterBuffer->OriginalGdtr));
+  AsmWriteIdtr (&(CpuOriginalRegisterBuffer->OriginalIdtr));
+  Tr = CpuOriginalRegisterBuffer->Tr;
+  if ((Tr != 0) && (Tr < CpuOriginalRegisterBuffer->OriginalGdtr.Limit)) {
+    Tss = (IA32_TSS_DESCRIPTOR *)(CpuOriginalRegisterBuffer->OriginalGdtr.Base + Tr);
+    if (Tss->Bits.P == 1) {
+      //
+      // Clear busy bit of TSS before write Tr
+      //
+      Tss->Bits.Type &= 0xD;
+      AsmWriteTr (Tr);
+    }
+  }
+}
+
+/**
+  Restore all cpu original register before test case.
+
+  @param[in] MpServices                 MpServices.
+  @param[in] CpuOriginalRegisterBuffer  Address of CpuOriginalRegisterBuffer.
+  @param[in] BspProcessorNum            Bsp processor number.
+**/
+VOID
+RestoreAllCpuRegisters (
+  MP_SERVICES *MpServices, OPTIONAL
+  CPU_REGISTER_BUFFER  *CpuOriginalRegisterBuffer,
+  UINTN                         BspProcessorNum
+  )
+{
+  CPU_REGISTER_BUFFER  *AllCpuOriginalRegisterBuffer;
+  UINTN                Index;
+  EFI_STATUS           Status;
+
+  for (Index = 0; Index < mNumberOfProcessors; ++Index) {
+    AllCpuOriginalRegisterBuffer = CpuOriginalRegisterBuffer + Index;
+    if (Index == BspProcessorNum) {
+      RestoreRegistersPerCpu ((VOID *)AllCpuOriginalRegisterBuffer);
+      continue;
+    }
+
+    ASSERT (MpServices != NULL);
+    Status = MpServicesUnitTestStartupThisAP (
+               *MpServices,
+               (EFI_AP_PROCEDURE)RestoreRegistersPerCpu,
+               Index,
+               0,
+               (VOID *)AllCpuOriginalRegisterBuffer
+               );
+    ASSERT_EFI_ERROR (Status);
+  }
+}
+
+/**
+  Store all cpu original register before test case.
+
+  @param[in] Buffer  Argument of the procedure.
+**/
+VOID
+EFIAPI
+SaveRegisterPerCpu (
+  IN VOID  *Buffer
+  )
+{
+  CPU_REGISTER_BUFFER  *CpuOriginalRegisterBuffer;
+  IA32_DESCRIPTOR      Gdtr;
+  IA32_DESCRIPTOR      Idtr;
+
+  CpuOriginalRegisterBuffer = (CPU_REGISTER_BUFFER *)Buffer;
+
+  AsmReadGdtr (&Gdtr);
+  AsmReadIdtr (&Idtr);
+  CpuOriginalRegisterBuffer->OriginalGdtr.Base  = Gdtr.Base;
+  CpuOriginalRegisterBuffer->OriginalGdtr.Limit = Gdtr.Limit;
+  CpuOriginalRegisterBuffer->OriginalIdtr.Base  = Idtr.Base;
+  CpuOriginalRegisterBuffer->OriginalIdtr.Limit = Idtr.Limit;
+  CpuOriginalRegisterBuffer->Tr                 = AsmReadTr ();
+}
+
+/**
+  Store all cpu original register before test case.
+
+  @param[in] MpServices       MpServices.
+  @param[in] BspProcessorNum  Bsp processor number.
+
+  @return Pointer to the allocated CPU_REGISTER_BUFFER.
+**/
+CPU_REGISTER_BUFFER *
+SaveAllCpuRegisters (
+  MP_SERVICES *MpServices, OPTIONAL
+  UINTN       BspProcessorNum
+  )
+{
+  CPU_REGISTER_BUFFER  *CpuOriginalRegisterBuffer;
+  CPU_REGISTER_BUFFER  *AllCpuOriginalRegisterBuffer;
+  EFI_STATUS           Status;
+  UINTN                Index;
+
+  CpuOriginalRegisterBuffer = AllocateZeroPool (mNumberOfProcessors * sizeof (CPU_REGISTER_BUFFER));
+  ASSERT (CpuOriginalRegisterBuffer != NULL);
+
+  for (Index = 0; Index < mNumberOfProcessors; ++Index) {
+    AllCpuOriginalRegisterBuffer = CpuOriginalRegisterBuffer + Index;
+    if (Index == BspProcessorNum) {
+      SaveRegisterPerCpu ((VOID *)AllCpuOriginalRegisterBuffer);
+      continue;
+    }
+
+    ASSERT (MpServices != NULL);
+    Status = MpServicesUnitTestStartupThisAP (
+               *MpServices,
+               (EFI_AP_PROCEDURE)SaveRegisterPerCpu,
+               Index,
+               0,
+               (VOID *)AllCpuOriginalRegisterBuffer
+               );
+    ASSERT_EFI_ERROR (Status);
+  }
+
+  return CpuOriginalRegisterBuffer;
+}
+
+/**
+  Initialize Ap Idt Procedure.
+
+  @param[in] Buffer  Argument of the procedure.
+**/
+VOID
+EFIAPI
+InitializeIdtPerAp (
+  IN VOID  *Buffer
+  )
+{
+  AsmWriteIdtr (Buffer);
+}
+
+/**
+  Initialize all Ap Idt.
+
+  @param[in] MpServices MpServices.
+  @param[in] BspIdtr    Pointer to IA32_DESCRIPTOR allocated by Bsp.
+**/
+VOID
+InitializeApIdt (
+  MP_SERVICES  MpServices,
+  VOID         *BspIdtr
+  )
+{
+  EFI_STATUS  Status;
+
+  Status = MpServicesUnitTestStartupAllAPs (
+             MpServices,
+             (EFI_AP_PROCEDURE)InitializeIdtPerAp,
+             FALSE,
+             0,
+             BspIdtr
+             );
+  ASSERT_EFI_ERROR (Status);
+}
+
+/**
+  Check if exception handler can registered/unregistered for no error code exception.
+
+  @param[in]  Context    [Optional] An optional parameter that enables:
+                         1) test-case reuse with varied parameters and
+                         2) test-case re-entry for Target tests that need a
+                         reboot.  This parameter is a VOID* and it is the
+                         responsibility of the test author to ensure that the
+                         contents are well understood by all test cases that may
+                         consume it.
+
+  @retval  UNIT_TEST_PASSED             The Unit test has completed and the test
+                                        case was successful.
+  @retval  UNIT_TEST_ERROR_TEST_FAILED  A test case assertion has failed.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+TestRegisterHandlerForNoErrorCodeException (
+  IN UNIT_TEST_CONTEXT  Context
+  )
+{
+  EFI_STATUS           Status;
+  UINTN                Index;
+  CPU_REGISTER_BUFFER  *CpuOriginalRegisterBuffer;
+  VOID                 *NewIdtr;
+
+  CpuOriginalRegisterBuffer = SaveAllCpuRegisters (NULL, 0);
+  NewIdtr                   = InitializeBspIdt ();
+  Status                    = InitializeCpuExceptionHandlers (NULL);
+  UT_ASSERT_EQUAL (Status, EFI_SUCCESS);
+
+  for (Index = 0; Index < SPEC_MAX_EXCEPTION_NUM; Index++) {
+    //
+    // Only test no error code exception by INT n instruction.
+    //
+    if ((mErrorCodeExceptionFlag & (1 << Index)) != 0) {
+      continue;
+    }
+
+    DEBUG ((DEBUG_INFO, "TestCase1: ExceptionType is %d\n", Index));
+    Status = RegisterCpuInterruptHandler (Index, INTnExceptionHandler);
+    UT_ASSERT_EQUAL (Status, EFI_SUCCESS);
+
+    TriggerINTnException (Index);
+    UT_ASSERT_EQUAL (mExceptionType, Index);
+    Status = RegisterCpuInterruptHandler (Index, NULL);
+    UT_ASSERT_EQUAL (Status, EFI_SUCCESS);
+  }
+
+  RestoreAllCpuRegisters (NULL, CpuOriginalRegisterBuffer, 0);
+  FreePool (CpuOriginalRegisterBuffer);
+  FreePool (NewIdtr);
+  return UNIT_TEST_PASSED;
+}
+
+/**
+  Get Bsp stack base.
+
+  @param[out] StackBase  Pointer to stack base of BSP.
+**/
+VOID
+GetBspStackBase (
+  OUT UINTN  *StackBase
+  )
+{
+  EFI_PEI_HOB_POINTERS       Hob;
+  EFI_HOB_MEMORY_ALLOCATION  *MemoryHob;
+
+  //
+  // Get the base of stack from Hob.
+  //
+  ASSERT (StackBase != NULL);
+  Hob.Raw = GetHobList ();
+  while ((Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw)) != NULL) {
+    MemoryHob = Hob.MemoryAllocation;
+    if (CompareGuid (&gEfiHobMemoryAllocStackGuid, &MemoryHob->AllocDescriptor.Name)) {
+      DEBUG ((
+        DEBUG_INFO,
+        "%a: Bsp StackBase = 0x%016lx  StackSize = 0x%016lx\n",
+        __FUNCTION__,
+        MemoryHob->AllocDescriptor.MemoryBaseAddress,
+        MemoryHob->AllocDescriptor.MemoryLength
+        ));
+
+      *StackBase = (UINTN)MemoryHob->AllocDescriptor.MemoryBaseAddress;
+      //
+      // Ensure the base of the stack is page-size aligned.
+      //
+      ASSERT ((*StackBase & EFI_PAGE_MASK) == 0);
+      break;
+    }
+
+    Hob.Raw = GET_NEXT_HOB (Hob);
+  }
+
+  ASSERT (*StackBase != 0);
+}
+
+/**
+  Initialize Ap Idt Procedure.
+
+  @param[in] Buffer  Argument of the procedure.
+**/
+VOID
+EFIAPI
+GetStackBasePerAp (
+  IN VOID  *Buffer
+  )
+{
+  UINTN  ApTopOfStack;
+
+  ApTopOfStack     = ALIGN_VALUE ((UINTN)&ApTopOfStack, (UINTN)PcdGet32 (PcdCpuApStackSize));
+  *(UINTN *)Buffer = ApTopOfStack - (UINTN)PcdGet32 (PcdCpuApStackSize);
+}
+
+/**
+  Get Ap stack Info.
+
+  @param[in] MpServices       MpServices.
+  @param[in] BspProcessorNum  Bsp processor number.
+
+  @return Pointer to the allocated CpuStackBaseBuffer.
+**/
+UINTN *
+GetAllCpuStackBase (
+  MP_SERVICES  *MpServices,
+  UINTN        BspProcessorNum
+  )
+{
+  UINTN       *CpuStackBaseBuffer;
+  UINTN       *AllCpuStackBaseBuffer;
+  EFI_STATUS  Status;
+  UINTN       Index;
+
+  CpuStackBaseBuffer = AllocateZeroPool (mNumberOfProcessors * sizeof (UINTN));
+  ASSERT (CpuStackBaseBuffer != NULL);
+
+  for (Index = 0; Index < mNumberOfProcessors; ++Index) {
+    AllCpuStackBaseBuffer = CpuStackBaseBuffer + Index;
+    if (Index == BspProcessorNum) {
+      GetBspStackBase (AllCpuStackBaseBuffer);
+      continue;
+    }
+
+    ASSERT (MpServices != NULL);
+    Status = MpServicesUnitTestStartupThisAP (
+               *MpServices,
+               (EFI_AP_PROCEDURE)GetStackBasePerAp,
+               Index,
+               0,
+               (VOID *)AllCpuStackBaseBuffer
+               );
+    ASSERT_EFI_ERROR (Status);
+    DEBUG ((DEBUG_INFO, "AP[%d] StackBase = 0x%x\n", Index, CpuStackBaseBuffer[Index]));
+  }
+
+  return CpuStackBaseBuffer;
+}
+
+/**
+  Return not present or ReadOnly address in page table.
+
+  @param[out] PFAddress  Access to the address which is not permitted will trigger PF exceptions.
+**/
+VOID
+PageFaultAddressInPageTable (
+  UINTN  *PFAddress
+  )
+{
+  IA32_CR0        Cr0;
+  IA32_CR4        Cr4;
+  UINTN           PageTable;
+  PAGING_MODE     PagingMode;
+  BOOLEAN         Enable5LevelPaging;
+  RETURN_STATUS   Status;
+  IA32_MAP_ENTRY  *Map;
+  UINTN           MapCount;
+  UINTN           Index;
+
+  ASSERT (PFAddress != NULL);
+  *PFAddress = 0;
+
+  Cr0.UintN = AsmReadCr0 ();
+  if (Cr0.Bits.PG == 0) {
+    return;
+  }
+
+  PageTable = AsmReadCr3 ();
+  Cr4.UintN = AsmReadCr4 ();
+  if (sizeof (UINTN) == sizeof (UINT32)) {
+    ASSERT (Cr4.Bits.PAE == 1);
+    PagingMode = PagingPae;
+  } else {
+    Enable5LevelPaging = (BOOLEAN)(Cr4.Bits.LA57 == 1);
+    PagingMode         = Enable5LevelPaging ? Paging5Level : Paging4Level;
+  }
+
+  MapCount = 0;
+  Status   = PageTableParse (PageTable, PagingMode, NULL, &MapCount);
+  ASSERT (Status == RETURN_BUFFER_TOO_SMALL);
+  Map    = AllocatePages (EFI_SIZE_TO_PAGES (MapCount * sizeof (IA32_MAP_ENTRY)));
+  Status = PageTableParse (PageTable, PagingMode, Map, &MapCount);
+  ASSERT (Status == RETURN_SUCCESS);
+
+  for (Index = 0; Index < MapCount; Index++) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "%02d: %016lx - %016lx, %016lx\n",
+      Index,
+      Map[Index].LinearAddress,
+      Map[Index].LinearAddress + Map[Index].Length,
+      Map[Index].Attribute.Uint64
+      ));
+
+    //
+    // Not present address in page table.
+    //
+    if ((Index >= 1) && (Map[Index].LinearAddress > Map[Index - 1].LinearAddress + Map[Index - 1].Length)) {
+      *PFAddress = (UINTN)(Map[Index - 1].LinearAddress + Map[Index - 1].Length);
+      return;
+    }
+
+    //
+    // ReadOnly address in page table.
+    //
+    if ((Cr0.Bits.WP != 0) && (Map[Index].Attribute.Bits.ReadWrite == 0)) {
+      *PFAddress = (UINTN)Map[Index].LinearAddress;
+      return;
+    }
+  }
+}
+
+/**
+  Test if exception handler can registered/unregistered for GP and PF.
+
+  @param[in]  Context    [Optional] An optional parameter that enables:
+                         1) test-case reuse with varied parameters and
+                         2) test-case re-entry for Target tests that need a
+                         reboot.  This parameter is a VOID* and it is the
+                         responsibility of the test author to ensure that the
+                         contents are well understood by all test cases that may
+                         consume it.
+
+  @retval  UNIT_TEST_PASSED             The Unit test has completed and the test
+                                        case was successful.
+  @retval  UNIT_TEST_ERROR_TEST_FAILED  A test case assertion has failed.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+TestRegisterHandlerForGPAndPF (
+  IN UNIT_TEST_CONTEXT  Context
+  )
+{
+  EFI_STATUS           Status;
+  CPU_REGISTER_BUFFER  *CpuOriginalRegisterBuffer;
+  UINTN                PFAddress;
+  VOID                 *NewIdtr;
+
+  PFAddress                 = 0;
+  CpuOriginalRegisterBuffer = SaveAllCpuRegisters (NULL, 0);
+  NewIdtr                   = InitializeBspIdt ();
+  Status                    = InitializeCpuExceptionHandlers (NULL);
+
+  UT_ASSERT_EQUAL (Status, EFI_SUCCESS);
+
+  //
+  // GP exception.
+  //
+  DEBUG ((DEBUG_INFO, "TestCase2: ExceptionType is %d\n", EXCEPT_IA32_GP_FAULT));
+  Status = RegisterCpuInterruptHandler (EXCEPT_IA32_GP_FAULT, AdjustRipForFaultHandler);
+  UT_ASSERT_EQUAL (Status, EFI_SUCCESS);
+
+  TriggerGPException (CR4_RESERVED_BIT);
+  UT_ASSERT_EQUAL (mExceptionType, EXCEPT_IA32_GP_FAULT);
+  Status = RegisterCpuInterruptHandler (EXCEPT_IA32_GP_FAULT, NULL);
+  UT_ASSERT_EQUAL (Status, EFI_SUCCESS);
+
+  //
+  // PF exception.
+  //
+  PageFaultAddressInPageTable (&PFAddress);
+
+  if (PFAddress > 0) {
+    DEBUG ((DEBUG_INFO, "TestCase2: ExceptionType is %d\n", EXCEPT_IA32_PAGE_FAULT));
+    Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, AdjustRipForFaultHandler);
+    UT_ASSERT_EQUAL (Status, EFI_SUCCESS);
+    TriggerPFException (PFAddress);
+
+    UT_ASSERT_EQUAL (mExceptionType, EXCEPT_IA32_PAGE_FAULT);
+    Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, NULL);
+    UT_ASSERT_EQUAL (Status, EFI_SUCCESS);
+  }
+
+  RestoreAllCpuRegisters (NULL, CpuOriginalRegisterBuffer, 0);
+  FreePool (CpuOriginalRegisterBuffer);
+  FreePool (NewIdtr);
+  return UNIT_TEST_PASSED;
+}
+
+/**
+  Test if Cpu Context is consistent before and after exception.
+
+  @param[in]  Context    [Optional] An optional parameter that enables:
+                         1) test-case reuse with varied parameters and
+                         2) test-case re-entry for Target tests that need a
+                         reboot.  This parameter is a VOID* and it is the
+                         responsibility of the test author to ensure that the
+                         contents are well understood by all test cases that may
+                         consume it.
+
+  @retval  UNIT_TEST_PASSED             The Unit test has completed and the test
+                                        case was successful.
+  @retval  UNIT_TEST_ERROR_TEST_FAILED  A test case assertion has failed.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+TestCpuContextConsistency (
+  IN UNIT_TEST_CONTEXT  Context
+  )
+{
+  EFI_STATUS           Status;
+  UINTN                Index;
+  CPU_REGISTER_BUFFER  *CpuOriginalRegisterBuffer;
+  UINTN                FaultParameter;
+  VOID                 *NewIdtr;
+
+  FaultParameter            = 0;
+  CpuOriginalRegisterBuffer = SaveAllCpuRegisters (NULL, 0);
+  NewIdtr                   = InitializeBspIdt ();
+  Status                    = InitializeCpuExceptionHandlers (NULL);
+
+  UT_ASSERT_EQUAL (Status, EFI_SUCCESS);
+
+  for (Index = 0; Index < 22; Index++) {
+    if (Index == EXCEPT_IA32_PAGE_FAULT) {
+      PageFaultAddressInPageTable (&FaultParameter);
+      if (FaultParameter == 0) {
+        continue;
+      }
+    } else if (Index == EXCEPT_IA32_GP_FAULT) {
+      FaultParameter = CR4_RESERVED_BIT;
+    } else {
+      if ((mErrorCodeExceptionFlag & (1 << Index)) != 0) {
+        continue;
+      }
+    }
+
+    DEBUG ((DEBUG_INFO, "TestCase3: ExceptionType is %d\n", Index));
+    Status = RegisterCpuInterruptHandler (Index, AdjustCpuContextHandler);
+    UT_ASSERT_EQUAL (Status, EFI_SUCCESS);
+
+    //
+    // Trigger different type exception and compare different stage cpu context.
+    //
+    AsmTestConsistencyOfCpuContext (Index, FaultParameter);
+    CompareCpuContext ();
+    Status = RegisterCpuInterruptHandler (Index, NULL);
+    UT_ASSERT_EQUAL (Status, EFI_SUCCESS);
+  }
+
+  RestoreAllCpuRegisters (NULL, CpuOriginalRegisterBuffer, 0);
+  FreePool (CpuOriginalRegisterBuffer);
+  FreePool (NewIdtr);
+  return UNIT_TEST_PASSED;
+}
+
+/**
+  Initializes CPU exceptions handlers for the sake of stack switch requirement.
+
+  This function is a wrapper of InitializeSeparateExceptionStacks. It's mainly
+  for the sake of AP's init because of EFI_AP_PROCEDURE API requirement.
+
+  @param[in,out] Buffer  The pointer to private data buffer.
+
+**/
+VOID
+EFIAPI
+InitializeExceptionStackSwitchHandlersPerAp (
+  IN OUT VOID  *Buffer
+  )
+{
+  EXCEPTION_STACK_SWITCH_CONTEXT  *CpuSwitchStackData;
+
+  CpuSwitchStackData = (EXCEPTION_STACK_SWITCH_CONTEXT *)Buffer;
+
+  //
+  // This may be called twice for each Cpu. Only run InitializeSeparateExceptionStacks
+  // if this is the first call or the first call failed because of size too small.
+  //
+  if ((CpuSwitchStackData->Status == EFI_NOT_STARTED) || (CpuSwitchStackData->Status == EFI_BUFFER_TOO_SMALL)) {
+    CpuSwitchStackData->Status = InitializeSeparateExceptionStacks (CpuSwitchStackData->Buffer, &CpuSwitchStackData->BufferSize);
+  }
+}
+
+/**
+  Initializes MP exceptions handlers for the sake of stack switch requirement.
+
+  This function will allocate required resources required to setup stack switch
+  and pass them through SwitchStackData to each logic processor.
+
+  @param[in, out] MpServices       MpServices.
+  @param[in, out] BspProcessorNum  Bsp processor number.
+
+  @return Pointer to the allocated SwitchStackData.
+**/
+EXCEPTION_STACK_SWITCH_CONTEXT  *
+InitializeMpExceptionStackSwitchHandlers (
+  MP_SERVICES  MpServices,
+  UINTN        BspProcessorNum
+  )
+{
+  UINTN                           Index;
+  EXCEPTION_STACK_SWITCH_CONTEXT  *SwitchStackData;
+  EXCEPTION_STACK_SWITCH_CONTEXT  *CpuSwitchStackData;
+  UINTN                           BufferSize;
+  EFI_STATUS                      Status;
+  UINT8                           *Buffer;
+
+  SwitchStackData = AllocateZeroPool (mNumberOfProcessors * sizeof (EXCEPTION_STACK_SWITCH_CONTEXT));
+  ASSERT (SwitchStackData != NULL);
+  for (Index = 0; Index < mNumberOfProcessors; ++Index) {
+    CpuSwitchStackData = SwitchStackData + Index;
+    //
+    // Because the procedure may runs multiple times, use the status EFI_NOT_STARTED
+    // to indicate the procedure haven't been run yet.
+    //
+    SwitchStackData[Index].Status = EFI_NOT_STARTED;
+    if (Index == BspProcessorNum) {
+      InitializeExceptionStackSwitchHandlersPerAp ((VOID *)CpuSwitchStackData);
+      continue;
+    }
+
+    Status = MpServicesUnitTestStartupThisAP (
+               MpServices,
+               InitializeExceptionStackSwitchHandlersPerAp,
+               Index,
+               0,
+               (VOID *)CpuSwitchStackData
+               );
+    ASSERT_EFI_ERROR (Status);
+  }
+
+  BufferSize = 0;
+  for (Index = 0; Index < mNumberOfProcessors; ++Index) {
+    if (SwitchStackData[Index].Status == EFI_BUFFER_TOO_SMALL) {
+      ASSERT (SwitchStackData[Index].BufferSize != 0);
+      BufferSize += SwitchStackData[Index].BufferSize;
+    } else {
+      ASSERT (SwitchStackData[Index].Status == EFI_SUCCESS);
+      ASSERT (SwitchStackData[Index].BufferSize == 0);
+    }
+  }
+
+  if (BufferSize != 0) {
+    Buffer = AllocateZeroPool (BufferSize);
+    ASSERT (Buffer != NULL);
+    BufferSize = 0;
+    for (Index = 0; Index < mNumberOfProcessors; ++Index) {
+      if (SwitchStackData[Index].Status == EFI_BUFFER_TOO_SMALL) {
+        SwitchStackData[Index].Buffer = (VOID *)(&Buffer[BufferSize]);
+        BufferSize                   += SwitchStackData[Index].BufferSize;
+        DEBUG ((
+          DEBUG_INFO,
+          "Buffer[cpu%lu] for InitializeExceptionStackSwitchHandlersPerAp: 0x%lX with size 0x%lX\n",
+          (UINT64)(UINTN)Index,
+          (UINT64)(UINTN)SwitchStackData[Index].Buffer,
+          (UINT64)(UINTN)SwitchStackData[Index].BufferSize
+          ));
+      }
+    }
+
+    for (Index = 0; Index < mNumberOfProcessors; ++Index) {
+      CpuSwitchStackData = SwitchStackData + Index;
+      if (Index == BspProcessorNum) {
+        InitializeExceptionStackSwitchHandlersPerAp ((VOID *)CpuSwitchStackData);
+        continue;
+      }
+
+      Status = MpServicesUnitTestStartupThisAP (
+                 MpServices,
+                 InitializeExceptionStackSwitchHandlersPerAp,
+                 Index,
+                 0,
+                 (VOID *)CpuSwitchStackData
+                 );
+      ASSERT_EFI_ERROR (Status);
+    }
+
+    for (Index = 0; Index < mNumberOfProcessors; ++Index) {
+      ASSERT (SwitchStackData[Index].Status == EFI_SUCCESS);
+    }
+  }
+
+  return SwitchStackData;
+}
+
+/**
+  Test if stack overflow is captured by CpuStackGuard in Bsp and AP.
+
+  @param[in]  Context    [Optional] An optional parameter that enables:
+                         1) test-case reuse with varied parameters and
+                         2) test-case re-entry for Target tests that need a
+                         reboot.  This parameter is a VOID* and it is the
+                         responsibility of the test author to ensure that the
+                         contents are well understood by all test cases that may
+                         consume it.
+
+  @retval  UNIT_TEST_PASSED             The Unit test has completed and the test
+                                        case was successful.
+  @retval  UNIT_TEST_ERROR_TEST_FAILED  A test case assertion has failed.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+TestCpuStackGuardInBspAndAp (
+  IN UNIT_TEST_CONTEXT  Context
+  )
+{
+  EFI_STATUS                      Status;
+  UINTN                           OriginalStackBase;
+  UINTN                           NewStackTop;
+  UINTN                           NewStackBase;
+  EXCEPTION_STACK_SWITCH_CONTEXT  *SwitchStackData;
+  MP_SERVICES                     MpServices;
+  UINTN                           ProcessorNumber;
+  UINTN                           EnabledProcessorNum;
+  CPU_REGISTER_BUFFER             *CpuOriginalRegisterBuffer;
+  UINTN                           Index;
+  UINTN                           BspProcessorNum;
+  VOID                            *NewIdtr;
+  UINTN                           *CpuStackBaseBuffer;
+
+  if (!PcdGetBool (PcdCpuStackGuard)) {
+    return UNIT_TEST_PASSED;
+  }
+
+  //
+  // Get MP Service Protocol
+  //
+  Status = GetMpServices (&MpServices);
+  Status = MpServicesUnitTestGetNumberOfProcessors (MpServices, &ProcessorNumber, &EnabledProcessorNum);
+  UT_ASSERT_EQUAL (Status, EFI_SUCCESS);
+  Status = MpServicesUnitTestWhoAmI (MpServices, &BspProcessorNum);
+  UT_ASSERT_EQUAL (Status, EFI_SUCCESS);
+  mNumberOfProcessors = ProcessorNumber;
+
+  CpuOriginalRegisterBuffer = SaveAllCpuRegisters (&MpServices, BspProcessorNum);
+
+  //
+  // Initialize Bsp and AP Idt.
+  // Idt buffer should not be empty or it will hang in MP API.
+  //
+  NewIdtr = InitializeBspIdt ();
+  Status  = InitializeCpuExceptionHandlers (NULL);
+  UT_ASSERT_EQUAL (Status, EFI_SUCCESS);
+  InitializeApIdt (MpServices, NewIdtr);
+
+  //
+  // Get BSP and AP original stack base.
+  //
+  CpuStackBaseBuffer = GetAllCpuStackBase (&MpServices, BspProcessorNum);
+
+  //
+  // InitializeMpExceptionStackSwitchHandlers and register exception handler.
+  //
+  SwitchStackData = InitializeMpExceptionStackSwitchHandlers (MpServices, BspProcessorNum);
+  Status          = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, CpuStackGuardExceptionHandler);
+  UT_ASSERT_EQUAL (Status, EFI_SUCCESS);
+  Status = RegisterCpuInterruptHandler (EXCEPT_IA32_DOUBLE_FAULT, AdjustRipForFaultHandler);
+  UT_ASSERT_EQUAL (Status, EFI_SUCCESS);
+
+  for (Index = 0; Index < mNumberOfProcessors; Index++) {
+    OriginalStackBase = CpuStackBaseBuffer[Index];
+    NewStackTop       = (UINTN)(SwitchStackData[Index].Buffer) + SwitchStackData[Index].BufferSize;
+    NewStackBase      = (UINTN)(SwitchStackData[Index].Buffer);
+    if (Index == BspProcessorNum) {
+      TriggerStackOverflowbyCpuStackGuard ();
+    } else {
+      MpServicesUnitTestStartupThisAP (
+        MpServices,
+        (EFI_AP_PROCEDURE)TriggerStackOverflowbyCpuStackGuard,
+        Index,
+        0,
+        NULL
+        );
+    }
+
+    DEBUG ((DEBUG_INFO, "TestCase4: mRspAddress[0] is 0x%x, mRspAddress[1] is 0x%x\n", mRspAddress[0], mRspAddress[1]));
+    UT_ASSERT_TRUE ((mRspAddress[0] >= OriginalStackBase) && (mRspAddress[0] <= (OriginalStackBase + SIZE_4KB)));
+    UT_ASSERT_TRUE ((mRspAddress[1] >= NewStackBase) && (mRspAddress[1] < NewStackTop));
+  }
+
+  Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, NULL);
+  UT_ASSERT_EQUAL (Status, EFI_SUCCESS);
+  Status = RegisterCpuInterruptHandler (EXCEPT_IA32_DOUBLE_FAULT, NULL);
+  UT_ASSERT_EQUAL (Status, EFI_SUCCESS);
+  RestoreAllCpuRegisters (&MpServices, CpuOriginalRegisterBuffer, BspProcessorNum);
+  FreePool (SwitchStackData);
+  FreePool (CpuOriginalRegisterBuffer);
+  FreePool (NewIdtr);
+
+  return UNIT_TEST_PASSED;
+}
+
+/**
+  Create CpuExceptionLibUnitTestSuite and add test case.
+
+  @param[in]  FrameworkHandle    Unit test framework.
+
+  @return  EFI_SUCCESS           The unit test suite was created.
+  @retval  EFI_OUT_OF_RESOURCES  There are not enough resources available to
+                                 initialize the unit test suite.
+**/
+EFI_STATUS
+AddCommonTestCase (
+  IN  UNIT_TEST_FRAMEWORK_HANDLE  Framework
+  )
+{
+  EFI_STATUS              Status;
+  UNIT_TEST_SUITE_HANDLE  CpuExceptionLibUnitTestSuite;
+
+  //
+  // Populate the Manual Test Cases.
+  //
+  Status = CreateUnitTestSuite (&CpuExceptionLibUnitTestSuite, Framework, "Test CpuExceptionHandlerLib", "CpuExceptionHandlerLib.Manual", NULL, NULL);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for CpuExceptionHandlerLib Test Cases\n"));
+    Status = EFI_OUT_OF_RESOURCES;
+    return Status;
+  }
+
+  AddTestCase (CpuExceptionLibUnitTestSuite, "Check if exception handler can be registered/unregistered for no error code exception", "TestRegisterHandlerForNoErrorCodeException", TestRegisterHandlerForNoErrorCodeException, NULL, NULL, NULL);
+  AddTestCase (CpuExceptionLibUnitTestSuite, "Check if exception handler can be registered/unregistered for GP and PF", "TestRegisterHandlerForGPAndPF", TestRegisterHandlerForGPAndPF, NULL, NULL, NULL);
+
+  AddTestCase (CpuExceptionLibUnitTestSuite, "Check if Cpu Context is consistent before and after exception.", "TestCpuContextConsistency", TestCpuContextConsistency, NULL, NULL, NULL);
+  AddTestCase (CpuExceptionLibUnitTestSuite, "Check if stack overflow is captured by CpuStackGuard in Bsp and AP", "TestCpuStackGuardInBspAndAp", TestCpuStackGuardInBspAndAp, NULL, NULL, NULL);
+
+  return EFI_SUCCESS;
+}
diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibUnitTest.inf b/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibUnitTest.inf
new file mode 100644
index 0000000000..e3dbe7b9ab
--- /dev/null
+++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibUnitTest.inf
@@ -0,0 +1,58 @@
+## @file
+# Unit tests of the DxeCpuExceptionHandlerLib instance.
+#
+# Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = CpuExceptionHandlerDxeTest
+  FILE_GUID                      = D76BFD9C-0B6D-46BD-AD66-2BBB6FA7031A
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = CpuExceptionHandlerTestEntry
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = X64
+#
+[Sources.X64]
+  X64/ArchExceptionHandlerTestAsm.nasm
+  X64/ArchExceptionHandlerTest.c
+
+[Sources.common]
+  CpuExceptionHandlerTest.h
+  CpuExceptionHandlerTestCommon.c
+  DxeCpuExceptionHandlerUnitTest.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  UefiCpuPkg/UefiCpuPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  DebugLib
+  UnitTestLib
+  MemoryAllocationLib
+  CpuExceptionHandlerLib
+  UefiDriverEntryPoint
+  HobLib
+  UefiBootServicesTableLib
+  CpuPageTableLib
+
+[Guids]
+  gEfiHobMemoryAllocStackGuid
+
+[Pcd]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard       ## CONSUMES
+  gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize           ## CONSUMES
+
+[Protocols]
+  gEfiMpServiceProtocolGuid
+
+[Depex]
+  gEfiMpServiceProtocolGuid
diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnitTest.c b/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnitTest.c
new file mode 100644
index 0000000000..917fc549bf
--- /dev/null
+++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerUnitTest.c
@@ -0,0 +1,196 @@
+/** @file
+  Unit tests of the CpuExceptionHandlerLib.
+
+  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "CpuExceptionHandlerTest.h"
+#include <Library/UefiBootServicesTableLib.h>
+
+/**
+  Initialize Bsp Idt with a new Idt table and return the IA32_DESCRIPTOR buffer.
+  In PEIM, store original PeiServicePointer before new Idt table.
+
+  @return Pointer to the allocated IA32_DESCRIPTOR buffer.
+**/
+VOID *
+InitializeBspIdt (
+  VOID
+  )
+{
+  UINTN            *NewIdtTable;
+  IA32_DESCRIPTOR  *Idtr;
+
+  Idtr = AllocateZeroPool (sizeof (IA32_DESCRIPTOR));
+  ASSERT (Idtr != NULL);
+  NewIdtTable = AllocateZeroPool (sizeof (IA32_IDT_GATE_DESCRIPTOR) * CPU_INTERRUPT_NUM);
+  ASSERT (NewIdtTable != NULL);
+  Idtr->Base  = (UINTN)NewIdtTable;
+  Idtr->Limit = (UINT16)(sizeof (IA32_IDT_GATE_DESCRIPTOR) * CPU_INTERRUPT_NUM - 1);
+
+  AsmWriteIdtr (Idtr);
+  return Idtr;
+}
+
+/**
+  Retrieve the number of logical processor in the platform and the number of those logical processors that
+  are enabled on this boot.
+
+  @param[in]  MpServices          MP_SERVICES structure.
+  @param[out] NumberOfProcessors  Pointer to the total number of logical processors in the system, including
+                                  the BSP and disabled APs.
+  @param[out] NumberOfEnabledProcessors Pointer to the number of processors in the system that are enabled.
+
+  @retval EFI_SUCCESS       Retrieve the number of logical processor successfully
+  @retval Others            Retrieve the number of logical processor unsuccessfully
+**/
+EFI_STATUS
+MpServicesUnitTestGetNumberOfProcessors (
+  IN MP_SERVICES  MpServices,
+  OUT UINTN       *NumberOfProcessors,
+  OUT UINTN       *NumberOfEnabledProcessors
+  )
+{
+  return MpServices.Protocol->GetNumberOfProcessors (MpServices.Protocol, NumberOfProcessors, NumberOfEnabledProcessors);
+}
+
+/**
+  Get the handle number for the calling processor.
+
+  @param[in]  MpServices      MP_SERVICES structure.
+  @param[out] ProcessorNumber The handle number for the calling processor.
+
+  @retval EFI_SUCCESS       Get the handle number for the calling processor successfully.
+  @retval Others            Get the handle number for the calling processor unsuccessfully.
+**/
+EFI_STATUS
+MpServicesUnitTestWhoAmI (
+  IN MP_SERVICES  MpServices,
+  OUT UINTN       *ProcessorNumber
+  )
+{
+  return MpServices.Protocol->WhoAmI (MpServices.Protocol, ProcessorNumber);
+}
+
+/**
+  Caller gets one enabled AP to execute a caller-provided function.
+
+  @param[in]  MpServices    MP_SERVICES structure.
+  @param[in]  Procedure     Pointer to the function to be run on enabled APs of the system.
+  @param[in]  ProcessorNumber       The handle number of the AP.
+  @param[in]  TimeoutInMicroSeconds Indicates the time limit in microseconds for APs to return from Procedure,
+                                    for blocking mode only. Zero means infinity.
+  @param[in]  ProcedureArgument     The parameter passed into Procedure for all APs.
+
+
+  @retval EFI_SUCCESS       Caller gets one enabled AP to execute a caller-provided function successfully
+  @retval Others            Caller gets one enabled AP to execute a caller-provided function unsuccessfully
+**/
+EFI_STATUS
+MpServicesUnitTestStartupThisAP (
+  IN MP_SERVICES       MpServices,
+  IN EFI_AP_PROCEDURE  Procedure,
+  IN UINTN             ProcessorNumber,
+  IN UINTN             TimeoutInMicroSeconds,
+  IN VOID              *ProcedureArgument
+  )
+{
+  return MpServices.Protocol->StartupThisAP (MpServices.Protocol, Procedure, ProcessorNumber, NULL, TimeoutInMicroSeconds, ProcedureArgument, NULL);
+}
+
+/**
+  Execute a caller provided function on all enabled APs.
+
+  @param[in]  MpServices    MP_SERVICES structure.
+  @param[in]  Procedure     Pointer to the function to be run on enabled APs of the system.
+  @param[in]  SingleThread  If TRUE, then all the enabled APs execute the function specified by Procedure
+                            one by one, in ascending order of processor handle number.
+                            If FALSE, then all the enabled APs execute the function specified by Procedure
+                            simultaneously.
+  @param[in]  TimeoutInMicroSeconds Indicates the time limit in microseconds for APs to return from Procedure,
+                                    for blocking mode only. Zero means infinity.
+  @param[in]  ProcedureArgument     The parameter passed into Procedure for all APs.
+
+  @retval EFI_SUCCESS       Execute a caller provided function on all enabled APs successfully
+  @retval Others            Execute a caller provided function on all enabled APs unsuccessfully
+**/
+EFI_STATUS
+MpServicesUnitTestStartupAllAPs (
+  IN MP_SERVICES       MpServices,
+  IN EFI_AP_PROCEDURE  Procedure,
+  IN BOOLEAN           SingleThread,
+  IN UINTN             TimeoutInMicroSeconds,
+  IN VOID              *ProcedureArgument
+  )
+{
+  return MpServices.Protocol->StartupAllAPs (MpServices.Protocol, Procedure, SingleThread, NULL, TimeoutInMicroSeconds, ProcedureArgument, NULL);
+}
+
+/**
+  Get EFI_MP_SERVICES_PROTOCOL pointer.
+
+  @param[out] MpServices    Pointer to the buffer where EFI_MP_SERVICES_PROTOCOL is stored
+
+  @retval EFI_SUCCESS       EFI_MP_SERVICES_PROTOCOL interface is returned
+  @retval EFI_NOT_FOUND     EFI_MP_SERVICES_PROTOCOL interface is not found
+**/
+EFI_STATUS
+GetMpServices (
+  OUT MP_SERVICES  *MpServices
+  )
+{
+  return gBS->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **)&MpServices->Protocol);
+}
+
+/**
+  Entry for CpuExceptionHandlerDxeTest driver.
+
+  @param ImageHandle     Image handle this driver.
+  @param SystemTable     Pointer to the System Table.
+
+  @retval EFI_SUCCESS    The driver executed normally.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuExceptionHandlerTestEntry (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  EFI_STATUS                  Status;
+  UNIT_TEST_FRAMEWORK_HANDLE  Framework;
+
+  Framework = NULL;
+
+  DEBUG ((DEBUG_INFO, "%a v%a\n", UNIT_TEST_APP_NAME, UNIT_TEST_APP_VERSION));
+
+  //
+  // Start setting up the test framework for running the tests.
+  //
+  Status = InitUnitTestFramework (&Framework, UNIT_TEST_APP_NAME, gEfiCallerBaseName, UNIT_TEST_APP_VERSION);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status));
+    goto EXIT;
+  }
+
+  Status = AddCommonTestCase (Framework);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Failed in AddCommonTestCase. Status = %r\n", Status));
+    goto EXIT;
+  }
+
+  //
+  // Execute the tests.
+  //
+  Status = RunAllTestSuites (Framework);
+
+EXIT:
+  if (Framework) {
+    FreeUnitTestFramework (Framework);
+  }
+
+  return Status;
+}
diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTest.c b/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTest.c
new file mode 100644
index 0000000000..9b086622f2
--- /dev/null
+++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTest.c
@@ -0,0 +1,167 @@
+/** @file
+  Unit tests of the CpuExceptionHandlerLib.
+
+  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "CpuExceptionHandlerTest.h"
+
+GENERAL_REGISTER  mRegisterForCheck[2];
+
+//
+// In TestCpuContextConsistency, Cpu registers will be set to mAdjustRegisterBeforeException.
+// Rcx in mAdjustRegisterInsideException is set runtime since Rcx is needed in assembly code.
+// For GP and PF, Rcx is set to FaultParameter. For other exception triggered by INTn, Rcx is set to ExceptionType.
+//
+GENERAL_REGISTER  mAdjustRegisterBeforeException = { 1, 2, 3, 4, 5, 0, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf };
+GENERAL_REGISTER  mAdjustRegisterInsideException = { 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f };
+
+/**
+  Special handler for fault exception.
+  Rip/Eip in SystemContext will be modified to the instruction after the exception instruction.
+
+  @param ExceptionType  Exception type.
+  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+VOID
+EFIAPI
+AdjustRipForFaultHandler (
+  IN EFI_EXCEPTION_TYPE  ExceptionType,
+  IN EFI_SYSTEM_CONTEXT  SystemContext
+  )
+{
+  mExceptionType = ExceptionType;
+  SystemContext.SystemContextX64->Rip += mFaultInstructionLength;
+}
+
+/**
+  Special handler for ConsistencyOfCpuContext test case.
+
+  @param ExceptionType  Exception type.
+  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+VOID
+EFIAPI
+AdjustCpuContextHandler (
+  IN EFI_EXCEPTION_TYPE  ExceptionType,
+  IN EFI_SYSTEM_CONTEXT  SystemContext
+  )
+{
+  //
+  // Do not handle Esp and ebp. They will not be restored.
+  // Store SystemContext in mRegisterForCheck[0] modified before exception.
+  //
+  mRegisterForCheck[0].Rdi         = SystemContext.SystemContextX64->Rdi;
+  mRegisterForCheck[0].Rsi         = SystemContext.SystemContextX64->Rsi;
+  mRegisterForCheck[0].Rbx         = SystemContext.SystemContextX64->Rbx;
+  mRegisterForCheck[0].Rdx         = SystemContext.SystemContextX64->Rdx;
+  mRegisterForCheck[0].Rcx         = SystemContext.SystemContextX64->Rcx;
+  mRegisterForCheck[0].Rax         = SystemContext.SystemContextX64->Rax;
+  mRegisterForCheck[0].R8Register  = SystemContext.SystemContextX64->R8;
+  mRegisterForCheck[0].R9Register  = SystemContext.SystemContextX64->R9;
+  mRegisterForCheck[0].R10Register = SystemContext.SystemContextX64->R10;
+  mRegisterForCheck[0].R11Register = SystemContext.SystemContextX64->R11;
+  mRegisterForCheck[0].R12Register = SystemContext.SystemContextX64->R12;
+  mRegisterForCheck[0].R13Register = SystemContext.SystemContextX64->R13;
+  mRegisterForCheck[0].R14Register = SystemContext.SystemContextX64->R14;
+  mRegisterForCheck[0].R15Register = SystemContext.SystemContextX64->R15;
+
+  //
+  // Modify cpu context which will be stored in mRegisterForCheck[1].
+  //
+  SystemContext.SystemContextX64->Rdi = mAdjustRegisterInsideException.Rdi;
+  SystemContext.SystemContextX64->Rsi = mAdjustRegisterInsideException.Rsi;
+  SystemContext.SystemContextX64->Rbx = mAdjustRegisterInsideException.Rbx;
+  SystemContext.SystemContextX64->Rdx = mAdjustRegisterInsideException.Rdx;
+  SystemContext.SystemContextX64->Rcx = mAdjustRegisterInsideException.Rcx;
+  SystemContext.SystemContextX64->Rax = mAdjustRegisterInsideException.Rax;
+  SystemContext.SystemContextX64->R8  = mAdjustRegisterInsideException.R8Register;
+  SystemContext.SystemContextX64->R9  = mAdjustRegisterInsideException.R9Register;
+  SystemContext.SystemContextX64->R10 = mAdjustRegisterInsideException.R10Register;
+  SystemContext.SystemContextX64->R11 = mAdjustRegisterInsideException.R11Register;
+  SystemContext.SystemContextX64->R12 = mAdjustRegisterInsideException.R12Register;
+  SystemContext.SystemContextX64->R13 = mAdjustRegisterInsideException.R13Register;
+  SystemContext.SystemContextX64->R14 = mAdjustRegisterInsideException.R14Register;
+  SystemContext.SystemContextX64->R15 = mAdjustRegisterInsideException.R15Register;
+
+  //
+  // When fault exception happens, eip/rip points to the faulting instruction.
+  // For now, olny GP and PF are tested in fault exception.
+  //
+  if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) || (ExceptionType == EXCEPT_IA32_GP_FAULT)) {
+    AdjustRipForFaultHandler (ExceptionType, SystemContext);
+  }
+}
+
+/**
+  Compare cpu context in ConsistencyOfCpuContext test case.
+  1.Compare mRegisterForCheck[0] with mAdjustRegisterBeforeException.
+  2.Compare mRegisterForCheck[1] with mAdjustRegisterAfterException.
+
+  @retval  UNIT_TEST_PASSED             The Unit test has completed and it was successful.
+  @retval  UNIT_TEST_ERROR_TEST_FAILED  A test case assertion has failed.
+**/
+UNIT_TEST_STATUS
+CompareCpuContext (
+  VOID
+  )
+{
+  //
+  // Do not handle Esp and ebp. They will not be restored.
+  //
+  UT_ASSERT_EQUAL (mRegisterForCheck[0].Rdi, mAdjustRegisterBeforeException.Rdi);
+  UT_ASSERT_EQUAL (mRegisterForCheck[0].Rsi, mAdjustRegisterBeforeException.Rsi);
+  UT_ASSERT_EQUAL (mRegisterForCheck[0].Rbx, mAdjustRegisterBeforeException.Rbx);
+  UT_ASSERT_EQUAL (mRegisterForCheck[0].Rdx, mAdjustRegisterBeforeException.Rdx);
+  UT_ASSERT_EQUAL (mRegisterForCheck[0].Rcx, mAdjustRegisterBeforeException.Rcx);
+  UT_ASSERT_EQUAL (mRegisterForCheck[0].Rax, mAdjustRegisterBeforeException.Rax);
+  UT_ASSERT_EQUAL (mRegisterForCheck[0].R8Register, mAdjustRegisterBeforeException.R8Register);
+  UT_ASSERT_EQUAL (mRegisterForCheck[0].R9Register, mAdjustRegisterBeforeException.R9Register);
+  UT_ASSERT_EQUAL (mRegisterForCheck[0].R10Register, mAdjustRegisterBeforeException.R10Register);
+  UT_ASSERT_EQUAL (mRegisterForCheck[0].R11Register, mAdjustRegisterBeforeException.R11Register);
+  UT_ASSERT_EQUAL (mRegisterForCheck[0].R12Register, mAdjustRegisterBeforeException.R12Register);
+  UT_ASSERT_EQUAL (mRegisterForCheck[0].R13Register, mAdjustRegisterBeforeException.R13Register);
+  UT_ASSERT_EQUAL (mRegisterForCheck[0].R14Register, mAdjustRegisterBeforeException.R14Register);
+  UT_ASSERT_EQUAL (mRegisterForCheck[0].R15Register, mAdjustRegisterBeforeException.R15Register);
+
+  UT_ASSERT_EQUAL (mRegisterForCheck[1].Rdi, mAdjustRegisterInsideException.Rdi);
+  UT_ASSERT_EQUAL (mRegisterForCheck[1].Rsi, mAdjustRegisterInsideException.Rsi);
+  UT_ASSERT_EQUAL (mRegisterForCheck[1].Rbx, mAdjustRegisterInsideException.Rbx);
+  UT_ASSERT_EQUAL (mRegisterForCheck[1].Rdx, mAdjustRegisterInsideException.Rdx);
+  UT_ASSERT_EQUAL (mRegisterForCheck[1].Rcx, mAdjustRegisterInsideException.Rcx);
+  UT_ASSERT_EQUAL (mRegisterForCheck[1].Rax, mAdjustRegisterInsideException.Rax);
+  UT_ASSERT_EQUAL (mRegisterForCheck[1].R8Register, mAdjustRegisterInsideException.R8Register);
+  UT_ASSERT_EQUAL (mRegisterForCheck[1].R9Register, mAdjustRegisterInsideException.R9Register);
+  UT_ASSERT_EQUAL (mRegisterForCheck[1].R10Register, mAdjustRegisterInsideException.R10Register);
+  UT_ASSERT_EQUAL (mRegisterForCheck[1].R11Register, mAdjustRegisterInsideException.R11Register);
+  UT_ASSERT_EQUAL (mRegisterForCheck[1].R12Register, mAdjustRegisterInsideException.R12Register);
+  UT_ASSERT_EQUAL (mRegisterForCheck[1].R13Register, mAdjustRegisterInsideException.R13Register);
+  UT_ASSERT_EQUAL (mRegisterForCheck[1].R14Register, mAdjustRegisterInsideException.R14Register);
+  UT_ASSERT_EQUAL (mRegisterForCheck[1].R15Register, mAdjustRegisterInsideException.R15Register);
+  return UNIT_TEST_PASSED;
+}
+
+/**
+  Special handler for CpuStackGuard test case.
+
+  @param ExceptionType  Exception type.
+  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+
+**/
+VOID
+EFIAPI
+CpuStackGuardExceptionHandler (
+  IN EFI_EXCEPTION_TYPE  ExceptionType,
+  IN EFI_SYSTEM_CONTEXT  SystemContext
+  )
+{
+  UINTN  LocalVariable;
+
+  AdjustRipForFaultHandler (ExceptionType, SystemContext);
+  mRspAddress[0] = (UINTN)SystemContext.SystemContextX64->Rsp;
+  mRspAddress[1] = (UINTN)(&LocalVariable);
+
+  return;
+}
diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTestAsm.nasm b/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTestAsm.nasm
new file mode 100644
index 0000000000..4838a12a67
--- /dev/null
+++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/X64/ArchExceptionHandlerTestAsm.nasm
@@ -0,0 +1,260 @@
+;------------------------------------------------------------------------------
+;
+; Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+; Module Name:
+;
+;   ArchExceptionHandlerTestAsm.nasm
+;
+; Abstract:
+;
+;   x64 CPU Exception Handler Lib Unit test
+;
+;------------------------------------------------------------------------------
+
+    DEFAULT REL
+    SECTION .text
+
+struc GENERAL_REGISTER
+  .Rdi:    resq    1
+  .Rsi:    resq    1
+  .Rbp:    resq    1
+  .Rbx:    resq    1
+  .Rdx:    resq    1
+  .Rcx:    resq    1
+  .Rax:    resq    1
+  .R8:     resq    1
+  .R9:     resq    1
+  .R10:    resq    1
+  .R11:    resq    1
+  .R12:    resq    1
+  .R13:    resq    1
+  .R14:    resq    1
+  .R15:    resq    1
+
+endstruc
+
+extern ASM_PFX(mRegisterForCheck)
+extern ASM_PFX(mAdjustRegisterBeforeException)
+extern ASM_PFX(mFaultInstructionLength)
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; TriggerGPException (
+;  UINTN  Cr4ReservedBit
+;  );
+;------------------------------------------------------------------------------
+global ASM_PFX(TriggerGPException)
+ASM_PFX(TriggerGPException):
+    ;
+    ; Set reserved bit 15 of cr4 to 1
+    ;
+    push rcx
+    lea  rcx, [ASM_PFX(mFaultInstructionLength)]
+    mov  qword[rcx], TriggerGPExceptionAfter - TriggerGPExceptionBefore
+    pop  rcx
+TriggerGPExceptionBefore:
+    mov  cr4, rcx
+TriggerGPExceptionAfter:
+    ret
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; TriggerPFException (
+;  UINTN  PFAddress
+;  );
+;------------------------------------------------------------------------------
+global ASM_PFX(TriggerPFException)
+ASM_PFX(TriggerPFException):
+    push rcx
+    lea  rcx, [ASM_PFX(mFaultInstructionLength)]
+    mov  qword[rcx], TriggerPFExceptionAfter - TriggerPFExceptionBefore
+    pop  rcx
+TriggerPFExceptionBefore:
+    mov  qword[rcx], 0x1
+TriggerPFExceptionAfter:
+    ret
+
+global ASM_PFX(ModifyRcxInGlobalBeforeException)
+ASM_PFX(ModifyRcxInGlobalBeforeException):
+    push rax
+    lea  rax, [ASM_PFX(mAdjustRegisterBeforeException)]
+    mov  [rax + GENERAL_REGISTER.Rcx], rcx
+    pop  rax
+    ret
+
+;------------------------------------------------------------------------------
+;VOID
+;EFIAPI
+;AsmTestConsistencyOfCpuContext (
+;  IN  EFI_EXCEPTION_TYPE ExceptionType
+;  IN  UINTN              FaultParameter   OPTIONAL
+;  );
+;------------------------------------------------------------------------------
+global ASM_PFX(AsmTestConsistencyOfCpuContext)
+ASM_PFX(AsmTestConsistencyOfCpuContext):
+    ;
+    ; push original register
+    ;
+    push r15
+    push r14
+    push r13
+    push r12
+    push r11
+    push r10
+    push r9
+    push r8
+    push rax
+    push rcx
+    push rbx
+    push rsi
+    push rdi
+    push rdx
+
+    ;
+    ; Modify register to mAdjustRegisterBeforeException
+    ;
+    lea r15, [ASM_PFX(mAdjustRegisterBeforeException)]
+    mov rdi, [r15 + GENERAL_REGISTER.Rdi]
+    mov rsi, [r15 + GENERAL_REGISTER.Rsi]
+    mov rbx, [r15 + GENERAL_REGISTER.Rbx]
+    mov rdx, [r15 + GENERAL_REGISTER.Rdx]
+    mov rax, [r15 + GENERAL_REGISTER.Rax]
+    mov r8,  [r15 + GENERAL_REGISTER.R8]
+    mov r9,  [r15 + GENERAL_REGISTER.R9]
+    mov r10, [r15 + GENERAL_REGISTER.R10]
+    mov r11, [r15 + GENERAL_REGISTER.R11]
+    mov r12, [r15 + GENERAL_REGISTER.R12]
+    mov r13, [r15 + GENERAL_REGISTER.R13]
+    mov r14, [r15 + GENERAL_REGISTER.R14]
+    mov r15, [r15 + GENERAL_REGISTER.R15]
+
+    cmp  rcx, 0xd
+    jz   GPException
+    cmp  rcx, 0xe
+    jz   PFException
+    jmp  INTnException
+
+PFException:
+    ;
+    ; Pop rdx(Pei StackBase) to rcx and restore stack.
+    ; Modify Rcx in mAdjustRegisterBeforeException.
+    ;
+    pop  rcx
+    push rcx
+    call ASM_PFX(ModifyRcxInGlobalBeforeException)
+    call ASM_PFX(TriggerPFException)
+    jmp  AfterException
+
+GPException:
+    ;
+    ; Prepare rcx value for GP.
+    ; Modify Rcx in mAdjustRegisterBeforeException.
+    ;
+    pop  rcx
+    push rcx
+    call ASM_PFX(ModifyRcxInGlobalBeforeException)
+    call ASM_PFX(TriggerGPException)
+    jmp  AfterException
+
+INTnException:
+    ;
+    ; Modify Rcx in mAdjustRegisterBeforeException.
+    ;
+    call ASM_PFX(ModifyRcxInGlobalBeforeException)
+    call ASM_PFX(TriggerINTnException)
+
+AfterException:
+    ;
+    ; Save register in mRegisterForCheck[1]
+    ;
+    push rax
+    lea  rax, [ASM_PFX(mRegisterForCheck)]
+    add  rax, GENERAL_REGISTER_size
+    mov  [rax + GENERAL_REGISTER.Rdi], rdi
+    mov  [rax + GENERAL_REGISTER.Rsi], rsi
+    mov  [rax + GENERAL_REGISTER.Rbx], rbx
+    mov  [rax + GENERAL_REGISTER.Rdx], rdx
+    mov  [rax + GENERAL_REGISTER.Rcx], rcx
+    pop  rcx
+    mov  [rax + GENERAL_REGISTER.Rax], rcx
+    mov  [rax + GENERAL_REGISTER.R8],  r8
+    mov  [rax + GENERAL_REGISTER.R9],  r9
+    mov  [rax + GENERAL_REGISTER.R10], r10
+    mov  [rax + GENERAL_REGISTER.R11], r11
+    mov  [rax + GENERAL_REGISTER.R12], r12
+    mov  [rax + GENERAL_REGISTER.R13], r13
+    mov  [rax + GENERAL_REGISTER.R14], r14
+    mov  [rax + GENERAL_REGISTER.R15], r15
+
+    ;
+    ; restore original register
+    ;
+    pop rdx
+    pop rdi
+    pop rsi
+    pop rbx
+    pop rcx
+    pop rax
+    pop r8
+    pop r9
+    pop r10
+    pop r11
+    pop r12
+    pop r13
+    pop r14
+    pop r15
+
+    ret
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; TriggerStackOverflowbyCpuStackGuard (
+;  VOID
+;  );
+;------------------------------------------------------------------------------
+global ASM_PFX(TriggerStackOverflowbyCpuStackGuard)
+ASM_PFX(TriggerStackOverflowbyCpuStackGuard):
+    push rcx
+    lea  rcx, [ASM_PFX(mFaultInstructionLength)]
+    mov  qword[rcx], TriggerCpuStackGuardAfter - TriggerCpuStackGuardBefore
+    pop  rcx
+TriggerCpuStackGuardBefore:
+    call TriggerCpuStackGuardBefore
+TriggerCpuStackGuardAfter:
+    ret
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; TriggerINTnException (
+;  IN  EFI_EXCEPTION_TYPE ExceptionType
+;  );
+;------------------------------------------------------------------------------
+global ASM_PFX(TriggerINTnException)
+ASM_PFX(TriggerINTnException):
+    push rax
+    push rdx
+    push rcx
+    lea  rax, [AsmTriggerException1 - AsmTriggerException0]
+    mul  rcx
+    mov  rcx, AsmTriggerException0
+    add  rax, rcx
+    pop  rcx
+    pop  rdx
+    jmp  rax
+    ;
+    ; rax = AsmTriggerException0 + (AsmTriggerException1 - AsmTriggerException0) * rcx
+    ;
+%assign Vector 0
+%rep  22
+AsmTriggerException %+ Vector:
+    pop rax
+    INT Vector
+    ret
+%assign Vector Vector+1
+%endrep
-- 
2.31.1.windows.1


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

* [PATCH 2/3] UefiCpuPkg: Add Unit tests for PeiCpuExceptionHandlerLib
  2022-10-11  6:03 [PATCH 0/3] Add Pei/DxeCpuExceptionHandlerLibUnitTest duntan
  2022-10-11  6:03 ` [PATCH 1/3] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib duntan
@ 2022-10-11  6:03 ` duntan
  2022-10-11  6:03 ` [PATCH 3/3] UefiCpuPkg: Add Pei/DxeCpuExceptionHandlerLibUnitTest in dsc duntan
  2 siblings, 0 replies; 6+ messages in thread
From: duntan @ 2022-10-11  6:03 UTC (permalink / raw)
  To: devel; +Cc: Eric Dong, Ray Ni, Rahul Kumar

Add target based unit tests for the PeiCpuExceptionHandlerLib.
A PEIM is created to test PeiCpuExceptionHandlerLib. Four kinds
of test cases are created in this module:
1.Test if exception handler can be registered/unregistered for
no error code exception. 2.Test if exception handler can be
registered/unregistered for GP and PF. 3.Test if Cpu Context is
consistent before and after exception. 4.Test if stack overflow
can be captured by CpuStackGuard in both Bsp and AP.

Signed-off-by: Dun Tan <dun.tan@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
---
 UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTest.c       | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTestAsm.nasm | 217 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerLibUnitTest.inf |  61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerUnitTest.c      | 204 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 615 insertions(+)

diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTest.c b/UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTest.c
new file mode 100644
index 0000000000..32188b9a81
--- /dev/null
+++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTest.c
@@ -0,0 +1,133 @@
+/** @file
+  Unit tests of the CpuExceptionHandlerLib.
+
+  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "CpuExceptionHandlerTest.h"
+
+GENERAL_REGISTER_IA32  mIa32RegisterForCheck[2];
+
+//
+// In TestCpuContextConsistency, Cpu registers will be set to mAdjustRegisterBeforeException/mAdjustRegisterInsideException.
+// Ecx in mAdjustRegisterInsideException is set runtime since Ecx is needed in assembly code.
+// For GP and PF, Ecx is set to FaultParameter. For other exception triggered by INTn, Ecx is set to ExceptionType.
+//
+GENERAL_REGISTER_IA32  mAdjustIa32RegisterBeforeException = { 1, 2, 3, 4, 5, 0, 7 };
+GENERAL_REGISTER_IA32  mAdjustIa32RegisterInsideException = { 11, 12, 13, 14, 15, 16, 17 };
+
+/**
+  Special handler for fault exception.
+  Rip/Eip in SystemContext will be modified to the instruction after the exception instruction.
+
+  @param ExceptionType  Exception type.
+  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+
+**/
+VOID
+EFIAPI
+AdjustRipForFaultHandler (
+  IN EFI_EXCEPTION_TYPE  ExceptionType,
+  IN EFI_SYSTEM_CONTEXT  SystemContext
+  )
+{
+  mExceptionType = ExceptionType;
+  SystemContext.SystemContextIa32->Eip += mFaultInstructionLength;
+}
+
+/**
+  Special handler for ConsistencyOfCpuContext test case.
+
+  @param ExceptionType  Exception type.
+  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+VOID
+EFIAPI
+AdjustCpuContextHandler (
+  IN EFI_EXCEPTION_TYPE  ExceptionType,
+  IN EFI_SYSTEM_CONTEXT  SystemContext
+  )
+{
+  //
+  // Do not handle Esp and ebp. They will not be restored.
+  // Store SystemContext modified before exception.
+  //
+  mIa32RegisterForCheck[0].Edi = SystemContext.SystemContextIa32->Edi;
+  mIa32RegisterForCheck[0].Esi = SystemContext.SystemContextIa32->Esi;
+  mIa32RegisterForCheck[0].Ebx = SystemContext.SystemContextIa32->Ebx;
+  mIa32RegisterForCheck[0].Edx = SystemContext.SystemContextIa32->Edx;
+  mIa32RegisterForCheck[0].Ecx = SystemContext.SystemContextIa32->Ecx;
+  mIa32RegisterForCheck[0].Eax = SystemContext.SystemContextIa32->Eax;
+
+  //
+  // Modify cpu context which will be stored in mIa32RegisterForCheck[1].
+  //
+  SystemContext.SystemContextIa32->Edi = mAdjustIa32RegisterInsideException.Edi;
+  SystemContext.SystemContextIa32->Esi = mAdjustIa32RegisterInsideException.Esi;
+  SystemContext.SystemContextIa32->Ebx = mAdjustIa32RegisterInsideException.Ebx;
+  SystemContext.SystemContextIa32->Edx = mAdjustIa32RegisterInsideException.Edx;
+  SystemContext.SystemContextIa32->Ecx = mAdjustIa32RegisterInsideException.Ecx;
+  SystemContext.SystemContextIa32->Eax = mAdjustIa32RegisterInsideException.Eax;
+
+  //
+  // When fault exception happens, eip/rip points to the faulting instruction.
+  // For now, olny GP and PF are tested in fault exception.
+  //
+  if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) || (ExceptionType == EXCEPT_IA32_GP_FAULT)) {
+    AdjustRipForFaultHandler (ExceptionType, SystemContext);
+  }
+}
+
+/**
+  Compare cpu context in ConsistencyOfCpuContext test case.
+  1.Compare mRegisterForCheck[0] with mAdjustRegisterBeforeException.
+  2.Compare mRegisterForCheck[1] with mAdjustRegisterAfterException.
+
+  @retval  UNIT_TEST_PASSED             The Unit test has completed and it was successful.
+  @retval  UNIT_TEST_ERROR_TEST_FAILED  A test case assertion has failed.
+**/
+UNIT_TEST_STATUS
+CompareCpuContext (
+  VOID
+  )
+{
+  UT_ASSERT_EQUAL (mIa32RegisterForCheck[0].Edi, mAdjustIa32RegisterBeforeException.Edi);
+  UT_ASSERT_EQUAL (mIa32RegisterForCheck[0].Esi, mAdjustIa32RegisterBeforeException.Esi);
+  UT_ASSERT_EQUAL (mIa32RegisterForCheck[0].Ebx, mAdjustIa32RegisterBeforeException.Ebx);
+  UT_ASSERT_EQUAL (mIa32RegisterForCheck[0].Edx, mAdjustIa32RegisterBeforeException.Edx);
+  UT_ASSERT_EQUAL (mIa32RegisterForCheck[0].Ecx, mAdjustIa32RegisterBeforeException.Ecx);
+  UT_ASSERT_EQUAL (mIa32RegisterForCheck[0].Eax, mAdjustIa32RegisterBeforeException.Eax);
+
+  UT_ASSERT_EQUAL (mIa32RegisterForCheck[1].Edi, mAdjustIa32RegisterInsideException.Edi);
+  UT_ASSERT_EQUAL (mIa32RegisterForCheck[1].Esi, mAdjustIa32RegisterInsideException.Esi);
+  UT_ASSERT_EQUAL (mIa32RegisterForCheck[1].Ebx, mAdjustIa32RegisterInsideException.Ebx);
+  UT_ASSERT_EQUAL (mIa32RegisterForCheck[1].Edx, mAdjustIa32RegisterInsideException.Edx);
+  UT_ASSERT_EQUAL (mIa32RegisterForCheck[1].Ecx, mAdjustIa32RegisterInsideException.Ecx);
+  UT_ASSERT_EQUAL (mIa32RegisterForCheck[1].Eax, mAdjustIa32RegisterInsideException.Eax);
+  return UNIT_TEST_PASSED;
+}
+
+/**
+  Special handler for CpuStackGuard test case.
+
+  @param ExceptionType  Exception type.
+  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+
+**/
+VOID
+EFIAPI
+CpuStackGuardExceptionHandler (
+  IN EFI_EXCEPTION_TYPE  ExceptionType,
+  IN EFI_SYSTEM_CONTEXT  SystemContext
+  )
+{
+  UINTN  LocalVariable;
+
+  AdjustRipForFaultHandler (ExceptionType, SystemContext);
+  mRspAddress[0] = (UINTN)SystemContext.SystemContextIa32->Esp;
+  mRspAddress[1] = (UINTN)(&LocalVariable);
+
+  return;
+}
diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTestAsm.nasm b/UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTestAsm.nasm
new file mode 100644
index 0000000000..ccadd8765f
--- /dev/null
+++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/Ia32/ArchExceptionHandlerTestAsm.nasm
@@ -0,0 +1,217 @@
+;------------------------------------------------------------------------------
+;
+; Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+; Module Name:
+;
+;   ArchExceptionHandlerTestAsm.nasm
+;
+; Abstract:
+;
+;   ia32 CPU Exception Handler Lib Unit test
+;
+;------------------------------------------------------------------------------
+
+    SECTION .text
+
+struc GENERAL_REGISTER_IA32
+  .Edi:    resd    1
+  .Esi:    resd    1
+  .Ebp:    resd    1
+  .Ebx:    resd    1
+  .Edx:    resd    1
+  .Ecx:    resd    1
+  .Eax:    resd    1
+
+endstruc
+
+extern ASM_PFX(mIa32RegisterForCheck)
+extern ASM_PFX(mAdjustIa32RegisterBeforeException)
+extern ASM_PFX(mFaultInstructionLength)
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; TriggerGPException (
+;  UINTN  Cr4ReservedBit
+;  );
+;------------------------------------------------------------------------------
+global ASM_PFX(TriggerGPException)
+ASM_PFX(TriggerGPException):
+    ;
+    ; Set reserved bit 15 of cr4 to 1
+    ;
+    lea  ecx, [ASM_PFX(mFaultInstructionLength)]
+    mov  dword[ecx], TriggerGPExceptionAfter - TriggerGPExceptionBefore
+    mov  ecx, dword [esp + 0x4]
+TriggerGPExceptionBefore:
+    mov  cr4, ecx
+TriggerGPExceptionAfter:
+    ret
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; TriggerPFException (
+;  UINTN  PfAddress
+;  );
+;------------------------------------------------------------------------------
+global ASM_PFX(TriggerPFException)
+ASM_PFX(TriggerPFException):
+    lea  ecx, [ASM_PFX(mFaultInstructionLength)]
+    mov  dword[ecx], TriggerPFExceptionAfter - TriggerPFExceptionBefore
+    mov  ecx, dword [esp + 0x4]
+TriggerPFExceptionBefore:
+    mov  dword[ecx], 0x1
+TriggerPFExceptionAfter:
+    ret
+
+global ASM_PFX(ModifyEcxInGlobalBeforeException)
+ASM_PFX(ModifyEcxInGlobalBeforeException):
+    push eax
+    lea  eax, [ASM_PFX(mAdjustIa32RegisterBeforeException)]
+    mov  [eax + GENERAL_REGISTER_IA32.Ecx], ecx
+    pop  eax
+    ret
+
+;------------------------------------------------------------------------------
+;VOID
+;EFIAPI
+;AsmTestConsistencyOfCpuContext (
+;  IN  EFI_EXCEPTION_TYPE ExceptionType
+;  IN  UINTN              FaultParameter   OPTIONAL
+;  );
+;------------------------------------------------------------------------------
+global ASM_PFX(AsmTestConsistencyOfCpuContext)
+ASM_PFX(AsmTestConsistencyOfCpuContext):
+    ;
+    ; push 7 general register plus 4 bytes
+    ;
+    pushad
+
+    ;
+    ; modify Modify register to mAdjustRegisterBeforeException
+    ;
+    lea eax, [ASM_PFX(mAdjustIa32RegisterBeforeException)]
+    mov edi, [eax + GENERAL_REGISTER_IA32.Edi]
+    mov esi, [eax + GENERAL_REGISTER_IA32.Esi]
+    mov ebx, [eax + GENERAL_REGISTER_IA32.Ebx]
+    mov edx, [eax + GENERAL_REGISTER_IA32.Edx]
+    ;
+    ; Set ecx to ExceptionType
+    ;
+    mov ecx, dword [esp + 0x24]
+    mov eax, [eax + GENERAL_REGISTER_IA32.Eax]
+
+    cmp  ecx, 0xd
+    jz   GPException
+    cmp  ecx, 0xe
+    jz   PFException
+    jmp  INTnException
+
+PFException:
+    ;
+    ; Set ecx to PFAddress and Modify Ecx in mAdjustRegisterBeforeException.
+    ; Push PfAddress into stack.
+    ;
+    mov  ecx, dword [esp + 0x28]
+    call ASM_PFX(ModifyEcxInGlobalBeforeException)
+    push ecx
+    call ASM_PFX(TriggerPFException)
+    jmp  AfterException
+
+GPException:
+    ;
+    ; Set ecx to CR4_RESERVED_BIT and Modify Ecx in mAdjustRegisterBeforeException.
+    ; Push CR4_RESERVED_BIT into stack
+    ;
+    mov  ecx, dword [esp + 0x28]
+    call ASM_PFX(ModifyEcxInGlobalBeforeException)
+    push ecx
+    call ASM_PFX(TriggerGPException)
+    jmp  AfterException
+
+INTnException:
+    ;
+    ; Modify Ecx in mAdjustRegisterBeforeException.
+    ; Push exception index into stack
+    ;
+    call ASM_PFX(ModifyEcxInGlobalBeforeException)
+    push ecx
+    call ASM_PFX(TriggerINTnException)
+
+AfterException:
+    ;
+    ; Save register in mRegisterForCheck[1].
+    ; Esp and ebp will not be synced to the value in SystemContext. Do not test esp and ebp.
+    ;
+    push eax
+    lea  eax, [ASM_PFX(mIa32RegisterForCheck)]
+    add  eax, GENERAL_REGISTER_IA32_size
+    mov  [eax + GENERAL_REGISTER_IA32.Edi], edi
+    mov  [eax + GENERAL_REGISTER_IA32.Esi], esi
+    mov  [eax + GENERAL_REGISTER_IA32.Ebx], ebx
+    mov  [eax + GENERAL_REGISTER_IA32.Edx], edx
+    mov  [eax + GENERAL_REGISTER_IA32.Ecx], ecx
+    pop  ecx
+    mov  [eax + GENERAL_REGISTER_IA32.Eax], ecx
+    add  esp, 4
+
+    ;
+    ; restore original register
+    ;
+    popad
+    ret
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; TriggerStackOverflowbyCpuStackGuard (
+;  VOID
+;  );
+;------------------------------------------------------------------------------
+global ASM_PFX(TriggerStackOverflowbyCpuStackGuard)
+ASM_PFX(TriggerStackOverflowbyCpuStackGuard):
+    lea  ecx, [ASM_PFX(mFaultInstructionLength)]
+    mov  dword[ecx], TriggerCpuStackGuardAfter - TriggerCpuStackGuardBefore
+TriggerCpuStackGuardBefore:
+    ;
+    ; Clear CR0.TS since it is set after return from a nested DF
+    ;
+    call TriggerCpuStackGuardBefore
+    clts
+TriggerCpuStackGuardAfter:
+    ret
+
+;------------------------------------------------------------------------------
+; VOID
+; EFIAPI
+; TriggerINTnException (
+;  IN  EFI_EXCEPTION_TYPE ExceptionType
+;  );
+;------------------------------------------------------------------------------
+global ASM_PFX(TriggerINTnException)
+ASM_PFX(TriggerINTnException):
+    push eax
+    push edx
+    lea  eax, [AsmTriggerException1 - AsmTriggerException0]
+    mov  ecx, dword [esp + 0xc]
+    push ecx
+    mul  ecx
+    mov  ecx, AsmTriggerException0
+    add  eax, ecx
+    pop  ecx
+    pop  edx
+    jmp  eax
+    ;
+    ; eax = AsmTriggerException0 + (AsmTriggerException1 - AsmTriggerException0) * ecx
+    ;
+%assign Vector 0
+%rep  22
+AsmTriggerException %+ Vector:
+    pop eax
+    INT Vector
+    ret
+%assign Vector Vector+1
+%endrep
diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerLibUnitTest.inf b/UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerLibUnitTest.inf
new file mode 100644
index 0000000000..25f8f8dbe0
--- /dev/null
+++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerLibUnitTest.inf
@@ -0,0 +1,61 @@
+## @file
+# Unit tests of the PeiCpuExceptionHandlerLib instance.
+#
+# Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010006
+  BASE_NAME                      = CpuExceptionHandlerPeiTest
+  FILE_GUID                      = 39A96CF7-F369-4357-9234-4B52F98A007F
+  MODULE_TYPE                    = PEIM
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = PeiEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64
+#
+[Sources.Ia32]
+  Ia32/ArchExceptionHandlerTestAsm.nasm
+  Ia32/ArchExceptionHandlerTest.c
+
+[Sources.X64]
+  X64/ArchExceptionHandlerTestAsm.nasm
+  X64/ArchExceptionHandlerTest.c
+
+[Sources.common]
+  CpuExceptionHandlerTest.h
+  CpuExceptionHandlerTestCommon.c
+  PeiCpuExceptionHandlerUnitTest.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  UefiCpuPkg/UefiCpuPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  DebugLib
+  UnitTestLib
+  MemoryAllocationLib
+  CpuExceptionHandlerLib
+  PeimEntryPoint
+  HobLib
+  PeiServicesLib
+  CpuPageTableLib
+  PeiServicesTablePointerLib
+
+[Pcd]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard   ## CONSUMES
+  gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize       ## CONSUMES
+
+[Ppis]
+  gEdkiiPeiMpServices2PpiGuid                       ## CONSUMES
+
+[Depex]
+  gEdkiiPeiMpServices2PpiGuid AND
+  gEfiPeiMemoryDiscoveredPpiGuid
diff --git a/UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerUnitTest.c b/UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerUnitTest.c
new file mode 100644
index 0000000000..d9408d2f5e
--- /dev/null
+++ b/UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerUnitTest.c
@@ -0,0 +1,204 @@
+/** @file
+  Unit tests of the CpuExceptionHandlerLib.
+
+  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "CpuExceptionHandlerTest.h"
+#include <Library/PeimEntryPoint.h>
+#include <Library/PeiServicesLib.h>
+#include <Library/PeiServicesTablePointerLib.h>
+
+/**
+  Initialize Bsp Idt with a new Idt table and return the IA32_DESCRIPTOR buffer.
+  In PEIM, store original PeiServicePointer before new Idt table.
+
+  @return Pointer to the allocated IA32_DESCRIPTOR buffer.
+**/
+VOID *
+InitializeBspIdt (
+  VOID
+  )
+{
+  UINTN            *NewIdtTable;
+  IA32_DESCRIPTOR  *Idtr;
+
+  Idtr = AllocateZeroPool (sizeof (IA32_DESCRIPTOR));
+  ASSERT (Idtr != NULL);
+  NewIdtTable = AllocateZeroPool (sizeof (IA32_IDT_GATE_DESCRIPTOR) * CPU_INTERRUPT_NUM + sizeof (UINTN));
+  ASSERT (NewIdtTable != NULL);
+  //
+  // Store original PeiServicePointer before new Idt table
+  //
+  *NewIdtTable = (UINTN)GetPeiServicesTablePointer ();
+  NewIdtTable  = (UINTN *)((UINTN)NewIdtTable + sizeof (UINTN));
+
+  Idtr->Base  = (UINTN)NewIdtTable;
+  Idtr->Limit = (UINT16)(sizeof (IA32_IDT_GATE_DESCRIPTOR) * CPU_INTERRUPT_NUM - 1);
+
+  AsmWriteIdtr (Idtr);
+  return Idtr;
+}
+
+/**
+  Retrieve the number of logical processor in the platform and the number of those logical processors that
+  are enabled on this boot.
+
+  @param[in]  MpServices          MP_SERVICES structure.
+  @param[out] NumberOfProcessors  Pointer to the total number of logical processors in the system, including
+                                  the BSP and disabled APs.
+  @param[out] NumberOfEnabledProcessors Pointer to the number of processors in the system that are enabled.
+
+  @retval EFI_SUCCESS       Retrieve the number of logical processor successfully
+  @retval Others            Retrieve the number of logical processor unsuccessfully
+**/
+EFI_STATUS
+MpServicesUnitTestGetNumberOfProcessors (
+  IN MP_SERVICES  MpServices,
+  OUT UINTN       *NumberOfProcessors,
+  OUT UINTN       *NumberOfEnabledProcessors
+  )
+{
+  return MpServices.Ppi->GetNumberOfProcessors (MpServices.Ppi, NumberOfProcessors, NumberOfEnabledProcessors);
+}
+
+/**
+  Caller gets one enabled AP to execute a caller-provided function.
+
+  @param[in]  MpServices    MP_SERVICES structure.
+  @param[in]  Procedure     Pointer to the function to be run on enabled APs of the system.
+  @param[in]  ProcessorNumber       The handle number of the AP.
+  @param[in]  TimeoutInMicroSeconds Indicates the time limit in microseconds for APs to return from Procedure,
+                                    for blocking mode only. Zero means infinity.
+  @param[in]  ProcedureArgument     The parameter passed into Procedure for all APs.
+
+
+  @retval EFI_SUCCESS       Caller gets one enabled AP to execute a caller-provided function successfully
+  @retval Others            Caller gets one enabled AP to execute a caller-provided function unsuccessfully
+**/
+EFI_STATUS
+MpServicesUnitTestStartupThisAP (
+  IN MP_SERVICES       MpServices,
+  IN EFI_AP_PROCEDURE  Procedure,
+  IN UINTN             ProcessorNumber,
+  IN UINTN             TimeoutInMicroSeconds,
+  IN VOID              *ProcedureArgument
+  )
+{
+  return MpServices.Ppi->StartupThisAP (MpServices.Ppi, Procedure, ProcessorNumber, TimeoutInMicroSeconds, ProcedureArgument);
+}
+
+/**
+  Execute a caller provided function on all enabled APs.
+
+  @param[in]  MpServices    MP_SERVICES structure.
+  @param[in]  Procedure     Pointer to the function to be run on enabled APs of the system.
+  @param[in]  SingleThread  If TRUE, then all the enabled APs execute the function specified by Procedure
+                            one by one, in ascending order of processor handle number.
+                            If FALSE, then all the enabled APs execute the function specified by Procedure
+                            simultaneously.
+  @param[in]  TimeoutInMicroSeconds Indicates the time limit in microseconds for APs to return from Procedure,
+                                    for blocking mode only. Zero means infinity.
+  @param[in]  ProcedureArgument     The parameter passed into Procedure for all APs.
+
+  @retval EFI_SUCCESS       Execute a caller provided function on all enabled APs successfully
+  @retval Others            Execute a caller provided function on all enabled APs unsuccessfully
+**/
+EFI_STATUS
+MpServicesUnitTestStartupAllAPs (
+  IN MP_SERVICES       MpServices,
+  IN EFI_AP_PROCEDURE  Procedure,
+  IN BOOLEAN           SingleThread,
+  IN UINTN             TimeoutInMicroSeconds,
+  IN VOID              *ProcedureArgument
+  )
+{
+  return MpServices.Ppi->StartupAllAPs (MpServices.Ppi, Procedure, SingleThread, TimeoutInMicroSeconds, ProcedureArgument);
+}
+
+/**
+  Get the handle number for the calling processor.
+
+  @param[in]  MpServices      MP_SERVICES structure.
+  @param[out] ProcessorNumber The handle number for the calling processor.
+
+  @retval EFI_SUCCESS       Get the handle number for the calling processor successfully.
+  @retval Others            Get the handle number for the calling processor unsuccessfully.
+**/
+EFI_STATUS
+MpServicesUnitTestWhoAmI (
+  IN MP_SERVICES  MpServices,
+  OUT UINTN       *ProcessorNumber
+  )
+{
+  return MpServices.Ppi->WhoAmI (MpServices.Ppi, ProcessorNumber);
+}
+
+/**
+  Get EDKII_PEI_MP_SERVICES2_PPI pointer.
+
+  @param[out] MpServices    Pointer to the buffer where EDKII_PEI_MP_SERVICES2_PPI is stored
+
+  @retval EFI_SUCCESS       EDKII_PEI_MP_SERVICES2_PPI interface is returned
+  @retval EFI_NOT_FOUND     EDKII_PEI_MP_SERVICES2_PPI interface is not found
+**/
+EFI_STATUS
+GetMpServices (
+  OUT MP_SERVICES  *MpServices
+  )
+{
+  return PeiServicesLocatePpi (&gEdkiiPeiMpServices2PpiGuid, 0, NULL, (VOID **)&MpServices->Ppi);
+}
+
+/**
+  Entry point of CpuExceptionHandlerPeiTest PEIM.
+
+  @param[in] FileHandle  Handle of the file being invoked.
+  @param[in] PeiServices Describes the list of possible PEI Services.
+
+  @retval EFI_SUCCESS    The PEIM executed normally.
+
+**/
+EFI_STATUS
+EFIAPI
+PeiEntryPoint (
+  IN EFI_PEI_FILE_HANDLE     FileHandle,
+  IN CONST EFI_PEI_SERVICES  **PeiServices
+  )
+{
+  EFI_STATUS                  Status;
+  UNIT_TEST_FRAMEWORK_HANDLE  Framework;
+
+  Framework = NULL;
+
+  DEBUG ((DEBUG_INFO, "%a v%a\n", UNIT_TEST_APP_NAME, UNIT_TEST_APP_VERSION));
+
+  //
+  // Start setting up the test framework for running the tests.
+  //
+  Status = InitUnitTestFramework (&Framework, UNIT_TEST_APP_NAME, gEfiCallerBaseName, UNIT_TEST_APP_VERSION);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status));
+    goto EXIT;
+  }
+
+  Status = AddCommonTestCase (Framework);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Failed in AddCommonTestCase. Status = %r\n", Status));
+    goto EXIT;
+  }
+
+  //
+  // Execute the tests.
+  //
+  Status = RunAllTestSuites (Framework);
+
+EXIT:
+  if (Framework) {
+    FreeUnitTestFramework (Framework);
+  }
+
+  return Status;
+}
-- 
2.31.1.windows.1


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

* [PATCH 3/3] UefiCpuPkg: Add Pei/DxeCpuExceptionHandlerLibUnitTest in dsc
  2022-10-11  6:03 [PATCH 0/3] Add Pei/DxeCpuExceptionHandlerLibUnitTest duntan
  2022-10-11  6:03 ` [PATCH 1/3] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib duntan
  2022-10-11  6:03 ` [PATCH 2/3] UefiCpuPkg: Add Unit tests for PeiCpuExceptionHandlerLib duntan
@ 2022-10-11  6:03 ` duntan
  2 siblings, 0 replies; 6+ messages in thread
From: duntan @ 2022-10-11  6:03 UTC (permalink / raw)
  To: devel; +Cc: Eric Dong, Ray Ni, Rahul Kumar

Add Pei/DxeCpuExceptionHandlerLibUnitTest module in UefiCpuPkg.dsc

Signed-off-by: Dun Tan <dun.tan@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
---
 UefiCpuPkg/UefiCpuPkg.dsc | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc
index f694b3a77c..31145dbe7e 100644
--- a/UefiCpuPkg/UefiCpuPkg.dsc
+++ b/UefiCpuPkg/UefiCpuPkg.dsc
@@ -63,6 +63,9 @@
   MicrocodeLib|UefiCpuPkg/Library/MicrocodeLib/MicrocodeLib.inf
   SmmCpuRendezvousLib|UefiCpuPkg/Library/SmmCpuRendezvousLib/SmmCpuRendezvousLib.inf
   CpuPageTableLib|UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf
+  UnitTestLib|UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.inf
+  UnitTestPersistenceLib|UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.inf
+  UnitTestResultReportLib|UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.inf
 
 [LibraryClasses.common.SEC]
   PlatformSecLib|UefiCpuPkg/Library/PlatformSecLibNull/PlatformSecLibNull.inf
@@ -177,6 +180,10 @@
   UefiCpuPkg/ResetVector/Vtf0/Bin/ResetVector.inf
   UefiCpuPkg/Library/SmmCpuRendezvousLib/SmmCpuRendezvousLib.inf
   UefiCpuPkg/Library/CpuPageTableLib/CpuPageTableLib.inf
+  UefiCpuPkg/CpuExceptionHandlerUnitTest/PeiCpuExceptionHandlerLibUnitTest.inf
+
+[Components.X64]
+  UefiCpuPkg/CpuExceptionHandlerUnitTest/DxeCpuExceptionHandlerLibUnitTest.inf
 
 [BuildOptions]
   *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES
-- 
2.31.1.windows.1


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

* Re: [PATCH 1/3] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib
  2022-10-11  6:03 ` [PATCH 1/3] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib duntan
@ 2022-10-11  9:37   ` Ni, Ray
  2022-10-12  1:31     ` duntan
  0 siblings, 1 reply; 6+ messages in thread
From: Ni, Ray @ 2022-10-11  9:37 UTC (permalink / raw)
  To: Tan, Dun, devel@edk2.groups.io; +Cc: Dong, Eric, Kumar, Rahul R

Can you provide more details in the commit message?
Especially explain what "consistent" means for #3 and what "CpuStackGuard in both BSP and AP" means in #4?

Thanks,
Ray

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


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

* Re: [PATCH 1/3] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib
  2022-10-11  9:37   ` Ni, Ray
@ 2022-10-12  1:31     ` duntan
  0 siblings, 0 replies; 6+ messages in thread
From: duntan @ 2022-10-12  1:31 UTC (permalink / raw)
  To: Ni, Ray, devel@edk2.groups.io; +Cc: Dong, Eric, Kumar, Rahul R

Ok, I'll do this in V2 patch. Thanks for your comments.

Thanks,
Dun
-----Original Message-----
From: Ni, Ray <ray.ni@intel.com> 
Sent: Tuesday, October 11, 2022 5:38 PM
To: Tan, Dun <dun.tan@intel.com>; devel@edk2.groups.io
Cc: Dong, Eric <eric.dong@intel.com>; Kumar, Rahul R <rahul.r.kumar@intel.com>
Subject: RE: [PATCH 1/3] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib

Can you provide more details in the commit message?
Especially explain what "consistent" means for #3 and what "CpuStackGuard in both BSP and AP" means in #4?

Thanks,
Ray

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


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

end of thread, other threads:[~2022-10-12  1:31 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-10-11  6:03 [PATCH 0/3] Add Pei/DxeCpuExceptionHandlerLibUnitTest duntan
2022-10-11  6:03 ` [PATCH 1/3] UefiCpuPkg: Add Unit tests for DxeCpuExceptionHandlerLib duntan
2022-10-11  9:37   ` Ni, Ray
2022-10-12  1:31     ` duntan
2022-10-11  6:03 ` [PATCH 2/3] UefiCpuPkg: Add Unit tests for PeiCpuExceptionHandlerLib duntan
2022-10-11  6:03 ` [PATCH 3/3] UefiCpuPkg: Add Pei/DxeCpuExceptionHandlerLibUnitTest in dsc duntan

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