]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/CpuS3DataDxe/CpuS3Data.c
OvmfPkg/CpuS3DataDxe: superficial cleanups
[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/MemoryAllocationLib.h>
27 #include <Library/MtrrLib.h>
28 #include <Library/UefiBootServicesTableLib.h>
29
30 #include <Protocol/MpService.h>
31 #include <Guid/EventGroup.h>
32
33 //
34 // Data structure used to allocate ACPI_CPU_DATA and its supporting structures
35 //
36 typedef struct {
37 ACPI_CPU_DATA AcpiCpuData;
38 MTRR_SETTINGS MtrrTable;
39 IA32_DESCRIPTOR GdtrProfile;
40 IA32_DESCRIPTOR IdtrProfile;
41 } ACPI_CPU_DATA_EX;
42
43 /**
44 Allocate EfiACPIMemoryNVS memory.
45
46 @param[in] Size Size of memory to allocate.
47
48 @return Allocated address for output.
49
50 **/
51 VOID *
52 AllocateAcpiNvsMemory (
53 IN UINTN Size
54 )
55 {
56 EFI_PHYSICAL_ADDRESS Address;
57 EFI_STATUS Status;
58 VOID *Buffer;
59
60 Status = gBS->AllocatePages (
61 AllocateAnyPages,
62 EfiACPIMemoryNVS,
63 EFI_SIZE_TO_PAGES (Size),
64 &Address
65 );
66 if (EFI_ERROR (Status)) {
67 return NULL;
68 }
69
70 Buffer = (VOID *)(UINTN)Address;
71 ZeroMem (Buffer, Size);
72
73 return Buffer;
74 }
75
76 /**
77 Allocate memory and clean it with zero.
78
79 @param[in] Size Size of memory to allocate.
80
81 @return Allocated address for output.
82
83 **/
84 VOID *
85 AllocateZeroPages (
86 IN UINTN Size
87 )
88 {
89 VOID *Buffer;
90
91 Buffer = AllocatePages (EFI_SIZE_TO_PAGES (Size));
92 if (Buffer != NULL) {
93 ZeroMem (Buffer, Size);
94 }
95
96 return Buffer;
97 }
98 /**
99 Callback function executed when the EndOfDxe event group is signaled.
100
101 We delay allocating StartupVector and saving the MTRR settings until BDS signals EndOfDxe.
102
103 @param[in] Event Event whose notification function is being invoked.
104 @param[out] Context Pointer to the MTRR_SETTINGS buffer to fill in.
105 **/
106 VOID
107 EFIAPI
108 CpuS3DataOnEndOfDxe (
109 IN EFI_EVENT Event,
110 OUT VOID *Context
111 )
112 {
113 EFI_STATUS Status;
114 ACPI_CPU_DATA_EX *AcpiCpuDataEx;
115
116 AcpiCpuDataEx = (ACPI_CPU_DATA_EX *) Context;
117 //
118 // Allocate a 4KB reserved page below 1MB
119 //
120 AcpiCpuDataEx->AcpiCpuData.StartupVector = BASE_1MB - 1;
121 Status = gBS->AllocatePages (
122 AllocateMaxAddress,
123 EfiReservedMemoryType,
124 1,
125 &AcpiCpuDataEx->AcpiCpuData.StartupVector
126 );
127 ASSERT_EFI_ERROR (Status);
128
129 DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__));
130 MtrrGetAllMtrrs (&AcpiCpuDataEx->MtrrTable);
131
132 //
133 // Close event, so it will not be invoked again.
134 //
135 gBS->CloseEvent (Event);
136 }
137
138 /**
139 The entry function of the CpuS3Data driver.
140
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.
145
146 @param[in] ImageHandle The firmware allocated handle for the EFI image.
147 @param[in] SystemTable A pointer to the EFI System Table.
148
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.
152
153 **/
154 EFI_STATUS
155 EFIAPI
156 CpuS3DataInitialize (
157 IN EFI_HANDLE ImageHandle,
158 IN EFI_SYSTEM_TABLE *SystemTable
159 )
160 {
161 EFI_STATUS Status;
162 ACPI_CPU_DATA_EX *AcpiCpuDataEx;
163 ACPI_CPU_DATA *AcpiCpuData;
164 EFI_MP_SERVICES_PROTOCOL *MpServices;
165 UINTN NumberOfCpus;
166 UINTN NumberOfEnabledProcessors;
167 VOID *Stack;
168 UINTN TableSize;
169 CPU_REGISTER_TABLE *RegisterTable;
170 UINTN Index;
171 EFI_PROCESSOR_INFORMATION ProcessorInfoBuffer;
172 UINTN GdtSize;
173 UINTN IdtSize;
174 VOID *Gdt;
175 VOID *Idt;
176 EFI_EVENT Event;
177 ACPI_CPU_DATA *OldAcpiCpuData;
178
179 if (!PcdGetBool (PcdAcpiS3Enable)) {
180 return EFI_UNSUPPORTED;
181 }
182
183 //
184 // Set PcdCpuS3DataAddress to the base address of the ACPI_CPU_DATA structure
185 //
186 OldAcpiCpuData = (ACPI_CPU_DATA *) (UINTN) PcdGet64 (PcdCpuS3DataAddress);
187
188 AcpiCpuDataEx = AllocateZeroPages (sizeof (ACPI_CPU_DATA_EX));
189 ASSERT (AcpiCpuDataEx != NULL);
190 AcpiCpuData = &AcpiCpuDataEx->AcpiCpuData;
191
192 //
193 // Get MP Services Protocol
194 //
195 Status = gBS->LocateProtocol (
196 &gEfiMpServiceProtocolGuid,
197 NULL,
198 (VOID **)&MpServices
199 );
200 ASSERT_EFI_ERROR (Status);
201
202 //
203 // Get the number of CPUs
204 //
205 Status = MpServices->GetNumberOfProcessors (
206 MpServices,
207 &NumberOfCpus,
208 &NumberOfEnabledProcessors
209 );
210 ASSERT_EFI_ERROR (Status);
211 AcpiCpuData->NumberOfCpus = (UINT32)NumberOfCpus;
212
213 //
214 // Initialize ACPI_CPU_DATA fields
215 //
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;
222
223 //
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.
229 //
230 Stack = AllocateAcpiNvsMemory (NumberOfCpus * AcpiCpuData->StackSize);
231 ASSERT (Stack != NULL);
232 AcpiCpuData->StackAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Stack;
233
234 //
235 // Get the boot processor's GDT and IDT
236 //
237 AsmReadGdtr (&AcpiCpuDataEx->GdtrProfile);
238 AsmReadIdtr (&AcpiCpuDataEx->IdtrProfile);
239
240 //
241 // Allocate GDT and IDT and copy current GDT and IDT contents
242 //
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;
252
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));
258 } else {
259 //
260 // Allocate buffer for empty RegisterTable and PreSmmInitRegisterTable for all CPUs
261 //
262 TableSize = 2 * NumberOfCpus * sizeof (CPU_REGISTER_TABLE);
263 RegisterTable = (CPU_REGISTER_TABLE *)AllocateZeroPages (TableSize);
264 ASSERT (RegisterTable != NULL);
265
266 for (Index = 0; Index < NumberOfCpus; Index++) {
267 Status = MpServices->GetProcessorInfo (
268 MpServices,
269 Index,
270 &ProcessorInfoBuffer
271 );
272 ASSERT_EFI_ERROR (Status);
273
274 RegisterTable[Index].InitialApicId = (UINT32)ProcessorInfoBuffer.ProcessorId;
275 RegisterTable[Index].TableLength = 0;
276 RegisterTable[Index].AllocatedSize = 0;
277 RegisterTable[Index].RegisterTableEntry = 0;
278
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;
283 }
284 AcpiCpuData->RegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)RegisterTable;
285 AcpiCpuData->PreSmmInitRegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)(RegisterTable + NumberOfCpus);
286 }
287
288 //
289 // Set PcdCpuS3DataAddress to the base address of the ACPI_CPU_DATA structure
290 //
291 Status = PcdSet64S (PcdCpuS3DataAddress, (UINT64)(UINTN)AcpiCpuData);
292 ASSERT_EFI_ERROR (Status);
293
294 //
295 // Register EFI_END_OF_DXE_EVENT_GROUP_GUID event.
296 // The notification function allocates StartupVector and saves MTRRs for ACPI_CPU_DATA
297 //
298 Status = gBS->CreateEventEx (
299 EVT_NOTIFY_SIGNAL,
300 TPL_CALLBACK,
301 CpuS3DataOnEndOfDxe,
302 AcpiCpuData,
303 &gEfiEndOfDxeEventGroupGuid,
304 &Event
305 );
306 ASSERT_EFI_ERROR (Status);
307
308 return EFI_SUCCESS;
309 }