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 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/UefiBootServicesTableLib.h>
26 #include <Library/DebugLib.h>
27 #include <Library/MtrrLib.h>
28 #include <Library/MemoryAllocationLib.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 *
AllocateAcpiNvsMemory(IN UINTN Size)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 *
AllocateZeroPages(IN UINTN Size)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
CpuS3DataOnEndOfDxe(IN EFI_EVENT Event,OUT VOID * Context)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 ((EFI_D_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
CpuS3DataInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)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 }
310