#include <Library/MemoryAllocationLib.h>\r
#include <Library/PcdLib.h>\r
#include <Library/QemuFwCfgLib.h>\r
+#include <Library/QemuFwCfgS3Lib.h>\r
\r
#include "SmiFeatures.h"\r
\r
\r
//\r
// Provides a scratch buffer (allocated in EfiReservedMemoryType type memory)\r
-// for the S3 boot script fragment to write to and read from. The buffer\r
-// captures a combined fw_cfg item selection + write command using the DMA\r
-// access method. Note that we don't trust the runtime OS to preserve the\r
-// contents of the buffer, the boot script will first rewrite it.\r
+// for the S3 boot script fragment to write to and read from.\r
//\r
#pragma pack (1)\r
-typedef struct {\r
- FW_CFG_DMA_ACCESS Access;\r
- UINT64 Features;\r
+typedef union {\r
+ UINT64 Features;\r
+ UINT8 FeaturesOk;\r
} SCRATCH_BUFFER;\r
#pragma pack ()\r
\r
//\r
// These carry the selector keys of the "etc/smi/requested-features" and\r
// "etc/smi/features-ok" fw_cfg files from NegotiateSmiFeatures() to\r
-// SaveSmiFeatures().\r
+// AppendFwCfgBootScript().\r
//\r
STATIC FIRMWARE_CONFIG_ITEM mRequestedFeaturesItem;\r
STATIC FIRMWARE_CONFIG_ITEM mFeaturesOkItem;\r
\r
//\r
// Carries the negotiated SMI features from NegotiateSmiFeatures() to\r
-// SaveSmiFeatures().\r
+// AppendFwCfgBootScript().\r
//\r
STATIC UINT64 mSmiFeatures;\r
\r
}\r
\r
/**\r
- Append a boot script fragment that will re-select the previously negotiated\r
- SMI features during S3 resume.\r
-\r
- @param[in] S3SaveState The EFI_S3_SAVE_STATE_PROTOCOL instance to append to\r
- the S3 boot script with.\r
+ FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION provided to QemuFwCfgS3Lib.\r
**/\r
+STATIC\r
VOID\r
-SaveSmiFeatures (\r
- IN EFI_S3_SAVE_STATE_PROTOCOL *S3SaveState\r
+EFIAPI\r
+AppendFwCfgBootScript (\r
+ IN OUT VOID *Context, OPTIONAL\r
+ IN OUT VOID *ExternalScratchBuffer\r
)\r
{\r
SCRATCH_BUFFER *ScratchBuffer;\r
- EFI_STATUS Status;\r
- UINT64 AccessAddress;\r
- UINT32 ControlPollData;\r
- UINT32 ControlPollMask;\r
- UINT16 FeaturesOkItemAsUint16;\r
- UINT8 FeaturesOkData;\r
- UINT8 FeaturesOkMask;\r
-\r
- ScratchBuffer = AllocateReservedPool (sizeof *ScratchBuffer);\r
- if (ScratchBuffer == NULL) {\r
- DEBUG ((DEBUG_ERROR, "%a: scratch buffer allocation failed\n",\r
- __FUNCTION__));\r
- goto FatalError;\r
- }\r
-\r
- //\r
- // Populate the scratch buffer with a select + write fw_cfg DMA command that\r
- // will write the negotiated feature bitmap into\r
- // "etc/smi/requested-features".\r
- //\r
- ScratchBuffer->Access.Control = SwapBytes32 (\r
- (UINT32)mRequestedFeaturesItem << 16 |\r
- FW_CFG_DMA_CTL_SELECT |\r
- FW_CFG_DMA_CTL_WRITE\r
- );\r
- ScratchBuffer->Access.Length = SwapBytes32 (\r
- (UINT32)sizeof ScratchBuffer->Features);\r
- ScratchBuffer->Access.Address = SwapBytes64 (\r
- (UINTN)&ScratchBuffer->Features);\r
- ScratchBuffer->Features = mSmiFeatures;\r
+ RETURN_STATUS Status;\r
\r
- //\r
- // Copy the scratch buffer into the boot script. When replayed, this\r
- // EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE will restore the current contents of the\r
- // scratch buffer, in-place.\r
- //\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
- (VOID*)ScratchBuffer // Buffer\r
- );\r
- if (EFI_ERROR (Status)) {\r
- DEBUG ((DEBUG_ERROR, "%a:%d: EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE: %r\n",\r
- __FUNCTION__, __LINE__, Status));\r
- goto FatalError;\r
- }\r
-\r
- //\r
- // Append an opcode that will write the address of the scratch buffer to the\r
- // fw_cfg DMA address register, which consists of two 32-bit IO ports. The\r
- // second (highest address, least significant) write will start the transfer.\r
- //\r
- AccessAddress = SwapBytes64 ((UINTN)&ScratchBuffer->Access);\r
- Status = S3SaveState->Write (\r
- S3SaveState, // This\r
- EFI_BOOT_SCRIPT_IO_WRITE_OPCODE, // OpCode\r
- EfiBootScriptWidthUint32, // Width\r
- (UINT64)0x514, // Address\r
- (UINTN)2, // Count\r
- &AccessAddress // Buffer\r
- );\r
- if (EFI_ERROR (Status)) {\r
- DEBUG ((DEBUG_ERROR, "%a:%d: EFI_BOOT_SCRIPT_IO_WRITE_OPCODE: %r\n",\r
- __FUNCTION__, __LINE__, Status));\r
- goto FatalError;\r
- }\r
+ ScratchBuffer = ExternalScratchBuffer;\r
\r
//\r
- // The EFI_BOOT_SCRIPT_MEM_POLL_OPCODE will wait until the Control word reads\r
- // as zero (transfer complete). As timeout we use MAX_UINT64 * 100ns, which\r
- // is approximately 58494 years.\r
+ // Write the negotiated feature bitmap into "etc/smi/requested-features".\r
//\r
- ControlPollData = 0;\r
- ControlPollMask = MAX_UINT32;\r
- Status = S3SaveState->Write (\r
- S3SaveState, // This\r
- EFI_BOOT_SCRIPT_MEM_POLL_OPCODE, // OpCode\r
- EfiBootScriptWidthUint32, // Width\r
- (UINT64)(UINTN)&ScratchBuffer->Access.Control, // Address\r
- &ControlPollData, // Data\r
- &ControlPollMask, // DataMask\r
- MAX_UINT64 // Delay\r
- );\r
- if (EFI_ERROR (Status)) {\r
- DEBUG ((DEBUG_ERROR, "%a:%d: EFI_BOOT_SCRIPT_MEM_POLL_OPCODE: %r\n",\r
- __FUNCTION__, __LINE__, Status));\r
+ ScratchBuffer->Features = mSmiFeatures;\r
+ Status = QemuFwCfgS3ScriptWriteBytes (mRequestedFeaturesItem,\r
+ sizeof ScratchBuffer->Features);\r
+ if (RETURN_ERROR (Status)) {\r
goto FatalError;\r
}\r
\r
//\r
- // Select the "etc/smi/features-ok" fw_cfg file, which invokes the feature\r
- // validation & lockdown. (The validation succeeded at first boot.)\r
+ // Read back "etc/smi/features-ok". This invokes the feature validation &\r
+ // lockdown. (The validation succeeded at first boot.)\r
//\r
- FeaturesOkItemAsUint16 = (UINT16)mFeaturesOkItem;\r
- Status = S3SaveState->Write (\r
- S3SaveState, // This\r
- EFI_BOOT_SCRIPT_IO_WRITE_OPCODE, // OpCode\r
- EfiBootScriptWidthUint16, // Width\r
- (UINT64)FW_CFG_IO_SELECTOR, // Address\r
- (UINTN)1, // Count\r
- &FeaturesOkItemAsUint16 // Buffer\r
- );\r
- if (EFI_ERROR (Status)) {\r
- DEBUG ((DEBUG_ERROR, "%a:%d: EFI_BOOT_SCRIPT_IO_WRITE_OPCODE: %r\n",\r
- __FUNCTION__, __LINE__, Status));\r
+ Status = QemuFwCfgS3ScriptReadBytes (mFeaturesOkItem,\r
+ sizeof ScratchBuffer->FeaturesOk);\r
+ if (RETURN_ERROR (Status)) {\r
goto FatalError;\r
}\r
\r
//\r
- // Read the contents (one byte) of "etc/smi/features-ok". If the value is\r
- // one, we're good. Otherwise, continue reading the data port: QEMU returns 0\r
- // past the end of the fw_cfg item, so this will hang the resume process,\r
- // which matches our intent.\r
+ // If "etc/smi/features-ok" read as 1, we're good. Otherwise, hang the S3\r
+ // resume process.\r
//\r
- FeaturesOkData = 1;\r
- FeaturesOkMask = MAX_UINT8;\r
- Status = S3SaveState->Write (\r
- S3SaveState, // This\r
- EFI_BOOT_SCRIPT_IO_POLL_OPCODE, // OpCode\r
- EfiBootScriptWidthUint8, // Width\r
- (UINT64)(UINTN)FW_CFG_IO_DATA, // Address\r
- &FeaturesOkData, // Data\r
- &FeaturesOkMask, // DataMask\r
- MAX_UINT64 // Delay\r
- );\r
- if (EFI_ERROR (Status)) {\r
- DEBUG ((DEBUG_ERROR, "%a:%d: EFI_BOOT_SCRIPT_IO_POLL_OPCODE: %r\n",\r
- __FUNCTION__, __LINE__, Status));\r
+ Status = QemuFwCfgS3ScriptCheckValue (&ScratchBuffer->FeaturesOk,\r
+ sizeof ScratchBuffer->FeaturesOk, MAX_UINT8, 1);\r
+ if (RETURN_ERROR (Status)) {\r
goto FatalError;\r
}\r
\r
- DEBUG ((DEBUG_VERBOSE, "%a: ScratchBuffer@%p\n", __FUNCTION__,\r
- (VOID *)ScratchBuffer));\r
+ DEBUG ((DEBUG_VERBOSE, "%a: SMI feature negotiation boot script saved\n",\r
+ __FUNCTION__));\r
return;\r
\r
FatalError:\r
ASSERT (FALSE);\r
CpuDeadLoop ();\r
}\r
+\r
+\r
+/**\r
+ Append a boot script fragment that will re-select the previously negotiated\r
+ SMI features during S3 resume.\r
+**/\r
+VOID\r
+SaveSmiFeatures (\r
+ VOID\r
+ )\r
+{\r
+ RETURN_STATUS Status;\r
+\r
+ //\r
+ // We are already running at TPL_CALLBACK, on the stack of\r
+ // OnS3SaveStateInstalled(). But that's okay, we can easily queue more\r
+ // notification functions while executing a notification function.\r
+ //\r
+ Status = QemuFwCfgS3CallWhenBootScriptReady (AppendFwCfgBootScript, NULL,\r
+ sizeof (SCRATCH_BUFFER));\r
+ if (RETURN_ERROR (Status)) {\r
+ ASSERT (FALSE);\r
+ CpuDeadLoop ();\r
+ }\r
+}\r