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, 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/UefiBootServicesTableLib.h>
26 #include <Library/DebugLib.h>
27 #include <Library/MtrrLib.h>
28 #include <Library/MemoryAllocationLib.h>
30 #include <Protocol/MpService.h>
31 #include <Guid/EventGroup.h>
34 // Data structure used to allocate ACPI_CPU_DATA and its supporting structures
37 ACPI_CPU_DATA AcpiCpuData
;
38 MTRR_SETTINGS MtrrTable
;
39 IA32_DESCRIPTOR GdtrProfile
;
40 IA32_DESCRIPTOR IdtrProfile
;
44 Allocate EfiACPIMemoryNVS memory.
46 @param[in] Size Size of memory to allocate.
48 @return Allocated address for output.
52 AllocateAcpiNvsMemory (
56 EFI_PHYSICAL_ADDRESS Address
;
60 Status
= gBS
->AllocatePages (
63 EFI_SIZE_TO_PAGES (Size
),
66 if (EFI_ERROR (Status
)) {
70 Buffer
= (VOID
*)(UINTN
)Address
;
71 ZeroMem (Buffer
, Size
);
77 Allocate memory and clean it with zero.
79 @param[in] Size Size of memory to allocate.
81 @return Allocated address for output.
91 Buffer
= AllocatePages (EFI_SIZE_TO_PAGES (Size
));
93 ZeroMem (Buffer
, Size
);
99 Callback function executed when the EndOfDxe event group is signaled.
101 We delay allocating StartupVector and saving the MTRR settings until BDS signals EndOfDxe.
103 @param[in] Event Event whose notification function is being invoked.
104 @param[out] Context Pointer to the MTRR_SETTINGS buffer to fill in.
108 CpuS3DataOnEndOfDxe (
114 ACPI_CPU_DATA_EX
*AcpiCpuDataEx
;
116 AcpiCpuDataEx
= (ACPI_CPU_DATA_EX
*) Context
;
118 // Allocate a 4KB reserved page below 1MB
120 AcpiCpuDataEx
->AcpiCpuData
.StartupVector
= BASE_1MB
- 1;
121 Status
= gBS
->AllocatePages (
123 EfiReservedMemoryType
,
125 &AcpiCpuDataEx
->AcpiCpuData
.StartupVector
127 ASSERT_EFI_ERROR (Status
);
129 DEBUG ((EFI_D_VERBOSE
, "%a\n", __FUNCTION__
));
130 MtrrGetAllMtrrs (&AcpiCpuDataEx
->MtrrTable
);
133 // Close event, so it will not be invoked again.
135 gBS
->CloseEvent (Event
);
139 The entry function of the CpuS3Data driver.
141 Allocate and initialize all fields of the ACPI_CPU_DATA structure except the
142 MTRR settings. Register an event notification on gEfiEndOfDxeEventGroupGuid
143 to capture the ACPI_CPU_DATA MTRR settings. The PcdCpuS3DataAddress is set
144 to the address that ACPI_CPU_DATA is allocated at.
146 @param[in] ImageHandle The firmware allocated handle for the EFI image.
147 @param[in] SystemTable A pointer to the EFI System Table.
149 @retval EFI_SUCCESS The entry point is executed successfully.
150 @retval EFI_UNSUPPORTED Do not support ACPI S3.
151 @retval other Some error occurs when executing this entry point.
156 CpuS3DataInitialize (
157 IN EFI_HANDLE ImageHandle
,
158 IN EFI_SYSTEM_TABLE
*SystemTable
162 ACPI_CPU_DATA_EX
*AcpiCpuDataEx
;
163 ACPI_CPU_DATA
*AcpiCpuData
;
164 EFI_MP_SERVICES_PROTOCOL
*MpServices
;
166 UINTN NumberOfEnabledProcessors
;
169 CPU_REGISTER_TABLE
*RegisterTable
;
171 EFI_PROCESSOR_INFORMATION ProcessorInfoBuffer
;
177 ACPI_CPU_DATA
*OldAcpiCpuData
;
179 if (!PcdGetBool (PcdAcpiS3Enable
)) {
180 return EFI_UNSUPPORTED
;
184 // Set PcdCpuS3DataAddress to the base address of the ACPI_CPU_DATA structure
186 OldAcpiCpuData
= (ACPI_CPU_DATA
*) (UINTN
) PcdGet64 (PcdCpuS3DataAddress
);
188 AcpiCpuDataEx
= AllocateZeroPages (sizeof (ACPI_CPU_DATA_EX
));
189 ASSERT (AcpiCpuDataEx
!= NULL
);
190 AcpiCpuData
= &AcpiCpuDataEx
->AcpiCpuData
;
193 // Get MP Services Protocol
195 Status
= gBS
->LocateProtocol (
196 &gEfiMpServiceProtocolGuid
,
200 ASSERT_EFI_ERROR (Status
);
203 // Get the number of CPUs
205 Status
= MpServices
->GetNumberOfProcessors (
208 &NumberOfEnabledProcessors
210 ASSERT_EFI_ERROR (Status
);
211 AcpiCpuData
->NumberOfCpus
= (UINT32
)NumberOfCpus
;
214 // Initialize ACPI_CPU_DATA fields
216 AcpiCpuData
->StackSize
= PcdGet32 (PcdCpuApStackSize
);
217 AcpiCpuData
->ApMachineCheckHandlerBase
= 0;
218 AcpiCpuData
->ApMachineCheckHandlerSize
= 0;
219 AcpiCpuData
->GdtrProfile
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)&AcpiCpuDataEx
->GdtrProfile
;
220 AcpiCpuData
->IdtrProfile
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)&AcpiCpuDataEx
->IdtrProfile
;
221 AcpiCpuData
->MtrrTable
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)&AcpiCpuDataEx
->MtrrTable
;
224 // Allocate stack space for all CPUs.
225 // Use ACPI NVS memory type because this data will be directly used by APs
226 // in S3 resume phase in long mode. Also during S3 resume, the stack buffer
227 // will only be used as scratch space. i.e. we won't read anything from it
228 // before we write to it, in PiSmmCpuDxeSmm.
230 Stack
= AllocateAcpiNvsMemory (NumberOfCpus
* AcpiCpuData
->StackSize
);
231 ASSERT (Stack
!= NULL
);
232 AcpiCpuData
->StackAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)Stack
;
235 // Get the boot processor's GDT and IDT
237 AsmReadGdtr (&AcpiCpuDataEx
->GdtrProfile
);
238 AsmReadIdtr (&AcpiCpuDataEx
->IdtrProfile
);
241 // Allocate GDT and IDT and copy current GDT and IDT contents
243 GdtSize
= AcpiCpuDataEx
->GdtrProfile
.Limit
+ 1;
244 IdtSize
= AcpiCpuDataEx
->IdtrProfile
.Limit
+ 1;
245 Gdt
= AllocateZeroPages (GdtSize
+ IdtSize
);
246 ASSERT (Gdt
!= NULL
);
247 Idt
= (VOID
*)((UINTN
)Gdt
+ GdtSize
);
248 CopyMem (Gdt
, (VOID
*)AcpiCpuDataEx
->GdtrProfile
.Base
, GdtSize
);
249 CopyMem (Idt
, (VOID
*)AcpiCpuDataEx
->IdtrProfile
.Base
, IdtSize
);
250 AcpiCpuDataEx
->GdtrProfile
.Base
= (UINTN
)Gdt
;
251 AcpiCpuDataEx
->IdtrProfile
.Base
= (UINTN
)Idt
;
253 if (OldAcpiCpuData
!= NULL
) {
254 AcpiCpuData
->RegisterTable
= OldAcpiCpuData
->RegisterTable
;
255 AcpiCpuData
->PreSmmInitRegisterTable
= OldAcpiCpuData
->PreSmmInitRegisterTable
;
256 AcpiCpuData
->ApLocation
= OldAcpiCpuData
->ApLocation
;
257 CopyMem (&AcpiCpuData
->CpuStatus
, &OldAcpiCpuData
->CpuStatus
, sizeof (CPU_STATUS_INFORMATION
));
260 // Allocate buffer for empty RegisterTable and PreSmmInitRegisterTable for all CPUs
262 TableSize
= 2 * NumberOfCpus
* sizeof (CPU_REGISTER_TABLE
);
263 RegisterTable
= (CPU_REGISTER_TABLE
*)AllocateZeroPages (TableSize
);
264 ASSERT (RegisterTable
!= NULL
);
266 for (Index
= 0; Index
< NumberOfCpus
; Index
++) {
267 Status
= MpServices
->GetProcessorInfo (
272 ASSERT_EFI_ERROR (Status
);
274 RegisterTable
[Index
].InitialApicId
= (UINT32
)ProcessorInfoBuffer
.ProcessorId
;
275 RegisterTable
[Index
].TableLength
= 0;
276 RegisterTable
[Index
].AllocatedSize
= 0;
277 RegisterTable
[Index
].RegisterTableEntry
= 0;
279 RegisterTable
[NumberOfCpus
+ Index
].InitialApicId
= (UINT32
)ProcessorInfoBuffer
.ProcessorId
;
280 RegisterTable
[NumberOfCpus
+ Index
].TableLength
= 0;
281 RegisterTable
[NumberOfCpus
+ Index
].AllocatedSize
= 0;
282 RegisterTable
[NumberOfCpus
+ Index
].RegisterTableEntry
= 0;
284 AcpiCpuData
->RegisterTable
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)RegisterTable
;
285 AcpiCpuData
->PreSmmInitRegisterTable
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)(RegisterTable
+ NumberOfCpus
);
289 // Set PcdCpuS3DataAddress to the base address of the ACPI_CPU_DATA structure
291 Status
= PcdSet64S (PcdCpuS3DataAddress
, (UINT64
)(UINTN
)AcpiCpuData
);
292 ASSERT_EFI_ERROR (Status
);
295 // Register EFI_END_OF_DXE_EVENT_GROUP_GUID event.
296 // The notification function allocates StartupVector and saves MTRRs for ACPI_CPU_DATA
298 Status
= gBS
->CreateEventEx (
303 &gEfiEndOfDxeEventGroupGuid
,
306 ASSERT_EFI_ERROR (Status
);