Home | History | Annotate | Download | only in AcpiS3SaveDxe
      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*
     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 *
     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 *
    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  *
    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  *
    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  *
    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
    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
    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
    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
    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