]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/CpuS3DataDxe/CpuS3Data.c
OvmfPkg/CpuS3DataDxe: enable S3 resume after CPU hotplug
[mirror_edk2.git] / OvmfPkg / CpuS3DataDxe / CpuS3Data.c
1 /** @file
2 ACPI CPU Data initialization module
3
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.
11
12 Copyright (c) 2013 - 2017, Intel Corporation. All rights reserved.<BR>
13 Copyright (c) 2015 - 2020, Red Hat, Inc.
14
15 SPDX-License-Identifier: BSD-2-Clause-Patent
16
17 **/
18
19 #include <PiDxe.h>
20
21 #include <AcpiCpuData.h>
22
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>
30
31 #include <Protocol/MpService.h>
32 #include <Guid/EventGroup.h>
33
34 #include <IndustryStandard/Q35MchIch9.h>
35 #include <IndustryStandard/QemuCpuHotplug.h>
36
37 //
38 // Data structure used to allocate ACPI_CPU_DATA and its supporting structures
39 //
40 typedef struct {
41 ACPI_CPU_DATA AcpiCpuData;
42 MTRR_SETTINGS MtrrTable;
43 IA32_DESCRIPTOR GdtrProfile;
44 IA32_DESCRIPTOR IdtrProfile;
45 } ACPI_CPU_DATA_EX;
46
47 /**
48 Allocate EfiACPIMemoryNVS memory.
49
50 @param[in] Size Size of memory to allocate.
51
52 @return Allocated address for output.
53
54 **/
55 VOID *
56 AllocateAcpiNvsMemory (
57 IN UINTN Size
58 )
59 {
60 EFI_PHYSICAL_ADDRESS Address;
61 EFI_STATUS Status;
62 VOID *Buffer;
63
64 Status = gBS->AllocatePages (
65 AllocateAnyPages,
66 EfiACPIMemoryNVS,
67 EFI_SIZE_TO_PAGES (Size),
68 &Address
69 );
70 if (EFI_ERROR (Status)) {
71 return NULL;
72 }
73
74 Buffer = (VOID *)(UINTN)Address;
75 ZeroMem (Buffer, Size);
76
77 return Buffer;
78 }
79
80 /**
81 Allocate memory and clean it with zero.
82
83 @param[in] Size Size of memory to allocate.
84
85 @return Allocated address for output.
86
87 **/
88 VOID *
89 AllocateZeroPages (
90 IN UINTN Size
91 )
92 {
93 VOID *Buffer;
94
95 Buffer = AllocatePages (EFI_SIZE_TO_PAGES (Size));
96 if (Buffer != NULL) {
97 ZeroMem (Buffer, Size);
98 }
99
100 return Buffer;
101 }
102 /**
103 Callback function executed when the EndOfDxe event group is signaled.
104
105 We delay allocating StartupVector and saving the MTRR settings until BDS signals EndOfDxe.
106
107 @param[in] Event Event whose notification function is being invoked.
108 @param[out] Context Pointer to the MTRR_SETTINGS buffer to fill in.
109 **/
110 VOID
111 EFIAPI
112 CpuS3DataOnEndOfDxe (
113 IN EFI_EVENT Event,
114 OUT VOID *Context
115 )
116 {
117 EFI_STATUS Status;
118 ACPI_CPU_DATA_EX *AcpiCpuDataEx;
119
120 AcpiCpuDataEx = (ACPI_CPU_DATA_EX *) Context;
121 //
122 // Allocate a 4KB reserved page below 1MB
123 //
124 AcpiCpuDataEx->AcpiCpuData.StartupVector = BASE_1MB - 1;
125 Status = gBS->AllocatePages (
126 AllocateMaxAddress,
127 EfiReservedMemoryType,
128 1,
129 &AcpiCpuDataEx->AcpiCpuData.StartupVector
130 );
131 ASSERT_EFI_ERROR (Status);
132
133 DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__));
134 MtrrGetAllMtrrs (&AcpiCpuDataEx->MtrrTable);
135
136 //
137 // Close event, so it will not be invoked again.
138 //
139 gBS->CloseEvent (Event);
140 }
141
142 /**
143 The entry function of the CpuS3Data driver.
144
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.
149
150 @param[in] ImageHandle The firmware allocated handle for the EFI image.
151 @param[in] SystemTable A pointer to the EFI System Table.
152
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.
156
157 **/
158 EFI_STATUS
159 EFIAPI
160 CpuS3DataInitialize (
161 IN EFI_HANDLE ImageHandle,
162 IN EFI_SYSTEM_TABLE *SystemTable
163 )
164 {
165 EFI_STATUS Status;
166 ACPI_CPU_DATA_EX *AcpiCpuDataEx;
167 ACPI_CPU_DATA *AcpiCpuData;
168 EFI_MP_SERVICES_PROTOCOL *MpServices;
169 UINTN NumberOfCpus;
170 VOID *Stack;
171 UINTN TableSize;
172 CPU_REGISTER_TABLE *RegisterTable;
173 UINTN Index;
174 EFI_PROCESSOR_INFORMATION ProcessorInfoBuffer;
175 UINTN GdtSize;
176 UINTN IdtSize;
177 VOID *Gdt;
178 VOID *Idt;
179 EFI_EVENT Event;
180 ACPI_CPU_DATA *OldAcpiCpuData;
181 BOOLEAN FetchPossibleApicIds;
182
183 if (!PcdGetBool (PcdAcpiS3Enable)) {
184 return EFI_UNSUPPORTED;
185 }
186
187 //
188 // Set PcdCpuS3DataAddress to the base address of the ACPI_CPU_DATA structure
189 //
190 OldAcpiCpuData = (ACPI_CPU_DATA *) (UINTN) PcdGet64 (PcdCpuS3DataAddress);
191
192 AcpiCpuDataEx = AllocateZeroPages (sizeof (ACPI_CPU_DATA_EX));
193 ASSERT (AcpiCpuDataEx != NULL);
194 AcpiCpuData = &AcpiCpuDataEx->AcpiCpuData;
195
196 //
197 // The "SMRAM at default SMBASE" feature guarantees that
198 // QEMU_CPUHP_CMD_GET_ARCH_ID too is available.
199 //
200 FetchPossibleApicIds = PcdGetBool (PcdQ35SmramAtDefaultSmbase);
201
202 if (FetchPossibleApicIds) {
203 NumberOfCpus = PcdGet32 (PcdCpuMaxLogicalProcessorNumber);
204 } else {
205 UINTN NumberOfEnabledProcessors;
206
207 //
208 // Get MP Services Protocol
209 //
210 Status = gBS->LocateProtocol (
211 &gEfiMpServiceProtocolGuid,
212 NULL,
213 (VOID **)&MpServices
214 );
215 ASSERT_EFI_ERROR (Status);
216
217 //
218 // Get the number of CPUs
219 //
220 Status = MpServices->GetNumberOfProcessors (
221 MpServices,
222 &NumberOfCpus,
223 &NumberOfEnabledProcessors
224 );
225 ASSERT_EFI_ERROR (Status);
226 }
227 AcpiCpuData->NumberOfCpus = (UINT32)NumberOfCpus;
228
229 //
230 // Initialize ACPI_CPU_DATA fields
231 //
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;
238
239 //
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.
245 //
246 Stack = AllocateAcpiNvsMemory (NumberOfCpus * AcpiCpuData->StackSize);
247 ASSERT (Stack != NULL);
248 AcpiCpuData->StackAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Stack;
249
250 //
251 // Get the boot processor's GDT and IDT
252 //
253 AsmReadGdtr (&AcpiCpuDataEx->GdtrProfile);
254 AsmReadIdtr (&AcpiCpuDataEx->IdtrProfile);
255
256 //
257 // Allocate GDT and IDT and copy current GDT and IDT contents
258 //
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;
268
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));
274 } else {
275 //
276 // Allocate buffer for empty RegisterTable and PreSmmInitRegisterTable for all CPUs
277 //
278 TableSize = 2 * NumberOfCpus * sizeof (CPU_REGISTER_TABLE);
279 RegisterTable = (CPU_REGISTER_TABLE *)AllocateZeroPages (TableSize);
280 ASSERT (RegisterTable != NULL);
281
282 if (FetchPossibleApicIds) {
283 //
284 // Write a valid selector so that other hotplug registers can be
285 // accessed.
286 //
287 IoWrite32 (ICH9_CPU_HOTPLUG_BASE + QEMU_CPUHP_W_CPU_SEL, 0);
288 //
289 // We'll be fetching the APIC IDs.
290 //
291 IoWrite8 (ICH9_CPU_HOTPLUG_BASE + QEMU_CPUHP_W_CMD,
292 QEMU_CPUHP_CMD_GET_ARCH_ID);
293 }
294 for (Index = 0; Index < NumberOfCpus; Index++) {
295 UINT32 InitialApicId;
296
297 if (FetchPossibleApicIds) {
298 IoWrite32 (ICH9_CPU_HOTPLUG_BASE + QEMU_CPUHP_W_CPU_SEL,
299 (UINT32)Index);
300 InitialApicId = IoRead32 (
301 ICH9_CPU_HOTPLUG_BASE + QEMU_CPUHP_RW_CMD_DATA);
302 } else {
303 Status = MpServices->GetProcessorInfo (
304 MpServices,
305 Index,
306 &ProcessorInfoBuffer
307 );
308 ASSERT_EFI_ERROR (Status);
309 InitialApicId = (UINT32)ProcessorInfoBuffer.ProcessorId;
310 }
311
312 DEBUG ((DEBUG_VERBOSE, "%a: Index=%05Lu ApicId=0x%08x\n", __FUNCTION__,
313 (UINT64)Index, InitialApicId));
314
315 RegisterTable[Index].InitialApicId = InitialApicId;
316 RegisterTable[Index].TableLength = 0;
317 RegisterTable[Index].AllocatedSize = 0;
318 RegisterTable[Index].RegisterTableEntry = 0;
319
320 RegisterTable[NumberOfCpus + Index].InitialApicId = InitialApicId;
321 RegisterTable[NumberOfCpus + Index].TableLength = 0;
322 RegisterTable[NumberOfCpus + Index].AllocatedSize = 0;
323 RegisterTable[NumberOfCpus + Index].RegisterTableEntry = 0;
324 }
325 AcpiCpuData->RegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)RegisterTable;
326 AcpiCpuData->PreSmmInitRegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)(RegisterTable + NumberOfCpus);
327 }
328
329 //
330 // Set PcdCpuS3DataAddress to the base address of the ACPI_CPU_DATA structure
331 //
332 Status = PcdSet64S (PcdCpuS3DataAddress, (UINT64)(UINTN)AcpiCpuData);
333 ASSERT_EFI_ERROR (Status);
334
335 //
336 // Register EFI_END_OF_DXE_EVENT_GROUP_GUID event.
337 // The notification function allocates StartupVector and saves MTRRs for ACPI_CPU_DATA
338 //
339 Status = gBS->CreateEventEx (
340 EVT_NOTIFY_SIGNAL,
341 TPL_CALLBACK,
342 CpuS3DataOnEndOfDxe,
343 AcpiCpuData,
344 &gEfiEndOfDxeEventGroupGuid,
345 &Event
346 );
347 ASSERT_EFI_ERROR (Status);
348
349 return EFI_SUCCESS;
350 }