/** @file Copyright (c) 2022, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent Four test cases are created in this Unit Test module. a.Test if exception handler can be registered/unregistered for no error code exception In this test case, only no error code exception is triggered and tested by INTn instruction. The special hanlder for these exception will modify a global variable for check. b.Test if exception handler can be registered/unregistered for GP and PF. In this test case, GP exception is triggered and tested by setting CR4_RESERVED_BIT to 1. PF exception is triggered and tested by writting to not-present or RO addres. The special hanlder for these exceptions will set a global vartiable for check and adjust Rip to return from fault exception. c.Test if Cpu Context is consistent before and after exception. In this test case: 1. Set Cpu register to mExpectedContextInHandler before exception. 2. Trigger exception specified by ExceptionType. 3. Store SystemContext in mActualContextInHandler and set SystemContext to mExpectedContextAfterException in handler. 4. After return from exception, store Cpu registers in mActualContextAfterException. The expectation is: 1. Register values in mActualContextInHandler are the same with register values in mExpectedContextInHandler. 2. Register values in mActualContextAfterException are the same with register values mActualContextAfterException. d.Test if stack overflow can be captured by CpuStackGuard in both Bsp and AP. In this test case, stack overflow is triggered by a funtion which calls itself continuously. This test case triggers stack overflow in both BSP and AP. All AP use same Idt with Bsp. The expectation is: 1. PF exception is triggered (leading to a DF if sepereated stack is not prepared for PF) when Rsp <= StackBase + SIZE_4KB since [StackBase, StackBase + SIZE_4KB] is marked as not present in page table when PcdCpuStackGuard is TRUE. 2. Stack for PF/DF exception handler in both Bsp and AP is succussfully switched by InitializeSeparateExceptionStacks. **/ #ifndef CPU_EXCEPTION_HANDLER_TEST_H_ #define CPU_EXCEPTION_HANDLER_TEST_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define UNIT_TEST_APP_NAME "Cpu Exception Handler Lib Unit Tests" #define UNIT_TEST_APP_VERSION "1.0" #define CPU_INTERRUPT_NUM 256 #define SPEC_MAX_EXCEPTION_NUM 22 #define CR4_RESERVED_BIT BIT15 typedef struct { IA32_DESCRIPTOR OriginalGdtr; IA32_DESCRIPTOR OriginalIdtr; UINT16 Tr; } CPU_REGISTER_BUFFER; typedef union { EDKII_PEI_MP_SERVICES2_PPI *Ppi; EFI_MP_SERVICES_PROTOCOL *Protocol; } MP_SERVICES; typedef struct { VOID *Buffer; UINTN BufferSize; EFI_STATUS Status; } EXCEPTION_STACK_SWITCH_CONTEXT; typedef struct { UINT64 Rdi; UINT64 Rsi; UINT64 Rbx; UINT64 Rdx; UINT64 Rcx; UINT64 Rax; UINT64 R8; UINT64 R9; UINT64 R10; UINT64 R11; UINT64 R12; UINT64 R13; UINT64 R14; UINT64 R15; } GENERAL_REGISTER; typedef struct { UINT32 Edi; UINT32 Esi; UINT32 Ebx; UINT32 Edx; UINT32 Ecx; UINT32 Eax; } GENERAL_REGISTER_IA32; extern UINTN mFaultInstructionLength; extern EFI_EXCEPTION_TYPE mExceptionType; extern UINTN mRspAddress[]; /** Initialize Bsp Idt with a new Idt table and return the IA32_DESCRIPTOR buffer. In PEIM, store original PeiServicePointer before new Idt table. @return Pointer to the allocated IA32_DESCRIPTOR buffer. **/ VOID * InitializeBspIdt ( VOID ); /** Trigger no error code exception by INT n instruction. @param[in] ExceptionType No error code exception type. **/ VOID EFIAPI TriggerINTnException ( IN EFI_EXCEPTION_TYPE ExceptionType ); /** Trigger GP exception by setting CR4_RESERVED_BIT to 1. @param[in] Cr4ReservedBit Cr4 reserved bit. **/ VOID EFIAPI TriggerGPException ( UINTN Cr4ReservedBit ); /** Trigger PF exception by write to not present or ReadOnly address. @param[in] PFAddress Not present or ReadOnly address in page table. **/ VOID EFIAPI TriggerPFException ( UINTN PFAddress ); /** Special handler for fault exception. This handler sets Rip/Eip in SystemContext to the instruction address after the exception instruction. @param ExceptionType Exception type. @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. **/ VOID EFIAPI AdjustRipForFaultHandler ( IN EFI_EXCEPTION_TYPE ExceptionType, IN EFI_SYSTEM_CONTEXT SystemContext ); /** Test consistency of Cpu context. Four steps: 1. Set Cpu register to mExpectedContextInHandler before exception. 2. Trigger exception specified by ExceptionType. 3. Store SystemContext in mActualContextInHandler and set SystemContext to mExpectedContextAfterException in handler. 4. After return from exception, store Cpu registers in mActualContextAfterException. Rcx/Ecx in mExpectedContextInHandler is decided by different exception type runtime since Rcx/Ecx is needed in assembly code. For GP and PF, Rcx/Ecx is set to FaultParameter. For other exception triggered by INTn, Rcx/Ecx is set to ExceptionType. @param[in] ExceptionType Exception type. @param[in] FaultParameter Parameter for GP and PF. OPTIONAL **/ VOID EFIAPI AsmTestConsistencyOfCpuContext ( IN EFI_EXCEPTION_TYPE ExceptionType, IN UINTN FaultParameter OPTIONAL ); /** Special handler for ConsistencyOfCpuContext test case. General register in SystemContext is modified to mExpectedContextInHandler in this handler. @param ExceptionType Exception type. @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. **/ VOID EFIAPI AdjustCpuContextHandler ( IN EFI_EXCEPTION_TYPE ExceptionType, IN EFI_SYSTEM_CONTEXT SystemContext ); /** Compare cpu context in ConsistencyOfCpuContext test case. 1.Compare mActualContextInHandler with mExpectedContextInHandler. 2.Compare mActualContextAfterException with mActualContextAfterException. @retval UNIT_TEST_PASSED The Unit test has completed and it was successful. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS CompareCpuContext ( VOID ); /** Get EFI_MP_SERVICES_PROTOCOL/EDKII_PEI_MP_SERVICES2_PPI pointer. @param[out] MpServices Pointer to the MP_SERVICES buffer @retval EFI_SUCCESS EFI_MP_SERVICES_PROTOCOL/PPI interface is returned @retval EFI_NOT_FOUND EFI_MP_SERVICES_PROTOCOL/PPI interface is not found **/ EFI_STATUS GetMpServices ( OUT MP_SERVICES *MpServices ); /** Create CpuExceptionLibUnitTestSuite and add test case. @param[in] FrameworkHandle Unit test framework. @return EFI_SUCCESS The unit test suite was created. @retval EFI_OUT_OF_RESOURCES There are not enough resources available to initialize the unit test suite. **/ EFI_STATUS AddCommonTestCase ( IN UNIT_TEST_FRAMEWORK_HANDLE Framework ); /** Execute a caller provided function on all enabled APs. @param[in] MpServices MP_SERVICES structure. @param[in] Procedure Pointer to the function to be run on enabled APs of the system. @param[in] SingleThread If TRUE, then all the enabled APs execute the function specified by Procedure one by one, in ascending order of processor handle number. If FALSE, then all the enabled APs execute the function specified by Procedure simultaneously. @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for APs to return from Procedure, for blocking mode only. Zero means infinity. @param[in] ProcedureArgument The parameter passed into Procedure for all APs. @retval EFI_SUCCESS Execute a caller provided function on all enabled APs successfully @retval Others Execute a caller provided function on all enabled APs unsuccessfully **/ EFI_STATUS MpServicesUnitTestStartupAllAPs ( IN MP_SERVICES MpServices, IN EFI_AP_PROCEDURE Procedure, IN BOOLEAN SingleThread, IN UINTN TimeoutInMicroSeconds, IN VOID *ProcedureArgument ); /** Caller gets one enabled AP to execute a caller-provided function. @param[in] MpServices MP_SERVICES structure. @param[in] Procedure Pointer to the function to be run on enabled APs of the system. @param[in] ProcessorNumber The handle number of the AP. @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for APs to return from Procedure, for blocking mode only. Zero means infinity. @param[in] ProcedureArgument The parameter passed into Procedure for all APs. @retval EFI_SUCCESS Caller gets one enabled AP to execute a caller-provided function successfully @retval Others Caller gets one enabled AP to execute a caller-provided function unsuccessfully **/ EFI_STATUS MpServicesUnitTestStartupThisAP ( IN MP_SERVICES MpServices, IN EFI_AP_PROCEDURE Procedure, IN UINTN ProcessorNumber, IN UINTN TimeoutInMicroSeconds, IN VOID *ProcedureArgument ); /** Get the handle number for the calling processor. @param[in] MpServices MP_SERVICES structure. @param[out] ProcessorNumber The handle number for the calling processor. @retval EFI_SUCCESS Get the handle number for the calling processor successfully. @retval Others Get the handle number for the calling processor unsuccessfully. **/ EFI_STATUS MpServicesUnitTestWhoAmI ( IN MP_SERVICES MpServices, OUT UINTN *ProcessorNumber ); /** Retrieve the number of logical processor in the platform and the number of those logical processors that are enabled on this boot. @param[in] MpServices MP_SERVICES structure. @param[out] NumberOfProcessors Pointer to the total number of logical processors in the system, including the BSP and disabled APs. @param[out] NumberOfEnabledProcessors Pointer to the number of processors in the system that are enabled. @retval EFI_SUCCESS Retrieve the number of logical processor successfully @retval Others Retrieve the number of logical processor unsuccessfully **/ EFI_STATUS MpServicesUnitTestGetNumberOfProcessors ( IN MP_SERVICES MpServices, OUT UINTN *NumberOfProcessors, OUT UINTN *NumberOfEnabledProcessors ); /** Trigger stack overflow by calling itself continuously. **/ VOID EFIAPI TriggerStackOverflow ( VOID ); /** Special handler for CpuStackGuard test case. @param ExceptionType Exception type. @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. **/ VOID EFIAPI CpuStackGuardExceptionHandler ( IN EFI_EXCEPTION_TYPE ExceptionType, IN EFI_SYSTEM_CONTEXT SystemContext ); #endif