- TURE -> TRUE - reseting -> resetting - retore -> restore - boundry -> boundary - tempory -> temporary Cc: Michael D Kinney <michael.d.kinney@intel.com> Cc: Kelly Steele <kelly.steele@intel.com> Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Gary Lin <glin@suse.com> Reviewed-by: Michael D Kinney <michael.d.kinney@intel.com>
		
			
				
	
	
		
			386 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			386 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| This is the code for Boot Script Executer module.
 | |
| 
 | |
| This driver is dispatched by Dxe core and the driver will reload itself to ACPI NVS memory
 | |
| in the entry point. The functionality is to interpret and restore the S3 boot script
 | |
| 
 | |
| Copyright (c) 2013-2016 Intel Corporation.
 | |
| 
 | |
| This program and the accompanying materials
 | |
| are licensed and made available under the terms and conditions of the BSD License
 | |
| which accompanies this distribution.  The full text of the license may be found at
 | |
| http://opensource.org/licenses/bsd-license.php
 | |
| 
 | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "ScriptExecute.h"
 | |
| 
 | |
| #pragma pack(1)
 | |
| typedef union {
 | |
|   struct {
 | |
|     UINT32  LimitLow    : 16;
 | |
|     UINT32  BaseLow     : 16;
 | |
|     UINT32  BaseMid     : 8;
 | |
|     UINT32  Type        : 4;
 | |
|     UINT32  System      : 1;
 | |
|     UINT32  Dpl         : 2;
 | |
|     UINT32  Present     : 1;
 | |
|     UINT32  LimitHigh   : 4;
 | |
|     UINT32  Software    : 1;
 | |
|     UINT32  Reserved    : 1;
 | |
|     UINT32  DefaultSize : 1;
 | |
|     UINT32  Granularity : 1;
 | |
|     UINT32  BaseHigh    : 8;
 | |
|   } Bits;
 | |
|   UINT64  Uint64;
 | |
| } IA32_GDT;
 | |
| 
 | |
| #pragma pack()
 | |
| 
 | |
| EFI_GUID              mBootScriptExecutorImageGuid = {
 | |
|   0x9a8d3433, 0x9fe8, 0x42b6, {0x87, 0xb, 0x1e, 0x31, 0xc8, 0x4e, 0xbe, 0x3b}
 | |
| };
 | |
| 
 | |
| //
 | |
| // Global Descriptor Table (GDT)
 | |
| //
 | |
| GLOBAL_REMOVE_IF_UNREFERENCED IA32_GDT mGdtEntries[] = {
 | |
| /* selector { Global Segment Descriptor                              } */
 | |
| /* 0x00 */  {{0,      0,  0,  0,    0,  0,  0,  0,    0,  0, 0,  0,  0}},
 | |
| /* 0x08 */  {{0,      0,  0,  0,    0,  0,  0,  0,    0,  0, 0,  0,  0}},
 | |
| /* 0x10 */  {{0xFFFF, 0,  0,  0xB,  1,  0,  1,  0xF,  0,  0, 1,  1,  0}},
 | |
| /* 0x18 */  {{0xFFFF, 0,  0,  0x3,  1,  0,  1,  0xF,  0,  0, 1,  1,  0}},
 | |
| /* 0x20 */  {{0,      0,  0,  0,    0,  0,  0,  0,    0,  0, 0,  0,  0}},
 | |
| /* 0x28 */  {{0xFFFF, 0,  0,  0xB,  1,  0,  1,  0xF,  0,  0, 0,  1,  0}},
 | |
| /* 0x30 */  {{0xFFFF, 0,  0,  0x3,  1,  0,  1,  0xF,  0,  0, 0,  1,  0}},
 | |
| /* 0x38 */  {{0xFFFF, 0,  0,  0xB,  1,  0,  1,  0xF,  0,  1, 0,  1,  0}},
 | |
| /* 0x40 */  {{0,      0,  0,  0,    0,  0,  0,  0,    0,  0, 0,  0,  0}},
 | |
| };
 | |
| 
 | |
| //
 | |
| // IA32 Gdt register
 | |
| //
 | |
| GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR mGdt = {
 | |
|   sizeof (mGdtEntries) - 1,
 | |
|   (UINTN) mGdtEntries
 | |
|   };
 | |
| 
 | |
| /**
 | |
|   Entry function of Boot script exector. This function will be executed in
 | |
|   S3 boot path.
 | |
|   This function should not return, because it is invoked by switch stack.
 | |
| 
 | |
|   @param  AcpiS3Context    a pointer to a structure of ACPI_S3_CONTEXT
 | |
|   @param  PeiS3ResumeState a pointer to a structure of PEI_S3_RESUME_STATE
 | |
| 
 | |
|   @retval EFI_INVALID_PARAMETER - OS waking vector not found
 | |
|   @retval EFI_UNSUPPORTED - something wrong when we resume to OS
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| S3BootScriptExecutorEntryFunction (
 | |
|   IN ACPI_S3_CONTEXT       *AcpiS3Context,
 | |
|   IN PEI_S3_RESUME_STATE   *PeiS3ResumeState
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                                    Status;
 | |
| 
 | |
|   //
 | |
|   // Disable interrupt of Debug timer, since new IDT table cannot handle it.
 | |
|   //
 | |
|   SaveAndSetDebugTimerInterrupt (FALSE);
 | |
| 
 | |
|   //
 | |
|   // Restore IDT for debug
 | |
|   //
 | |
|   SetIdtEntry (AcpiS3Context);
 | |
| 
 | |
|   //
 | |
|   // Initialize Debug Agent to support source level debug in S3 path.
 | |
|   //
 | |
|   InitializeDebugAgent (DEBUG_AGENT_INIT_S3, NULL, NULL);
 | |
| 
 | |
|   //
 | |
|   // Because not install BootScriptExecute PPI(used just in this module), So just pass NULL
 | |
|   // for that parameter.
 | |
|   //
 | |
|   Status = S3BootScriptExecute ();
 | |
| 
 | |
|   AsmWbinvd ();
 | |
| 
 | |
|   //
 | |
|   // We need turn back to S3Resume - install boot script done ppi and report status code on S3resume.
 | |
|   //
 | |
|   if (PeiS3ResumeState != 0) {
 | |
|     //
 | |
|     // Need report status back to S3ResumePeim.
 | |
|     // If boot script execution is failed, S3ResumePeim wil report the error status code.
 | |
|     //
 | |
|     PeiS3ResumeState->ReturnStatus = (UINT64)(UINTN)Status;
 | |
|     //
 | |
|     // IA32 S3 Resume
 | |
|     //
 | |
|     DEBUG ((EFI_D_INFO, "Call SwitchStack() to return to S3 Resume in PEI Phase\n"));
 | |
|     PeiS3ResumeState->AsmTransferControl = (EFI_PHYSICAL_ADDRESS)(UINTN)PlatformTransferControl16;
 | |
| 
 | |
|     SwitchStack (
 | |
|       (SWITCH_STACK_ENTRY_POINT)(UINTN)PeiS3ResumeState->ReturnEntryPoint,
 | |
|       (VOID *)(UINTN)AcpiS3Context,
 | |
|       (VOID *)(UINTN)PeiS3ResumeState,
 | |
|       (VOID *)(UINTN)PeiS3ResumeState->ReturnStackPointer
 | |
|       );
 | |
| 
 | |
|     //
 | |
|     // Never run to here
 | |
|     //
 | |
|     CpuDeadLoop();
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Never run to here
 | |
|   //
 | |
|   CpuDeadLoop();
 | |
|   return EFI_UNSUPPORTED;
 | |
| }
 | |
| /**
 | |
|   Entrypoint of Boot script exector driver, this function will be executed in
 | |
|   normal boot phase and invoked by DXE dispatch.
 | |
| 
 | |
|   @param[in] ImageHandle    The firmware allocated handle for the EFI image.
 | |
|   @param[in] SystemTable    A pointer to the EFI System Table.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The entry point is executed successfully.
 | |
|   @retval other             Some error occurs when executing this entry point.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| BootScriptExecutorEntryPoint (
 | |
|   IN EFI_HANDLE           ImageHandle,
 | |
|   IN EFI_SYSTEM_TABLE     *SystemTable
 | |
|   )
 | |
| {
 | |
|   UINT8                                         *Buffer;
 | |
|   UINTN                                         BufferSize;
 | |
|   UINTN                                         Pages;
 | |
|   EFI_PHYSICAL_ADDRESS                          FfsBuffer;
 | |
|   PE_COFF_LOADER_IMAGE_CONTEXT                  ImageContext;
 | |
|   BOOT_SCRIPT_EXECUTOR_VARIABLE                 *EfiBootScriptExecutorVariable;
 | |
|   EFI_PHYSICAL_ADDRESS                          BootScriptExecutorBuffer;
 | |
|   EFI_STATUS                                    Status;
 | |
|   VOID                                          *DevicePath;
 | |
|   EFI_HANDLE                                    NewImageHandle;
 | |
| 
 | |
|   //
 | |
|   // Test if the gEfiCallerIdGuid of this image is already installed. if not, the entry
 | |
|   // point is loaded by DXE code which is the first time loaded. or else, it is already
 | |
|   // be reloaded be itself.This is a work-around
 | |
|   //
 | |
|   Status = gBS->LocateProtocol (&gEfiCallerIdGuid, NULL, &DevicePath);
 | |
|   if (EFI_ERROR (Status)) {
 | |
| 
 | |
|       //
 | |
|       // This is the first-time loaded by DXE core. reload itself to NVS mem
 | |
|       //
 | |
|       //
 | |
|       // A workarouond: Here we install a dummy handle
 | |
|       //
 | |
|       NewImageHandle = NULL;
 | |
|       Status = gBS->InstallProtocolInterface (
 | |
|                   &NewImageHandle,
 | |
|                   &gEfiCallerIdGuid,
 | |
|                   EFI_NATIVE_INTERFACE,
 | |
|                   NULL
 | |
|                   );
 | |
| 
 | |
|       Status = GetSectionFromAnyFv  (
 | |
|                  &gEfiCallerIdGuid,
 | |
|                  EFI_SECTION_PE32,
 | |
|                  0,
 | |
|                  (VOID **) &Buffer,
 | |
|                  &BufferSize
 | |
|                  );
 | |
|       ImageContext.Handle    = Buffer;
 | |
|       ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;
 | |
|       //
 | |
|       // Get information about the image being loaded
 | |
|       //
 | |
|       Status = PeCoffLoaderGetImageInfo (&ImageContext);
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         return Status;
 | |
|       }
 | |
|       Pages = EFI_SIZE_TO_PAGES(BufferSize + ImageContext.SectionAlignment);
 | |
|       FfsBuffer = 0xFFFFFFFF;
 | |
|       Status = gBS->AllocatePages (
 | |
|                     AllocateMaxAddress,
 | |
|                     EfiACPIMemoryNVS,
 | |
|                     Pages,
 | |
|                     &FfsBuffer
 | |
|                     );
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         return EFI_OUT_OF_RESOURCES;
 | |
|       }
 | |
|       ImageContext.ImageAddress = (PHYSICAL_ADDRESS)(UINTN)FfsBuffer;
 | |
|       //
 | |
|       // Align buffer on section boundary
 | |
|       //
 | |
|       ImageContext.ImageAddress += ImageContext.SectionAlignment - 1;
 | |
|       ImageContext.ImageAddress &= ~(ImageContext.SectionAlignment - 1);
 | |
|       //
 | |
|       // Load the image to our new buffer
 | |
|       //
 | |
|       Status = PeCoffLoaderLoadImage (&ImageContext);
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         gBS->FreePages (FfsBuffer, Pages);
 | |
|         return Status;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // Relocate the image in our new buffer
 | |
|       //
 | |
|       Status = PeCoffLoaderRelocateImage (&ImageContext);
 | |
| 
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         PeCoffLoaderUnloadImage (&ImageContext);
 | |
|         gBS->FreePages (FfsBuffer, Pages);
 | |
|         return Status;
 | |
|       }
 | |
|       //
 | |
|       // Flush the instruction cache so the image data is written before we execute it
 | |
|       //
 | |
|       InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize);
 | |
|       Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)(ImageContext.EntryPoint)) (NewImageHandle, SystemTable);
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         gBS->FreePages (FfsBuffer, Pages);
 | |
|         return Status;
 | |
|       }
 | |
|       //
 | |
|       // Additional step for BootScript integrity
 | |
|       // Save BootScriptExecutor image
 | |
|       //
 | |
|       Status = SaveLockBox (
 | |
|                  &mBootScriptExecutorImageGuid,
 | |
|                  (VOID *)(UINTN)ImageContext.ImageAddress,
 | |
|                  (UINTN)ImageContext.ImageSize
 | |
|                  );
 | |
|       ASSERT_EFI_ERROR (Status);
 | |
| 
 | |
|       Status = SetLockBoxAttributes (&mBootScriptExecutorImageGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
 | |
|       ASSERT_EFI_ERROR (Status);
 | |
| 
 | |
|     } else {
 | |
|       //
 | |
|       // the entry point is invoked after reloading. following code only run in  ACPI NVS
 | |
|       //
 | |
|       BufferSize = sizeof (BOOT_SCRIPT_EXECUTOR_VARIABLE);
 | |
| 
 | |
|       BootScriptExecutorBuffer = 0xFFFFFFFF;
 | |
|       Pages = EFI_SIZE_TO_PAGES(BufferSize);
 | |
|       Status = gBS->AllocatePages (
 | |
|                       AllocateMaxAddress,
 | |
|                       EfiACPIMemoryNVS,
 | |
|                       Pages,
 | |
|                       &BootScriptExecutorBuffer
 | |
|                       );
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         return EFI_OUT_OF_RESOURCES;
 | |
|       }
 | |
| 
 | |
|       EfiBootScriptExecutorVariable = (BOOT_SCRIPT_EXECUTOR_VARIABLE *)(UINTN)BootScriptExecutorBuffer;
 | |
|       EfiBootScriptExecutorVariable->BootScriptExecutorEntrypoint = (UINTN) S3BootScriptExecutorEntryFunction ;
 | |
| 
 | |
|       Status = SaveLockBox (
 | |
|                  &gEfiBootScriptExecutorVariableGuid,
 | |
|                  &BootScriptExecutorBuffer,
 | |
|                  sizeof(BootScriptExecutorBuffer)
 | |
|                  );
 | |
|       ASSERT_EFI_ERROR (Status);
 | |
| 
 | |
|       //
 | |
|       // Additional step for BootScript integrity
 | |
|       // Save BootScriptExecutor context
 | |
|       //
 | |
|       Status = SaveLockBox (
 | |
|                  &gEfiBootScriptExecutorContextGuid,
 | |
|                  EfiBootScriptExecutorVariable,
 | |
|                  sizeof(*EfiBootScriptExecutorVariable)
 | |
|                  );
 | |
|       ASSERT_EFI_ERROR (Status);
 | |
| 
 | |
|       Status = SetLockBoxAttributes (&gEfiBootScriptExecutorContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
 | |
|       ASSERT_EFI_ERROR (Status);
 | |
| 
 | |
|     }
 | |
| 
 | |
|     return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Platform specific mechanism to transfer control to 16bit OS waking vector
 | |
| 
 | |
|   @param[in] AcpiWakingVector    The 16bit OS waking vector
 | |
|   @param[in] AcpiLowMemoryBase   A buffer under 1M which could be used during the transfer
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| PlatformTransferControl16 (
 | |
|   IN UINT32       AcpiWakingVector,
 | |
|   IN UINT32       AcpiLowMemoryBase
 | |
|   )
 | |
| {
 | |
|   UINT32      NewValue;
 | |
|   UINT64      BaseAddress;
 | |
|   UINT64      SmramLength;
 | |
|   UINTN       Index;
 | |
| 
 | |
|   DEBUG (( EFI_D_INFO, "PlatformTransferControl - Entry\r\n"));
 | |
| 
 | |
|   //
 | |
|   // Need to make sure the GDT is loaded with values that support long mode and real mode.
 | |
|   //
 | |
|   AsmWriteGdtr (&mGdt);
 | |
| 
 | |
|   //
 | |
|   // Disable eSram block (this will also clear/zero eSRAM)
 | |
|   // We only use eSRAM in the PEI phase. Disable now that we are resuming the OS
 | |
|   //
 | |
|   NewValue = QNCPortRead (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, QUARK_NC_MEMORY_MANAGER_ESRAMPGCTRL_BLOCK);
 | |
|   NewValue |= BLOCK_DISABLE_PG;
 | |
|   QNCPortWrite (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, QUARK_NC_MEMORY_MANAGER_ESRAMPGCTRL_BLOCK, NewValue);
 | |
| 
 | |
|   //
 | |
|   // Update HMBOUND to top of DDR3 memory and LOCK
 | |
|   // We disabled eSRAM so now we move HMBOUND down to top of DDR3
 | |
|   //
 | |
|   QNCGetTSEGMemoryRange (&BaseAddress, &SmramLength);
 | |
|   NewValue = (UINT32)(BaseAddress + SmramLength);
 | |
|   DEBUG ((EFI_D_INFO,"Locking HMBOUND at: = 0x%8x\n",NewValue));
 | |
|   QNCPortWrite (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QUARK_NC_HOST_BRIDGE_HMBOUND_REG, (NewValue | HMBOUND_LOCK));
 | |
| 
 | |
|   //
 | |
|   // Lock all IMR regions now that HMBOUND is locked
 | |
|   //
 | |
|   for (Index = (QUARK_NC_MEMORY_MANAGER_IMR0+QUARK_NC_MEMORY_MANAGER_IMRXL); Index <= (QUARK_NC_MEMORY_MANAGER_IMR7+QUARK_NC_MEMORY_MANAGER_IMRXL); Index += 4) {
 | |
|     NewValue = QNCPortRead (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, Index);
 | |
|     NewValue |= IMR_LOCK;
 | |
|     QNCPortWrite (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, Index, NewValue);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Call ASM routine to switch to real mode and jump to 16bit OS waking vector
 | |
|   //
 | |
|   AsmTransferControl(AcpiWakingVector, 0);
 | |
| 
 | |
|   //
 | |
|   // Never run to here
 | |
|   //
 | |
|   CpuDeadLoop();
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 |