/** @file Unit tests of the CpuExceptionHandlerLib. Copyright (c) 2022, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "CpuExceptionHandlerTest.h" // // Length of the assembly falut instruction. // UINTN mFaultInstructionLength = 0; EFI_EXCEPTION_TYPE mExceptionType = 256; UINTN mNumberOfProcessors = 1; UINTN mRspAddress[2] = { 0 }; // // Error code flag indicating whether or not an error code will be // pushed on the stack if an exception occurs. // // 1 means an error code will be pushed, otherwise 0 // CONST UINT32 mErrorCodeExceptionFlag = 0x20227d00; /** Special handler for exception triggered by INTn instruction. This hanlder only modifies a global variable for check. @param ExceptionType Exception type. @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. **/ VOID EFIAPI INTnExceptionHandler ( IN EFI_EXCEPTION_TYPE ExceptionType, IN EFI_SYSTEM_CONTEXT SystemContext ) { mExceptionType = ExceptionType; } /** Restore cpu original registers before exit test case. @param[in] Buffer Argument of the procedure. **/ VOID EFIAPI RestoreRegistersPerCpu ( IN VOID *Buffer ) { CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; UINT16 Tr; IA32_TSS_DESCRIPTOR *Tss; CpuOriginalRegisterBuffer = (CPU_REGISTER_BUFFER *)Buffer; AsmWriteGdtr (&(CpuOriginalRegisterBuffer->OriginalGdtr)); AsmWriteIdtr (&(CpuOriginalRegisterBuffer->OriginalIdtr)); Tr = CpuOriginalRegisterBuffer->Tr; if ((Tr != 0) && (Tr < CpuOriginalRegisterBuffer->OriginalGdtr.Limit)) { Tss = (IA32_TSS_DESCRIPTOR *)(CpuOriginalRegisterBuffer->OriginalGdtr.Base + Tr); if (Tss->Bits.P == 1) { // // Clear busy bit of TSS before write Tr // Tss->Bits.Type &= 0xD; AsmWriteTr (Tr); } } } /** Restore cpu original registers before exit test case. @param[in] MpServices MpServices. @param[in] CpuOriginalRegisterBuffer Address of CpuOriginalRegisterBuffer. @param[in] BspProcessorNum Bsp processor number. **/ VOID RestoreAllCpuRegisters ( MP_SERVICES *MpServices, OPTIONAL CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer, UINTN BspProcessorNum ) { UINTN Index; EFI_STATUS Status; for (Index = 0; Index < mNumberOfProcessors; ++Index) { if (Index == BspProcessorNum) { RestoreRegistersPerCpu ((VOID *)&CpuOriginalRegisterBuffer[Index]); continue; } ASSERT (MpServices != NULL); Status = MpServicesUnitTestStartupThisAP ( *MpServices, (EFI_AP_PROCEDURE)RestoreRegistersPerCpu, Index, 0, (VOID *)&CpuOriginalRegisterBuffer[Index] ); ASSERT_EFI_ERROR (Status); } } /** Store cpu registers before the test case starts. @param[in] Buffer Argument of the procedure. **/ VOID EFIAPI SaveRegisterPerCpu ( IN VOID *Buffer ) { CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; IA32_DESCRIPTOR Gdtr; IA32_DESCRIPTOR Idtr; CpuOriginalRegisterBuffer = (CPU_REGISTER_BUFFER *)Buffer; AsmReadGdtr (&Gdtr); AsmReadIdtr (&Idtr); CpuOriginalRegisterBuffer->OriginalGdtr.Base = Gdtr.Base; CpuOriginalRegisterBuffer->OriginalGdtr.Limit = Gdtr.Limit; CpuOriginalRegisterBuffer->OriginalIdtr.Base = Idtr.Base; CpuOriginalRegisterBuffer->OriginalIdtr.Limit = Idtr.Limit; CpuOriginalRegisterBuffer->Tr = AsmReadTr (); } /** Store cpu registers before the test case starts. @param[in] MpServices MpServices. @param[in] BspProcessorNum Bsp processor number. @return Pointer to the allocated CPU_REGISTER_BUFFER. **/ CPU_REGISTER_BUFFER * SaveAllCpuRegisters ( MP_SERVICES *MpServices, OPTIONAL UINTN BspProcessorNum ) { CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; EFI_STATUS Status; UINTN Index; CpuOriginalRegisterBuffer = AllocateZeroPool (mNumberOfProcessors * sizeof (CPU_REGISTER_BUFFER)); ASSERT (CpuOriginalRegisterBuffer != NULL); for (Index = 0; Index < mNumberOfProcessors; ++Index) { if (Index == BspProcessorNum) { SaveRegisterPerCpu ((VOID *)&CpuOriginalRegisterBuffer[Index]); continue; } ASSERT (MpServices != NULL); Status = MpServicesUnitTestStartupThisAP ( *MpServices, (EFI_AP_PROCEDURE)SaveRegisterPerCpu, Index, 0, (VOID *)&CpuOriginalRegisterBuffer[Index] ); ASSERT_EFI_ERROR (Status); } return CpuOriginalRegisterBuffer; } /** Initialize Ap Idt Procedure. @param[in] Buffer Argument of the procedure. **/ VOID EFIAPI InitializeIdtPerAp ( IN VOID *Buffer ) { AsmWriteIdtr (Buffer); } /** Initialize all Ap Idt. @param[in] MpServices MpServices. @param[in] BspIdtr Pointer to IA32_DESCRIPTOR allocated by Bsp. **/ VOID InitializeApIdt ( MP_SERVICES MpServices, VOID *BspIdtr ) { EFI_STATUS Status; Status = MpServicesUnitTestStartupAllAPs ( MpServices, (EFI_AP_PROCEDURE)InitializeIdtPerAp, FALSE, 0, BspIdtr ); ASSERT_EFI_ERROR (Status); } /** Check if exception handler can registered/unregistered for no error code exception. @param[in] Context [Optional] An optional parameter that enables: 1) test-case reuse with varied parameters and 2) test-case re-entry for Target tests that need a reboot. This parameter is a VOID* and it is the responsibility of the test author to ensure that the contents are well understood by all test cases that may consume it. @retval UNIT_TEST_PASSED The Unit test has completed and the test case was successful. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestRegisterHandlerForNoErrorCodeException ( IN UNIT_TEST_CONTEXT Context ) { EFI_STATUS Status; UINTN Index; CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; VOID *NewIdtr; CpuOriginalRegisterBuffer = SaveAllCpuRegisters (NULL, 0); NewIdtr = InitializeBspIdt (); Status = InitializeCpuExceptionHandlers (NULL); UT_ASSERT_EQUAL (Status, EFI_SUCCESS); for (Index = 0; Index < SPEC_MAX_EXCEPTION_NUM; Index++) { // // Only test no error code exception by INT n instruction. // if ((mErrorCodeExceptionFlag & (1 << Index)) != 0) { continue; } DEBUG ((DEBUG_INFO, "TestCase1: ExceptionType is %d\n", Index)); Status = RegisterCpuInterruptHandler (Index, INTnExceptionHandler); UT_ASSERT_EQUAL (Status, EFI_SUCCESS); TriggerINTnException (Index); UT_ASSERT_EQUAL (mExceptionType, Index); Status = RegisterCpuInterruptHandler (Index, NULL); UT_ASSERT_EQUAL (Status, EFI_SUCCESS); } RestoreAllCpuRegisters (NULL, CpuOriginalRegisterBuffer, 0); FreePool (CpuOriginalRegisterBuffer); FreePool (NewIdtr); return UNIT_TEST_PASSED; } /** Get Bsp stack base. @param[out] StackBase Pointer to stack base of BSP. **/ VOID GetBspStackBase ( OUT UINTN *StackBase ) { EFI_PEI_HOB_POINTERS Hob; EFI_HOB_MEMORY_ALLOCATION *MemoryHob; // // Get the base of stack from Hob. // ASSERT (StackBase != NULL); Hob.Raw = GetHobList (); while ((Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw)) != NULL) { MemoryHob = Hob.MemoryAllocation; if (CompareGuid (&gEfiHobMemoryAllocStackGuid, &MemoryHob->AllocDescriptor.Name)) { DEBUG (( DEBUG_INFO, "%a: Bsp StackBase = 0x%016lx StackSize = 0x%016lx\n", __func__, MemoryHob->AllocDescriptor.MemoryBaseAddress, MemoryHob->AllocDescriptor.MemoryLength )); *StackBase = (UINTN)MemoryHob->AllocDescriptor.MemoryBaseAddress; // // Ensure the base of the stack is page-size aligned. // ASSERT ((*StackBase & EFI_PAGE_MASK) == 0); break; } Hob.Raw = GET_NEXT_HOB (Hob); } ASSERT (*StackBase != 0); } /** Get Ap stack base procedure. @param[out] ApStackBase Pointer to Ap stack base. **/ VOID EFIAPI GetStackBasePerAp ( OUT VOID *ApStackBase ) { UINTN ApTopOfStack; ApTopOfStack = ALIGN_VALUE ((UINTN)&ApTopOfStack, (UINTN)PcdGet32 (PcdCpuApStackSize)); *(UINTN *)ApStackBase = ApTopOfStack - (UINTN)PcdGet32 (PcdCpuApStackSize); } /** Get all Cpu stack base. @param[in] MpServices MpServices. @param[in] BspProcessorNum Bsp processor number. @return Pointer to the allocated CpuStackBaseBuffer. **/ UINTN * GetAllCpuStackBase ( MP_SERVICES *MpServices, UINTN BspProcessorNum ) { UINTN *CpuStackBaseBuffer; EFI_STATUS Status; UINTN Index; CpuStackBaseBuffer = AllocateZeroPool (mNumberOfProcessors * sizeof (UINTN)); ASSERT (CpuStackBaseBuffer != NULL); for (Index = 0; Index < mNumberOfProcessors; ++Index) { if (Index == BspProcessorNum) { GetBspStackBase (&CpuStackBaseBuffer[Index]); continue; } ASSERT (MpServices != NULL); Status = MpServicesUnitTestStartupThisAP ( *MpServices, (EFI_AP_PROCEDURE)GetStackBasePerAp, Index, 0, (VOID *)&CpuStackBaseBuffer[Index] ); ASSERT_EFI_ERROR (Status); DEBUG ((DEBUG_INFO, "AP[%d] StackBase = 0x%x\n", Index, CpuStackBaseBuffer[Index])); } return CpuStackBaseBuffer; } /** Find not present or ReadOnly address in page table. @param[out] PFAddress Access to the address which is not permitted will trigger PF exceptions. @retval TRUE Found not present or ReadOnly address in page table. @retval FALSE Failed to found PFAddress in page table. **/ BOOLEAN FindPFAddressInPageTable ( OUT UINTN *PFAddress ) { IA32_CR0 Cr0; IA32_CR4 Cr4; UINTN PageTable; PAGING_MODE PagingMode; BOOLEAN Enable5LevelPaging; RETURN_STATUS Status; IA32_MAP_ENTRY *Map; UINTN MapCount; UINTN Index; UINTN PreviousAddress; ASSERT (PFAddress != NULL); Cr0.UintN = AsmReadCr0 (); if (Cr0.Bits.PG == 0) { return FALSE; } PageTable = AsmReadCr3 (); Cr4.UintN = AsmReadCr4 (); if (sizeof (UINTN) == sizeof (UINT32)) { ASSERT (Cr4.Bits.PAE == 1); PagingMode = PagingPae; } else { Enable5LevelPaging = (BOOLEAN)(Cr4.Bits.LA57 == 1); PagingMode = Enable5LevelPaging ? Paging5Level : Paging4Level; } MapCount = 0; Status = PageTableParse (PageTable, PagingMode, NULL, &MapCount); ASSERT (Status == RETURN_BUFFER_TOO_SMALL); Map = AllocatePages (EFI_SIZE_TO_PAGES (MapCount * sizeof (IA32_MAP_ENTRY))); Status = PageTableParse (PageTable, PagingMode, Map, &MapCount); ASSERT (Status == RETURN_SUCCESS); PreviousAddress = 0; for (Index = 0; Index < MapCount; Index++) { DEBUG (( DEBUG_ERROR, "%02d: %016lx - %016lx, %016lx\n", Index, Map[Index].LinearAddress, Map[Index].LinearAddress + Map[Index].Length, Map[Index].Attribute.Uint64 )); // // Not present address in page table. // if (Map[Index].LinearAddress > PreviousAddress) { *PFAddress = PreviousAddress; return TRUE; } PreviousAddress = (UINTN)(Map[Index].LinearAddress + Map[Index].Length); // // ReadOnly address in page table. // if ((Cr0.Bits.WP != 0) && (Map[Index].Attribute.Bits.ReadWrite == 0)) { *PFAddress = (UINTN)Map[Index].LinearAddress; return TRUE; } } return FALSE; } /** Test if exception handler can registered/unregistered for GP and PF. @param[in] Context [Optional] An optional parameter that enables: 1) test-case reuse with varied parameters and 2) test-case re-entry for Target tests that need a reboot. This parameter is a VOID* and it is the responsibility of the test author to ensure that the contents are well understood by all test cases that may consume it. @retval UNIT_TEST_PASSED The Unit test has completed and the test case was successful. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestRegisterHandlerForGPAndPF ( IN UNIT_TEST_CONTEXT Context ) { EFI_STATUS Status; CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; UINTN PFAddress; VOID *NewIdtr; PFAddress = 0; CpuOriginalRegisterBuffer = SaveAllCpuRegisters (NULL, 0); NewIdtr = InitializeBspIdt (); Status = InitializeCpuExceptionHandlers (NULL); UT_ASSERT_EQUAL (Status, EFI_SUCCESS); // // GP exception. // DEBUG ((DEBUG_INFO, "TestCase2: ExceptionType is %d\n", EXCEPT_IA32_GP_FAULT)); Status = RegisterCpuInterruptHandler (EXCEPT_IA32_GP_FAULT, AdjustRipForFaultHandler); UT_ASSERT_EQUAL (Status, EFI_SUCCESS); TriggerGPException (CR4_RESERVED_BIT); UT_ASSERT_EQUAL (mExceptionType, EXCEPT_IA32_GP_FAULT); Status = RegisterCpuInterruptHandler (EXCEPT_IA32_GP_FAULT, NULL); UT_ASSERT_EQUAL (Status, EFI_SUCCESS); // // PF exception. // if (FindPFAddressInPageTable (&PFAddress)) { DEBUG ((DEBUG_INFO, "TestCase2: ExceptionType is %d\n", EXCEPT_IA32_PAGE_FAULT)); Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, AdjustRipForFaultHandler); UT_ASSERT_EQUAL (Status, EFI_SUCCESS); TriggerPFException (PFAddress); UT_ASSERT_EQUAL (mExceptionType, EXCEPT_IA32_PAGE_FAULT); Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, NULL); UT_ASSERT_EQUAL (Status, EFI_SUCCESS); } RestoreAllCpuRegisters (NULL, CpuOriginalRegisterBuffer, 0); FreePool (CpuOriginalRegisterBuffer); FreePool (NewIdtr); return UNIT_TEST_PASSED; } /** Test if Cpu Context is consistent before and after exception. @param[in] Context [Optional] An optional parameter that enables: 1) test-case reuse with varied parameters and 2) test-case re-entry for Target tests that need a reboot. This parameter is a VOID* and it is the responsibility of the test author to ensure that the contents are well understood by all test cases that may consume it. @retval UNIT_TEST_PASSED The Unit test has completed and the test case was successful. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestCpuContextConsistency ( IN UNIT_TEST_CONTEXT Context ) { EFI_STATUS Status; UINTN Index; CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; UINTN FaultParameter; VOID *NewIdtr; FaultParameter = 0; CpuOriginalRegisterBuffer = SaveAllCpuRegisters (NULL, 0); NewIdtr = InitializeBspIdt (); Status = InitializeCpuExceptionHandlers (NULL); UT_ASSERT_EQUAL (Status, EFI_SUCCESS); for (Index = 0; Index < 22; Index++) { if (Index == EXCEPT_IA32_PAGE_FAULT) { if (!FindPFAddressInPageTable (&FaultParameter)) { continue; } } else if (Index == EXCEPT_IA32_GP_FAULT) { FaultParameter = CR4_RESERVED_BIT; } else { if ((mErrorCodeExceptionFlag & (1 << Index)) != 0) { continue; } } DEBUG ((DEBUG_INFO, "TestCase3: ExceptionType is %d\n", Index)); Status = RegisterCpuInterruptHandler (Index, AdjustCpuContextHandler); UT_ASSERT_EQUAL (Status, EFI_SUCCESS); // // Trigger different type exception and compare different stage cpu context. // AsmTestConsistencyOfCpuContext (Index, FaultParameter); CompareCpuContext (); Status = RegisterCpuInterruptHandler (Index, NULL); UT_ASSERT_EQUAL (Status, EFI_SUCCESS); } RestoreAllCpuRegisters (NULL, CpuOriginalRegisterBuffer, 0); FreePool (CpuOriginalRegisterBuffer); FreePool (NewIdtr); return UNIT_TEST_PASSED; } /** Initializes CPU exceptions handlers for the sake of stack switch requirement. This function is a wrapper of InitializeSeparateExceptionStacks. It's mainly for the sake of AP's init because of EFI_AP_PROCEDURE API requirement. @param[in,out] Buffer The pointer to private data buffer. **/ VOID EFIAPI InitializeExceptionStackSwitchHandlersPerAp ( IN OUT VOID *Buffer ) { EXCEPTION_STACK_SWITCH_CONTEXT *CpuSwitchStackData; CpuSwitchStackData = (EXCEPTION_STACK_SWITCH_CONTEXT *)Buffer; // // This may be called twice for each Cpu. Only run InitializeSeparateExceptionStacks // if this is the first call or the first call failed because of size too small. // if ((CpuSwitchStackData->Status == EFI_NOT_STARTED) || (CpuSwitchStackData->Status == EFI_BUFFER_TOO_SMALL)) { CpuSwitchStackData->Status = InitializeSeparateExceptionStacks (CpuSwitchStackData->Buffer, &CpuSwitchStackData->BufferSize); } } /** Initializes MP exceptions handlers for the sake of stack switch requirement. This function will allocate required resources required to setup stack switch and pass them through SwitchStackData to each logic processor. @param[in, out] MpServices MpServices. @param[in, out] BspProcessorNum Bsp processor number. @return Pointer to the allocated SwitchStackData. **/ EXCEPTION_STACK_SWITCH_CONTEXT * InitializeMpExceptionStackSwitchHandlers ( MP_SERVICES MpServices, UINTN BspProcessorNum ) { UINTN Index; EXCEPTION_STACK_SWITCH_CONTEXT *SwitchStackData; UINTN BufferSize; EFI_STATUS Status; UINT8 *Buffer; SwitchStackData = AllocateZeroPool (mNumberOfProcessors * sizeof (EXCEPTION_STACK_SWITCH_CONTEXT)); ASSERT (SwitchStackData != NULL); for (Index = 0; Index < mNumberOfProcessors; ++Index) { // // Because the procedure may runs multiple times, use the status EFI_NOT_STARTED // to indicate the procedure haven't been run yet. // SwitchStackData[Index].Status = EFI_NOT_STARTED; if (Index == BspProcessorNum) { InitializeExceptionStackSwitchHandlersPerAp ((VOID *)&SwitchStackData[Index]); continue; } Status = MpServicesUnitTestStartupThisAP ( MpServices, InitializeExceptionStackSwitchHandlersPerAp, Index, 0, (VOID *)&SwitchStackData[Index] ); ASSERT_EFI_ERROR (Status); } BufferSize = 0; for (Index = 0; Index < mNumberOfProcessors; ++Index) { if (SwitchStackData[Index].Status == EFI_BUFFER_TOO_SMALL) { ASSERT (SwitchStackData[Index].BufferSize != 0); BufferSize += SwitchStackData[Index].BufferSize; } else { ASSERT (SwitchStackData[Index].Status == EFI_SUCCESS); ASSERT (SwitchStackData[Index].BufferSize == 0); } } if (BufferSize != 0) { Buffer = AllocateZeroPool (BufferSize); ASSERT (Buffer != NULL); BufferSize = 0; for (Index = 0; Index < mNumberOfProcessors; ++Index) { if (SwitchStackData[Index].Status == EFI_BUFFER_TOO_SMALL) { SwitchStackData[Index].Buffer = (VOID *)(&Buffer[BufferSize]); BufferSize += SwitchStackData[Index].BufferSize; DEBUG (( DEBUG_INFO, "Buffer[cpu%lu] for InitializeExceptionStackSwitchHandlersPerAp: 0x%lX with size 0x%lX\n", (UINT64)(UINTN)Index, (UINT64)(UINTN)SwitchStackData[Index].Buffer, (UINT64)(UINTN)SwitchStackData[Index].BufferSize )); } } for (Index = 0; Index < mNumberOfProcessors; ++Index) { if (Index == BspProcessorNum) { InitializeExceptionStackSwitchHandlersPerAp ((VOID *)&SwitchStackData[Index]); continue; } Status = MpServicesUnitTestStartupThisAP ( MpServices, InitializeExceptionStackSwitchHandlersPerAp, Index, 0, (VOID *)&SwitchStackData[Index] ); ASSERT_EFI_ERROR (Status); } for (Index = 0; Index < mNumberOfProcessors; ++Index) { ASSERT (SwitchStackData[Index].Status == EFI_SUCCESS); } } return SwitchStackData; } /** Test if stack overflow is captured by CpuStackGuard in both Bsp and AP. @param[in] Context [Optional] An optional parameter that enables: 1) test-case reuse with varied parameters and 2) test-case re-entry for Target tests that need a reboot. This parameter is a VOID* and it is the responsibility of the test author to ensure that the contents are well understood by all test cases that may consume it. @retval UNIT_TEST_PASSED The Unit test has completed and the test case was successful. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestCpuStackGuardInBspAndAp ( IN UNIT_TEST_CONTEXT Context ) { EFI_STATUS Status; UINTN OriginalStackBase; UINTN NewStackTop; UINTN NewStackBase; EXCEPTION_STACK_SWITCH_CONTEXT *SwitchStackData; MP_SERVICES MpServices; UINTN ProcessorNumber; UINTN EnabledProcessorNum; CPU_REGISTER_BUFFER *CpuOriginalRegisterBuffer; UINTN Index; UINTN BspProcessorNum; VOID *NewIdtr; UINTN *CpuStackBaseBuffer; if (!PcdGetBool (PcdCpuStackGuard)) { return UNIT_TEST_PASSED; } // // Get MP Service Protocol // Status = GetMpServices (&MpServices); Status = MpServicesUnitTestGetNumberOfProcessors (MpServices, &ProcessorNumber, &EnabledProcessorNum); UT_ASSERT_EQUAL (Status, EFI_SUCCESS); Status = MpServicesUnitTestWhoAmI (MpServices, &BspProcessorNum); UT_ASSERT_EQUAL (Status, EFI_SUCCESS); mNumberOfProcessors = ProcessorNumber; CpuOriginalRegisterBuffer = SaveAllCpuRegisters (&MpServices, BspProcessorNum); // // Initialize Bsp and AP Idt. // Idt buffer should not be empty or it will hang in MP API. // NewIdtr = InitializeBspIdt (); Status = InitializeCpuExceptionHandlers (NULL); UT_ASSERT_EQUAL (Status, EFI_SUCCESS); InitializeApIdt (MpServices, NewIdtr); // // Get BSP and AP original stack base. // CpuStackBaseBuffer = GetAllCpuStackBase (&MpServices, BspProcessorNum); // // InitializeMpExceptionStackSwitchHandlers and register exception handler. // SwitchStackData = InitializeMpExceptionStackSwitchHandlers (MpServices, BspProcessorNum); Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, CpuStackGuardExceptionHandler); UT_ASSERT_EQUAL (Status, EFI_SUCCESS); Status = RegisterCpuInterruptHandler (EXCEPT_IA32_DOUBLE_FAULT, AdjustRipForFaultHandler); UT_ASSERT_EQUAL (Status, EFI_SUCCESS); for (Index = 0; Index < mNumberOfProcessors; Index++) { OriginalStackBase = CpuStackBaseBuffer[Index]; NewStackTop = (UINTN)(SwitchStackData[Index].Buffer) + SwitchStackData[Index].BufferSize; NewStackBase = (UINTN)(SwitchStackData[Index].Buffer); if (Index == BspProcessorNum) { TriggerStackOverflow (); } else { MpServicesUnitTestStartupThisAP ( MpServices, (EFI_AP_PROCEDURE)TriggerStackOverflow, Index, 0, NULL ); } DEBUG ((DEBUG_INFO, "TestCase4: mRspAddress[0] is 0x%x, mRspAddress[1] is 0x%x\n", mRspAddress[0], mRspAddress[1])); UT_ASSERT_TRUE ((mRspAddress[0] >= OriginalStackBase) && (mRspAddress[0] <= (OriginalStackBase + SIZE_4KB))); UT_ASSERT_TRUE ((mRspAddress[1] >= NewStackBase) && (mRspAddress[1] < NewStackTop)); } Status = RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, NULL); UT_ASSERT_EQUAL (Status, EFI_SUCCESS); Status = RegisterCpuInterruptHandler (EXCEPT_IA32_DOUBLE_FAULT, NULL); UT_ASSERT_EQUAL (Status, EFI_SUCCESS); RestoreAllCpuRegisters (&MpServices, CpuOriginalRegisterBuffer, BspProcessorNum); FreePool (SwitchStackData); FreePool (CpuOriginalRegisterBuffer); FreePool (NewIdtr); return UNIT_TEST_PASSED; } /** Create CpuExceptionLibUnitTestSuite and add test case. @param[in] FrameworkHandle Unit test framework. @return EFI_SUCCESS The unit test suite was created. @retval EFI_OUT_OF_RESOURCES There are not enough resources available to initialize the unit test suite. **/ EFI_STATUS AddCommonTestCase ( IN UNIT_TEST_FRAMEWORK_HANDLE Framework ) { EFI_STATUS Status; UNIT_TEST_SUITE_HANDLE CpuExceptionLibUnitTestSuite; // // Populate the Manual Test Cases. // Status = CreateUnitTestSuite (&CpuExceptionLibUnitTestSuite, Framework, "Test CpuExceptionHandlerLib", "CpuExceptionHandlerLib.Manual", NULL, NULL); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for CpuExceptionHandlerLib Test Cases\n")); Status = EFI_OUT_OF_RESOURCES; return Status; } AddTestCase (CpuExceptionLibUnitTestSuite, "Check if exception handler can be registered/unregistered for no error code exception", "TestRegisterHandlerForNoErrorCodeException", TestRegisterHandlerForNoErrorCodeException, NULL, NULL, NULL); AddTestCase (CpuExceptionLibUnitTestSuite, "Check if exception handler can be registered/unregistered for GP and PF", "TestRegisterHandlerForGPAndPF", TestRegisterHandlerForGPAndPF, NULL, NULL, NULL); AddTestCase (CpuExceptionLibUnitTestSuite, "Check if Cpu Context is consistent before and after exception.", "TestCpuContextConsistency", TestCpuContextConsistency, NULL, NULL, NULL); AddTestCase (CpuExceptionLibUnitTestSuite, "Check if stack overflow is captured by CpuStackGuard in Bsp and AP", "TestCpuStackGuardInBspAndAp", TestCpuStackGuardInBspAndAp, NULL, NULL, NULL); return EFI_SUCCESS; }