\r
#include <Library/MemoryAllocationLib.h>\r
#include <Library/QemuFwCfgLib.h>\r
-#include <Protocol/S3SaveState.h>\r
+#include <Library/QemuFwCfgS3Lib.h>\r
\r
#include "AcpiPlatform.h"\r
\r
\r
//\r
// Scratch buffer, allocated in EfiReservedMemoryType type memory, for the ACPI\r
-// S3 Boot Script opcodes to work on. We use the buffer to compose and to\r
-// replay several fw_cfg select+skip and write operations, using the DMA access\r
-// method. The fw_cfg operations will implement the actions dictated by\r
-// CONDENSED_WRITE_POINTER objects.\r
+// S3 Boot Script opcodes to work on.\r
//\r
#pragma pack (1)\r
-typedef struct {\r
- FW_CFG_DMA_ACCESS Access; // filled in from\r
- // CONDENSED_WRITE_POINTER.PointerItem,\r
- // CONDENSED_WRITE_POINTER.PointerSize,\r
- // CONDENSED_WRITE_POINTER.PointerOffset\r
- UINT64 PointerValue; // filled in from\r
- // CONDENSED_WRITE_POINTER.PointerValue\r
+typedef union {\r
+ UINT64 PointerValue; // filled in from CONDENSED_WRITE_POINTER.PointerValue\r
} SCRATCH_BUFFER;\r
#pragma pack ()\r
\r
\r
\r
/**\r
- Translate and append the information from an S3_CONTEXT object to the ACPI S3\r
- Boot Script.\r
-\r
- The effects of a successful call to this function cannot be undone.\r
-\r
- @param[in] S3Context The S3_CONTEXT object to translate to ACPI S3 Boot\r
- Script opcodes.\r
-\r
- @retval EFI_OUT_OF_RESOURCES Out of memory.\r
-\r
- @retval EFI_SUCCESS The translation of S3Context to ACPI S3 Boot\r
- Script opcodes has been successful.\r
-\r
- @return Error codes from underlying functions.\r
+ FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION provided to QemuFwCfgS3Lib.\r
**/\r
-EFI_STATUS\r
-TransferS3ContextToBootScript (\r
- IN CONST S3_CONTEXT *S3Context\r
+STATIC\r
+VOID\r
+EFIAPI\r
+AppendFwCfgBootScript (\r
+ IN OUT VOID *Context, OPTIONAL\r
+ IN OUT VOID *ExternalScratchBuffer\r
)\r
{\r
- EFI_STATUS Status;\r
- EFI_S3_SAVE_STATE_PROTOCOL *S3SaveState;\r
- SCRATCH_BUFFER *ScratchBuffer;\r
- FW_CFG_DMA_ACCESS *Access;\r
- UINT64 BigEndianAddressOfAccess;\r
- UINT32 ControlPollData;\r
- UINT32 ControlPollMask;\r
- UINTN Index;\r
-\r
- //\r
- // If the following protocol lookup fails, it shall not happen due to an\r
- // unexpected DXE driver dispatch order.\r
- //\r
- // Namely, this function is only invoked on QEMU. Therefore it is only\r
- // reached after Platform BDS signals gRootBridgesConnectedEventGroupGuid\r
- // (see OnRootBridgesConnected() in "EntryPoint.c"). Hence, because\r
- // TransferS3ContextToBootScript() is invoked in BDS, all DXE drivers,\r
- // including S3SaveStateDxe (producing EFI_S3_SAVE_STATE_PROTOCOL), have been\r
- // dispatched by the time we get here. (S3SaveStateDxe is not expected to\r
- // have any stricter-than-TRUE DEPEX -- not a DEPEX that gets unblocked only\r
- // within BDS anyway.)\r
- //\r
- // Reaching this function also depends on QemuFwCfgS3Enabled(). That implies\r
- // S3SaveStateDxe has not exited immediately due to S3 being disabled. Thus\r
- // EFI_S3_SAVE_STATE_PROTOCOL can only be missing for genuinely unforeseeable\r
- // reasons.\r
- //\r
- Status = gBS->LocateProtocol (&gEfiS3SaveStateProtocolGuid,\r
- NULL /* Registration */, (VOID **)&S3SaveState);\r
- if (EFI_ERROR (Status)) {\r
- DEBUG ((DEBUG_ERROR, "%a: LocateProtocol(): %r\n", __FUNCTION__, Status));\r
- return Status;\r
- }\r
+ S3_CONTEXT *S3Context;\r
+ SCRATCH_BUFFER *ScratchBuffer;\r
+ UINTN Index;\r
\r
- ScratchBuffer = AllocateReservedPool (sizeof *ScratchBuffer);\r
- if (ScratchBuffer == NULL) {\r
- return EFI_OUT_OF_RESOURCES;\r
- }\r
+ S3Context = Context;\r
+ ScratchBuffer = ExternalScratchBuffer;\r
\r
- //\r
- // Set up helper variables that we'll use identically for all\r
- // CONDENSED_WRITE_POINTER elements.\r
- //\r
- Access = &ScratchBuffer->Access;\r
- BigEndianAddressOfAccess = SwapBytes64 ((UINTN)Access);\r
- ControlPollData = 0;\r
- ControlPollMask = MAX_UINT32;\r
-\r
- //\r
- // For each CONDENSED_WRITE_POINTER, we need six ACPI S3 Boot Script opcodes:\r
- // (1) restore an FW_CFG_DMA_ACCESS object in reserved memory that selects\r
- // the writeable fw_cfg file PointerFile (through PointerItem), and skips\r
- // to PointerOffset in it,\r
- // (2) call QEMU with the FW_CFG_DMA_ACCESS object,\r
- // (3) wait for the select+skip to finish,\r
- // (4) restore a SCRATCH_BUFFER object in reserved memory that writes\r
- // PointerValue (base address of the allocated / downloaded PointeeFile,\r
- // plus PointeeOffset), of size PointerSize, into the fw_cfg file\r
- // selected in (1), at the offset sought to in (1),\r
- // (5) call QEMU with the FW_CFG_DMA_ACCESS object,\r
- // (6) wait for the write to finish.\r
- //\r
- // EFI_S3_SAVE_STATE_PROTOCOL does not allow rolling back opcode additions,\r
- // therefore we treat any failure here as fatal.\r
- //\r
for (Index = 0; Index < S3Context->Used; ++Index) {\r
CONST CONDENSED_WRITE_POINTER *Condensed;\r
+ RETURN_STATUS Status;\r
\r
Condensed = &S3Context->WritePointers[Index];\r
\r
- //\r
- // (1) restore an FW_CFG_DMA_ACCESS object in reserved memory that selects\r
- // the writeable fw_cfg file PointerFile (through PointerItem), and\r
- // skips to PointerOffset in it,\r
- //\r
- Access->Control = SwapBytes32 ((UINT32)Condensed->PointerItem << 16 |\r
- FW_CFG_DMA_CTL_SELECT | FW_CFG_DMA_CTL_SKIP);\r
- Access->Length = SwapBytes32 (Condensed->PointerOffset);\r
- Access->Address = 0;\r
- Status = S3SaveState->Write (\r
- S3SaveState, // This\r
- EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE, // OpCode\r
- EfiBootScriptWidthUint8, // Width\r
- (UINT64)(UINTN)Access, // Address\r
- sizeof *Access, // Count\r
- Access // Buffer\r
- );\r
- if (EFI_ERROR (Status)) {\r
- DEBUG ((DEBUG_ERROR, "%a: Index %Lu opcode 1: %r\n", __FUNCTION__,\r
- (UINT64)Index, Status));\r
- goto FatalError;\r
- }\r
-\r
- //\r
- // (2) call QEMU with the FW_CFG_DMA_ACCESS object,\r
- //\r
- Status = S3SaveState->Write (\r
- S3SaveState, // 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
- &BigEndianAddressOfAccess // Buffer\r
- );\r
- if (EFI_ERROR (Status)) {\r
- DEBUG ((DEBUG_ERROR, "%a: Index %Lu opcode 2: %r\n", __FUNCTION__,\r
- (UINT64)Index, Status));\r
+ Status = QemuFwCfgS3ScriptSkipBytes (Condensed->PointerItem,\r
+ Condensed->PointerOffset);\r
+ if (RETURN_ERROR (Status)) {\r
goto FatalError;\r
}\r
\r
- //\r
- // (3) wait for the select+skip to finish,\r
- //\r
- Status = S3SaveState->Write (\r
- S3SaveState, // This\r
- EFI_BOOT_SCRIPT_MEM_POLL_OPCODE, // OpCode\r
- EfiBootScriptWidthUint32, // Width\r
- (UINT64)(UINTN)&Access->Control, // Address\r
- &ControlPollData, // Data\r
- &ControlPollMask, // DataMask\r
- MAX_UINT64 // Delay\r
- );\r
- if (EFI_ERROR (Status)) {\r
- DEBUG ((DEBUG_ERROR, "%a: Index %Lu opcode 3: %r\n", __FUNCTION__,\r
- (UINT64)Index, Status));\r
- goto FatalError;\r
- }\r
-\r
- //\r
- // (4) restore a SCRATCH_BUFFER object in reserved memory that writes\r
- // PointerValue (base address of the allocated / downloaded\r
- // PointeeFile, plus PointeeOffset), of size PointerSize, into the\r
- // fw_cfg file selected in (1), at the offset sought to in (1),\r
- //\r
- Access->Control = SwapBytes32 (FW_CFG_DMA_CTL_WRITE);\r
- Access->Length = SwapBytes32 (Condensed->PointerSize);\r
- Access->Address = SwapBytes64 ((UINTN)&ScratchBuffer->PointerValue);\r
ScratchBuffer->PointerValue = Condensed->PointerValue;\r
- Status = S3SaveState->Write (\r
- S3SaveState, // This\r
- EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE, // OpCode\r
- EfiBootScriptWidthUint8, // Width\r
- (UINT64)(UINTN)ScratchBuffer, // Address\r
- sizeof *ScratchBuffer, // Count\r
- ScratchBuffer // Buffer\r
- );\r
- if (EFI_ERROR (Status)) {\r
- DEBUG ((DEBUG_ERROR, "%a: Index %Lu opcode 4: %r\n", __FUNCTION__,\r
- (UINT64)Index, Status));\r
- goto FatalError;\r
- }\r
-\r
- //\r
- // (5) call QEMU with the FW_CFG_DMA_ACCESS object,\r
- //\r
- Status = S3SaveState->Write (\r
- S3SaveState, // 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
- &BigEndianAddressOfAccess // Buffer\r
- );\r
- if (EFI_ERROR (Status)) {\r
- DEBUG ((DEBUG_ERROR, "%a: Index %Lu opcode 5: %r\n", __FUNCTION__,\r
- (UINT64)Index, Status));\r
- goto FatalError;\r
- }\r
-\r
- //\r
- // (6) wait for the write to finish.\r
- //\r
- Status = S3SaveState->Write (\r
- S3SaveState, // This\r
- EFI_BOOT_SCRIPT_MEM_POLL_OPCODE, // OpCode\r
- EfiBootScriptWidthUint32, // Width\r
- (UINT64)(UINTN)&Access->Control, // Address\r
- &ControlPollData, // Data\r
- &ControlPollMask, // DataMask\r
- MAX_UINT64 // Delay\r
- );\r
- if (EFI_ERROR (Status)) {\r
- DEBUG ((DEBUG_ERROR, "%a: Index %Lu opcode 6: %r\n", __FUNCTION__,\r
- (UINT64)Index, Status));\r
+ Status = QemuFwCfgS3ScriptWriteBytes (-1, Condensed->PointerSize);\r
+ if (RETURN_ERROR (Status)) {\r
goto FatalError;\r
}\r
}\r
\r
- DEBUG ((DEBUG_VERBOSE, "%a: boot script fragment saved, ScratchBuffer=%p\n",\r
- __FUNCTION__, (VOID *)ScratchBuffer));\r
- return EFI_SUCCESS;\r
+ DEBUG ((DEBUG_VERBOSE, "%a: boot script fragment saved\n", __FUNCTION__));\r
+\r
+ ReleaseS3Context (S3Context);\r
+ return;\r
\r
FatalError:\r
ASSERT (FALSE);\r
CpuDeadLoop ();\r
- return Status;\r
+}\r
+\r
+\r
+/**\r
+ Translate and append the information from an S3_CONTEXT object to the ACPI S3\r
+ Boot Script.\r
+\r
+ The effects of a successful call to this function cannot be undone.\r
+\r
+ @param[in] S3Context The S3_CONTEXT object to translate to ACPI S3 Boot\r
+ Script opcodes. If the function returns successfully,\r
+ the caller must set the S3Context pointer -- originally\r
+ returned by AllocateS3Context() -- immediately to NULL,\r
+ because the ownership of S3Context has been transfered.\r
+\r
+ @retval EFI_SUCCESS The translation of S3Context to ACPI S3 Boot Script\r
+ opcodes has been successfully executed or queued. (This\r
+ includes the case when S3Context was empty on input and\r
+ no ACPI S3 Boot Script opcodes have been necessary to\r
+ produce.)\r
+\r
+ @return Error codes from underlying functions.\r
+**/\r
+EFI_STATUS\r
+TransferS3ContextToBootScript (\r
+ IN S3_CONTEXT *S3Context\r
+ )\r
+{\r
+ RETURN_STATUS Status;\r
+\r
+ if (S3Context->Used == 0) {\r
+ ReleaseS3Context (S3Context);\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ Status = QemuFwCfgS3CallWhenBootScriptReady (AppendFwCfgBootScript,\r
+ S3Context, sizeof (SCRATCH_BUFFER));\r
+ return (EFI_STATUS)Status;\r
}\r