]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/CpuS3DataDxe/CpuS3Data.c
1b847e453aa7d6cfb223c08168d764b5ea98f8c7
[mirror_edk2.git] / UefiCpuPkg / 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, Red Hat, Inc.
14
15 This program and the accompanying materials
16 are licensed and made available under the terms and conditions of the BSD License
17 which accompanies this distribution. The full text of the license may be found at
18 http://opensource.org/licenses/bsd-license.php
19
20 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
21 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
22
23 **/
24
25 #include <PiDxe.h>
26
27 #include <AcpiCpuData.h>
28
29 #include <Library/BaseLib.h>
30 #include <Library/BaseMemoryLib.h>
31 #include <Library/UefiBootServicesTableLib.h>
32 #include <Library/DebugLib.h>
33 #include <Library/MtrrLib.h>
34 #include <Library/MemoryAllocationLib.h>
35
36 #include <Protocol/MpService.h>
37 #include <Guid/EventGroup.h>
38
39 //
40 // Data structure used to allocate ACPI_CPU_DATA and its supporting structures
41 //
42 typedef struct {
43 ACPI_CPU_DATA AcpiCpuData;
44 MTRR_SETTINGS MtrrTable;
45 IA32_DESCRIPTOR GdtrProfile;
46 IA32_DESCRIPTOR IdtrProfile;
47 } ACPI_CPU_DATA_EX;
48
49 /**
50 Allocate EfiACPIMemoryNVS memory.
51
52 @param[in] Size Size of memory to allocate.
53
54 @return Allocated address for output.
55
56 **/
57 VOID *
58 AllocateAcpiNvsMemory (
59 IN UINTN Size
60 )
61 {
62 EFI_PHYSICAL_ADDRESS Address;
63 EFI_STATUS Status;
64 VOID *Buffer;
65
66 Status = gBS->AllocatePages (
67 AllocateAnyPages,
68 EfiACPIMemoryNVS,
69 EFI_SIZE_TO_PAGES (Size),
70 &Address
71 );
72 if (EFI_ERROR (Status)) {
73 return NULL;
74 }
75
76 Buffer = (VOID *)(UINTN)Address;
77 ZeroMem (Buffer, Size);
78
79 return Buffer;
80 }
81
82 /**
83 Allocate memory and clean it with zero.
84
85 @param[in] Size Size of memory to allocate.
86
87 @return Allocated address for output.
88
89 **/
90 VOID *
91 AllocateZeroPages (
92 IN UINTN Size
93 )
94 {
95 VOID *Buffer;
96
97 Buffer = AllocatePages (EFI_SIZE_TO_PAGES (Size));
98 if (Buffer != NULL) {
99 ZeroMem (Buffer, Size);
100 }
101
102 return Buffer;
103 }
104 /**
105 Callback function executed when the EndOfDxe event group is signaled.
106
107 We delay allocating StartupVector and saving the MTRR settings until BDS signals EndOfDxe.
108
109 @param[in] Event Event whose notification function is being invoked.
110 @param[out] Context Pointer to the MTRR_SETTINGS buffer to fill in.
111 **/
112 VOID
113 EFIAPI
114 CpuS3DataOnEndOfDxe (
115 IN EFI_EVENT Event,
116 OUT VOID *Context
117 )
118 {
119 EFI_STATUS Status;
120 ACPI_CPU_DATA_EX *AcpiCpuDataEx;
121
122 AcpiCpuDataEx = (ACPI_CPU_DATA_EX *) Context;
123 //
124 // Allocate a 4KB reserved page below 1MB
125 //
126 AcpiCpuDataEx->AcpiCpuData.StartupVector = BASE_1MB - 1;
127 Status = gBS->AllocatePages (
128 AllocateMaxAddress,
129 EfiReservedMemoryType,
130 1,
131 &AcpiCpuDataEx->AcpiCpuData.StartupVector
132 );
133 ASSERT_EFI_ERROR (Status);
134
135 DEBUG ((EFI_D_VERBOSE, "%a\n", __FUNCTION__));
136 MtrrGetAllMtrrs (&AcpiCpuDataEx->MtrrTable);
137
138 //
139 // Close event, so it will not be invoked again.
140 //
141 gBS->CloseEvent (Event);
142 }
143
144 /**
145 The entry function of the CpuS3Data driver.
146
147 Allocate and initialize all fields of the ACPI_CPU_DATA structure except the
148 MTRR settings. Register an event notification on gEfiEndOfDxeEventGroupGuid
149 to capture the ACPI_CPU_DATA MTRR settings. The PcdCpuS3DataAddress is set
150 to the address that ACPI_CPU_DATA is allocated at.
151
152 @param[in] ImageHandle The firmware allocated handle for the EFI image.
153 @param[in] SystemTable A pointer to the EFI System Table.
154
155 @retval EFI_SUCCESS The entry point is executed successfully.
156 @retval EFI_UNSUPPORTED Do not support ACPI S3.
157 @retval other Some error occurs when executing this entry point.
158
159 **/
160 EFI_STATUS
161 EFIAPI
162 CpuS3DataInitialize (
163 IN EFI_HANDLE ImageHandle,
164 IN EFI_SYSTEM_TABLE *SystemTable
165 )
166 {
167 EFI_STATUS Status;
168 ACPI_CPU_DATA_EX *AcpiCpuDataEx;
169 ACPI_CPU_DATA *AcpiCpuData;
170 EFI_MP_SERVICES_PROTOCOL *MpServices;
171 UINTN NumberOfCpus;
172 UINTN NumberOfEnabledProcessors;
173 VOID *Stack;
174 UINTN TableSize;
175 CPU_REGISTER_TABLE *RegisterTable;
176 UINTN Index;
177 EFI_PROCESSOR_INFORMATION ProcessorInfoBuffer;
178 UINTN GdtSize;
179 UINTN IdtSize;
180 VOID *Gdt;
181 VOID *Idt;
182 EFI_EVENT Event;
183 ACPI_CPU_DATA *OldAcpiCpuData;
184
185 if (!PcdGetBool (PcdAcpiS3Enable)) {
186 return EFI_UNSUPPORTED;
187 }
188
189 //
190 // Set PcdCpuS3DataAddress to the base address of the ACPI_CPU_DATA structure
191 //
192 OldAcpiCpuData = (ACPI_CPU_DATA *) (UINTN) PcdGet64 (PcdCpuS3DataAddress);
193
194 AcpiCpuDataEx = AllocateZeroPages (sizeof (ACPI_CPU_DATA_EX));
195 ASSERT (AcpiCpuDataEx != NULL);
196 AcpiCpuData = &AcpiCpuDataEx->AcpiCpuData;
197
198 //
199 // Get MP Services Protocol
200 //
201 Status = gBS->LocateProtocol (
202 &gEfiMpServiceProtocolGuid,
203 NULL,
204 (VOID **)&MpServices
205 );
206 ASSERT_EFI_ERROR (Status);
207
208 //
209 // Get the number of CPUs
210 //
211 Status = MpServices->GetNumberOfProcessors (
212 MpServices,
213 &NumberOfCpus,
214 &NumberOfEnabledProcessors
215 );
216 ASSERT_EFI_ERROR (Status);
217 AcpiCpuData->NumberOfCpus = (UINT32)NumberOfCpus;
218
219 //
220 // Initialize ACPI_CPU_DATA fields
221 //
222 AcpiCpuData->StackSize = PcdGet32 (PcdCpuApStackSize);
223 AcpiCpuData->ApMachineCheckHandlerBase = 0;
224 AcpiCpuData->ApMachineCheckHandlerSize = 0;
225 AcpiCpuData->GdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)&AcpiCpuDataEx->GdtrProfile;
226 AcpiCpuData->IdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)&AcpiCpuDataEx->IdtrProfile;
227 AcpiCpuData->MtrrTable = (EFI_PHYSICAL_ADDRESS)(UINTN)&AcpiCpuDataEx->MtrrTable;
228
229 //
230 // Allocate stack space for all CPUs.
231 // Use ACPI NVS memory type because this data will be directly used by APs
232 // in S3 resume phase in long mode. Also during S3 resume, the stack buffer
233 // will only be used as scratch space. i.e. we won't read anything from it
234 // before we write to it, in PiSmmCpuDxeSmm.
235 //
236 Stack = AllocateAcpiNvsMemory (NumberOfCpus * AcpiCpuData->StackSize);
237 ASSERT (Stack != NULL);
238 AcpiCpuData->StackAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Stack;
239
240 //
241 // Get the boot processor's GDT and IDT
242 //
243 AsmReadGdtr (&AcpiCpuDataEx->GdtrProfile);
244 AsmReadIdtr (&AcpiCpuDataEx->IdtrProfile);
245
246 //
247 // Allocate GDT and IDT and copy current GDT and IDT contents
248 //
249 GdtSize = AcpiCpuDataEx->GdtrProfile.Limit + 1;
250 IdtSize = AcpiCpuDataEx->IdtrProfile.Limit + 1;
251 Gdt = AllocateZeroPages (GdtSize + IdtSize);
252 ASSERT (Gdt != NULL);
253 Idt = (VOID *)((UINTN)Gdt + GdtSize);
254 CopyMem (Gdt, (VOID *)AcpiCpuDataEx->GdtrProfile.Base, GdtSize);
255 CopyMem (Idt, (VOID *)AcpiCpuDataEx->IdtrProfile.Base, IdtSize);
256 AcpiCpuDataEx->GdtrProfile.Base = (UINTN)Gdt;
257 AcpiCpuDataEx->IdtrProfile.Base = (UINTN)Idt;
258
259 if (OldAcpiCpuData != NULL) {
260 AcpiCpuData->RegisterTable = OldAcpiCpuData->RegisterTable;
261 AcpiCpuData->PreSmmInitRegisterTable = OldAcpiCpuData->PreSmmInitRegisterTable;
262 AcpiCpuData->ApLocation = OldAcpiCpuData->ApLocation;
263 CopyMem (&AcpiCpuData->CpuStatus, &OldAcpiCpuData->CpuStatus, sizeof (CPU_STATUS_INFORMATION));
264 } else {
265 //
266 // Allocate buffer for empty RegisterTable and PreSmmInitRegisterTable for all CPUs
267 //
268 TableSize = 2 * NumberOfCpus * sizeof (CPU_REGISTER_TABLE);
269 RegisterTable = (CPU_REGISTER_TABLE *)AllocateZeroPages (TableSize);
270 ASSERT (RegisterTable != NULL);
271
272 for (Index = 0; Index < NumberOfCpus; Index++) {
273 Status = MpServices->GetProcessorInfo (
274 MpServices,
275 Index,
276 &ProcessorInfoBuffer
277 );
278 ASSERT_EFI_ERROR (Status);
279
280 RegisterTable[Index].InitialApicId = (UINT32)ProcessorInfoBuffer.ProcessorId;
281 RegisterTable[Index].TableLength = 0;
282 RegisterTable[Index].AllocatedSize = 0;
283 RegisterTable[Index].RegisterTableEntry = 0;
284
285 RegisterTable[NumberOfCpus + Index].InitialApicId = (UINT32)ProcessorInfoBuffer.ProcessorId;
286 RegisterTable[NumberOfCpus + Index].TableLength = 0;
287 RegisterTable[NumberOfCpus + Index].AllocatedSize = 0;
288 RegisterTable[NumberOfCpus + Index].RegisterTableEntry = 0;
289 }
290 AcpiCpuData->RegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)RegisterTable;
291 AcpiCpuData->PreSmmInitRegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)(RegisterTable + NumberOfCpus);
292 }
293
294 //
295 // Set PcdCpuS3DataAddress to the base address of the ACPI_CPU_DATA structure
296 //
297 Status = PcdSet64S (PcdCpuS3DataAddress, (UINT64)(UINTN)AcpiCpuData);
298 ASSERT_EFI_ERROR (Status);
299
300 //
301 // Register EFI_END_OF_DXE_EVENT_GROUP_GUID event.
302 // The notification function allocates StartupVector and saves MTRRs for ACPI_CPU_DATA
303 //
304 Status = gBS->CreateEventEx (
305 EVT_NOTIFY_SIGNAL,
306 TPL_CALLBACK,
307 CpuS3DataOnEndOfDxe,
308 AcpiCpuData,
309 &gEfiEndOfDxeEventGroupGuid,
310 &Event
311 );
312 ASSERT_EFI_ERROR (Status);
313
314 return EFI_SUCCESS;
315 }