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 - 2015, 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 
35 #include <Protocol/MpService.h>
36 #include <Guid/EventGroup.h>
37 
38 //
39 // Data structure used to allocate ACPI_CPU_DATA and its supporting structures
40 //
41 typedef struct {
42   ACPI_CPU_DATA             AcpiCpuData;
43   MTRR_SETTINGS             MtrrTable;
44   IA32_DESCRIPTOR           GdtrProfile;
45   IA32_DESCRIPTOR           IdtrProfile;
46 } ACPI_CPU_DATA_EX;
47 
48 /**
49   Allocate EfiACPIMemoryNVS below 4G memory address.
50 
51   This function allocates EfiACPIMemoryNVS below 4G memory address.
52 
53   @param[in] Size   Size of memory to allocate.
54 
55   @return       Allocated address for output.
56 
57 **/
58 VOID *
AllocateAcpiNvsMemoryBelow4G(IN UINTN Size)59 AllocateAcpiNvsMemoryBelow4G (
60   IN UINTN  Size
61   )
62 {
63   EFI_PHYSICAL_ADDRESS  Address;
64   EFI_STATUS            Status;
65   VOID                  *Buffer;
66 
67   Address = BASE_4GB - 1;
68   Status  = gBS->AllocatePages (
69                    AllocateMaxAddress,
70                    EfiACPIMemoryNVS,
71                    EFI_SIZE_TO_PAGES (Size),
72                    &Address
73                    );
74   if (EFI_ERROR (Status)) {
75     return NULL;
76   }
77 
78   Buffer = (VOID *)(UINTN)Address;
79   ZeroMem (Buffer, Size);
80 
81   return Buffer;
82 }
83 
84 /**
85   Callback function executed when the EndOfDxe event group is signaled.
86 
87   We delay saving the MTRR settings until BDS signals EndOfDxe.
88 
89   @param[in]  Event    Event whose notification function is being invoked.
90   @param[out] Context  Pointer to the MTRR_SETTINGS buffer to fill in.
91 **/
92 VOID
93 EFIAPI
SaveMtrrsOnEndOfDxe(IN EFI_EVENT Event,OUT VOID * Context)94 SaveMtrrsOnEndOfDxe (
95   IN  EFI_EVENT  Event,
96   OUT VOID       *Context
97   )
98 {
99   DEBUG ((EFI_D_VERBOSE, "%a\n", __FUNCTION__));
100   MtrrGetAllMtrrs (Context);
101 
102   //
103   // Close event, so it will not be invoked again.
104   //
105   gBS->CloseEvent (Event);
106 }
107 
108 /**
109    The entry function of the CpuS3Data driver.
110 
111    Allocate and initialize all fields of the ACPI_CPU_DATA structure except the
112    MTRR settings.  Register an event notification on gEfiEndOfDxeEventGroupGuid
113    to capture the ACPI_CPU_DATA MTRR settings.  The PcdCpuS3DataAddress is set
114    to the address that ACPI_CPU_DATA is allocated at.
115 
116    @param[in] ImageHandle  The firmware allocated handle for the EFI image.
117    @param[in] SystemTable  A pointer to the EFI System Table.
118 
119    @retval EFI_SUCCESS     The entry point is executed successfully.
120    @retval other           Some error occurs when executing this entry point.
121 
122 **/
123 EFI_STATUS
124 EFIAPI
CpuS3DataInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)125 CpuS3DataInitialize (
126   IN EFI_HANDLE        ImageHandle,
127   IN EFI_SYSTEM_TABLE  *SystemTable
128   )
129 {
130   EFI_STATUS                 Status;
131   ACPI_CPU_DATA_EX           *AcpiCpuDataEx;
132   ACPI_CPU_DATA              *AcpiCpuData;
133   EFI_MP_SERVICES_PROTOCOL   *MpServices;
134   UINTN                      NumberOfCpus;
135   UINTN                      NumberOfEnabledProcessors;
136   VOID                       *Stack;
137   UINTN                      TableSize;
138   CPU_REGISTER_TABLE         *RegisterTable;
139   UINTN                      Index;
140   EFI_PROCESSOR_INFORMATION  ProcessorInfoBuffer;
141   UINTN                      GdtSize;
142   UINTN                      IdtSize;
143   VOID                       *Gdt;
144   VOID                       *Idt;
145   EFI_EVENT                  Event;
146 
147   //
148   // Allocate ACPI NVS memory below 4G memory for use on ACPI S3 resume.
149   //
150   AcpiCpuDataEx = AllocateAcpiNvsMemoryBelow4G (sizeof (ACPI_CPU_DATA_EX));
151   ASSERT (AcpiCpuDataEx != NULL);
152   AcpiCpuData = &AcpiCpuDataEx->AcpiCpuData;
153 
154   //
155   // Get MP Services Protocol
156   //
157   Status = gBS->LocateProtocol (
158                   &gEfiMpServiceProtocolGuid,
159                   NULL,
160                   (VOID **)&MpServices
161                   );
162   ASSERT_EFI_ERROR (Status);
163 
164   //
165   // Allocate a 4KB reserved page below 1MB
166   //
167   AcpiCpuData->StartupVector = BASE_1MB - 1;
168   Status = gBS->AllocatePages (
169                   AllocateMaxAddress,
170                   EfiReservedMemoryType,
171                   1,
172                   &AcpiCpuData->StartupVector
173                   );
174   ASSERT_EFI_ERROR (Status);
175 
176   //
177   // Get the number of CPUs
178   //
179   Status = MpServices->GetNumberOfProcessors (
180                          MpServices,
181                          &NumberOfCpus,
182                          &NumberOfEnabledProcessors
183                          );
184   ASSERT_EFI_ERROR (Status);
185   AcpiCpuData->NumberOfCpus = (UINT32)NumberOfCpus;
186 
187   //
188   // Initialize ACPI_CPU_DATA fields
189   //
190   AcpiCpuData->StackSize                 = PcdGet32 (PcdCpuApStackSize);
191   AcpiCpuData->ApMachineCheckHandlerBase = 0;
192   AcpiCpuData->ApMachineCheckHandlerSize = 0;
193   AcpiCpuData->GdtrProfile  = (EFI_PHYSICAL_ADDRESS)(UINTN)&AcpiCpuDataEx->GdtrProfile;
194   AcpiCpuData->IdtrProfile  = (EFI_PHYSICAL_ADDRESS)(UINTN)&AcpiCpuDataEx->IdtrProfile;
195   AcpiCpuData->MtrrTable    = (EFI_PHYSICAL_ADDRESS)(UINTN)&AcpiCpuDataEx->MtrrTable;
196 
197   //
198   // Allocate stack space for all CPUs
199   //
200   Stack = AllocateAcpiNvsMemoryBelow4G (NumberOfCpus * AcpiCpuData->StackSize);
201   ASSERT (Stack != NULL);
202   AcpiCpuData->StackAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Stack;
203 
204   //
205   // Get the boot processor's GDT and IDT
206   //
207   AsmReadGdtr (&AcpiCpuDataEx->GdtrProfile);
208   AsmReadIdtr (&AcpiCpuDataEx->IdtrProfile);
209 
210   //
211   // Allocate GDT and IDT in ACPI NVS and copy current GDT and IDT contents
212   //
213   GdtSize = AcpiCpuDataEx->GdtrProfile.Limit + 1;
214   IdtSize = AcpiCpuDataEx->IdtrProfile.Limit + 1;
215   Gdt = AllocateAcpiNvsMemoryBelow4G (GdtSize + IdtSize);
216   ASSERT (Gdt != NULL);
217   Idt = (VOID *)((UINTN)Gdt + GdtSize);
218   CopyMem (Gdt, (VOID *)AcpiCpuDataEx->GdtrProfile.Base, GdtSize);
219   CopyMem (Idt, (VOID *)AcpiCpuDataEx->IdtrProfile.Base, IdtSize);
220   AcpiCpuDataEx->GdtrProfile.Base = (UINTN)Gdt;
221   AcpiCpuDataEx->IdtrProfile.Base = (UINTN)Idt;
222 
223   //
224   // Allocate buffer for empty RegisterTable and PreSmmInitRegisterTable for all CPUs
225   //
226   TableSize = 2 * NumberOfCpus * sizeof (CPU_REGISTER_TABLE);
227   RegisterTable = (CPU_REGISTER_TABLE *)AllocateAcpiNvsMemoryBelow4G (TableSize);
228   ASSERT (RegisterTable != NULL);
229   for (Index = 0; Index < NumberOfCpus; Index++) {
230     Status = MpServices->GetProcessorInfo (
231                            MpServices,
232                            Index,
233                            &ProcessorInfoBuffer
234                            );
235     ASSERT_EFI_ERROR (Status);
236 
237     RegisterTable[Index].InitialApicId      = (UINT32)ProcessorInfoBuffer.ProcessorId;
238     RegisterTable[Index].TableLength        = 0;
239     RegisterTable[Index].AllocatedSize      = 0;
240     RegisterTable[Index].RegisterTableEntry = NULL;
241 
242     RegisterTable[NumberOfCpus + Index].InitialApicId      = (UINT32)ProcessorInfoBuffer.ProcessorId;
243     RegisterTable[NumberOfCpus + Index].TableLength        = 0;
244     RegisterTable[NumberOfCpus + Index].AllocatedSize      = 0;
245     RegisterTable[NumberOfCpus + Index].RegisterTableEntry = NULL;
246   }
247   AcpiCpuData->RegisterTable           = (EFI_PHYSICAL_ADDRESS)(UINTN)RegisterTable;
248   AcpiCpuData->PreSmmInitRegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)(RegisterTable + NumberOfCpus);
249 
250   //
251   // Set PcdCpuS3DataAddress to the base address of the ACPI_CPU_DATA structure
252   //
253   Status = PcdSet64S (PcdCpuS3DataAddress, (UINT64)(UINTN)AcpiCpuData);
254   ASSERT_EFI_ERROR (Status);
255 
256   //
257   // Register EFI_END_OF_DXE_EVENT_GROUP_GUID event.
258   // The notification function saves MTRRs for ACPI_CPU_DATA
259   //
260   Status = gBS->CreateEventEx (
261                   EVT_NOTIFY_SIGNAL,
262                   TPL_CALLBACK,
263                   SaveMtrrsOnEndOfDxe,
264                   &AcpiCpuDataEx->MtrrTable,
265                   &gEfiEndOfDxeEventGroupGuid,
266                   &Event
267                   );
268   ASSERT_EFI_ERROR (Status);
269 
270   return EFI_SUCCESS;
271 }
272