/** Entry point function of the Boot Script Thunk Helper SMM driver. @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 BootScriptThunkHelperMain ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { BOOT_SCRIPT_THUNK_DATA *BootScriptThunkData; EFI_STATUS Status; // // Get BootScriptThunk variable // BootScriptThunkData = (BOOT_SCRIPT_THUNK_DATA *)(UINTN)PcdGet64(BootScriptThunkDataPtr); ASSERT (BootScriptThunkData != NULL); if (BootScriptThunkData == NULL) { return EFI_NOT_FOUND; } // // Save BootScriptThunk image // Status = SaveLockBox ( &mBootScriptThunkGuid, (VOID *)(UINTN)BootScriptThunkData->BootScriptThunkBase, (UINTN)BootScriptThunkData->BootScriptThunkLength ); ASSERT_EFI_ERROR (Status); Status = SetLockBoxAttributes (&mBootScriptThunkGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE); ASSERT_EFI_ERROR (Status); return Status; }
/** Dispatch function for SMM lock box save. Caution: This function may receive untrusted input. Restore buffer and length are external input, so this function will validate it is in SMRAM. @param LockBoxParameterSave parameter of lock box save **/ VOID SmmLockBoxSave ( IN EFI_SMM_LOCK_BOX_PARAMETER_SAVE *LockBoxParameterSave ) { EFI_STATUS Status; EFI_SMM_LOCK_BOX_PARAMETER_SAVE TempLockBoxParameterSave; // // Sanity check // if (mLocked) { DEBUG ((EFI_D_ERROR, "SmmLockBox Locked!\n")); LockBoxParameterSave->Header.ReturnStatus = (UINT64)EFI_ACCESS_DENIED; return ; } CopyMem (&TempLockBoxParameterSave, LockBoxParameterSave, sizeof (EFI_SMM_LOCK_BOX_PARAMETER_SAVE)); // // Sanity check // if (!SmmIsBufferOutsideSmmValid ((UINTN)TempLockBoxParameterSave.Buffer, (UINTN)TempLockBoxParameterSave.Length)) { DEBUG ((EFI_D_ERROR, "SmmLockBox Save address in SMRAM or buffer overflow!\n")); LockBoxParameterSave->Header.ReturnStatus = (UINT64)EFI_ACCESS_DENIED; return ; } // // The AsmLfence() call here is to ensure the above range check for the // CommBuffer have been completed before calling into SaveLockBox(). // AsmLfence (); // // Save data // Status = SaveLockBox ( &TempLockBoxParameterSave.Guid, (VOID *)(UINTN)TempLockBoxParameterSave.Buffer, (UINTN)TempLockBoxParameterSave.Length ); LockBoxParameterSave->Header.ReturnStatus = (UINT64)Status; return ; }
/** Dispatch function for SMM lock box save. Caution: This function may receive untrusted input. Restore buffer and length are external input, so this function will validate it is in SMRAM. @param LockBoxParameterSave parameter of lock box save **/ VOID SmmLockBoxSave ( IN EFI_SMM_LOCK_BOX_PARAMETER_SAVE *LockBoxParameterSave ) { EFI_STATUS Status; EFI_SMM_LOCK_BOX_PARAMETER_SAVE TempLockBoxParameterSave; // // Sanity check // if (mLocked) { DEBUG ((EFI_D_ERROR, "SmmLockBox Locked!\n")); LockBoxParameterSave->Header.ReturnStatus = (UINT64)EFI_ACCESS_DENIED; return ; } CopyMem (&TempLockBoxParameterSave, LockBoxParameterSave, sizeof (EFI_SMM_LOCK_BOX_PARAMETER_SAVE)); // // Sanity check // if (!IsAddressValid ((UINTN)TempLockBoxParameterSave.Buffer, (UINTN)TempLockBoxParameterSave.Length)) { DEBUG ((EFI_D_ERROR, "SmmLockBox Save address in SMRAM or buffer overflow!\n")); LockBoxParameterSave->Header.ReturnStatus = (UINT64)EFI_ACCESS_DENIED; return ; } // // Save data // Status = SaveLockBox ( &TempLockBoxParameterSave.Guid, (VOID *)(UINTN)TempLockBoxParameterSave.Buffer, (UINTN)TempLockBoxParameterSave.Length ); LockBoxParameterSave->Header.ReturnStatus = (UINT64)Status; return ; }
/** Report status code listener for SMM. This is used to record the performance data for S3 Suspend Start and S3 Suspend End in FPDT. @param[in] CodeType Indicates the type of status code being reported. @param[in] Value Describes the current status of a hardware or software entity. This included information about the class and subclass that is used to classify the entity as well as an operation. @param[in] Instance The enumeration of a hardware or software entity within the system. Valid instance numbers start with 1. @param[in] CallerId This optional parameter may be used to identify the caller. This parameter allows the status code driver to apply different rules to different callers. @param[in] Data This optional parameter may be used to pass additional data. @retval EFI_SUCCESS Status code is what we expected. @retval EFI_UNSUPPORTED Status code not supported. **/ EFI_STATUS EFIAPI FpdtStatusCodeListenerSmm ( IN EFI_STATUS_CODE_TYPE CodeType, IN EFI_STATUS_CODE_VALUE Value, IN UINT32 Instance, IN EFI_GUID *CallerId, IN EFI_STATUS_CODE_DATA *Data ) { EFI_STATUS Status; UINT64 CurrentTime; EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD S3SuspendRecord; UINT8 *NewRecordBuffer; // // Check whether status code is what we are interested in. // if ((CodeType & EFI_STATUS_CODE_TYPE_MASK) != EFI_PROGRESS_CODE) { return EFI_UNSUPPORTED; } // // Collect one or more Boot records in boot time // if (Data != NULL && CompareGuid (&Data->Type, &gEfiFirmwarePerformanceGuid)) { AcquireSpinLock (&mSmmFpdtLock); if (mBootRecordSize + Data->Size > mBootRecordMaxSize) { // // Try to allocate big SMRAM data to store Boot record. // if (mSmramIsOutOfResource) { ReleaseSpinLock (&mSmmFpdtLock); return EFI_OUT_OF_RESOURCES; } NewRecordBuffer = ReallocatePool (mBootRecordSize, mBootRecordSize + Data->Size + EXTENSION_RECORD_SIZE, mBootRecordBuffer); if (NewRecordBuffer == NULL) { ReleaseSpinLock (&mSmmFpdtLock); mSmramIsOutOfResource = TRUE; return EFI_OUT_OF_RESOURCES; } mBootRecordBuffer = NewRecordBuffer; mBootRecordMaxSize = mBootRecordSize + Data->Size + EXTENSION_RECORD_SIZE; } // // Save boot record into the temp memory space. // CopyMem (mBootRecordBuffer + mBootRecordSize, Data + 1, Data->Size); mBootRecordSize += Data->Size; ReleaseSpinLock (&mSmmFpdtLock); return EFI_SUCCESS; } if ((Value != PcdGet32 (PcdProgressCodeS3SuspendStart)) && (Value != PcdGet32 (PcdProgressCodeS3SuspendEnd))) { return EFI_UNSUPPORTED; } // // Retrieve current time. // CurrentTime = GetTimeInNanoSecond (GetPerformanceCounter ()); if (Value == PcdGet32 (PcdProgressCodeS3SuspendStart)) { // // S3 Suspend started, record the performance data and return. // mSuspendStartTime = CurrentTime; return EFI_SUCCESS; } // // We are going to S3 sleep, record S3 Suspend End performance data. // S3SuspendRecord.SuspendStart = mSuspendStartTime; S3SuspendRecord.SuspendEnd = CurrentTime; // // Save S3 suspend performance data to lock box, it will be used by Firmware Performance PEIM. // if (!mS3SuspendLockBoxSaved) { Status = SaveLockBox ( &gEfiFirmwarePerformanceGuid, &S3SuspendRecord, sizeof (EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD) ); ASSERT_EFI_ERROR (Status); mS3SuspendLockBoxSaved = TRUE; } else { Status = UpdateLockBox ( &gEfiFirmwarePerformanceGuid, 0, &S3SuspendRecord, sizeof (EFI_ACPI_5_0_FPDT_S3_SUSPEND_RECORD) ); ASSERT_EFI_ERROR (Status); } return EFI_SUCCESS; }
/** 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 ) { UINTN BufferSize; UINTN Pages; BOOT_SCRIPT_EXECUTOR_VARIABLE *EfiBootScriptExecutorVariable; EFI_PHYSICAL_ADDRESS BootScriptExecutorBuffer; EFI_STATUS Status; VOID *DevicePath; EFI_EVENT ReadyToLockEvent; VOID *Registration; UINT32 RegEax; UINT32 RegEdx; if (!PcdGetBool (PcdAcpiS3Enable)) { return EFI_UNSUPPORTED; } // // 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)) { // // Create ReadyToLock event to reload BootScriptExecutor image // to RESERVED mem and save it to LockBox. // ReadyToLockEvent = EfiCreateProtocolNotifyEvent ( &gEfiDxeSmmReadyToLockProtocolGuid, TPL_NOTIFY, ReadyToLockEventNotify, NULL, &Registration ); ASSERT (ReadyToLockEvent != NULL); } else { // // the entry point is invoked after reloading. following code only run in RESERVED mem // if (PcdGetBool(PcdUse1GPageTable)) { AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); if (RegEax >= 0x80000001) { AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx); if ((RegEdx & BIT26) != 0) { mPage1GSupport = TRUE; } } } BufferSize = sizeof (BOOT_SCRIPT_EXECUTOR_VARIABLE); BootScriptExecutorBuffer = 0xFFFFFFFF; Pages = EFI_SIZE_TO_PAGES(BufferSize); Status = gBS->AllocatePages ( AllocateMaxAddress, EfiReservedMemoryType, Pages, &BootScriptExecutorBuffer ); ASSERT_EFI_ERROR (Status); 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; }
/** This is the Event notification function to reload BootScriptExecutor image to RESERVED mem and save it to LockBox. @param Event Pointer to this event @param Context Event handler private data **/ VOID EFIAPI ReadyToLockEventNotify ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_STATUS Status; VOID *Interface; UINT8 *Buffer; UINTN BufferSize; EFI_HANDLE NewImageHandle; UINTN Pages; EFI_PHYSICAL_ADDRESS FfsBuffer; PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; Status = gBS->LocateProtocol (&gEfiDxeSmmReadyToLockProtocolGuid, NULL, &Interface); if (EFI_ERROR (Status)) { return; } // // A workaround: Here we install a dummy handle // NewImageHandle = NULL; Status = gBS->InstallProtocolInterface ( &NewImageHandle, &gEfiCallerIdGuid, EFI_NATIVE_INTERFACE, NULL ); ASSERT_EFI_ERROR (Status); // // Reload BootScriptExecutor image itself to RESERVED mem // Status = GetSectionFromAnyFv ( &gEfiCallerIdGuid, EFI_SECTION_PE32, 0, (VOID **) &Buffer, &BufferSize ); ASSERT_EFI_ERROR (Status); ImageContext.Handle = Buffer; ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; // // Get information about the image being loaded // Status = PeCoffLoaderGetImageInfo (&ImageContext); ASSERT_EFI_ERROR (Status); if (ImageContext.SectionAlignment > EFI_PAGE_SIZE) { Pages = EFI_SIZE_TO_PAGES ((UINTN) (ImageContext.ImageSize + ImageContext.SectionAlignment)); } else { Pages = EFI_SIZE_TO_PAGES ((UINTN) ImageContext.ImageSize); } FfsBuffer = 0xFFFFFFFF; Status = gBS->AllocatePages ( AllocateMaxAddress, EfiReservedMemoryType, Pages, &FfsBuffer ); ASSERT_EFI_ERROR (Status); ImageContext.ImageAddress = (PHYSICAL_ADDRESS)(UINTN)FfsBuffer; // // Align buffer on section boundry // ImageContext.ImageAddress += ImageContext.SectionAlignment - 1; ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)(ImageContext.SectionAlignment - 1)); // // Load the image to our new buffer // Status = PeCoffLoaderLoadImage (&ImageContext); ASSERT_EFI_ERROR (Status); // // Relocate the image in our new buffer // Status = PeCoffLoaderRelocateImage (&ImageContext); ASSERT_EFI_ERROR (Status); // // Free the buffer allocated by ReadSection since the image has been relocated in the new buffer // gBS->FreePool (Buffer); // // Flush the instruction cache so the image data is written before we execute it // InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); RegisterMemoryProfileImage ( &gEfiCallerIdGuid, ImageContext.ImageAddress, ImageContext.ImageSize, EFI_FV_FILETYPE_DRIVER ); Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)(ImageContext.EntryPoint)) (NewImageHandle, gST); ASSERT_EFI_ERROR (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); gBS->CloseEvent (Event); }
/** Prepares all information that is needed in the S3 resume boot path. Allocate the resources or prepare informations and save in ACPI variable set for S3 resume boot path @param This A pointer to the EFI_ACPI_S3_SAVE_PROTOCOL instance. @param LegacyMemoryAddress The base address of legacy memory. @retval EFI_NOT_FOUND Some necessary information cannot be found. @retval EFI_SUCCESS All information was saved successfully. @retval EFI_OUT_OF_RESOURCES Resources were insufficient to save all the information. @retval EFI_INVALID_PARAMETER The memory range is not located below 1 MB. **/ EFI_STATUS EFIAPI S3Ready ( IN EFI_ACPI_S3_SAVE_PROTOCOL *This, IN VOID *LegacyMemoryAddress ) { EFI_STATUS Status; EFI_PHYSICAL_ADDRESS AcpiS3ContextBuffer; ACPI_S3_CONTEXT *AcpiS3Context; STATIC BOOLEAN AlreadyEntered; IA32_DESCRIPTOR *Idtr; IA32_IDT_GATE_DESCRIPTOR *IdtGate; DEBUG ((EFI_D_INFO, "S3Ready!\n")); // // Platform may invoke AcpiS3Save->S3Save() before ExitPmAuth, because we need save S3 information there, while BDS ReadyToBoot may invoke it again. // So if 2nd S3Save() is triggered later, we need ignore it. // if (AlreadyEntered) { return EFI_SUCCESS; } AlreadyEntered = TRUE; AcpiS3Context = AllocateMemoryBelow4G (EfiReservedMemoryType, sizeof(*AcpiS3Context)); ASSERT (AcpiS3Context != NULL); AcpiS3ContextBuffer = (EFI_PHYSICAL_ADDRESS)(UINTN)AcpiS3Context; // // Get ACPI Table because we will save its position to variable // AcpiS3Context->AcpiFacsTable = (EFI_PHYSICAL_ADDRESS)(UINTN)FindAcpiFacsTable (); ASSERT (AcpiS3Context->AcpiFacsTable != 0); IdtGate = AllocateMemoryBelow4G (EfiReservedMemoryType, sizeof(IA32_IDT_GATE_DESCRIPTOR) * 0x100 + sizeof(IA32_DESCRIPTOR)); Idtr = (IA32_DESCRIPTOR *)(IdtGate + 0x100); Idtr->Base = (UINTN)IdtGate; Idtr->Limit = (UINT16)(sizeof(IA32_IDT_GATE_DESCRIPTOR) * 0x100 - 1); AcpiS3Context->IdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)Idtr; Status = SaveLockBox ( &mAcpiS3IdtrProfileGuid, (VOID *)(UINTN)Idtr, (UINTN)sizeof(IA32_DESCRIPTOR) ); ASSERT_EFI_ERROR (Status); Status = SetLockBoxAttributes (&mAcpiS3IdtrProfileGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE); ASSERT_EFI_ERROR (Status); // // Allocate page table // AcpiS3Context->S3NvsPageTableAddress = S3CreateIdentityMappingPageTables (); // // Allocate stack // AcpiS3Context->BootScriptStackSize = PcdGet32 (PcdS3BootScriptStackSize); AcpiS3Context->BootScriptStackBase = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, PcdGet32 (PcdS3BootScriptStackSize)); ASSERT (AcpiS3Context->BootScriptStackBase != 0); // // Allocate a code buffer < 4G for S3 debug to load external code // AcpiS3Context->S3DebugBufferAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, EFI_PAGE_SIZE); DEBUG((EFI_D_INFO, "AcpiS3Context: AcpiFacsTable is 0x%8x\n", AcpiS3Context->AcpiFacsTable)); DEBUG((EFI_D_INFO, "AcpiS3Context: IdtrProfile is 0x%8x\n", AcpiS3Context->IdtrProfile)); DEBUG((EFI_D_INFO, "AcpiS3Context: S3NvsPageTableAddress is 0x%8x\n", AcpiS3Context->S3NvsPageTableAddress)); DEBUG((EFI_D_INFO, "AcpiS3Context: S3DebugBufferAddress is 0x%8x\n", AcpiS3Context->S3DebugBufferAddress)); Status = SaveLockBox ( &gEfiAcpiVariableGuid, &AcpiS3ContextBuffer, sizeof(AcpiS3ContextBuffer) ); ASSERT_EFI_ERROR (Status); Status = SaveLockBox ( &gEfiAcpiS3ContextGuid, (VOID *)(UINTN)AcpiS3Context, (UINTN)sizeof(*AcpiS3Context) ); ASSERT_EFI_ERROR (Status); Status = SetLockBoxAttributes (&gEfiAcpiS3ContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE); ASSERT_EFI_ERROR (Status); if (FeaturePcdGet(PcdFrameworkCompatibilitySupport)) { S3ReadyThunkPlatform (AcpiS3Context); } return EFI_SUCCESS; }
/** Callback function upon VariableArchProtocol and LockBoxProtocol to allocate S3 performance table memory and save the pointer to LockBox. @param[in] Event Event whose notification function is being invoked. @param[in] Context Pointer to the notification function's context. **/ VOID EFIAPI FpdtAllocateS3PerformanceTableMemory ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_STATUS Status; VOID *Interface; FIRMWARE_PERFORMANCE_VARIABLE PerformanceVariable; UINTN Size; EFI_PHYSICAL_ADDRESS S3PerformanceTablePointer; if (mLockBoxReady && (mAcpiS3PerformanceTable != NULL)) { // // The memory for S3 performance table should have been ready, // and the pointer should have been saved to LockBox, just return. // return; } if (!mLockBoxReady) { Status = gBS->LocateProtocol (&gEfiLockBoxProtocolGuid, NULL, &Interface); if (!EFI_ERROR (Status)) { // // LockBox services has been ready. // mLockBoxReady = TRUE; } } if (mAcpiS3PerformanceTable == NULL) { Status = gBS->LocateProtocol (&gEfiVariableArchProtocolGuid, NULL, &Interface); if (!EFI_ERROR (Status)) { // // Try to allocate the same runtime buffer as last time boot. // ZeroMem (&PerformanceVariable, sizeof (PerformanceVariable)); Size = sizeof (PerformanceVariable); Status = gRT->GetVariable ( EFI_FIRMWARE_PERFORMANCE_VARIABLE_NAME, &gEfiFirmwarePerformanceGuid, NULL, &Size, &PerformanceVariable ); if (!EFI_ERROR (Status)) { Status = gBS->AllocatePages ( AllocateAddress, EfiReservedMemoryType, EFI_SIZE_TO_PAGES (sizeof (S3_PERFORMANCE_TABLE)), &PerformanceVariable.S3PerformanceTablePointer ); if (!EFI_ERROR (Status)) { mAcpiS3PerformanceTable = (S3_PERFORMANCE_TABLE *) (UINTN) PerformanceVariable.S3PerformanceTablePointer; } } if (mAcpiS3PerformanceTable == NULL) { // // Fail to allocate at specified address, continue to allocate at any address. // mAcpiS3PerformanceTable = (S3_PERFORMANCE_TABLE *) FpdtAllocateReservedMemoryBelow4G (sizeof (S3_PERFORMANCE_TABLE)); } DEBUG ((EFI_D_INFO, "FPDT: ACPI S3 Performance Table address = 0x%x\n", mAcpiS3PerformanceTable)); if (mAcpiS3PerformanceTable != NULL) { CopyMem (mAcpiS3PerformanceTable, &mS3PerformanceTableTemplate, sizeof (mS3PerformanceTableTemplate)); } } } if (mLockBoxReady && (mAcpiS3PerformanceTable != NULL)) { // // If LockBox services has been ready and memory for FPDT S3 performance table has been allocated, // save the pointer to LockBox for use in S3 resume. // S3PerformanceTablePointer = (EFI_PHYSICAL_ADDRESS) (UINTN) mAcpiS3PerformanceTable; Status = SaveLockBox ( &gFirmwarePerformanceS3PointerGuid, &S3PerformanceTablePointer, sizeof (EFI_PHYSICAL_ADDRESS) ); ASSERT_EFI_ERROR (Status); } }
/** Prepares all information that is needed in the S3 resume boot path. Allocate the resources or prepare informations and save in ACPI variable set for S3 resume boot path @retval EFI_SUCCESS All information was saved successfully. **/ STATIC EFI_STATUS EFIAPI S3Ready ( VOID ) { EFI_STATUS Status; EFI_PHYSICAL_ADDRESS AcpiS3ContextBuffer; ACPI_S3_CONTEXT *AcpiS3Context; STATIC BOOLEAN AlreadyEntered; IA32_DESCRIPTOR *Idtr; IA32_IDT_GATE_DESCRIPTOR *IdtGate; DEBUG ((EFI_D_INFO, "S3Ready!\n")); ASSERT (!AlreadyEntered); if (AlreadyEntered) { return EFI_SUCCESS; } AlreadyEntered = TRUE; AcpiS3Context = AllocateMemoryBelow4G (EfiReservedMemoryType, sizeof(*AcpiS3Context)); ASSERT (AcpiS3Context != NULL); AcpiS3ContextBuffer = (EFI_PHYSICAL_ADDRESS)(UINTN)AcpiS3Context; // // Get ACPI Table because we will save its position to variable // AcpiS3Context->AcpiFacsTable = (EFI_PHYSICAL_ADDRESS)(UINTN)FindAcpiFacsTable (); ASSERT (AcpiS3Context->AcpiFacsTable != 0); IdtGate = AllocateMemoryBelow4G (EfiReservedMemoryType, sizeof(IA32_IDT_GATE_DESCRIPTOR) * 0x100 + sizeof(IA32_DESCRIPTOR)); Idtr = (IA32_DESCRIPTOR *)(IdtGate + 0x100); Idtr->Base = (UINTN)IdtGate; Idtr->Limit = (UINT16)(sizeof(IA32_IDT_GATE_DESCRIPTOR) * 0x100 - 1); AcpiS3Context->IdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)Idtr; Status = SaveLockBox ( &mAcpiS3IdtrProfileGuid, (VOID *)(UINTN)Idtr, (UINTN)sizeof(IA32_DESCRIPTOR) ); ASSERT_EFI_ERROR (Status); Status = SetLockBoxAttributes (&mAcpiS3IdtrProfileGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE); ASSERT_EFI_ERROR (Status); // // Allocate page table // AcpiS3Context->S3NvsPageTableAddress = S3CreateIdentityMappingPageTables (); // // Allocate stack // AcpiS3Context->BootScriptStackSize = PcdGet32 (PcdS3BootScriptStackSize); AcpiS3Context->BootScriptStackBase = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, PcdGet32 (PcdS3BootScriptStackSize)); ASSERT (AcpiS3Context->BootScriptStackBase != 0); // // Allocate a code buffer < 4G for S3 debug to load external code, set invalid code instructions in it. // AcpiS3Context->S3DebugBufferAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, EFI_PAGE_SIZE); SetMem ((VOID *)(UINTN)AcpiS3Context->S3DebugBufferAddress, EFI_PAGE_SIZE, 0xff); DEBUG ((EFI_D_INFO, "AcpiS3Context: AcpiFacsTable is 0x%8Lx\n", AcpiS3Context->AcpiFacsTable)); DEBUG ((EFI_D_INFO, "AcpiS3Context: IdtrProfile is 0x%8Lx\n", AcpiS3Context->IdtrProfile)); DEBUG ((EFI_D_INFO, "AcpiS3Context: S3NvsPageTableAddress is 0x%8Lx\n", AcpiS3Context->S3NvsPageTableAddress)); DEBUG ((EFI_D_INFO, "AcpiS3Context: S3DebugBufferAddress is 0x%8Lx\n", AcpiS3Context->S3DebugBufferAddress)); Status = SaveLockBox ( &gEfiAcpiVariableGuid, &AcpiS3ContextBuffer, sizeof(AcpiS3ContextBuffer) ); ASSERT_EFI_ERROR (Status); Status = SaveLockBox ( &gEfiAcpiS3ContextGuid, (VOID *)(UINTN)AcpiS3Context, (UINTN)sizeof(*AcpiS3Context) ); ASSERT_EFI_ERROR (Status); Status = SetLockBoxAttributes (&gEfiAcpiS3ContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE); ASSERT_EFI_ERROR (Status); return EFI_SUCCESS; }
/** 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 boundry // 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; }