CpuS3DataDxe allocates the "RegisterTable" and "PreSmmInitRegisterTable" arrays in ACPI_CPU_DATA just so every processor in the system can have its own empty register table, matched by APIC ID. This has never been useful in practice. Given commite992cc3f48("UefiCpuPkg PiSmmCpuDxeSmm: Reduce SMRAM consumption in CpuS3.c", 2021-01-11), simply leave both "AcpiCpuData->RegisterTable" and "AcpiCpuData->PreSmmInitRegisterTable" initialized to the zero address. This simplifies the driver, and saves both normal RAM (boot services data type memory) and -- in PiSmmCpuDxeSmm -- SMRAM. (This simplification backs out a good chunk of commit1158fc8e2c("OvmfPkg/CpuS3DataDxe: enable S3 resume after CPU hotplug", 2020-03-04). But CpuS3DataDxe still differs between UefiCpuPkg and OvmfPkg, due to the latter supporting CPU hotplug; thus, we can't remove OvmfPkg/CpuS3DataDxe altogether.) Cc: Ard Biesheuvel <ard.biesheuvel@arm.com> Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Philippe Mathieu-Daudé <philmd@redhat.com> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=3159 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Acked-by: Ard Biesheuvel <ard.biesheuvel@arm.com> Message-Id: <20210119155440.2262-5-lersek@redhat.com> Reviewed-by: Star Zeng <star.zeng@intel.com>
		
			
				
	
	
		
			283 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			283 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
ACPI CPU Data initialization module
 | 
						|
 | 
						|
This module initializes the ACPI_CPU_DATA structure and registers the address
 | 
						|
of this structure in the PcdCpuS3DataAddress PCD.  This is a generic/simple
 | 
						|
version of this module.  It does not provide a machine check handler or CPU
 | 
						|
register initialization tables for ACPI S3 resume.  It also only supports the
 | 
						|
number of CPUs reported by the MP Services Protocol, so this module does not
 | 
						|
support hot plug CPUs.  This module can be copied into a CPU specific package
 | 
						|
and customized if these additional features are required.
 | 
						|
 | 
						|
Copyright (c) 2013 - 2017, Intel Corporation. All rights reserved.<BR>
 | 
						|
Copyright (c) 2015 - 2020, Red Hat, Inc.
 | 
						|
 | 
						|
SPDX-License-Identifier: BSD-2-Clause-Patent
 | 
						|
 | 
						|
**/
 | 
						|
 | 
						|
#include <PiDxe.h>
 | 
						|
 | 
						|
#include <AcpiCpuData.h>
 | 
						|
 | 
						|
#include <Library/BaseLib.h>
 | 
						|
#include <Library/BaseMemoryLib.h>
 | 
						|
#include <Library/DebugLib.h>
 | 
						|
#include <Library/MemoryAllocationLib.h>
 | 
						|
#include <Library/MtrrLib.h>
 | 
						|
#include <Library/UefiBootServicesTableLib.h>
 | 
						|
 | 
						|
#include <Protocol/MpService.h>
 | 
						|
#include <Guid/EventGroup.h>
 | 
						|
 | 
						|
//
 | 
						|
// Data structure used to allocate ACPI_CPU_DATA and its supporting structures
 | 
						|
//
 | 
						|
typedef struct {
 | 
						|
  ACPI_CPU_DATA             AcpiCpuData;
 | 
						|
  MTRR_SETTINGS             MtrrTable;
 | 
						|
  IA32_DESCRIPTOR           GdtrProfile;
 | 
						|
  IA32_DESCRIPTOR           IdtrProfile;
 | 
						|
} ACPI_CPU_DATA_EX;
 | 
						|
 | 
						|
/**
 | 
						|
  Allocate EfiACPIMemoryNVS memory.
 | 
						|
 | 
						|
  @param[in] Size   Size of memory to allocate.
 | 
						|
 | 
						|
  @return       Allocated address for output.
 | 
						|
 | 
						|
**/
 | 
						|
VOID *
 | 
						|
AllocateAcpiNvsMemory (
 | 
						|
  IN UINTN  Size
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_PHYSICAL_ADDRESS  Address;
 | 
						|
  EFI_STATUS            Status;
 | 
						|
  VOID                  *Buffer;
 | 
						|
 | 
						|
  Status  = gBS->AllocatePages (
 | 
						|
                   AllocateAnyPages,
 | 
						|
                   EfiACPIMemoryNVS,
 | 
						|
                   EFI_SIZE_TO_PAGES (Size),
 | 
						|
                   &Address
 | 
						|
                   );
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  Buffer = (VOID *)(UINTN)Address;
 | 
						|
  ZeroMem (Buffer, Size);
 | 
						|
 | 
						|
  return Buffer;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Allocate memory and clean it with zero.
 | 
						|
 | 
						|
  @param[in] Size   Size of memory to allocate.
 | 
						|
 | 
						|
  @return       Allocated address for output.
 | 
						|
 | 
						|
**/
 | 
						|
VOID *
 | 
						|
AllocateZeroPages (
 | 
						|
  IN UINTN  Size
 | 
						|
  )
 | 
						|
{
 | 
						|
  VOID                  *Buffer;
 | 
						|
 | 
						|
  Buffer = AllocatePages (EFI_SIZE_TO_PAGES (Size));
 | 
						|
  if (Buffer != NULL) {
 | 
						|
    ZeroMem (Buffer, Size);
 | 
						|
  }
 | 
						|
 | 
						|
  return Buffer;
 | 
						|
}
 | 
						|
/**
 | 
						|
  Callback function executed when the EndOfDxe event group is signaled.
 | 
						|
 | 
						|
  We delay allocating StartupVector and saving the MTRR settings until BDS signals EndOfDxe.
 | 
						|
 | 
						|
  @param[in]  Event    Event whose notification function is being invoked.
 | 
						|
  @param[out] Context  Pointer to the MTRR_SETTINGS buffer to fill in.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EFIAPI
 | 
						|
CpuS3DataOnEndOfDxe (
 | 
						|
  IN  EFI_EVENT  Event,
 | 
						|
  OUT VOID       *Context
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS         Status;
 | 
						|
  ACPI_CPU_DATA_EX   *AcpiCpuDataEx;
 | 
						|
 | 
						|
  AcpiCpuDataEx = (ACPI_CPU_DATA_EX *) Context;
 | 
						|
  //
 | 
						|
  // Allocate a 4KB reserved page below 1MB
 | 
						|
  //
 | 
						|
  AcpiCpuDataEx->AcpiCpuData.StartupVector = BASE_1MB - 1;
 | 
						|
  Status = gBS->AllocatePages (
 | 
						|
                  AllocateMaxAddress,
 | 
						|
                  EfiReservedMemoryType,
 | 
						|
                  1,
 | 
						|
                  &AcpiCpuDataEx->AcpiCpuData.StartupVector
 | 
						|
                  );
 | 
						|
  ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
  DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__));
 | 
						|
  MtrrGetAllMtrrs (&AcpiCpuDataEx->MtrrTable);
 | 
						|
 | 
						|
  //
 | 
						|
  // Close event, so it will not be invoked again.
 | 
						|
  //
 | 
						|
  gBS->CloseEvent (Event);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
   The entry function of the CpuS3Data driver.
 | 
						|
 | 
						|
   Allocate and initialize all fields of the ACPI_CPU_DATA structure except the
 | 
						|
   MTRR settings.  Register an event notification on gEfiEndOfDxeEventGroupGuid
 | 
						|
   to capture the ACPI_CPU_DATA MTRR settings.  The PcdCpuS3DataAddress is set
 | 
						|
   to the address that ACPI_CPU_DATA is allocated at.
 | 
						|
 | 
						|
   @param[in] ImageHandle  The firmware allocated handle for the EFI image.
 | 
						|
   @param[in] SystemTable  A pointer to the EFI System Table.
 | 
						|
 | 
						|
   @retval EFI_SUCCESS     The entry point is executed successfully.
 | 
						|
   @retval EFI_UNSUPPORTED Do not support ACPI S3.
 | 
						|
   @retval other           Some error occurs when executing this entry point.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
CpuS3DataInitialize (
 | 
						|
  IN EFI_HANDLE        ImageHandle,
 | 
						|
  IN EFI_SYSTEM_TABLE  *SystemTable
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                 Status;
 | 
						|
  ACPI_CPU_DATA_EX           *AcpiCpuDataEx;
 | 
						|
  ACPI_CPU_DATA              *AcpiCpuData;
 | 
						|
  EFI_MP_SERVICES_PROTOCOL   *MpServices;
 | 
						|
  UINTN                      NumberOfCpus;
 | 
						|
  VOID                       *Stack;
 | 
						|
  UINTN                      GdtSize;
 | 
						|
  UINTN                      IdtSize;
 | 
						|
  VOID                       *Gdt;
 | 
						|
  VOID                       *Idt;
 | 
						|
  EFI_EVENT                  Event;
 | 
						|
  ACPI_CPU_DATA              *OldAcpiCpuData;
 | 
						|
 | 
						|
  if (!PcdGetBool (PcdAcpiS3Enable)) {
 | 
						|
    return EFI_UNSUPPORTED;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Set PcdCpuS3DataAddress to the base address of the ACPI_CPU_DATA structure
 | 
						|
  //
 | 
						|
  OldAcpiCpuData = (ACPI_CPU_DATA *) (UINTN) PcdGet64 (PcdCpuS3DataAddress);
 | 
						|
 | 
						|
  AcpiCpuDataEx = AllocateZeroPages (sizeof (ACPI_CPU_DATA_EX));
 | 
						|
  ASSERT (AcpiCpuDataEx != NULL);
 | 
						|
  AcpiCpuData = &AcpiCpuDataEx->AcpiCpuData;
 | 
						|
 | 
						|
  if (PcdGetBool (PcdQ35SmramAtDefaultSmbase)) {
 | 
						|
    NumberOfCpus = PcdGet32 (PcdCpuMaxLogicalProcessorNumber);
 | 
						|
  } else {
 | 
						|
    UINTN NumberOfEnabledProcessors;
 | 
						|
 | 
						|
    //
 | 
						|
    // Get MP Services Protocol
 | 
						|
    //
 | 
						|
    Status = gBS->LocateProtocol (
 | 
						|
                    &gEfiMpServiceProtocolGuid,
 | 
						|
                    NULL,
 | 
						|
                    (VOID **)&MpServices
 | 
						|
                    );
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
    //
 | 
						|
    // Get the number of CPUs
 | 
						|
    //
 | 
						|
    Status = MpServices->GetNumberOfProcessors (
 | 
						|
                           MpServices,
 | 
						|
                           &NumberOfCpus,
 | 
						|
                           &NumberOfEnabledProcessors
 | 
						|
                           );
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
  }
 | 
						|
  AcpiCpuData->NumberOfCpus = (UINT32)NumberOfCpus;
 | 
						|
 | 
						|
  //
 | 
						|
  // Initialize ACPI_CPU_DATA fields
 | 
						|
  //
 | 
						|
  AcpiCpuData->StackSize                 = PcdGet32 (PcdCpuApStackSize);
 | 
						|
  AcpiCpuData->ApMachineCheckHandlerBase = 0;
 | 
						|
  AcpiCpuData->ApMachineCheckHandlerSize = 0;
 | 
						|
  AcpiCpuData->GdtrProfile  = (EFI_PHYSICAL_ADDRESS)(UINTN)&AcpiCpuDataEx->GdtrProfile;
 | 
						|
  AcpiCpuData->IdtrProfile  = (EFI_PHYSICAL_ADDRESS)(UINTN)&AcpiCpuDataEx->IdtrProfile;
 | 
						|
  AcpiCpuData->MtrrTable    = (EFI_PHYSICAL_ADDRESS)(UINTN)&AcpiCpuDataEx->MtrrTable;
 | 
						|
 | 
						|
  //
 | 
						|
  // Allocate stack space for all CPUs.
 | 
						|
  // Use ACPI NVS memory type because this data will be directly used by APs
 | 
						|
  // in S3 resume phase in long mode. Also during S3 resume, the stack buffer
 | 
						|
  // will only be used as scratch space. i.e. we won't read anything from it
 | 
						|
  // before we write to it, in PiSmmCpuDxeSmm.
 | 
						|
  //
 | 
						|
  Stack = AllocateAcpiNvsMemory (NumberOfCpus * AcpiCpuData->StackSize);
 | 
						|
  ASSERT (Stack != NULL);
 | 
						|
  AcpiCpuData->StackAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Stack;
 | 
						|
 | 
						|
  //
 | 
						|
  // Get the boot processor's GDT and IDT
 | 
						|
  //
 | 
						|
  AsmReadGdtr (&AcpiCpuDataEx->GdtrProfile);
 | 
						|
  AsmReadIdtr (&AcpiCpuDataEx->IdtrProfile);
 | 
						|
 | 
						|
  //
 | 
						|
  // Allocate GDT and IDT and copy current GDT and IDT contents
 | 
						|
  //
 | 
						|
  GdtSize = AcpiCpuDataEx->GdtrProfile.Limit + 1;
 | 
						|
  IdtSize = AcpiCpuDataEx->IdtrProfile.Limit + 1;
 | 
						|
  Gdt = AllocateZeroPages (GdtSize + IdtSize);
 | 
						|
  ASSERT (Gdt != NULL);
 | 
						|
  Idt = (VOID *)((UINTN)Gdt + GdtSize);
 | 
						|
  CopyMem (Gdt, (VOID *)AcpiCpuDataEx->GdtrProfile.Base, GdtSize);
 | 
						|
  CopyMem (Idt, (VOID *)AcpiCpuDataEx->IdtrProfile.Base, IdtSize);
 | 
						|
  AcpiCpuDataEx->GdtrProfile.Base = (UINTN)Gdt;
 | 
						|
  AcpiCpuDataEx->IdtrProfile.Base = (UINTN)Idt;
 | 
						|
 | 
						|
  if (OldAcpiCpuData != NULL) {
 | 
						|
    AcpiCpuData->RegisterTable           = OldAcpiCpuData->RegisterTable;
 | 
						|
    AcpiCpuData->PreSmmInitRegisterTable = OldAcpiCpuData->PreSmmInitRegisterTable;
 | 
						|
    AcpiCpuData->ApLocation              = OldAcpiCpuData->ApLocation;
 | 
						|
    CopyMem (&AcpiCpuData->CpuStatus, &OldAcpiCpuData->CpuStatus, sizeof (CPU_STATUS_INFORMATION));
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Set PcdCpuS3DataAddress to the base address of the ACPI_CPU_DATA structure
 | 
						|
  //
 | 
						|
  Status = PcdSet64S (PcdCpuS3DataAddress, (UINT64)(UINTN)AcpiCpuData);
 | 
						|
  ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
  //
 | 
						|
  // Register EFI_END_OF_DXE_EVENT_GROUP_GUID event.
 | 
						|
  // The notification function allocates StartupVector and saves MTRRs for ACPI_CPU_DATA
 | 
						|
  //
 | 
						|
  Status = gBS->CreateEventEx (
 | 
						|
                  EVT_NOTIFY_SIGNAL,
 | 
						|
                  TPL_CALLBACK,
 | 
						|
                  CpuS3DataOnEndOfDxe,
 | 
						|
                  AcpiCpuData,
 | 
						|
                  &gEfiEndOfDxeEventGroupGuid,
 | 
						|
                  &Event
 | 
						|
                  );
 | 
						|
  ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 |