summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf8
-rw-r--r--OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3Dxe.c792
2 files changed, 800 insertions, 0 deletions
diff --git a/OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf b/OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
index 7016575f3d..a0e4275cb8 100644
--- a/OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
+++ b/OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
@@ -28,6 +28,7 @@
#
[Sources]
+ QemuFwCfgS3Dxe.c
QemuFwCfgS3PeiDxe.c
[Packages]
@@ -35,4 +36,11 @@
OvmfPkg/OvmfPkg.dec
[LibraryClasses]
+ BaseLib
+ DebugLib
+ MemoryAllocationLib
QemuFwCfgLib
+ UefiBootServicesTableLib
+
+[Protocols]
+ gEfiS3SaveStateProtocolGuid ## SOMETIMES_CONSUMES
diff --git a/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3Dxe.c b/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3Dxe.c
new file mode 100644
index 0000000000..0bd6cf9399
--- /dev/null
+++ b/OvmfPkg/Library/QemuFwCfgS3Lib/QemuFwCfgS3Dxe.c
@@ -0,0 +1,792 @@
+/** @file
+ Full functionality QemuFwCfgS3Lib instance, for DXE phase modules.
+
+ 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/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/QemuFwCfgS3Lib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/S3SaveState.h>
+
+
+//
+// Event to signal when the S3SaveState protocol interface is installed.
+//
+STATIC EFI_EVENT mS3SaveStateInstalledEvent;
+
+//
+// Reference to the S3SaveState protocol interface, after it is installed.
+//
+STATIC EFI_S3_SAVE_STATE_PROTOCOL *mS3SaveState;
+
+//
+// The control structure is allocated in reserved memory, aligned at 8 bytes.
+// The client-requested ScratchBuffer will be allocated adjacently, also
+// aligned at 8 bytes.
+//
+#define RESERVED_MEM_ALIGNMENT 8
+
+STATIC FW_CFG_DMA_ACCESS *mDmaAccess;
+STATIC VOID *mScratchBuffer;
+STATIC UINTN mScratchBufferSize;
+
+//
+// Callback provided by the client, for appending ACPI S3 Boot Script opcodes.
+// To be called from S3SaveStateInstalledNotify().
+//
+STATIC FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION *mCallback;
+
+
+/**
+ Event notification function for mS3SaveStateInstalledEvent.
+**/
+STATIC
+VOID
+EFIAPI
+S3SaveStateInstalledNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT (Event == mS3SaveStateInstalledEvent);
+
+ Status = gBS->LocateProtocol (&gEfiS3SaveStateProtocolGuid,
+ NULL /* Registration */, (VOID **)&mS3SaveState);
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+
+ ASSERT (mCallback != NULL);
+
+ DEBUG ((DEBUG_INFO, "%a: %a: DmaAccess@0x%Lx ScratchBuffer@[0x%Lx+0x%Lx]\n",
+ gEfiCallerBaseName, __FUNCTION__, (UINT64)(UINTN)mDmaAccess,
+ (UINT64)(UINTN)mScratchBuffer, (UINT64)mScratchBufferSize));
+ mCallback (Context, mScratchBuffer);
+
+ gBS->CloseEvent (mS3SaveStateInstalledEvent);
+ mS3SaveStateInstalledEvent = NULL;
+}
+
+
+/**
+ Install the client module's FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION callback for
+ when the production of ACPI S3 Boot Script opcodes becomes possible.
+
+ Take ownership of the client-provided Context, and pass it to the callback
+ function, when the latter is invoked.
+
+ Allocate scratch space for those ACPI S3 Boot Script opcodes to work upon
+ that the client will produce in the callback function.
+
+ @param[in] Callback FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION to invoke
+ when the production of ACPI S3 Boot Script
+ opcodes becomes possible. Callback() may be
+ called immediately from
+ QemuFwCfgS3CallWhenBootScriptReady().
+
+ @param[in,out] Context Client-provided data structure for the
+ Callback() callback function to consume.
+
+ If Context points to dynamically allocated
+ memory, then Callback() must release it.
+
+ If Context points to dynamically allocated
+ memory, and
+ QemuFwCfgS3CallWhenBootScriptReady() returns
+ successfully, then the caller of
+ QemuFwCfgS3CallWhenBootScriptReady() must
+ neither dereference nor even evaluate Context
+ any longer, as ownership of the referenced area
+ has been transferred to Callback().
+
+ @param[in] ScratchBufferSize The size of the scratch buffer that will hold,
+ in reserved memory, all client data read,
+ written, and checked by the ACPI S3 Boot Script
+ opcodes produced by Callback().
+
+ @retval RETURN_UNSUPPORTED The library instance does not support this
+ function.
+
+ @retval RETURN_NOT_FOUND The fw_cfg DMA interface to QEMU is
+ unavailable.
+
+ @retval RETURN_BAD_BUFFER_SIZE ScratchBufferSize is too large.
+
+ @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
+
+ @retval RETURN_SUCCESS Callback() has been installed, and the
+ ownership of Context has been transferred.
+ Reserved memory has been allocated for the
+ scratch buffer.
+
+ A successful invocation of
+ QemuFwCfgS3CallWhenBootScriptReady() cannot
+ be rolled back.
+
+ @return Error codes from underlying functions.
+**/
+EFIAPI
+RETURN_STATUS
+QemuFwCfgS3CallWhenBootScriptReady (
+ IN FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION *Callback,
+ IN OUT VOID *Context, OPTIONAL
+ IN UINTN ScratchBufferSize
+ )
+{
+ EFI_STATUS Status;
+ VOID *Registration;
+
+ //
+ // Basic fw_cfg is certainly available, as we can only be here after a
+ // successful call to QemuFwCfgS3Enabled(). Check fw_cfg DMA availability.
+ //
+ ASSERT (QemuFwCfgIsAvailable ());
+ QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);
+ if ((QemuFwCfgRead32 () & FW_CFG_F_DMA) == 0) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: fw_cfg DMA unavailable\n",
+ gEfiCallerBaseName, __FUNCTION__));
+ return RETURN_NOT_FOUND;
+ }
+
+ //
+ // Allocate a reserved buffer for the DMA access control structure and the
+ // client data together.
+ //
+ if (ScratchBufferSize >
+ MAX_UINT32 - (RESERVED_MEM_ALIGNMENT - 1) - sizeof *mDmaAccess) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: ScratchBufferSize too big: %Lu\n",
+ gEfiCallerBaseName, __FUNCTION__, (UINT64)ScratchBufferSize));
+ return RETURN_BAD_BUFFER_SIZE;
+ }
+ mDmaAccess = AllocateReservedPool ((RESERVED_MEM_ALIGNMENT - 1) +
+ sizeof *mDmaAccess + ScratchBufferSize);
+ if (mDmaAccess == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: AllocateReservedPool(): out of resources\n",
+ gEfiCallerBaseName, __FUNCTION__));
+ return RETURN_OUT_OF_RESOURCES;
+ }
+ mDmaAccess = ALIGN_POINTER (mDmaAccess, RESERVED_MEM_ALIGNMENT);
+
+ //
+ // Set up a protocol notify for EFI_S3_SAVE_STATE_PROTOCOL. Forward the
+ // client's Context to the callback.
+ //
+ Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
+ S3SaveStateInstalledNotify, Context,
+ &mS3SaveStateInstalledEvent);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: CreateEvent(): %r\n", gEfiCallerBaseName,
+ __FUNCTION__, Status));
+ goto FreeDmaAccess;
+ }
+ Status = gBS->RegisterProtocolNotify (&gEfiS3SaveStateProtocolGuid,
+ mS3SaveStateInstalledEvent, &Registration);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: RegisterProtocolNotify(): %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ goto CloseEvent;
+ }
+
+ //
+ // Set the remaining global variables. For the alignment guarantee on
+ // mScratchBuffer, we rely on the fact that *mDmaAccess has a size that is an
+ // integral multiple of RESERVED_MEM_ALIGNMENT.
+ //
+ ASSERT (sizeof *mDmaAccess % RESERVED_MEM_ALIGNMENT == 0);
+ mScratchBuffer = mDmaAccess + 1;
+ mScratchBufferSize = ScratchBufferSize;
+ mCallback = Callback;
+
+ //
+ // Kick the event; EFI_S3_SAVE_STATE_PROTOCOL could be available already.
+ //
+ Status = gBS->SignalEvent (mS3SaveStateInstalledEvent);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: SignalEvent(): %r\n", gEfiCallerBaseName,
+ __FUNCTION__, Status));
+ goto NullGlobals;
+ }
+
+ return RETURN_SUCCESS;
+
+NullGlobals:
+ mScratchBuffer = NULL;
+ mScratchBufferSize = 0;
+ mCallback = NULL;
+
+CloseEvent:
+ gBS->CloseEvent (mS3SaveStateInstalledEvent);
+ mS3SaveStateInstalledEvent = NULL;
+
+FreeDmaAccess:
+ FreePool (mDmaAccess);
+ mDmaAccess = NULL;
+
+ return (RETURN_STATUS)Status;
+}
+
+
+/**
+ Produce ACPI S3 Boot Script opcodes that (optionally) select an fw_cfg item,
+ and transfer data to it.
+
+ The opcodes produced by QemuFwCfgS3ScriptWriteBytes() will first restore
+ NumberOfBytes bytes in ScratchBuffer in-place, in reserved memory, then write
+ them to fw_cfg using DMA.
+
+ If the operation fails during S3 resume, the boot script will hang.
+
+ This function may only be called from the client module's
+ FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION, which was passed to
+ QemuFwCfgS3CallWhenBootScriptReady() as Callback.
+
+ @param[in] FirmwareConfigItem The UINT16 selector key of the firmware config
+ item to write, expressed as INT32. If
+ FirmwareConfigItem is -1, no selection is
+ made, the write will occur to the currently
+ selected item, at its currently selected
+ offset. Otherwise, the specified item will be
+ selected, and the write will occur at offset
+ 0.
+
+ @param[in] NumberOfBytes Size of the data to restore in ScratchBuffer,
+ and to write from ScratchBuffer, during S3
+ resume. NumberOfBytes must not exceed
+ ScratchBufferSize, which was passed to
+ QemuFwCfgS3CallWhenBootScriptReady().
+
+ @retval RETURN_SUCCESS The opcodes were appended to the ACPI S3
+ Boot Script successfully. There is no way
+ to undo this action.
+
+ @retval RETURN_INVALID_PARAMETER FirmwareConfigItem is invalid.
+
+ @retval RETURN_BAD_BUFFER_SIZE NumberOfBytes is larger than
+ ScratchBufferSize.
+
+ @return Error codes from underlying functions.
+**/
+EFIAPI
+RETURN_STATUS
+QemuFwCfgS3ScriptWriteBytes (
+ IN INT32 FirmwareConfigItem,
+ IN UINTN NumberOfBytes
+ )
+{
+ UINTN Count;
+ EFI_STATUS Status;
+ UINT64 AccessAddress;
+ UINT32 ControlPollData;
+ UINT32 ControlPollMask;
+
+ ASSERT (mDmaAccess != NULL);
+ ASSERT (mS3SaveState != NULL);
+
+ if (FirmwareConfigItem < -1 || FirmwareConfigItem > MAX_UINT16) {
+ return RETURN_INVALID_PARAMETER;
+ }
+ if (NumberOfBytes > mScratchBufferSize) {
+ return RETURN_BAD_BUFFER_SIZE;
+ }
+
+ //
+ // Set up a write[+select] fw_cfg DMA command.
+ //
+ mDmaAccess->Control = FW_CFG_DMA_CTL_WRITE;
+ if (FirmwareConfigItem != -1) {
+ mDmaAccess->Control |= FW_CFG_DMA_CTL_SELECT;
+ mDmaAccess->Control |= (UINT32)FirmwareConfigItem << 16;
+ }
+ mDmaAccess->Control = SwapBytes32 (mDmaAccess->Control);
+
+ //
+ // We ensured the following constraint via mScratchBufferSize in
+ // QemuFwCfgS3CallWhenBootScriptReady().
+ //
+ ASSERT (NumberOfBytes <= MAX_UINT32);
+ mDmaAccess->Length = SwapBytes32 ((UINT32)NumberOfBytes);
+
+ mDmaAccess->Address = SwapBytes64 ((UINTN)mScratchBuffer);
+
+ //
+ // Copy mDmaAccess and NumberOfBytes bytes from mScratchBuffer into the boot
+ // script. When executed at S3 resume, this opcode will restore all of them
+ // in-place.
+ //
+ Count = (UINTN)mScratchBuffer + NumberOfBytes - (UINTN)mDmaAccess;
+ Status = mS3SaveState->Write (
+ mS3SaveState, // This
+ EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE, // OpCode
+ EfiBootScriptWidthUint8, // Width
+ (UINT64)(UINTN)mDmaAccess, // Address
+ Count, // Count
+ (VOID *)mDmaAccess // Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE: %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ return (RETURN_STATUS)Status;
+ }
+
+ //
+ // Append an opcode that will write the address of the fw_cfg DMA command to
+ // the fw_cfg DMA address register, which consists of two 32-bit IO ports.
+ // The second (highest address, least significant) write will start the
+ // transfer.
+ //
+ AccessAddress = SwapBytes64 ((UINTN)mDmaAccess);
+ Status = mS3SaveState->Write (
+ mS3SaveState, // This
+ EFI_BOOT_SCRIPT_IO_WRITE_OPCODE, // OpCode
+ EfiBootScriptWidthUint32, // Width
+ (UINT64)FW_CFG_IO_DMA_ADDRESS, // Address
+ (UINTN)2, // Count
+ (VOID *)&AccessAddress // Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: EFI_BOOT_SCRIPT_IO_WRITE_OPCODE: %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ return (RETURN_STATUS)Status;
+ }
+
+ //
+ // The following opcode will wait until the Control word reads as zero
+ // (transfer complete). As timeout we use MAX_UINT64 * 100ns, which is
+ // approximately 58494 years.
+ //
+ ControlPollData = 0;
+ ControlPollMask = MAX_UINT32;
+ Status = mS3SaveState->Write (
+ mS3SaveState, // This
+ EFI_BOOT_SCRIPT_MEM_POLL_OPCODE, // OpCode
+ EfiBootScriptWidthUint32, // Width
+ (UINT64)(UINTN)&mDmaAccess->Control, // Address
+ (VOID *)&ControlPollData, // Data
+ (VOID *)&ControlPollMask, // DataMask
+ MAX_UINT64 // Delay
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: EFI_BOOT_SCRIPT_MEM_POLL_OPCODE: %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ return (RETURN_STATUS)Status;
+ }
+
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ Produce ACPI S3 Boot Script opcodes that (optionally) select an fw_cfg item,
+ and transfer data from it.
+
+ The opcodes produced by QemuFwCfgS3ScriptReadBytes() will read NumberOfBytes
+ bytes from fw_cfg using DMA, storing the result in ScratchBuffer, in reserved
+ memory.
+
+ If the operation fails during S3 resume, the boot script will hang.
+
+ This function may only be called from the client module's
+ FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION, which was passed to
+ QemuFwCfgS3CallWhenBootScriptReady() as Callback.
+
+ @param[in] FirmwareConfigItem The UINT16 selector key of the firmware config
+ item to read, expressed as INT32. If
+ FirmwareConfigItem is -1, no selection is
+ made, the read will occur from the currently
+ selected item, from its currently selected
+ offset. Otherwise, the specified item will be
+ selected, and the read will occur from offset
+ 0.
+
+ @param[in] NumberOfBytes Size of the data to read during S3 resume.
+ NumberOfBytes must not exceed
+ ScratchBufferSize, which was passed to
+ QemuFwCfgS3CallWhenBootScriptReady().
+
+ @retval RETURN_SUCCESS The opcodes were appended to the ACPI S3
+ Boot Script successfully. There is no way
+ to undo this action.
+
+ @retval RETURN_INVALID_PARAMETER FirmwareConfigItem is invalid.
+
+ @retval RETURN_BAD_BUFFER_SIZE NumberOfBytes is larger than
+ ScratchBufferSize.
+
+ @return Error codes from underlying functions.
+**/
+EFIAPI
+RETURN_STATUS
+QemuFwCfgS3ScriptReadBytes (
+ IN INT32 FirmwareConfigItem,
+ IN UINTN NumberOfBytes
+ )
+{
+ EFI_STATUS Status;
+ UINT64 AccessAddress;
+ UINT32 ControlPollData;
+ UINT32 ControlPollMask;
+
+ ASSERT (mDmaAccess != NULL);
+ ASSERT (mS3SaveState != NULL);
+
+ if (FirmwareConfigItem < -1 || FirmwareConfigItem > MAX_UINT16) {
+ return RETURN_INVALID_PARAMETER;
+ }
+ if (NumberOfBytes > mScratchBufferSize) {
+ return RETURN_BAD_BUFFER_SIZE;
+ }
+
+ //
+ // Set up a read[+select] fw_cfg DMA command.
+ //
+ mDmaAccess->Control = FW_CFG_DMA_CTL_READ;
+ if (FirmwareConfigItem != -1) {
+ mDmaAccess->Control |= FW_CFG_DMA_CTL_SELECT;
+ mDmaAccess->Control |= (UINT32)FirmwareConfigItem << 16;
+ }
+ mDmaAccess->Control = SwapBytes32 (mDmaAccess->Control);
+
+ //
+ // We ensured the following constraint via mScratchBufferSize in
+ // QemuFwCfgS3CallWhenBootScriptReady().
+ //
+ ASSERT (NumberOfBytes <= MAX_UINT32);
+ mDmaAccess->Length = SwapBytes32 ((UINT32)NumberOfBytes);
+
+ mDmaAccess->Address = SwapBytes64 ((UINTN)mScratchBuffer);
+
+ //
+ // Copy mDmaAccess into the boot script. When executed at S3 resume, this
+ // opcode will restore it in-place.
+ //
+ Status = mS3SaveState->Write (
+ mS3SaveState, // This
+ EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE, // OpCode
+ EfiBootScriptWidthUint8, // Width
+ (UINT64)(UINTN)mDmaAccess, // Address
+ sizeof *mDmaAccess, // Count
+ (VOID *)mDmaAccess // Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE: %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ return (RETURN_STATUS)Status;
+ }
+
+ //
+ // Append an opcode that will write the address of the fw_cfg DMA command to
+ // the fw_cfg DMA address register, which consists of two 32-bit IO ports.
+ // The second (highest address, least significant) write will start the
+ // transfer.
+ //
+ AccessAddress = SwapBytes64 ((UINTN)mDmaAccess);
+ Status = mS3SaveState->Write (
+ mS3SaveState, // This
+ EFI_BOOT_SCRIPT_IO_WRITE_OPCODE, // OpCode
+ EfiBootScriptWidthUint32, // Width
+ (UINT64)FW_CFG_IO_DMA_ADDRESS, // Address
+ (UINTN)2, // Count
+ (VOID *)&AccessAddress // Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: EFI_BOOT_SCRIPT_IO_WRITE_OPCODE: %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ return (RETURN_STATUS)Status;
+ }
+
+ //
+ // The following opcode will wait until the Control word reads as zero
+ // (transfer complete). As timeout we use MAX_UINT64 * 100ns, which is
+ // approximately 58494 years.
+ //
+ ControlPollData = 0;
+ ControlPollMask = MAX_UINT32;
+ Status = mS3SaveState->Write (
+ mS3SaveState, // This
+ EFI_BOOT_SCRIPT_MEM_POLL_OPCODE, // OpCode
+ EfiBootScriptWidthUint32, // Width
+ (UINT64)(UINTN)&mDmaAccess->Control, // Address
+ (VOID *)&ControlPollData, // Data
+ (VOID *)&ControlPollMask, // DataMask
+ MAX_UINT64 // Delay
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: EFI_BOOT_SCRIPT_MEM_POLL_OPCODE: %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ return (RETURN_STATUS)Status;
+ }
+
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ Produce ACPI S3 Boot Script opcodes that (optionally) select an fw_cfg item,
+ and increase its offset.
+
+ If the operation fails during S3 resume, the boot script will hang.
+
+ This function may only be called from the client module's
+ FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION, which was passed to
+ QemuFwCfgS3CallWhenBootScriptReady() as Callback.
+
+ @param[in] FirmwareConfigItem The UINT16 selector key of the firmware config
+ item to advance the offset of, expressed as
+ INT32. If FirmwareConfigItem is -1, no
+ selection is made, and the offset for the
+ currently selected item is increased.
+ Otherwise, the specified item will be
+ selected, and the offset increment will occur
+ from offset 0.
+
+ @param[in] NumberOfBytes The number of bytes to skip in the subject
+ fw_cfg item.
+
+ @retval RETURN_SUCCESS The opcodes were appended to the ACPI S3
+ Boot Script successfully. There is no way
+ to undo this action.
+
+ @retval RETURN_INVALID_PARAMETER FirmwareConfigItem is invalid.
+
+ @retval RETURN_BAD_BUFFER_SIZE NumberOfBytes is too large.
+
+ @return Error codes from underlying functions.
+**/
+EFIAPI
+RETURN_STATUS
+QemuFwCfgS3ScriptSkipBytes (
+ IN INT32 FirmwareConfigItem,
+ IN UINTN NumberOfBytes
+ )
+{
+ EFI_STATUS Status;
+ UINT64 AccessAddress;
+ UINT32 ControlPollData;
+ UINT32 ControlPollMask;
+
+ ASSERT (mDmaAccess != NULL);
+ ASSERT (mS3SaveState != NULL);
+
+ if (FirmwareConfigItem < -1 || FirmwareConfigItem > MAX_UINT16) {
+ return RETURN_INVALID_PARAMETER;
+ }
+ if (NumberOfBytes > MAX_UINT32) {
+ return RETURN_BAD_BUFFER_SIZE;
+ }
+
+ //
+ // Set up a skip[+select] fw_cfg DMA command.
+ //
+ mDmaAccess->Control = FW_CFG_DMA_CTL_SKIP;
+ if (FirmwareConfigItem != -1) {
+ mDmaAccess->Control |= FW_CFG_DMA_CTL_SELECT;
+ mDmaAccess->Control |= (UINT32)FirmwareConfigItem << 16;
+ }
+ mDmaAccess->Control = SwapBytes32 (mDmaAccess->Control);
+
+ mDmaAccess->Length = SwapBytes32 ((UINT32)NumberOfBytes);
+ mDmaAccess->Address = 0;
+
+ //
+ // Copy mDmaAccess into the boot script. When executed at S3 resume, this
+ // opcode will restore it in-place.
+ //
+ Status = mS3SaveState->Write (
+ mS3SaveState, // This
+ EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE, // OpCode
+ EfiBootScriptWidthUint8, // Width
+ (UINT64)(UINTN)mDmaAccess, // Address
+ sizeof *mDmaAccess, // Count
+ (VOID *)mDmaAccess // Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE: %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ return (RETURN_STATUS)Status;
+ }
+
+ //
+ // Append an opcode that will write the address of the fw_cfg DMA command to
+ // the fw_cfg DMA address register, which consists of two 32-bit IO ports.
+ // The second (highest address, least significant) write will start the
+ // transfer.
+ //
+ AccessAddress = SwapBytes64 ((UINTN)mDmaAccess);
+ Status = mS3SaveState->Write (
+ mS3SaveState, // This
+ EFI_BOOT_SCRIPT_IO_WRITE_OPCODE, // OpCode
+ EfiBootScriptWidthUint32, // Width
+ (UINT64)FW_CFG_IO_DMA_ADDRESS, // Address
+ (UINTN)2, // Count
+ (VOID *)&AccessAddress // Buffer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: EFI_BOOT_SCRIPT_IO_WRITE_OPCODE: %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ return (RETURN_STATUS)Status;
+ }
+
+ //
+ // The following opcode will wait until the Control word reads as zero
+ // (transfer complete). As timeout we use MAX_UINT64 * 100ns, which is
+ // approximately 58494 years.
+ //
+ ControlPollData = 0;
+ ControlPollMask = MAX_UINT32;
+ Status = mS3SaveState->Write (
+ mS3SaveState, // This
+ EFI_BOOT_SCRIPT_MEM_POLL_OPCODE, // OpCode
+ EfiBootScriptWidthUint32, // Width
+ (UINT64)(UINTN)&mDmaAccess->Control, // Address
+ (VOID *)&ControlPollData, // Data
+ (VOID *)&ControlPollMask, // DataMask
+ MAX_UINT64 // Delay
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: EFI_BOOT_SCRIPT_MEM_POLL_OPCODE: %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ return (RETURN_STATUS)Status;
+ }
+
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ Produce ACPI S3 Boot Script opcodes that check a value in ScratchBuffer.
+
+ If the check fails during S3 resume, the boot script will hang.
+
+ This function may only be called from the client module's
+ FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION, which was passed to
+ QemuFwCfgS3CallWhenBootScriptReady() as Callback.
+
+ @param[in] ScratchData Pointer to the UINT8, UINT16, UINT32 or UINT64 field
+ in ScratchBuffer that should be checked. The caller
+ is responsible for populating the field during S3
+ resume, by calling QemuFwCfgS3ScriptReadBytes() ahead
+ of QemuFwCfgS3ScriptCheckValue().
+
+ ScratchData must point into ScratchBuffer, which was
+ allocated, and passed to Callback(), by
+ QemuFwCfgS3CallWhenBootScriptReady().
+
+ ScratchData must be aligned at ValueSize bytes.
+
+ @param[in] ValueSize One of 1, 2, 4 or 8, specifying the size of the field
+ to check.
+
+ @param[in] ValueMask The value read from ScratchData is binarily AND-ed
+ with ValueMask, and the result is compared against
+ Value. If the masked data equals Value, the check
+ passes, and the boot script can proceed. Otherwise,
+ the check fails, and the boot script hangs.
+
+ @param[in] Value Refer to ValueMask.
+
+ @retval RETURN_SUCCESS The opcodes were appended to the ACPI S3
+ Boot Script successfully. There is no way
+ to undo this action.
+
+ @retval RETURN_INVALID_PARAMETER ValueSize is invalid.
+
+ @retval RETURN_INVALID_PARAMETER ValueMask or Value cannot be represented in
+ ValueSize bytes.
+
+ @retval RETURN_INVALID_PARAMETER ScratchData is not aligned at ValueSize
+ bytes.
+
+ @retval RETURN_BAD_BUFFER_SIZE The ValueSize bytes at ScratchData aren't
+ wholly contained in the ScratchBufferSize
+ bytes at ScratchBuffer.
+
+ @return Error codes from underlying functions.
+**/
+EFIAPI
+RETURN_STATUS
+QemuFwCfgS3ScriptCheckValue (
+ IN VOID *ScratchData,
+ IN UINT8 ValueSize,
+ IN UINT64 ValueMask,
+ IN UINT64 Value
+ )
+{
+ EFI_BOOT_SCRIPT_WIDTH Width;
+ EFI_STATUS Status;
+
+ ASSERT (mS3SaveState != NULL);
+
+ switch (ValueSize) {
+ case 1:
+ Width = EfiBootScriptWidthUint8;
+ break;
+
+ case 2:
+ Width = EfiBootScriptWidthUint16;
+ break;
+
+ case 4:
+ Width = EfiBootScriptWidthUint32;
+ break;
+
+ case 8:
+ Width = EfiBootScriptWidthUint64;
+ break;
+
+ default:
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ if (ValueSize < 8 &&
+ (RShiftU64 (ValueMask, ValueSize * 8) > 0 ||
+ RShiftU64 (Value, ValueSize * 8) > 0)) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ if ((UINTN)ScratchData % ValueSize > 0) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ if (((UINTN)ScratchData < (UINTN)mScratchBuffer) ||
+ ((UINTN)ScratchData > MAX_UINTN - ValueSize) ||
+ ((UINTN)ScratchData + ValueSize >
+ (UINTN)mScratchBuffer + mScratchBufferSize)) {
+ return RETURN_BAD_BUFFER_SIZE;
+ }
+
+ //
+ // The following opcode will wait "until" (*ScratchData & ValueMask) reads as
+ // Value, considering the least significant ValueSize bytes. As timeout we
+ // use MAX_UINT64 * 100ns, which is approximately 58494 years.
+ //
+ Status = mS3SaveState->Write (
+ mS3SaveState, // This
+ EFI_BOOT_SCRIPT_MEM_POLL_OPCODE, // OpCode
+ Width, // Width
+ (UINT64)(UINTN)ScratchData, // Address
+ (VOID *)&Value, // Data
+ (VOID *)&ValueMask, // DataMask
+ MAX_UINT64 // Delay
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: %a: EFI_BOOT_SCRIPT_MEM_POLL_OPCODE: %r\n",
+ gEfiCallerBaseName, __FUNCTION__, Status));
+ return (RETURN_STATUS)Status;
+ }
+
+ return RETURN_SUCCESS;
+}