Home | History | Annotate | Download | only in BootScriptExecutorDxe
      1 /** @file
      2   This is the code for Boot Script Executer module.
      3 
      4   This driver is dispatched by Dxe core and the driver will reload itself to ACPI reserved memory
      5   in the entry point. The functionality is to interpret and restore the S3 boot script
      6 
      7 Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
      8 
      9 This program and the accompanying materials
     10 are licensed and made available under the terms and conditions of the BSD License
     11 which accompanies this distribution.  The full text of the license may be found at
     12 http://opensource.org/licenses/bsd-license.php
     13 
     14 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     15 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     16 
     17 **/
     18 
     19 #include "ScriptExecute.h"
     20 
     21 EFI_GUID              mBootScriptExecutorImageGuid = {
     22   0x9a8d3433, 0x9fe8, 0x42b6, { 0x87, 0xb, 0x1e, 0x31, 0xc8, 0x4e, 0xbe, 0x3b }
     23 };
     24 
     25 BOOLEAN               mPage1GSupport = FALSE;
     26 
     27 /**
     28   Entry function of Boot script exector. This function will be executed in
     29   S3 boot path.
     30   This function should not return, because it is invoked by switch stack.
     31 
     32   @param  AcpiS3Context    a pointer to a structure of ACPI_S3_CONTEXT
     33   @param  PeiS3ResumeState a pointer to a structure of PEI_S3_RESUME_STATE
     34 
     35   @retval EFI_INVALID_PARAMETER - OS waking vector not found
     36   @retval EFI_UNSUPPORTED - something wrong when we resume to OS
     37 **/
     38 EFI_STATUS
     39 EFIAPI
     40 S3BootScriptExecutorEntryFunction (
     41   IN ACPI_S3_CONTEXT       *AcpiS3Context,
     42   IN PEI_S3_RESUME_STATE   *PeiS3ResumeState
     43   )
     44 {
     45   EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE  *Facs;
     46   EFI_STATUS                                    Status;
     47   UINTN                                         TempStackTop;
     48   UINTN                                         TempStack[0x10];
     49   UINTN                                         AsmTransferControl16Address;
     50   IA32_DESCRIPTOR                               IdtDescriptor;
     51 
     52   //
     53   // Disable interrupt of Debug timer, since new IDT table cannot handle it.
     54   //
     55   SaveAndSetDebugTimerInterrupt (FALSE);
     56 
     57   AsmReadIdtr (&IdtDescriptor);
     58   //
     59   // Restore IDT for debug
     60   //
     61   SetIdtEntry (AcpiS3Context);
     62 
     63   //
     64   // Initialize Debug Agent to support source level debug in S3 path, it will disable interrupt and Debug Timer.
     65   //
     66   InitializeDebugAgent (DEBUG_AGENT_INIT_S3, (VOID *)&IdtDescriptor, NULL);
     67 
     68   //
     69   // Because not install BootScriptExecute PPI(used just in this module), So just pass NULL
     70   // for that parameter.
     71   //
     72   Status = S3BootScriptExecute ();
     73 
     74   //
     75   // If invalid script table or opcode in S3 boot script table.
     76   //
     77   ASSERT_EFI_ERROR (Status);
     78 
     79   if (EFI_ERROR (Status)) {
     80     CpuDeadLoop ();
     81     return Status;
     82   }
     83 
     84   AsmWbinvd ();
     85 
     86   //
     87   // Get ACPI Table Address
     88   //
     89   Facs = (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *) ((UINTN) (AcpiS3Context->AcpiFacsTable));
     90 
     91   //
     92   // We need turn back to S3Resume - install boot script done ppi and report status code on S3resume.
     93   //
     94   if (PeiS3ResumeState != 0) {
     95     //
     96     // Need report status back to S3ResumePeim.
     97     // If boot script execution is failed, S3ResumePeim wil report the error status code.
     98     //
     99     PeiS3ResumeState->ReturnStatus = (UINT64)(UINTN)Status;
    100     if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
    101       //
    102       // X64 S3 Resume
    103       //
    104       DEBUG ((EFI_D_ERROR, "Call AsmDisablePaging64() to return to S3 Resume in PEI Phase\n"));
    105       PeiS3ResumeState->AsmTransferControl = (EFI_PHYSICAL_ADDRESS)(UINTN)AsmTransferControl32;
    106 
    107       if ((Facs != NULL) &&
    108           (Facs->Signature == EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) &&
    109           (Facs->FirmwareWakingVector != 0) ) {
    110         //
    111         // more step needed - because relative address is handled differently between X64 and IA32.
    112         //
    113         AsmTransferControl16Address = (UINTN)AsmTransferControl16;
    114         AsmFixAddress16 = (UINT32)AsmTransferControl16Address;
    115         AsmJmpAddr32 = (UINT32)((Facs->FirmwareWakingVector & 0xF) | ((Facs->FirmwareWakingVector & 0xFFFF0) << 12));
    116       }
    117 
    118       AsmDisablePaging64 (
    119         PeiS3ResumeState->ReturnCs,
    120         (UINT32)PeiS3ResumeState->ReturnEntryPoint,
    121         (UINT32)(UINTN)AcpiS3Context,
    122         (UINT32)(UINTN)PeiS3ResumeState,
    123         (UINT32)PeiS3ResumeState->ReturnStackPointer
    124         );
    125     } else {
    126       //
    127       // IA32 S3 Resume
    128       //
    129       DEBUG ((EFI_D_ERROR, "Call SwitchStack() to return to S3 Resume in PEI Phase\n"));
    130       PeiS3ResumeState->AsmTransferControl = (EFI_PHYSICAL_ADDRESS)(UINTN)AsmTransferControl;
    131 
    132       SwitchStack (
    133         (SWITCH_STACK_ENTRY_POINT)(UINTN)PeiS3ResumeState->ReturnEntryPoint,
    134         (VOID *)(UINTN)AcpiS3Context,
    135         (VOID *)(UINTN)PeiS3ResumeState,
    136         (VOID *)(UINTN)PeiS3ResumeState->ReturnStackPointer
    137         );
    138     }
    139 
    140     //
    141     // Never run to here
    142     //
    143     CpuDeadLoop();
    144     return EFI_UNSUPPORTED;
    145   }
    146 
    147   //
    148   // S3ResumePeim does not provide a way to jump back to itself, so resume to OS here directly
    149   //
    150   if (Facs->XFirmwareWakingVector != 0) {
    151     //
    152     // Switch to native waking vector
    153     //
    154     TempStackTop = (UINTN)&TempStack + sizeof(TempStack);
    155     if ((Facs->Version == EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION) &&
    156         ((Facs->Flags & EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F) != 0) &&
    157         ((Facs->Flags & EFI_ACPI_4_0_OSPM_64BIT_WAKE__F) != 0)) {
    158       //
    159       // X64 long mode waking vector
    160       //
    161       DEBUG (( EFI_D_ERROR, "Transfer to 64bit OS waking vector - %x\r\n", (UINTN)Facs->XFirmwareWakingVector));
    162       if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
    163         SwitchStack (
    164           (SWITCH_STACK_ENTRY_POINT)(UINTN)Facs->XFirmwareWakingVector,
    165           NULL,
    166           NULL,
    167           (VOID *)(UINTN)TempStackTop
    168           );
    169       } else {
    170         // Unsupported for 32bit DXE, 64bit OS vector
    171         DEBUG (( EFI_D_ERROR, "Unsupported for 32bit DXE transfer to 64bit OS waking vector!\r\n"));
    172         ASSERT (FALSE);
    173       }
    174     } else {
    175       //
    176       // IA32 protected mode waking vector (Page disabled)
    177       //
    178       DEBUG (( EFI_D_ERROR, "Transfer to 32bit OS waking vector - %x\r\n", (UINTN)Facs->XFirmwareWakingVector));
    179       if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
    180         AsmDisablePaging64 (
    181           0x10,
    182           (UINT32)Facs->XFirmwareWakingVector,
    183           0,
    184           0,
    185           (UINT32)TempStackTop
    186           );
    187       } else {
    188         SwitchStack (
    189           (SWITCH_STACK_ENTRY_POINT)(UINTN)Facs->XFirmwareWakingVector,
    190           NULL,
    191           NULL,
    192           (VOID *)(UINTN)TempStackTop
    193           );
    194       }
    195     }
    196   } else {
    197     //
    198     // 16bit Realmode waking vector
    199     //
    200     DEBUG (( EFI_D_ERROR, "Transfer to 16bit OS waking vector - %x\r\n", (UINTN)Facs->FirmwareWakingVector));
    201     AsmTransferControl (Facs->FirmwareWakingVector, 0x0);
    202   }
    203 
    204   //
    205   // Never run to here
    206   //
    207   CpuDeadLoop();
    208   return EFI_UNSUPPORTED;
    209 }
    210 
    211 /**
    212   Register image to memory profile.
    213 
    214   @param FileName       File name of the image.
    215   @param ImageBase      Image base address.
    216   @param ImageSize      Image size.
    217   @param FileType       File type of the image.
    218 
    219 **/
    220 VOID
    221 RegisterMemoryProfileImage (
    222   IN EFI_GUID                       *FileName,
    223   IN PHYSICAL_ADDRESS               ImageBase,
    224   IN UINT64                         ImageSize,
    225   IN EFI_FV_FILETYPE                FileType
    226   )
    227 {
    228   EFI_STATUS                        Status;
    229   EDKII_MEMORY_PROFILE_PROTOCOL     *ProfileProtocol;
    230   MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FilePath;
    231   UINT8                             TempBuffer[sizeof (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH) + sizeof (EFI_DEVICE_PATH_PROTOCOL)];
    232 
    233   if ((PcdGet8 (PcdMemoryProfilePropertyMask) & BIT0) != 0) {
    234 
    235     FilePath = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)TempBuffer;
    236     Status = gBS->LocateProtocol (&gEdkiiMemoryProfileGuid, NULL, (VOID **) &ProfileProtocol);
    237     if (!EFI_ERROR (Status)) {
    238       EfiInitializeFwVolDevicepathNode (FilePath, FileName);
    239       SetDevicePathEndNode (FilePath + 1);
    240 
    241       Status = ProfileProtocol->RegisterImage (
    242                                   ProfileProtocol,
    243                                   (EFI_DEVICE_PATH_PROTOCOL *) FilePath,
    244                                   ImageBase,
    245                                   ImageSize,
    246                                   FileType
    247                                   );
    248     }
    249   }
    250 }
    251 
    252 /**
    253   This is the Event notification function to reload BootScriptExecutor image
    254   to RESERVED mem and save it to LockBox.
    255 
    256   @param    Event   Pointer to this event
    257   @param    Context Event handler private data
    258  **/
    259 VOID
    260 EFIAPI
    261 ReadyToLockEventNotify (
    262   IN EFI_EVENT  Event,
    263   IN VOID       *Context
    264   )
    265 {
    266   EFI_STATUS                                    Status;
    267   VOID                                          *Interface;
    268   UINT8                                         *Buffer;
    269   UINTN                                         BufferSize;
    270   EFI_HANDLE                                    NewImageHandle;
    271   UINTN                                         Pages;
    272   EFI_PHYSICAL_ADDRESS                          FfsBuffer;
    273   PE_COFF_LOADER_IMAGE_CONTEXT                  ImageContext;
    274 
    275   Status = gBS->LocateProtocol (&gEfiDxeSmmReadyToLockProtocolGuid, NULL, &Interface);
    276   if (EFI_ERROR (Status)) {
    277     return;
    278   }
    279 
    280   //
    281   // A workaround: Here we install a dummy handle
    282   //
    283   NewImageHandle = NULL;
    284   Status = gBS->InstallProtocolInterface (
    285                   &NewImageHandle,
    286                   &gEfiCallerIdGuid,
    287                   EFI_NATIVE_INTERFACE,
    288                   NULL
    289                   );
    290   ASSERT_EFI_ERROR (Status);
    291 
    292   //
    293   // Reload BootScriptExecutor image itself to RESERVED mem
    294   //
    295   Status = GetSectionFromAnyFv  (
    296              &gEfiCallerIdGuid,
    297              EFI_SECTION_PE32,
    298              0,
    299              (VOID **) &Buffer,
    300              &BufferSize
    301              );
    302   ASSERT_EFI_ERROR (Status);
    303   ImageContext.Handle    = Buffer;
    304   ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;
    305   //
    306   // Get information about the image being loaded
    307   //
    308   Status = PeCoffLoaderGetImageInfo (&ImageContext);
    309   ASSERT_EFI_ERROR (Status);
    310   if (ImageContext.SectionAlignment > EFI_PAGE_SIZE) {
    311     Pages = EFI_SIZE_TO_PAGES ((UINTN) (ImageContext.ImageSize + ImageContext.SectionAlignment));
    312   } else {
    313     Pages = EFI_SIZE_TO_PAGES ((UINTN) ImageContext.ImageSize);
    314   }
    315   FfsBuffer = 0xFFFFFFFF;
    316   Status = gBS->AllocatePages (
    317                   AllocateMaxAddress,
    318                   EfiReservedMemoryType,
    319                   Pages,
    320                   &FfsBuffer
    321                   );
    322   ASSERT_EFI_ERROR (Status);
    323   ImageContext.ImageAddress = (PHYSICAL_ADDRESS)(UINTN)FfsBuffer;
    324   //
    325   // Align buffer on section boundry
    326   //
    327   ImageContext.ImageAddress += ImageContext.SectionAlignment - 1;
    328   ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)(ImageContext.SectionAlignment - 1));
    329   //
    330   // Load the image to our new buffer
    331   //
    332   Status = PeCoffLoaderLoadImage (&ImageContext);
    333   ASSERT_EFI_ERROR (Status);
    334 
    335   //
    336   // Relocate the image in our new buffer
    337   //
    338   Status = PeCoffLoaderRelocateImage (&ImageContext);
    339   ASSERT_EFI_ERROR (Status);
    340 
    341   //
    342   // Free the buffer allocated by ReadSection since the image has been relocated in the new buffer
    343   //
    344   gBS->FreePool (Buffer);
    345 
    346   //
    347   // Flush the instruction cache so the image data is written before we execute it
    348   //
    349   InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize);
    350 
    351   RegisterMemoryProfileImage (
    352     &gEfiCallerIdGuid,
    353     ImageContext.ImageAddress,
    354     ImageContext.ImageSize,
    355     EFI_FV_FILETYPE_DRIVER
    356     );
    357 
    358   Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)(ImageContext.EntryPoint)) (NewImageHandle, gST);
    359   ASSERT_EFI_ERROR (Status);
    360 
    361   //
    362   // Additional step for BootScript integrity
    363   // Save BootScriptExecutor image
    364   //
    365   Status = SaveLockBox (
    366              &mBootScriptExecutorImageGuid,
    367              (VOID *)(UINTN)ImageContext.ImageAddress,
    368              (UINTN)ImageContext.ImageSize
    369              );
    370   ASSERT_EFI_ERROR (Status);
    371 
    372   Status = SetLockBoxAttributes (&mBootScriptExecutorImageGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
    373   ASSERT_EFI_ERROR (Status);
    374 
    375   gBS->CloseEvent (Event);
    376 }
    377 
    378 /**
    379   Entrypoint of Boot script exector driver, this function will be executed in
    380   normal boot phase and invoked by DXE dispatch.
    381 
    382   @param[in] ImageHandle    The firmware allocated handle for the EFI image.
    383   @param[in] SystemTable    A pointer to the EFI System Table.
    384 
    385   @retval EFI_SUCCESS       The entry point is executed successfully.
    386   @retval other             Some error occurs when executing this entry point.
    387 **/
    388 EFI_STATUS
    389 EFIAPI
    390 BootScriptExecutorEntryPoint (
    391   IN EFI_HANDLE           ImageHandle,
    392   IN EFI_SYSTEM_TABLE     *SystemTable
    393   )
    394 {
    395   UINTN                                         BufferSize;
    396   UINTN                                         Pages;
    397   BOOT_SCRIPT_EXECUTOR_VARIABLE                 *EfiBootScriptExecutorVariable;
    398   EFI_PHYSICAL_ADDRESS                          BootScriptExecutorBuffer;
    399   EFI_STATUS                                    Status;
    400   VOID                                          *DevicePath;
    401   EFI_EVENT                                     ReadyToLockEvent;
    402   VOID                                          *Registration;
    403   UINT32                                        RegEax;
    404   UINT32                                        RegEdx;
    405 
    406   //
    407   // Test if the gEfiCallerIdGuid of this image is already installed. if not, the entry
    408   // point is loaded by DXE code which is the first time loaded. or else, it is already
    409   // be reloaded be itself.This is a work-around
    410   //
    411   Status = gBS->LocateProtocol (&gEfiCallerIdGuid, NULL, &DevicePath);
    412   if (EFI_ERROR (Status)) {
    413       //
    414       // Create ReadyToLock event to reload BootScriptExecutor image
    415       // to RESERVED mem and save it to LockBox.
    416       //
    417       ReadyToLockEvent = EfiCreateProtocolNotifyEvent  (
    418                            &gEfiDxeSmmReadyToLockProtocolGuid,
    419                            TPL_NOTIFY,
    420                            ReadyToLockEventNotify,
    421                            NULL,
    422                            &Registration
    423                            );
    424       ASSERT (ReadyToLockEvent != NULL);
    425     } else {
    426       //
    427       // the entry point is invoked after reloading. following code only run in RESERVED mem
    428       //
    429       if (PcdGetBool(PcdUse1GPageTable)) {
    430         AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
    431         if (RegEax >= 0x80000001) {
    432           AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
    433           if ((RegEdx & BIT26) != 0) {
    434             mPage1GSupport = TRUE;
    435           }
    436         }
    437       }
    438 
    439       BufferSize = sizeof (BOOT_SCRIPT_EXECUTOR_VARIABLE);
    440 
    441       BootScriptExecutorBuffer = 0xFFFFFFFF;
    442       Pages = EFI_SIZE_TO_PAGES(BufferSize);
    443       Status = gBS->AllocatePages (
    444                       AllocateMaxAddress,
    445                       EfiReservedMemoryType,
    446                       Pages,
    447                       &BootScriptExecutorBuffer
    448                       );
    449       ASSERT_EFI_ERROR (Status);
    450 
    451       EfiBootScriptExecutorVariable = (BOOT_SCRIPT_EXECUTOR_VARIABLE *)(UINTN)BootScriptExecutorBuffer;
    452       EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint = (UINTN) S3BootScriptExecutorEntryFunction ;
    453 
    454       Status = SaveLockBox (
    455                  &gEfiBootScriptExecutorVariableGuid,
    456                  &BootScriptExecutorBuffer,
    457                  sizeof(BootScriptExecutorBuffer)
    458                  );
    459       ASSERT_EFI_ERROR (Status);
    460 
    461       //
    462       // Additional step for BootScript integrity
    463       // Save BootScriptExecutor context
    464       //
    465       Status = SaveLockBox (
    466                  &gEfiBootScriptExecutorContextGuid,
    467                  EfiBootScriptExecutorVariable,
    468                  sizeof(*EfiBootScriptExecutorVariable)
    469                  );
    470       ASSERT_EFI_ERROR (Status);
    471 
    472       Status = SetLockBoxAttributes (&gEfiBootScriptExecutorContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
    473       ASSERT_EFI_ERROR (Status);
    474     }
    475 
    476     return EFI_SUCCESS;
    477 }
    478 
    479