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