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