1 /** @file
2   This is the implementation to save ACPI S3 Context.
3 
4 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
5 
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7 
8 **/
9 
10 #include <PiDxe.h>
11 #include <Library/BaseLib.h>
12 #include <Library/BaseMemoryLib.h>
13 #include <Library/UefiBootServicesTableLib.h>
14 #include <Library/HobLib.h>
15 #include <Library/LockBoxLib.h>
16 #include <Library/PcdLib.h>
17 #include <Library/DebugLib.h>
18 #include <Library/UefiLib.h>
19 #include <Guid/AcpiS3Context.h>
20 #include <IndustryStandard/Acpi.h>
21 #include <Protocol/LockBox.h>
22 
23 //
24 // 8 extra pages for PF handler.
25 //
26 #define EXTRA_PAGE_TABLE_PAGES   8
27 
28 EFI_GUID              mAcpiS3IdtrProfileGuid = {
29   0xdea652b0, 0xd587, 0x4c54, { 0xb5, 0xb4, 0xc6, 0x82, 0xe7, 0xa0, 0xaa, 0x3d }
30 };
31 
32 /**
33   Allocate memory below 4G memory address.
34 
35   This function allocates memory below 4G memory address.
36 
37   @param  MemoryType   Memory type of memory to allocate.
38   @param  Size         Size of memory to allocate.
39 
40   @return Allocated address for output.
41 
42 **/
43 VOID*
AllocateMemoryBelow4G(IN EFI_MEMORY_TYPE MemoryType,IN UINTN Size)44 AllocateMemoryBelow4G (
45   IN EFI_MEMORY_TYPE    MemoryType,
46   IN UINTN              Size
47   )
48 {
49   UINTN                 Pages;
50   EFI_PHYSICAL_ADDRESS  Address;
51   EFI_STATUS            Status;
52   VOID*                 Buffer;
53 
54   Pages = EFI_SIZE_TO_PAGES (Size);
55   Address = 0xffffffff;
56 
57   Status  = gBS->AllocatePages (
58                    AllocateMaxAddress,
59                    MemoryType,
60                    Pages,
61                    &Address
62                    );
63   ASSERT_EFI_ERROR (Status);
64 
65   Buffer = (VOID *) (UINTN) Address;
66   ZeroMem (Buffer, Size);
67 
68   return Buffer;
69 }
70 
71 /**
72   The function will check if long mode waking vector is supported.
73 
74   @param[in] Facs   Pointer to FACS table.
75 
76   @retval TRUE   Long mode waking vector is supported.
77   @retval FALSE  Long mode waking vector is not supported.
78 
79 **/
80 BOOLEAN
IsLongModeWakingVectorSupport(IN EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE * Facs)81 IsLongModeWakingVectorSupport (
82   IN EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs
83   )
84 {
85   if ((Facs == NULL) ||
86       (Facs->Signature != EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) ) {
87     //
88     // Something wrong with FACS.
89     //
90     return FALSE;
91   }
92   if ((Facs->Version == EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION) &&
93       ((Facs->Flags & EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F) != 0)) {
94     //
95     // BIOS supports 64bit waking vector.
96     //
97     if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
98       return TRUE;
99     }
100   }
101   return FALSE;
102 }
103 
104 /**
105   Allocates page table buffer.
106 
107   @param[in] LongModeWakingVectorSupport    Support long mode waking vector or not.
108 
109   If BootScriptExector driver will run in 64-bit mode, this function will establish the 1:1
110   virtual to physical mapping page table when long mode waking vector is supported, otherwise
111   create 4G page table when long mode waking vector is not supported and let PF handler to
112   handle > 4G request.
113   If BootScriptExector driver will not run in 64-bit mode, this function will do nothing.
114 
115   @return Page table base address.
116 
117 **/
118 EFI_PHYSICAL_ADDRESS
S3AllocatePageTablesBuffer(IN BOOLEAN LongModeWakingVectorSupport)119 S3AllocatePageTablesBuffer (
120   IN BOOLEAN    LongModeWakingVectorSupport
121   )
122 {
123   if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
124     UINTN                                         ExtraPageTablePages;
125     UINT32                                        RegEax;
126     UINT32                                        RegEdx;
127     UINT8                                         PhysicalAddressBits;
128     UINT32                                        NumberOfPml4EntriesNeeded;
129     UINT32                                        NumberOfPdpEntriesNeeded;
130     EFI_PHYSICAL_ADDRESS                          S3NvsPageTableAddress;
131     UINTN                                         TotalPageTableSize;
132     VOID                                          *Hob;
133     BOOLEAN                                       Page1GSupport;
134 
135     Page1GSupport = FALSE;
136     if (PcdGetBool(PcdUse1GPageTable)) {
137       AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
138       if (RegEax >= 0x80000001) {
139         AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
140         if ((RegEdx & BIT26) != 0) {
141           Page1GSupport = TRUE;
142         }
143       }
144     }
145 
146     //
147     // Get physical address bits supported.
148     //
149     Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
150     if (Hob != NULL) {
151       PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;
152     } else {
153       AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
154       if (RegEax >= 0x80000008) {
155         AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
156         PhysicalAddressBits = (UINT8) RegEax;
157       } else {
158         PhysicalAddressBits = 36;
159       }
160     }
161 
162     //
163     // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
164     //
165     ASSERT (PhysicalAddressBits <= 52);
166     if (PhysicalAddressBits > 48) {
167       PhysicalAddressBits = 48;
168     }
169 
170     ExtraPageTablePages = 0;
171     if (!LongModeWakingVectorSupport) {
172       //
173       // Create 4G page table when BIOS does not support long mode waking vector,
174       // and let PF handler to handle > 4G request.
175       //
176       PhysicalAddressBits = 32;
177       ExtraPageTablePages = EXTRA_PAGE_TABLE_PAGES;
178     }
179 
180     //
181     // Calculate the table entries needed.
182     //
183     if (PhysicalAddressBits <= 39 ) {
184       NumberOfPml4EntriesNeeded = 1;
185       NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));
186     } else {
187       NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));
188       NumberOfPdpEntriesNeeded = 512;
189     }
190 
191     //
192     // We need calculate whole page size then allocate once, because S3 restore page table does not know each page in Nvs.
193     //
194     if (!Page1GSupport) {
195       TotalPageTableSize = 1 + NumberOfPml4EntriesNeeded + NumberOfPml4EntriesNeeded * NumberOfPdpEntriesNeeded;
196     } else {
197       TotalPageTableSize = 1 + NumberOfPml4EntriesNeeded;
198     }
199 
200     TotalPageTableSize += ExtraPageTablePages;
201     DEBUG ((DEBUG_INFO, "AcpiS3ContextSave TotalPageTableSize - 0x%x pages\n", TotalPageTableSize));
202 
203     //
204     // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
205     //
206     S3NvsPageTableAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, EFI_PAGES_TO_SIZE(TotalPageTableSize));
207     ASSERT (S3NvsPageTableAddress != 0);
208     return S3NvsPageTableAddress;
209   } else {
210     //
211     // If DXE is running 32-bit mode, no need to establish page table.
212     //
213     return  (EFI_PHYSICAL_ADDRESS) 0;
214   }
215 }
216 
217 /**
218   Callback function executed when the EndOfDxe event group is signaled.
219 
220   @param[in] Event      Event whose notification function is being invoked.
221   @param[in] Context    The pointer to the notification function's context, which
222                         is implementation-dependent.
223 **/
224 VOID
225 EFIAPI
AcpiS3ContextSaveOnEndOfDxe(IN EFI_EVENT Event,IN VOID * Context)226 AcpiS3ContextSaveOnEndOfDxe (
227   IN EFI_EVENT  Event,
228   IN VOID       *Context
229   )
230 {
231   EFI_STATUS                                    Status;
232   EFI_PHYSICAL_ADDRESS                          AcpiS3ContextBuffer;
233   ACPI_S3_CONTEXT                               *AcpiS3Context;
234   IA32_DESCRIPTOR                               *Idtr;
235   IA32_IDT_GATE_DESCRIPTOR                      *IdtGate;
236   EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE  *Facs;
237   VOID                                          *Interface;
238 
239   DEBUG ((EFI_D_INFO, "AcpiS3ContextSave!\n"));
240 
241   Status = gBS->LocateProtocol (&gEfiLockBoxProtocolGuid, NULL, &Interface);
242   if (EFI_ERROR (Status)) {
243     DEBUG ((EFI_D_INFO | EFI_D_WARN, "ACPI S3 context can't be saved without LockBox!\n"));
244     goto Done;
245   }
246 
247   AcpiS3Context = AllocateMemoryBelow4G (EfiReservedMemoryType, sizeof(*AcpiS3Context));
248   ASSERT (AcpiS3Context != NULL);
249   AcpiS3ContextBuffer = (EFI_PHYSICAL_ADDRESS)(UINTN)AcpiS3Context;
250 
251   //
252   // Get ACPI Table because we will save its position to variable
253   //
254   Facs = (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *) EfiLocateFirstAcpiTable (
255                                                             EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
256                                                             );
257   AcpiS3Context->AcpiFacsTable = (EFI_PHYSICAL_ADDRESS) (UINTN) Facs;
258   ASSERT (AcpiS3Context->AcpiFacsTable != 0);
259 
260   IdtGate = AllocateMemoryBelow4G (EfiReservedMemoryType, sizeof(IA32_IDT_GATE_DESCRIPTOR) * 0x100 + sizeof(IA32_DESCRIPTOR));
261   Idtr = (IA32_DESCRIPTOR *)(IdtGate + 0x100);
262   Idtr->Base  = (UINTN)IdtGate;
263   Idtr->Limit = (UINT16)(sizeof(IA32_IDT_GATE_DESCRIPTOR) * 0x100 - 1);
264   AcpiS3Context->IdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)Idtr;
265 
266   Status = SaveLockBox (
267              &mAcpiS3IdtrProfileGuid,
268              (VOID *)(UINTN)Idtr,
269              (UINTN)sizeof(IA32_DESCRIPTOR)
270              );
271   ASSERT_EFI_ERROR (Status);
272 
273   Status = SetLockBoxAttributes (&mAcpiS3IdtrProfileGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
274   ASSERT_EFI_ERROR (Status);
275 
276   //
277   // Allocate page table
278   //
279   AcpiS3Context->S3NvsPageTableAddress = S3AllocatePageTablesBuffer (IsLongModeWakingVectorSupport (Facs));
280 
281   //
282   // Allocate stack
283   //
284   AcpiS3Context->BootScriptStackSize = PcdGet32 (PcdS3BootScriptStackSize);
285   AcpiS3Context->BootScriptStackBase = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, PcdGet32 (PcdS3BootScriptStackSize));
286   ASSERT (AcpiS3Context->BootScriptStackBase != 0);
287 
288   //
289   // Allocate a code buffer < 4G for S3 debug to load external code, set invalid code instructions in it.
290   //
291   AcpiS3Context->S3DebugBufferAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, EFI_PAGE_SIZE);
292   SetMem ((VOID *)(UINTN)AcpiS3Context->S3DebugBufferAddress, EFI_PAGE_SIZE, 0xff);
293 
294   DEBUG((EFI_D_INFO, "AcpiS3Context: AcpiFacsTable is 0x%8x\n", AcpiS3Context->AcpiFacsTable));
295   DEBUG((EFI_D_INFO, "AcpiS3Context: IdtrProfile is 0x%8x\n", AcpiS3Context->IdtrProfile));
296   DEBUG((EFI_D_INFO, "AcpiS3Context: S3NvsPageTableAddress is 0x%8x\n", AcpiS3Context->S3NvsPageTableAddress));
297   DEBUG((EFI_D_INFO, "AcpiS3Context: S3DebugBufferAddress is 0x%8x\n", AcpiS3Context->S3DebugBufferAddress));
298   DEBUG((EFI_D_INFO, "AcpiS3Context: BootScriptStackBase is 0x%8x\n", AcpiS3Context->BootScriptStackBase));
299   DEBUG((EFI_D_INFO, "AcpiS3Context: BootScriptStackSize is 0x%8x\n", AcpiS3Context->BootScriptStackSize));
300 
301   Status = SaveLockBox (
302              &gEfiAcpiVariableGuid,
303              &AcpiS3ContextBuffer,
304              sizeof(AcpiS3ContextBuffer)
305              );
306   ASSERT_EFI_ERROR (Status);
307 
308   Status = SaveLockBox (
309              &gEfiAcpiS3ContextGuid,
310              (VOID *)(UINTN)AcpiS3Context,
311              (UINTN)sizeof(*AcpiS3Context)
312              );
313   ASSERT_EFI_ERROR (Status);
314 
315   Status = SetLockBoxAttributes (&gEfiAcpiS3ContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
316   ASSERT_EFI_ERROR (Status);
317 
318 Done:
319   //
320   // Close the event, deregistering the callback and freeing resources.
321   //
322   Status = gBS->CloseEvent (Event);
323   ASSERT_EFI_ERROR (Status);
324 }
325 
326