/** @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; | |
} |