2 ACPI CPU Data initialization module
4 This module initializes the ACPI_CPU_DATA structure and registers the address
5 of this structure in the PcdCpuS3DataAddress PCD. This is a generic/simple
6 version of this module. It does not provide a machine check handler or CPU
7 register initialization tables for ACPI S3 resume. It also only supports the
8 number of CPUs reported by the MP Services Protocol, so this module does not
9 support hot plug CPUs. This module can be copied into a CPU specific package
10 and customized if these additional features are required.
12 Copyright (c) 2013 - 2017, Intel Corporation. All rights reserved.<BR>
13 Copyright (c) 2015 - 2020, Red Hat, Inc.
15 SPDX-License-Identifier: BSD-2-Clause-Patent
21 #include <AcpiCpuData.h>
23 #include <Library/BaseLib.h>
24 #include <Library/BaseMemoryLib.h>
25 #include <Library/DebugLib.h>
26 #include <Library/IoLib.h>
27 #include <Library/MemoryAllocationLib.h>
28 #include <Library/MtrrLib.h>
29 #include <Library/UefiBootServicesTableLib.h>
31 #include <Protocol/MpService.h>
32 #include <Guid/EventGroup.h>
34 #include <IndustryStandard/Q35MchIch9.h>
35 #include <IndustryStandard/QemuCpuHotplug.h>
38 // Data structure used to allocate ACPI_CPU_DATA and its supporting structures
41 ACPI_CPU_DATA AcpiCpuData
;
42 MTRR_SETTINGS MtrrTable
;
43 IA32_DESCRIPTOR GdtrProfile
;
44 IA32_DESCRIPTOR IdtrProfile
;
48 Allocate EfiACPIMemoryNVS memory.
50 @param[in] Size Size of memory to allocate.
52 @return Allocated address for output.
56 AllocateAcpiNvsMemory (
60 EFI_PHYSICAL_ADDRESS Address
;
64 Status
= gBS
->AllocatePages (
67 EFI_SIZE_TO_PAGES (Size
),
70 if (EFI_ERROR (Status
)) {
74 Buffer
= (VOID
*)(UINTN
)Address
;
75 ZeroMem (Buffer
, Size
);
81 Allocate memory and clean it with zero.
83 @param[in] Size Size of memory to allocate.
85 @return Allocated address for output.
95 Buffer
= AllocatePages (EFI_SIZE_TO_PAGES (Size
));
97 ZeroMem (Buffer
, Size
);
103 Callback function executed when the EndOfDxe event group is signaled.
105 We delay allocating StartupVector and saving the MTRR settings until BDS signals EndOfDxe.
107 @param[in] Event Event whose notification function is being invoked.
108 @param[out] Context Pointer to the MTRR_SETTINGS buffer to fill in.
112 CpuS3DataOnEndOfDxe (
118 ACPI_CPU_DATA_EX
*AcpiCpuDataEx
;
120 AcpiCpuDataEx
= (ACPI_CPU_DATA_EX
*) Context
;
122 // Allocate a 4KB reserved page below 1MB
124 AcpiCpuDataEx
->AcpiCpuData
.StartupVector
= BASE_1MB
- 1;
125 Status
= gBS
->AllocatePages (
127 EfiReservedMemoryType
,
129 &AcpiCpuDataEx
->AcpiCpuData
.StartupVector
131 ASSERT_EFI_ERROR (Status
);
133 DEBUG ((DEBUG_VERBOSE
, "%a\n", __FUNCTION__
));
134 MtrrGetAllMtrrs (&AcpiCpuDataEx
->MtrrTable
);
137 // Close event, so it will not be invoked again.
139 gBS
->CloseEvent (Event
);
143 The entry function of the CpuS3Data driver.
145 Allocate and initialize all fields of the ACPI_CPU_DATA structure except the
146 MTRR settings. Register an event notification on gEfiEndOfDxeEventGroupGuid
147 to capture the ACPI_CPU_DATA MTRR settings. The PcdCpuS3DataAddress is set
148 to the address that ACPI_CPU_DATA is allocated at.
150 @param[in] ImageHandle The firmware allocated handle for the EFI image.
151 @param[in] SystemTable A pointer to the EFI System Table.
153 @retval EFI_SUCCESS The entry point is executed successfully.
154 @retval EFI_UNSUPPORTED Do not support ACPI S3.
155 @retval other Some error occurs when executing this entry point.
160 CpuS3DataInitialize (
161 IN EFI_HANDLE ImageHandle
,
162 IN EFI_SYSTEM_TABLE
*SystemTable
166 ACPI_CPU_DATA_EX
*AcpiCpuDataEx
;
167 ACPI_CPU_DATA
*AcpiCpuData
;
168 EFI_MP_SERVICES_PROTOCOL
*MpServices
;
172 CPU_REGISTER_TABLE
*RegisterTable
;
174 EFI_PROCESSOR_INFORMATION ProcessorInfoBuffer
;
180 ACPI_CPU_DATA
*OldAcpiCpuData
;
181 BOOLEAN FetchPossibleApicIds
;
183 if (!PcdGetBool (PcdAcpiS3Enable
)) {
184 return EFI_UNSUPPORTED
;
188 // Set PcdCpuS3DataAddress to the base address of the ACPI_CPU_DATA structure
190 OldAcpiCpuData
= (ACPI_CPU_DATA
*) (UINTN
) PcdGet64 (PcdCpuS3DataAddress
);
192 AcpiCpuDataEx
= AllocateZeroPages (sizeof (ACPI_CPU_DATA_EX
));
193 ASSERT (AcpiCpuDataEx
!= NULL
);
194 AcpiCpuData
= &AcpiCpuDataEx
->AcpiCpuData
;
197 // The "SMRAM at default SMBASE" feature guarantees that
198 // QEMU_CPUHP_CMD_GET_ARCH_ID too is available.
200 FetchPossibleApicIds
= PcdGetBool (PcdQ35SmramAtDefaultSmbase
);
202 if (FetchPossibleApicIds
) {
203 NumberOfCpus
= PcdGet32 (PcdCpuMaxLogicalProcessorNumber
);
205 UINTN NumberOfEnabledProcessors
;
208 // Get MP Services Protocol
210 Status
= gBS
->LocateProtocol (
211 &gEfiMpServiceProtocolGuid
,
215 ASSERT_EFI_ERROR (Status
);
218 // Get the number of CPUs
220 Status
= MpServices
->GetNumberOfProcessors (
223 &NumberOfEnabledProcessors
225 ASSERT_EFI_ERROR (Status
);
227 AcpiCpuData
->NumberOfCpus
= (UINT32
)NumberOfCpus
;
230 // Initialize ACPI_CPU_DATA fields
232 AcpiCpuData
->StackSize
= PcdGet32 (PcdCpuApStackSize
);
233 AcpiCpuData
->ApMachineCheckHandlerBase
= 0;
234 AcpiCpuData
->ApMachineCheckHandlerSize
= 0;
235 AcpiCpuData
->GdtrProfile
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)&AcpiCpuDataEx
->GdtrProfile
;
236 AcpiCpuData
->IdtrProfile
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)&AcpiCpuDataEx
->IdtrProfile
;
237 AcpiCpuData
->MtrrTable
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)&AcpiCpuDataEx
->MtrrTable
;
240 // Allocate stack space for all CPUs.
241 // Use ACPI NVS memory type because this data will be directly used by APs
242 // in S3 resume phase in long mode. Also during S3 resume, the stack buffer
243 // will only be used as scratch space. i.e. we won't read anything from it
244 // before we write to it, in PiSmmCpuDxeSmm.
246 Stack
= AllocateAcpiNvsMemory (NumberOfCpus
* AcpiCpuData
->StackSize
);
247 ASSERT (Stack
!= NULL
);
248 AcpiCpuData
->StackAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)Stack
;
251 // Get the boot processor's GDT and IDT
253 AsmReadGdtr (&AcpiCpuDataEx
->GdtrProfile
);
254 AsmReadIdtr (&AcpiCpuDataEx
->IdtrProfile
);
257 // Allocate GDT and IDT and copy current GDT and IDT contents
259 GdtSize
= AcpiCpuDataEx
->GdtrProfile
.Limit
+ 1;
260 IdtSize
= AcpiCpuDataEx
->IdtrProfile
.Limit
+ 1;
261 Gdt
= AllocateZeroPages (GdtSize
+ IdtSize
);
262 ASSERT (Gdt
!= NULL
);
263 Idt
= (VOID
*)((UINTN
)Gdt
+ GdtSize
);
264 CopyMem (Gdt
, (VOID
*)AcpiCpuDataEx
->GdtrProfile
.Base
, GdtSize
);
265 CopyMem (Idt
, (VOID
*)AcpiCpuDataEx
->IdtrProfile
.Base
, IdtSize
);
266 AcpiCpuDataEx
->GdtrProfile
.Base
= (UINTN
)Gdt
;
267 AcpiCpuDataEx
->IdtrProfile
.Base
= (UINTN
)Idt
;
269 if (OldAcpiCpuData
!= NULL
) {
270 AcpiCpuData
->RegisterTable
= OldAcpiCpuData
->RegisterTable
;
271 AcpiCpuData
->PreSmmInitRegisterTable
= OldAcpiCpuData
->PreSmmInitRegisterTable
;
272 AcpiCpuData
->ApLocation
= OldAcpiCpuData
->ApLocation
;
273 CopyMem (&AcpiCpuData
->CpuStatus
, &OldAcpiCpuData
->CpuStatus
, sizeof (CPU_STATUS_INFORMATION
));
276 // Allocate buffer for empty RegisterTable and PreSmmInitRegisterTable for all CPUs
278 TableSize
= 2 * NumberOfCpus
* sizeof (CPU_REGISTER_TABLE
);
279 RegisterTable
= (CPU_REGISTER_TABLE
*)AllocateZeroPages (TableSize
);
280 ASSERT (RegisterTable
!= NULL
);
282 if (FetchPossibleApicIds
) {
284 // Write a valid selector so that other hotplug registers can be
287 IoWrite32 (ICH9_CPU_HOTPLUG_BASE
+ QEMU_CPUHP_W_CPU_SEL
, 0);
289 // We'll be fetching the APIC IDs.
291 IoWrite8 (ICH9_CPU_HOTPLUG_BASE
+ QEMU_CPUHP_W_CMD
,
292 QEMU_CPUHP_CMD_GET_ARCH_ID
);
294 for (Index
= 0; Index
< NumberOfCpus
; Index
++) {
295 UINT32 InitialApicId
;
297 if (FetchPossibleApicIds
) {
298 IoWrite32 (ICH9_CPU_HOTPLUG_BASE
+ QEMU_CPUHP_W_CPU_SEL
,
300 InitialApicId
= IoRead32 (
301 ICH9_CPU_HOTPLUG_BASE
+ QEMU_CPUHP_RW_CMD_DATA
);
303 Status
= MpServices
->GetProcessorInfo (
308 ASSERT_EFI_ERROR (Status
);
309 InitialApicId
= (UINT32
)ProcessorInfoBuffer
.ProcessorId
;
312 DEBUG ((DEBUG_VERBOSE
, "%a: Index=%05Lu ApicId=0x%08x\n", __FUNCTION__
,
313 (UINT64
)Index
, InitialApicId
));
315 RegisterTable
[Index
].InitialApicId
= InitialApicId
;
316 RegisterTable
[Index
].TableLength
= 0;
317 RegisterTable
[Index
].AllocatedSize
= 0;
318 RegisterTable
[Index
].RegisterTableEntry
= 0;
320 RegisterTable
[NumberOfCpus
+ Index
].InitialApicId
= InitialApicId
;
321 RegisterTable
[NumberOfCpus
+ Index
].TableLength
= 0;
322 RegisterTable
[NumberOfCpus
+ Index
].AllocatedSize
= 0;
323 RegisterTable
[NumberOfCpus
+ Index
].RegisterTableEntry
= 0;
325 AcpiCpuData
->RegisterTable
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)RegisterTable
;
326 AcpiCpuData
->PreSmmInitRegisterTable
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)(RegisterTable
+ NumberOfCpus
);
330 // Set PcdCpuS3DataAddress to the base address of the ACPI_CPU_DATA structure
332 Status
= PcdSet64S (PcdCpuS3DataAddress
, (UINT64
)(UINTN
)AcpiCpuData
);
333 ASSERT_EFI_ERROR (Status
);
336 // Register EFI_END_OF_DXE_EVENT_GROUP_GUID event.
337 // The notification function allocates StartupVector and saves MTRRs for ACPI_CPU_DATA
339 Status
= gBS
->CreateEventEx (
344 &gEfiEndOfDxeEventGroupGuid
,
347 ASSERT_EFI_ERROR (Status
);