• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   This is a replacement for the ACPI S3 Save protocol.
3 
4   The ACPI S3 Save protocol used to be defined in the S3 boot path
5   specification 0.9. Instead, the same functionality is now hooked to the
6   End-of-Dxe event.
7 
8 Copyright (c) 2014-2015, Red Hat, Inc.<BR>
9 Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
10 
11 This program and the accompanying materials
12 are licensed and made available under the terms and conditions
13 of the BSD License which accompanies this distribution.  The
14 full text of the license may be found at
15 http://opensource.org/licenses/bsd-license.php
16 
17 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
18 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
19 
20 **/
21 
22 #include <PiDxe.h>
23 #include <Library/BaseLib.h>
24 #include <Library/BaseMemoryLib.h>
25 #include <Library/UefiBootServicesTableLib.h>
26 #include <Library/UefiRuntimeServicesTableLib.h>
27 #include <Library/HobLib.h>
28 #include <Library/LockBoxLib.h>
29 #include <Library/PcdLib.h>
30 #include <Library/DebugLib.h>
31 #include <Library/QemuFwCfgLib.h>
32 #include <Guid/AcpiVariableCompatibility.h>
33 #include <Guid/AcpiS3Context.h>
34 #include <Guid/Acpi.h>
35 #include <Guid/EventGroup.h>
36 #include <Protocol/LockBox.h>
37 #include <IndustryStandard/Acpi.h>
38 
39 EFI_GUID              mAcpiS3IdtrProfileGuid = {
40   0xdea652b0, 0xd587, 0x4c54, { 0xb5, 0xb4, 0xc6, 0x82, 0xe7, 0xa0, 0xaa, 0x3d }
41 };
42 
43 /**
44   Allocate memory below 4G memory address.
45 
46   This function allocates memory below 4G memory address.
47 
48   @param  MemoryType   Memory type of memory to allocate.
49   @param  Size         Size of memory to allocate.
50 
51   @return Allocated address for output.
52 
53 **/
54 VOID*
AllocateMemoryBelow4G(IN EFI_MEMORY_TYPE MemoryType,IN UINTN Size)55 AllocateMemoryBelow4G (
56   IN EFI_MEMORY_TYPE    MemoryType,
57   IN UINTN              Size
58   )
59 {
60   UINTN                 Pages;
61   EFI_PHYSICAL_ADDRESS  Address;
62   EFI_STATUS            Status;
63   VOID*                 Buffer;
64 
65   Pages = EFI_SIZE_TO_PAGES (Size);
66   Address = 0xffffffff;
67 
68   Status  = gBS->AllocatePages (
69                    AllocateMaxAddress,
70                    MemoryType,
71                    Pages,
72                    &Address
73                    );
74   ASSERT_EFI_ERROR (Status);
75 
76   Buffer = (VOID *) (UINTN) Address;
77   ZeroMem (Buffer, Size);
78 
79   return Buffer;
80 }
81 
82 /**
83 
84   This function scan ACPI table in RSDT.
85 
86   @param Rsdt      ACPI RSDT
87   @param Signature ACPI table signature
88 
89   @return ACPI table
90 
91 **/
92 VOID *
ScanTableInRSDT(IN EFI_ACPI_DESCRIPTION_HEADER * Rsdt,IN UINT32 Signature)93 ScanTableInRSDT (
94   IN EFI_ACPI_DESCRIPTION_HEADER    *Rsdt,
95   IN UINT32                         Signature
96   )
97 {
98   UINTN                              Index;
99   UINT32                             EntryCount;
100   UINT32                             *EntryPtr;
101   EFI_ACPI_DESCRIPTION_HEADER        *Table;
102 
103   if (Rsdt == NULL) {
104     return NULL;
105   }
106 
107   EntryCount = (Rsdt->Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) / sizeof(UINT32);
108 
109   EntryPtr = (UINT32 *)(Rsdt + 1);
110   for (Index = 0; Index < EntryCount; Index ++, EntryPtr ++) {
111     Table = (EFI_ACPI_DESCRIPTION_HEADER *)((UINTN)(*EntryPtr));
112     if (Table->Signature == Signature) {
113       return Table;
114     }
115   }
116 
117   return NULL;
118 }
119 
120 /**
121 
122   This function scan ACPI table in XSDT.
123 
124   @param Xsdt      ACPI XSDT
125   @param Signature ACPI table signature
126 
127   @return ACPI table
128 
129 **/
130 VOID *
ScanTableInXSDT(IN EFI_ACPI_DESCRIPTION_HEADER * Xsdt,IN UINT32 Signature)131 ScanTableInXSDT (
132   IN EFI_ACPI_DESCRIPTION_HEADER    *Xsdt,
133   IN UINT32                         Signature
134   )
135 {
136   UINTN                          Index;
137   UINT32                         EntryCount;
138   UINT64                         EntryPtr;
139   UINTN                          BasePtr;
140   EFI_ACPI_DESCRIPTION_HEADER    *Table;
141 
142   if (Xsdt == NULL) {
143     return NULL;
144   }
145 
146   EntryCount = (Xsdt->Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) / sizeof(UINT64);
147 
148   BasePtr = (UINTN)(Xsdt + 1);
149   for (Index = 0; Index < EntryCount; Index ++) {
150     CopyMem (&EntryPtr, (VOID *)(BasePtr + Index * sizeof(UINT64)), sizeof(UINT64));
151     Table = (EFI_ACPI_DESCRIPTION_HEADER *)((UINTN)(EntryPtr));
152     if (Table->Signature == Signature) {
153       return Table;
154     }
155   }
156 
157   return NULL;
158 }
159 
160 /**
161   To find Facs in FADT.
162 
163   @param Fadt   FADT table pointer
164 
165   @return  Facs table pointer.
166 **/
167 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE  *
FindAcpiFacsFromFadt(IN EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE * Fadt)168 FindAcpiFacsFromFadt (
169   IN EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE     *Fadt
170   )
171 {
172   EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE  *Facs;
173   UINT64                                        Data64;
174 
175   if (Fadt == NULL) {
176     return NULL;
177   }
178 
179   if (Fadt->Header.Revision < EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_REVISION) {
180     Facs = (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)Fadt->FirmwareCtrl;
181   } else {
182     if (Fadt->FirmwareCtrl != 0) {
183       Facs = (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)Fadt->FirmwareCtrl;
184     } else {
185       CopyMem (&Data64, &Fadt->XFirmwareCtrl, sizeof(UINT64));
186       Facs = (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)Data64;
187     }
188   }
189   return Facs;
190 }
191 
192 /**
193   To find Facs in Acpi tables.
194 
195   To find Firmware ACPI control strutcure in Acpi Tables since the S3 waking vector is stored
196   in the table.
197 
198   @param AcpiTableGuid   The guid used to find ACPI table in UEFI ConfigurationTable.
199 
200   @return  Facs table pointer.
201 **/
202 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE  *
FindAcpiFacsTableByAcpiGuid(IN EFI_GUID * AcpiTableGuid)203 FindAcpiFacsTableByAcpiGuid (
204   IN EFI_GUID  *AcpiTableGuid
205   )
206 {
207   EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER  *Rsdp;
208   EFI_ACPI_DESCRIPTION_HEADER                   *Rsdt;
209   EFI_ACPI_DESCRIPTION_HEADER                   *Xsdt;
210   EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE     *Fadt;
211   EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE  *Facs;
212   UINTN                                         Index;
213 
214   Rsdp  = NULL;
215   //
216   // found ACPI table RSD_PTR from system table
217   //
218   for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
219     if (CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), AcpiTableGuid)) {
220       //
221       // A match was found.
222       //
223       Rsdp = gST->ConfigurationTable[Index].VendorTable;
224       break;
225     }
226   }
227 
228   if (Rsdp == NULL) {
229     return NULL;
230   }
231 
232   //
233   // Search XSDT
234   //
235   if (Rsdp->Revision >= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION) {
236     Xsdt = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN) Rsdp->XsdtAddress;
237     Fadt = ScanTableInXSDT (Xsdt, EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE);
238     if (Fadt != NULL) {
239       Facs = FindAcpiFacsFromFadt (Fadt);
240       if (Facs != NULL) {
241         return Facs;
242       }
243     }
244   }
245 
246   //
247   // Search RSDT
248   //
249   Rsdt = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN) Rsdp->RsdtAddress;
250   Fadt = ScanTableInRSDT (Rsdt, EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE);
251   if (Fadt != NULL) {
252     Facs = FindAcpiFacsFromFadt (Fadt);
253     if (Facs != NULL) {
254       return Facs;
255     }
256   }
257 
258   return NULL;
259 }
260 
261 /**
262   To find Facs in Acpi tables.
263 
264   To find Firmware ACPI control strutcure in Acpi Tables since the S3 waking vector is stored
265   in the table.
266 
267   @return  Facs table pointer.
268 **/
269 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE  *
FindAcpiFacsTable(VOID)270 FindAcpiFacsTable (
271   VOID
272   )
273 {
274   EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;
275 
276   Facs = FindAcpiFacsTableByAcpiGuid (&gEfiAcpi20TableGuid);
277   if (Facs != NULL) {
278     return Facs;
279   }
280 
281   return FindAcpiFacsTableByAcpiGuid (&gEfiAcpi10TableGuid);
282 }
283 
284 /**
285   Allocates and fills in the Page Directory and Page Table Entries to
286   establish a 1:1 Virtual to Physical mapping.
287   If BootScriptExector driver will run in 64-bit mode, this function will establish the 1:1
288   virtual to physical mapping page table.
289   If BootScriptExector driver will not run in 64-bit mode, this function will do nothing.
290 
291   @return  the 1:1 Virtual to Physical identity mapping page table base address.
292 
293 **/
294 EFI_PHYSICAL_ADDRESS
S3CreateIdentityMappingPageTables(VOID)295 S3CreateIdentityMappingPageTables (
296   VOID
297   )
298 {
299   if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
300     UINT32                                        RegEax;
301     UINT32                                        RegEdx;
302     UINT8                                         PhysicalAddressBits;
303     UINT32                                        NumberOfPml4EntriesNeeded;
304     UINT32                                        NumberOfPdpEntriesNeeded;
305     EFI_PHYSICAL_ADDRESS                          S3NvsPageTableAddress;
306     UINTN                                         TotalPageTableSize;
307     VOID                                          *Hob;
308     BOOLEAN                                       Page1GSupport;
309 
310     Page1GSupport = FALSE;
311     if (PcdGetBool(PcdUse1GPageTable)) {
312       AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
313       if (RegEax >= 0x80000001) {
314         AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
315         if ((RegEdx & BIT26) != 0) {
316           Page1GSupport = TRUE;
317         }
318       }
319     }
320 
321     //
322     // Get physical address bits supported.
323     //
324     Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
325     if (Hob != NULL) {
326       PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;
327     } else {
328       AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
329       if (RegEax >= 0x80000008) {
330         AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
331         PhysicalAddressBits = (UINT8) RegEax;
332       } else {
333         PhysicalAddressBits = 36;
334       }
335     }
336 
337     //
338     // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
339     //
340     ASSERT (PhysicalAddressBits <= 52);
341     if (PhysicalAddressBits > 48) {
342       PhysicalAddressBits = 48;
343     }
344 
345     //
346     // Calculate the table entries needed.
347     //
348     if (PhysicalAddressBits <= 39 ) {
349       NumberOfPml4EntriesNeeded = 1;
350       NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));
351     } else {
352       NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));
353       NumberOfPdpEntriesNeeded = 512;
354     }
355 
356     //
357     // We need calculate whole page size then allocate once, because S3 restore page table does not know each page in Nvs.
358     //
359     if (!Page1GSupport) {
360       TotalPageTableSize = (UINTN)(1 + NumberOfPml4EntriesNeeded + NumberOfPml4EntriesNeeded * NumberOfPdpEntriesNeeded);
361     } else {
362       TotalPageTableSize = (UINTN)(1 + NumberOfPml4EntriesNeeded);
363     }
364     DEBUG ((EFI_D_ERROR, "TotalPageTableSize - %Lx pages\n",
365       (UINT64)TotalPageTableSize));
366 
367     //
368     // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
369     //
370     S3NvsPageTableAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, EFI_PAGES_TO_SIZE(TotalPageTableSize));
371     ASSERT (S3NvsPageTableAddress != 0);
372     return S3NvsPageTableAddress;
373   } else {
374     //
375     // If DXE is running 32-bit mode, no need to establish page table.
376     //
377     return  (EFI_PHYSICAL_ADDRESS) 0;
378   }
379 }
380 
381 /**
382   Prepares all information that is needed in the S3 resume boot path.
383 
384   Allocate the resources or prepare informations and save in ACPI variable set for S3 resume boot path
385 
386   @retval EFI_SUCCESS           All information was saved successfully.
387 **/
388 STATIC
389 EFI_STATUS
390 EFIAPI
S3Ready(VOID)391 S3Ready (
392   VOID
393   )
394 {
395   EFI_STATUS                                    Status;
396   EFI_PHYSICAL_ADDRESS                          AcpiS3ContextBuffer;
397   ACPI_S3_CONTEXT                               *AcpiS3Context;
398   STATIC BOOLEAN                                AlreadyEntered;
399   IA32_DESCRIPTOR                               *Idtr;
400   IA32_IDT_GATE_DESCRIPTOR                      *IdtGate;
401 
402   DEBUG ((EFI_D_INFO, "S3Ready!\n"));
403 
404   ASSERT (!AlreadyEntered);
405   if (AlreadyEntered) {
406     return EFI_SUCCESS;
407   }
408   AlreadyEntered = TRUE;
409 
410   AcpiS3Context = AllocateMemoryBelow4G (EfiReservedMemoryType, sizeof(*AcpiS3Context));
411   ASSERT (AcpiS3Context != NULL);
412   AcpiS3ContextBuffer = (EFI_PHYSICAL_ADDRESS)(UINTN)AcpiS3Context;
413 
414   //
415   // Get ACPI Table because we will save its position to variable
416   //
417   AcpiS3Context->AcpiFacsTable = (EFI_PHYSICAL_ADDRESS)(UINTN)FindAcpiFacsTable ();
418   ASSERT (AcpiS3Context->AcpiFacsTable != 0);
419 
420   IdtGate = AllocateMemoryBelow4G (EfiReservedMemoryType, sizeof(IA32_IDT_GATE_DESCRIPTOR) * 0x100 + sizeof(IA32_DESCRIPTOR));
421   Idtr = (IA32_DESCRIPTOR *)(IdtGate + 0x100);
422   Idtr->Base  = (UINTN)IdtGate;
423   Idtr->Limit = (UINT16)(sizeof(IA32_IDT_GATE_DESCRIPTOR) * 0x100 - 1);
424   AcpiS3Context->IdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)Idtr;
425 
426   Status = SaveLockBox (
427              &mAcpiS3IdtrProfileGuid,
428              (VOID *)(UINTN)Idtr,
429              (UINTN)sizeof(IA32_DESCRIPTOR)
430              );
431   ASSERT_EFI_ERROR (Status);
432 
433   Status = SetLockBoxAttributes (&mAcpiS3IdtrProfileGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
434   ASSERT_EFI_ERROR (Status);
435 
436   //
437   // Allocate page table
438   //
439   AcpiS3Context->S3NvsPageTableAddress = S3CreateIdentityMappingPageTables ();
440 
441   //
442   // Allocate stack
443   //
444   AcpiS3Context->BootScriptStackSize = PcdGet32 (PcdS3BootScriptStackSize);
445   AcpiS3Context->BootScriptStackBase = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, PcdGet32 (PcdS3BootScriptStackSize));
446   ASSERT (AcpiS3Context->BootScriptStackBase != 0);
447 
448   //
449   // Allocate a code buffer < 4G for S3 debug to load external code, set invalid code instructions in it.
450   //
451   AcpiS3Context->S3DebugBufferAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, EFI_PAGE_SIZE);
452   SetMem ((VOID *)(UINTN)AcpiS3Context->S3DebugBufferAddress, EFI_PAGE_SIZE, 0xff);
453 
454   DEBUG ((EFI_D_INFO, "AcpiS3Context: AcpiFacsTable is 0x%8Lx\n",
455     AcpiS3Context->AcpiFacsTable));
456   DEBUG ((EFI_D_INFO, "AcpiS3Context: IdtrProfile is 0x%8Lx\n",
457     AcpiS3Context->IdtrProfile));
458   DEBUG ((EFI_D_INFO, "AcpiS3Context: S3NvsPageTableAddress is 0x%8Lx\n",
459     AcpiS3Context->S3NvsPageTableAddress));
460   DEBUG ((EFI_D_INFO, "AcpiS3Context: S3DebugBufferAddress is 0x%8Lx\n",
461     AcpiS3Context->S3DebugBufferAddress));
462 
463   Status = SaveLockBox (
464              &gEfiAcpiVariableGuid,
465              &AcpiS3ContextBuffer,
466              sizeof(AcpiS3ContextBuffer)
467              );
468   ASSERT_EFI_ERROR (Status);
469 
470   Status = SaveLockBox (
471              &gEfiAcpiS3ContextGuid,
472              (VOID *)(UINTN)AcpiS3Context,
473              (UINTN)sizeof(*AcpiS3Context)
474              );
475   ASSERT_EFI_ERROR (Status);
476 
477   Status = SetLockBoxAttributes (&gEfiAcpiS3ContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
478   ASSERT_EFI_ERROR (Status);
479 
480   return EFI_SUCCESS;
481 }
482 
483 /**
484   Callback function executed when the EndOfDxe event group is signaled.
485 
486   @param[in] Event    Event whose notification function is being invoked.
487   @param[in] Context  The pointer to the notification function's context, which
488                       is implementation-dependent.
489 **/
490 VOID
491 EFIAPI
OnEndOfDxe(IN EFI_EVENT Event,IN VOID * Context)492 OnEndOfDxe (
493   IN EFI_EVENT Event,
494   IN VOID      *Context
495   )
496 {
497   EFI_STATUS Status;
498 
499   //
500   // Our S3Ready() function always succeeds.
501   //
502   Status = S3Ready ();
503   ASSERT_EFI_ERROR (Status);
504 
505   //
506   // Close the event, deregistering the callback and freeing resources.
507   //
508   Status = gBS->CloseEvent (Event);
509   ASSERT_EFI_ERROR (Status);
510 }
511 
512 
513 /**
514   The Driver Entry Point.
515 
516   The function is the driver Entry point that will register the End-of-Dxe
517   callback.
518 
519   @param ImageHandle   A handle for the image that is initializing this driver
520   @param SystemTable   A pointer to the EFI system table
521 
522   @retval EFI_SUCCESS:              Driver initialized successfully
523   @retval EFI_LOAD_ERROR:           Failed to Initialize or has been loaded
524   @retval EFI_OUT_OF_RESOURCES      Could not allocate needed resources
525 
526 **/
527 EFI_STATUS
528 EFIAPI
InstallEndOfDxeCallback(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)529 InstallEndOfDxeCallback (
530   IN EFI_HANDLE           ImageHandle,
531   IN EFI_SYSTEM_TABLE     *SystemTable
532   )
533 {
534   EFI_STATUS        Status;
535   EFI_EVENT         EndOfDxeEvent;
536 
537   if (!QemuFwCfgS3Enabled()) {
538     return EFI_LOAD_ERROR;
539   }
540 
541   if (!FeaturePcdGet (PcdSmmSmramRequire)) {
542     Status = gBS->InstallMultipleProtocolInterfaces (
543                     &ImageHandle,
544                     &gEfiLockBoxProtocolGuid, NULL,
545                     NULL
546                     );
547     ASSERT_EFI_ERROR (Status);
548   }
549 
550   Status = gBS->CreateEventEx (
551                   EVT_NOTIFY_SIGNAL,
552                   TPL_CALLBACK,
553                   OnEndOfDxe,
554                   NULL, /* NotifyContext */
555                   &gEfiEndOfDxeEventGroupGuid,
556                   &EndOfDxeEvent
557                   );
558   ASSERT_EFI_ERROR (Status);
559   return Status;
560 }
561