| /** @file | |
| Append an ACPI S3 Boot Script fragment from the QEMU_LOADER_WRITE_POINTER | |
| commands of QEMU's fully processed table linker/loader script. | |
| Copyright (C) 2017, Red Hat, Inc. | |
| 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 <Library/MemoryAllocationLib.h> | |
| #include <Library/QemuFwCfgLib.h> | |
| #include <Library/QemuFwCfgS3Lib.h> | |
| #include "AcpiPlatform.h" | |
| // | |
| // Condensed structure for capturing the fw_cfg operations -- select, skip, | |
| // write -- inherent in executing a QEMU_LOADER_WRITE_POINTER command. | |
| // | |
| typedef struct { | |
| UINT16 PointerItem; // resolved from QEMU_LOADER_WRITE_POINTER.PointerFile | |
| UINT8 PointerSize; // copied as-is from QEMU_LOADER_WRITE_POINTER | |
| UINT32 PointerOffset; // copied as-is from QEMU_LOADER_WRITE_POINTER | |
| UINT64 PointerValue; // resolved from QEMU_LOADER_WRITE_POINTER.PointeeFile | |
| // and QEMU_LOADER_WRITE_POINTER.PointeeOffset | |
| } CONDENSED_WRITE_POINTER; | |
| // | |
| // Context structure to accumulate CONDENSED_WRITE_POINTER objects from | |
| // QEMU_LOADER_WRITE_POINTER commands. | |
| // | |
| // Any pointers in this structure own the pointed-to objects; that is, when the | |
| // context structure is released, all pointed-to objects must be released too. | |
| // | |
| struct S3_CONTEXT { | |
| CONDENSED_WRITE_POINTER *WritePointers; // one array element per processed | |
| // QEMU_LOADER_WRITE_POINTER | |
| // command | |
| UINTN Allocated; // number of elements allocated for | |
| // WritePointers | |
| UINTN Used; // number of elements populated in | |
| // WritePointers | |
| }; | |
| // | |
| // Scratch buffer, allocated in EfiReservedMemoryType type memory, for the ACPI | |
| // S3 Boot Script opcodes to work on. | |
| // | |
| #pragma pack (1) | |
| typedef union { | |
| UINT64 PointerValue; // filled in from CONDENSED_WRITE_POINTER.PointerValue | |
| } SCRATCH_BUFFER; | |
| #pragma pack () | |
| /** | |
| Allocate an S3_CONTEXT object. | |
| @param[out] S3Context The allocated S3_CONTEXT object is returned | |
| through this parameter. | |
| @param[in] WritePointerCount Number of CONDENSED_WRITE_POINTER elements to | |
| allocate room for. WritePointerCount must be | |
| positive. | |
| @retval EFI_SUCCESS Allocation successful. | |
| @retval EFI_OUT_OF_RESOURCES Out of memory. | |
| @retval EFI_INVALID_PARAMETER WritePointerCount is zero. | |
| **/ | |
| EFI_STATUS | |
| AllocateS3Context ( | |
| OUT S3_CONTEXT **S3Context, | |
| IN UINTN WritePointerCount | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| S3_CONTEXT *Context; | |
| if (WritePointerCount == 0) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| Context = AllocateZeroPool (sizeof *Context); | |
| if (Context == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| Context->WritePointers = AllocatePool (WritePointerCount * | |
| sizeof *Context->WritePointers); | |
| if (Context->WritePointers == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto FreeContext; | |
| } | |
| Context->Allocated = WritePointerCount; | |
| *S3Context = Context; | |
| return EFI_SUCCESS; | |
| FreeContext: | |
| FreePool (Context); | |
| return Status; | |
| } | |
| /** | |
| Release an S3_CONTEXT object. | |
| @param[in] S3Context The object to release. | |
| **/ | |
| VOID | |
| ReleaseS3Context ( | |
| IN S3_CONTEXT *S3Context | |
| ) | |
| { | |
| FreePool (S3Context->WritePointers); | |
| FreePool (S3Context); | |
| } | |
| /** | |
| Save the information necessary to replicate a QEMU_LOADER_WRITE_POINTER | |
| command during S3 resume, in condensed format. | |
| This function is to be called from ProcessCmdWritePointer(), after all the | |
| sanity checks have passed, and before the fw_cfg operations are performed. | |
| @param[in,out] S3Context The S3_CONTEXT object into which the caller wants | |
| to save the information that was derived from | |
| QEMU_LOADER_WRITE_POINTER. | |
| @param[in] PointerItem The FIRMWARE_CONFIG_ITEM that | |
| QEMU_LOADER_WRITE_POINTER.PointerFile was resolved | |
| to, expressed as a UINT16 value. | |
| @param[in] PointerSize Copied directly from | |
| QEMU_LOADER_WRITE_POINTER.PointerSize. | |
| @param[in] PointerOffset Copied directly from | |
| QEMU_LOADER_WRITE_POINTER.PointerOffset. | |
| @param[in] PointerValue The base address of the allocated / downloaded | |
| fw_cfg blob that is identified by | |
| QEMU_LOADER_WRITE_POINTER.PointeeFile, plus | |
| QEMU_LOADER_WRITE_POINTER.PointeeOffset. | |
| @retval EFI_SUCCESS The information derived from | |
| QEMU_LOADER_WRITE_POINTER has been successfully | |
| absorbed into S3Context. | |
| @retval EFI_OUT_OF_RESOURCES No room available in S3Context. | |
| **/ | |
| EFI_STATUS | |
| SaveCondensedWritePointerToS3Context ( | |
| IN OUT S3_CONTEXT *S3Context, | |
| IN UINT16 PointerItem, | |
| IN UINT8 PointerSize, | |
| IN UINT32 PointerOffset, | |
| IN UINT64 PointerValue | |
| ) | |
| { | |
| CONDENSED_WRITE_POINTER *Condensed; | |
| if (S3Context->Used == S3Context->Allocated) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| Condensed = S3Context->WritePointers + S3Context->Used; | |
| Condensed->PointerItem = PointerItem; | |
| Condensed->PointerSize = PointerSize; | |
| Condensed->PointerOffset = PointerOffset; | |
| Condensed->PointerValue = PointerValue; | |
| DEBUG ((DEBUG_VERBOSE, "%a: 0x%04x/[0x%08x+%d] := 0x%Lx (%Lu)\n", | |
| __FUNCTION__, PointerItem, PointerOffset, PointerSize, PointerValue, | |
| (UINT64)S3Context->Used)); | |
| ++S3Context->Used; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION provided to QemuFwCfgS3Lib. | |
| **/ | |
| STATIC | |
| VOID | |
| EFIAPI | |
| AppendFwCfgBootScript ( | |
| IN OUT VOID *Context, OPTIONAL | |
| IN OUT VOID *ExternalScratchBuffer | |
| ) | |
| { | |
| S3_CONTEXT *S3Context; | |
| SCRATCH_BUFFER *ScratchBuffer; | |
| UINTN Index; | |
| S3Context = Context; | |
| ScratchBuffer = ExternalScratchBuffer; | |
| for (Index = 0; Index < S3Context->Used; ++Index) { | |
| CONST CONDENSED_WRITE_POINTER *Condensed; | |
| RETURN_STATUS Status; | |
| Condensed = &S3Context->WritePointers[Index]; | |
| Status = QemuFwCfgS3ScriptSkipBytes (Condensed->PointerItem, | |
| Condensed->PointerOffset); | |
| if (RETURN_ERROR (Status)) { | |
| goto FatalError; | |
| } | |
| ScratchBuffer->PointerValue = Condensed->PointerValue; | |
| Status = QemuFwCfgS3ScriptWriteBytes (-1, Condensed->PointerSize); | |
| if (RETURN_ERROR (Status)) { | |
| goto FatalError; | |
| } | |
| } | |
| DEBUG ((DEBUG_VERBOSE, "%a: boot script fragment saved\n", __FUNCTION__)); | |
| ReleaseS3Context (S3Context); | |
| return; | |
| FatalError: | |
| ASSERT (FALSE); | |
| CpuDeadLoop (); | |
| } | |
| /** | |
| Translate and append the information from an S3_CONTEXT object to the ACPI S3 | |
| Boot Script. | |
| The effects of a successful call to this function cannot be undone. | |
| @param[in] S3Context The S3_CONTEXT object to translate to ACPI S3 Boot | |
| Script opcodes. If the function returns successfully, | |
| the caller must set the S3Context pointer -- originally | |
| returned by AllocateS3Context() -- immediately to NULL, | |
| because the ownership of S3Context has been transfered. | |
| @retval EFI_SUCCESS The translation of S3Context to ACPI S3 Boot Script | |
| opcodes has been successfully executed or queued. | |
| @return Error codes from underlying functions. | |
| **/ | |
| EFI_STATUS | |
| TransferS3ContextToBootScript ( | |
| IN S3_CONTEXT *S3Context | |
| ) | |
| { | |
| RETURN_STATUS Status; | |
| Status = QemuFwCfgS3CallWhenBootScriptReady (AppendFwCfgBootScript, | |
| S3Context, sizeof (SCRATCH_BUFFER)); | |
| return (EFI_STATUS)Status; | |
| } |