__FUNCTION__ is a pre-standard extension that gcc and Visual C++ among others support, while __func__ was standardized in C99. Since it's more standard, replace __FUNCTION__ with __func__ throughout UefiCpuPkg. Signed-off-by: Rebecca Cran <rebecca@bsdio.com> Reviewed-by: Michael D Kinney <michael.d.kinney@intel.com> Reviewed-by: Ard Biesheuvel <ardb@kernel.org> Reviewed-by: Ray Ni <ray.ni@intel.com> Reviewed-by: Sunil V L <sunilvl@ventanamicro.com>
		
			
				
	
	
		
			853 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			853 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  Unit tests of the CpuExceptionHandlerLib.
 | 
						|
 | 
						|
  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
 | 
						|
  SPDX-License-Identifier: BSD-2-Clause-Patent
 | 
						|
 | 
						|
**/
 | 
						|
 | 
						|
#include "CpuExceptionHandlerTest.h"
 | 
						|
 | 
						|
//
 | 
						|
// Length of the assembly falut instruction.
 | 
						|
//
 | 
						|
UINTN               mFaultInstructionLength = 0;
 | 
						|
EFI_EXCEPTION_TYPE  mExceptionType          = 256;
 | 
						|
UINTN               mNumberOfProcessors     = 1;
 | 
						|
UINTN               mRspAddress[2]          = { 0 };
 | 
						|
 | 
						|
//
 | 
						|
// Error code flag indicating whether or not an error code will be
 | 
						|
// pushed on the stack if an exception occurs.
 | 
						|
//
 | 
						|
// 1 means an error code will be pushed, otherwise 0
 | 
						|
//
 | 
						|
CONST UINT32  mErrorCodeExceptionFlag = 0x20227d00;
 | 
						|
 | 
						|
/**
 | 
						|
  Special handler for exception triggered by INTn instruction.
 | 
						|
  This hanlder only modifies a global variable for check.
 | 
						|
 | 
						|
  @param ExceptionType  Exception type.
 | 
						|
  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EFIAPI
 | 
						|
INTnExceptionHandler (
 | 
						|
  IN EFI_EXCEPTION_TYPE  ExceptionType,
 | 
						|
  IN EFI_SYSTEM_CONTEXT  SystemContext
 | 
						|
  )
 | 
						|
{
 | 
						|
  mExceptionType = ExceptionType;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Restore cpu original registers before exit test case.
 | 
						|
 | 
						|
  @param[in] Buffer  Argument of the procedure.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EFIAPI
 | 
						|
RestoreRegistersPerCpu (
 | 
						|
  IN VOID  *Buffer
 | 
						|
  )
 | 
						|
{
 | 
						|
  CPU_REGISTER_BUFFER  *CpuOriginalRegisterBuffer;
 | 
						|
  UINT16               Tr;
 | 
						|
  IA32_TSS_DESCRIPTOR  *Tss;
 | 
						|
 | 
						|
  CpuOriginalRegisterBuffer = (CPU_REGISTER_BUFFER *)Buffer;
 | 
						|
 | 
						|
  AsmWriteGdtr (&(CpuOriginalRegisterBuffer->OriginalGdtr));
 | 
						|
  AsmWriteIdtr (&(CpuOriginalRegisterBuffer->OriginalIdtr));
 | 
						|
  Tr = CpuOriginalRegisterBuffer->Tr;
 | 
						|
  if ((Tr != 0) && (Tr < CpuOriginalRegisterBuffer->OriginalGdtr.Limit)) {
 | 
						|
    Tss = (IA32_TSS_DESCRIPTOR *)(CpuOriginalRegisterBuffer->OriginalGdtr.Base + Tr);
 | 
						|
    if (Tss->Bits.P == 1) {
 | 
						|
      //
 | 
						|
      // Clear busy bit of TSS before write Tr
 | 
						|
      //
 | 
						|
      Tss->Bits.Type &= 0xD;
 | 
						|
      AsmWriteTr (Tr);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Restore cpu original registers before exit test case.
 | 
						|
 | 
						|
  @param[in] MpServices                 MpServices.
 | 
						|
  @param[in] CpuOriginalRegisterBuffer  Address of CpuOriginalRegisterBuffer.
 | 
						|
  @param[in] BspProcessorNum            Bsp processor number.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
RestoreAllCpuRegisters (
 | 
						|
  MP_SERVICES *MpServices, OPTIONAL
 | 
						|
  CPU_REGISTER_BUFFER  *CpuOriginalRegisterBuffer,
 | 
						|
  UINTN                         BspProcessorNum
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN       Index;
 | 
						|
  EFI_STATUS  Status;
 | 
						|
 | 
						|
  for (Index = 0; Index < mNumberOfProcessors; ++Index) {
 | 
						|
    if (Index == BspProcessorNum) {
 | 
						|
      RestoreRegistersPerCpu ((VOID *)&CpuOriginalRegisterBuffer[Index]);
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    ASSERT (MpServices != NULL);
 | 
						|
    Status = MpServicesUnitTestStartupThisAP (
 | 
						|
               *MpServices,
 | 
						|
               (EFI_AP_PROCEDURE)RestoreRegistersPerCpu,
 | 
						|
               Index,
 | 
						|
               0,
 | 
						|
               (VOID *)&CpuOriginalRegisterBuffer[Index]
 | 
						|
               );
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Store cpu registers before the test case starts.
 | 
						|
 | 
						|
  @param[in] Buffer  Argument of the procedure.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EFIAPI
 | 
						|
SaveRegisterPerCpu (
 | 
						|
  IN VOID  *Buffer
 | 
						|
  )
 | 
						|
{
 | 
						|
  CPU_REGISTER_BUFFER  *CpuOriginalRegisterBuffer;
 | 
						|
  IA32_DESCRIPTOR      Gdtr;
 | 
						|
  IA32_DESCRIPTOR      Idtr;
 | 
						|
 | 
						|
  CpuOriginalRegisterBuffer = (CPU_REGISTER_BUFFER *)Buffer;
 | 
						|
 | 
						|
  AsmReadGdtr (&Gdtr);
 | 
						|
  AsmReadIdtr (&Idtr);
 | 
						|
  CpuOriginalRegisterBuffer->OriginalGdtr.Base  = Gdtr.Base;
 | 
						|
  CpuOriginalRegisterBuffer->OriginalGdtr.Limit = Gdtr.Limit;
 | 
						|
  CpuOriginalRegisterBuffer->OriginalIdtr.Base  = Idtr.Base;
 | 
						|
  CpuOriginalRegisterBuffer->OriginalIdtr.Limit = Idtr.Limit;
 | 
						|
  CpuOriginalRegisterBuffer->Tr                 = AsmReadTr ();
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Store cpu registers before the test case starts.
 | 
						|
 | 
						|
  @param[in] MpServices       MpServices.
 | 
						|
  @param[in] BspProcessorNum  Bsp processor number.
 | 
						|
 | 
						|
  @return Pointer to the allocated CPU_REGISTER_BUFFER.
 | 
						|
**/
 | 
						|
CPU_REGISTER_BUFFER *
 | 
						|
SaveAllCpuRegisters (
 | 
						|
  MP_SERVICES *MpServices, OPTIONAL
 | 
						|
  UINTN       BspProcessorNum
 | 
						|
  )
 | 
						|
{
 | 
						|
  CPU_REGISTER_BUFFER  *CpuOriginalRegisterBuffer;
 | 
						|
  EFI_STATUS           Status;
 | 
						|
  UINTN                Index;
 | 
						|
 | 
						|
  CpuOriginalRegisterBuffer = AllocateZeroPool (mNumberOfProcessors * sizeof (CPU_REGISTER_BUFFER));
 | 
						|
  ASSERT (CpuOriginalRegisterBuffer != NULL);
 | 
						|
 | 
						|
  for (Index = 0; Index < mNumberOfProcessors; ++Index) {
 | 
						|
    if (Index == BspProcessorNum) {
 | 
						|
      SaveRegisterPerCpu ((VOID *)&CpuOriginalRegisterBuffer[Index]);
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    ASSERT (MpServices != NULL);
 | 
						|
    Status = MpServicesUnitTestStartupThisAP (
 | 
						|
               *MpServices,
 | 
						|
               (EFI_AP_PROCEDURE)SaveRegisterPerCpu,
 | 
						|
               Index,
 | 
						|
               0,
 | 
						|
               (VOID *)&CpuOriginalRegisterBuffer[Index]
 | 
						|
               );
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
  }
 | 
						|
 | 
						|
  return CpuOriginalRegisterBuffer;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Initialize Ap Idt Procedure.
 | 
						|
 | 
						|
  @param[in] Buffer  Argument of the procedure.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EFIAPI
 | 
						|
InitializeIdtPerAp (
 | 
						|
  IN VOID  *Buffer
 | 
						|
  )
 | 
						|
{
 | 
						|
  AsmWriteIdtr (Buffer);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Initialize all Ap Idt.
 | 
						|
 | 
						|
  @param[in] MpServices MpServices.
 | 
						|
  @param[in] BspIdtr    Pointer to IA32_DESCRIPTOR allocated by Bsp.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
InitializeApIdt (
 | 
						|
  MP_SERVICES  MpServices,
 | 
						|
  VOID         *BspIdtr
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS  Status;
 | 
						|
 | 
						|
  Status = MpServicesUnitTestStartupAllAPs (
 | 
						|
             MpServices,
 | 
						|
             (EFI_AP_PROCEDURE)InitializeIdtPerAp,
 | 
						|
             FALSE,
 | 
						|
             0,
 | 
						|
             BspIdtr
 | 
						|
             );
 | 
						|
  ASSERT_EFI_ERROR (Status);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Check if exception handler can registered/unregistered for no error code exception.
 | 
						|
 | 
						|
  @param[in]  Context    [Optional] An optional parameter that enables:
 | 
						|
                         1) test-case reuse with varied parameters and
 | 
						|
                         2) test-case re-entry for Target tests that need a
 | 
						|
                         reboot.  This parameter is a VOID* and it is the
 | 
						|
                         responsibility of the test author to ensure that the
 | 
						|
                         contents are well understood by all test cases that may
 | 
						|
                         consume it.
 | 
						|
 | 
						|
  @retval  UNIT_TEST_PASSED             The Unit test has completed and the test
 | 
						|
                                        case was successful.
 | 
						|
  @retval  UNIT_TEST_ERROR_TEST_FAILED  A test case assertion has failed.
 | 
						|
**/
 | 
						|
UNIT_TEST_STATUS
 | 
						|
EFIAPI
 | 
						|
TestRegisterHandlerForNoErrorCodeException (
 | 
						|
  IN UNIT_TEST_CONTEXT  Context
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS           Status;
 | 
						|
  UINTN                Index;
 | 
						|
  CPU_REGISTER_BUFFER  *CpuOriginalRegisterBuffer;
 | 
						|
  VOID                 *NewIdtr;
 | 
						|
 | 
						|
  CpuOriginalRegisterBuffer = SaveAllCpuRegisters (NULL, 0);
 | 
						|
  NewIdtr                   = InitializeBspIdt ();
 | 
						|
  Status                    = InitializeCpuExceptionHandlers (NULL);
 | 
						|
  UT_ASSERT_EQUAL (Status, EFI_SUCCESS);
 | 
						|
 | 
						|
  for (Index = 0; Index < SPEC_MAX_EXCEPTION_NUM; Index++) {
 | 
						|
    //
 | 
						|
    // Only test no error code exception by INT n instruction.
 | 
						|
    //
 | 
						|
    if ((mErrorCodeExceptionFlag & (1 << Index)) != 0) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    DEBUG ((DEBUG_INFO, "TestCase1: ExceptionType is %d\n", Index));
 | 
						|
    Status = RegisterCpuInterruptHandler (Index, INTnExceptionHandler);
 | 
						|
    UT_ASSERT_EQUAL (Status, EFI_SUCCESS);
 | 
						|
 | 
						|
    TriggerINTnException (Index);
 | 
						|
    UT_ASSERT_EQUAL (mExceptionType, Index);
 | 
						|
    Status = RegisterCpuInterruptHandler (Index, NULL);
 | 
						|
    UT_ASSERT_EQUAL (Status, EFI_SUCCESS);
 | 
						|
  }
 | 
						|
 | 
						|
  RestoreAllCpuRegisters (NULL, CpuOriginalRegisterBuffer, 0);
 | 
						|
  FreePool (CpuOriginalRegisterBuffer);
 | 
						|
  FreePool (NewIdtr);
 | 
						|
  return UNIT_TEST_PASSED;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Get Bsp stack base.
 | 
						|
 | 
						|
  @param[out] StackBase  Pointer to stack base of BSP.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
GetBspStackBase (
 | 
						|
  OUT UINTN  *StackBase
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_PEI_HOB_POINTERS       Hob;
 | 
						|
  EFI_HOB_MEMORY_ALLOCATION  *MemoryHob;
 | 
						|
 | 
						|
  //
 | 
						|
  // Get the base of stack from Hob.
 | 
						|
  //
 | 
						|
  ASSERT (StackBase != NULL);
 | 
						|
  Hob.Raw = GetHobList ();
 | 
						|
  while ((Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw)) != NULL) {
 | 
						|
    MemoryHob = Hob.MemoryAllocation;
 | 
						|
    if (CompareGuid (&gEfiHobMemoryAllocStackGuid, &MemoryHob->AllocDescriptor.Name)) {
 | 
						|
      DEBUG ((
 | 
						|
        DEBUG_INFO,
 | 
						|
        "%a: Bsp StackBase = 0x%016lx  StackSize = 0x%016lx\n",
 | 
						|
        __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;
 | 
						|
}
 |