[edk2-devel] [PATCH 16/16] OvmfPkg/CpuS3DataDxe: enable S3 resume after CPU hotplug
Laszlo Ersek
lersek at redhat.com
Sun Feb 23 17:25:37 UTC 2020
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 at linaro.org>
Cc: Igor Mammedov <imammedo at redhat.com>
Cc: Jiewen Yao <jiewen.yao at intel.com>
Cc: Jordan Justen <jordan.l.justen at intel.com>
Cc: Michael Kinney <michael.d.kinney at intel.com>
Cc: Philippe Mathieu-Daudé <philmd at redhat.com>
Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=1512
Signed-off-by: Laszlo Ersek <lersek at redhat.com>
---
OvmfPkg/CpuS3DataDxe/CpuS3DataDxe.inf | 4 +
OvmfPkg/CpuS3DataDxe/CpuS3Data.c | 91 ++++++++++++++------
2 files changed, 70 insertions(+), 25 deletions(-)
diff --git a/OvmfPkg/CpuS3DataDxe/CpuS3DataDxe.inf b/OvmfPkg/CpuS3DataDxe/CpuS3DataDxe.inf
index f9679e0c33b3..ceae1d4078c7 100644
--- a/OvmfPkg/CpuS3DataDxe/CpuS3DataDxe.inf
+++ b/OvmfPkg/CpuS3DataDxe/CpuS3DataDxe.inf
@@ -16,46 +16,50 @@
#
##
[Defines]
INF_VERSION = 1.29
BASE_NAME = CpuS3DataDxe
FILE_GUID = 229B7EFD-DA02-46B9-93F4-E20C009F94E9
MODULE_TYPE = DXE_DRIVER
VERSION_STRING = 1.0
ENTRY_POINT = CpuS3DataInitialize
# The following information is for reference only and not required by the build
# tools.
#
# VALID_ARCHITECTURES = IA32 X64
[Sources]
CpuS3Data.c
[Packages]
MdeModulePkg/MdeModulePkg.dec
MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
UefiCpuPkg/UefiCpuPkg.dec
[LibraryClasses]
BaseLib
BaseMemoryLib
DebugLib
+ IoLib
MemoryAllocationLib
MtrrLib
UefiBootServicesTableLib
UefiDriverEntryPoint
[Guids]
gEfiEndOfDxeEventGroupGuid ## CONSUMES ## Event
[Protocols]
gEfiMpServiceProtocolGuid ## CONSUMES
[Pcd]
gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiS3Enable ## CONSUMES
gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize ## CONSUMES
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber ## CONSUMES
gUefiCpuPkgTokenSpaceGuid.PcdCpuS3DataAddress ## PRODUCES
+ gUefiOvmfPkgTokenSpaceGuid.PcdQ35SmramAtDefaultSmbase ## CONSUMES
[Depex]
gEfiMpServiceProtocolGuid
diff --git a/OvmfPkg/CpuS3DataDxe/CpuS3Data.c b/OvmfPkg/CpuS3DataDxe/CpuS3Data.c
index 8bb9807cd501..bac7285aa2f3 100644
--- a/OvmfPkg/CpuS3DataDxe/CpuS3Data.c
+++ b/OvmfPkg/CpuS3DataDxe/CpuS3Data.c
@@ -4,51 +4,55 @@ 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/IoLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/MtrrLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/MpService.h>
#include <Guid/EventGroup.h>
+#include <IndustryStandard/Q35MchIch9.h>
+#include <IndustryStandard/QemuCpuHotplug.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
)
@@ -144,89 +148,101 @@ CpuS3DataOnEndOfDxe (
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;
- UINTN NumberOfEnabledProcessors;
VOID *Stack;
UINTN TableSize;
CPU_REGISTER_TABLE *RegisterTable;
UINTN Index;
EFI_PROCESSOR_INFORMATION ProcessorInfoBuffer;
UINTN GdtSize;
UINTN IdtSize;
VOID *Gdt;
VOID *Idt;
EFI_EVENT Event;
ACPI_CPU_DATA *OldAcpiCpuData;
+ BOOLEAN FetchPossibleApicIds;
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;
//
- // Get MP Services Protocol
+ // The "SMRAM at default SMBASE" feature guarantees that
+ // QEMU_CPUHP_CMD_GET_ARCH_ID too is available.
//
- Status = gBS->LocateProtocol (
- &gEfiMpServiceProtocolGuid,
- NULL,
- (VOID **)&MpServices
- );
- ASSERT_EFI_ERROR (Status);
+ FetchPossibleApicIds = PcdGetBool (PcdQ35SmramAtDefaultSmbase);
- //
- // Get the number of CPUs
- //
- Status = MpServices->GetNumberOfProcessors (
- MpServices,
- &NumberOfCpus,
- &NumberOfEnabledProcessors
- );
- ASSERT_EFI_ERROR (Status);
+ if (FetchPossibleApicIds) {
+ 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;
@@ -244,58 +260,83 @@ CpuS3DataInitialize (
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));
} else {
//
// Allocate buffer for empty RegisterTable and PreSmmInitRegisterTable for all CPUs
//
TableSize = 2 * NumberOfCpus * sizeof (CPU_REGISTER_TABLE);
RegisterTable = (CPU_REGISTER_TABLE *)AllocateZeroPages (TableSize);
ASSERT (RegisterTable != NULL);
+ if (FetchPossibleApicIds) {
+ //
+ // Write a valid selector so that other hotplug registers can be
+ // accessed.
+ //
+ IoWrite32 (ICH9_CPU_HOTPLUG_BASE + QEMU_CPUHP_W_CPU_SEL, 0);
+ //
+ // We'll be fetching the APIC IDs.
+ //
+ IoWrite8 (ICH9_CPU_HOTPLUG_BASE + QEMU_CPUHP_W_CMD,
+ QEMU_CPUHP_CMD_GET_ARCH_ID);
+ }
for (Index = 0; Index < NumberOfCpus; Index++) {
- Status = MpServices->GetProcessorInfo (
- MpServices,
- Index,
- &ProcessorInfoBuffer
- );
- ASSERT_EFI_ERROR (Status);
+ UINT32 InitialApicId;
- RegisterTable[Index].InitialApicId = (UINT32)ProcessorInfoBuffer.ProcessorId;
+ if (FetchPossibleApicIds) {
+ IoWrite32 (ICH9_CPU_HOTPLUG_BASE + QEMU_CPUHP_W_CPU_SEL,
+ (UINT32)Index);
+ InitialApicId = IoRead32 (
+ ICH9_CPU_HOTPLUG_BASE + QEMU_CPUHP_RW_CMD_DATA);
+ } else {
+ Status = MpServices->GetProcessorInfo (
+ MpServices,
+ Index,
+ &ProcessorInfoBuffer
+ );
+ ASSERT_EFI_ERROR (Status);
+ InitialApicId = (UINT32)ProcessorInfoBuffer.ProcessorId;
+ }
+
+ DEBUG ((DEBUG_VERBOSE, "%a: Index=%05Lu ApicId=0x%08x\n", __FUNCTION__,
+ (UINT64)Index, InitialApicId));
+
+ RegisterTable[Index].InitialApicId = InitialApicId;
RegisterTable[Index].TableLength = 0;
RegisterTable[Index].AllocatedSize = 0;
RegisterTable[Index].RegisterTableEntry = 0;
- RegisterTable[NumberOfCpus + Index].InitialApicId = (UINT32)ProcessorInfoBuffer.ProcessorId;
+ RegisterTable[NumberOfCpus + Index].InitialApicId = InitialApicId;
RegisterTable[NumberOfCpus + Index].TableLength = 0;
RegisterTable[NumberOfCpus + Index].AllocatedSize = 0;
RegisterTable[NumberOfCpus + Index].RegisterTableEntry = 0;
}
AcpiCpuData->RegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)RegisterTable;
AcpiCpuData->PreSmmInitRegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)(RegisterTable + NumberOfCpus);
}
//
// 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,
--
2.19.1.3.g30247aa5d201
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#54748): https://edk2.groups.io/g/devel/message/54748
Mute This Topic: https://groups.io/mt/71494235/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-
More information about the edk2-devel-archive
mailing list