2020-02-26 23:11:54 +01:00
|
|
|
/** @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.
|
|
|
|
|
2021-09-16 11:27:11 +02:00
|
|
|
Copyright (c) 2013 - 2021, Intel Corporation. All rights reserved.<BR>
|
2020-02-26 23:11:54 +01:00
|
|
|
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>
|
2020-02-26 23:11:55 +01:00
|
|
|
#include <Library/MtrrLib.h>
|
|
|
|
#include <Library/UefiBootServicesTableLib.h>
|
2020-02-26 23:11:54 +01:00
|
|
|
|
|
|
|
#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;
|
|
|
|
|
2021-01-19 16:54:40 +01:00
|
|
|
if (PcdGetBool (PcdQ35SmramAtDefaultSmbase)) {
|
OvmfPkg/CpuS3DataDxe: enable S3 resume after CPU hotplug
During normal boot, CpuS3DataDxe allocates
- an empty CPU_REGISTER_TABLE entry in the
"ACPI_CPU_DATA.PreSmmInitRegisterTable" array, and
- an empty CPU_REGISTER_TABLE entry in the "ACPI_CPU_DATA.RegisterTable"
array,
for every CPU whose APIC ID CpuS3DataDxe can learn.
Currently EFI_MP_SERVICES_PROTOCOL is used for both determining the number
of CPUs -- the protocol reports the present-at-boot CPU count --, and for
retrieving the APIC IDs of those CPUs.
Consequently, if a CPU is hot-plugged at OS runtime, then S3 resume
breaks. That's because PiSmmCpuDxeSmm will not find the hot-added CPU's
APIC ID associated with any CPU_REGISTER_TABLE object, in the SMRAM copies
of either of the "RegisterTable" and "PreSmmInitRegisterTable" arrays. The
failure to match the hot-added CPU's APIC ID trips the ASSERT() in
SetRegister() [UefiCpuPkg/PiSmmCpuDxeSmm/CpuS3.c].
If "PcdQ35SmramAtDefaultSmbase" is TRUE, then:
- prepare CPU_REGISTER_TABLE objects for all possible CPUs, not just the
present-at-boot CPUs (PlatformPei stored the possible CPU count to
"PcdCpuMaxLogicalProcessorNumber");
- use QEMU_CPUHP_CMD_GET_ARCH_ID for filling in the "InitialApicId" fields
of the CPU_REGISTER_TABLE objects.
This provides full APIC ID coverage for PiSmmCpuDxeSmm during S3 resume,
accommodating CPUs hot-added at OS runtime.
This patch is best reviewed with
$ git show -b
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Igor Mammedov <imammedo@redhat.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Michael Kinney <michael.d.kinney@intel.com>
Cc: Philippe Mathieu-Daudé <philmd@redhat.com>
Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=1512
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Message-Id: <20200226221156.29589-17-lersek@redhat.com>
Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Tested-by: Boris Ostrovsky <boris.ostrovsky@oracle.com>
2020-02-26 23:11:56 +01:00
|
|
|
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);
|
|
|
|
}
|
2020-02-26 23:11:54 +01:00
|
|
|
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) {
|
2021-09-16 11:27:11 +02:00
|
|
|
CopyMem (&AcpiCpuData->CpuFeatureInitData, &OldAcpiCpuData->CpuFeatureInitData, sizeof (CPU_FEATURE_INIT_DATA));
|
2020-02-26 23:11:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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;
|
|
|
|
}
|