From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 75EE78213C for ; Fri, 17 Feb 2017 13:25:50 -0800 (PST) Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by fmsmga102.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Feb 2017 13:25:50 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.35,173,1484035200"; d="scan'208";a="1130873460" Received: from rwanders-mobl.amr.corp.intel.com (HELO localhost) ([10.252.138.193]) by fmsmga002.fm.intel.com with ESMTP; 17 Feb 2017 13:25:49 -0800 MIME-Version: 1.0 To: Laszlo Ersek , edk2-devel-01 Message-ID: <148736674932.16600.18157429547029640715@jljusten-ivb> From: Jordan Justen In-Reply-To: <20170216204137.30221-6-lersek@redhat.com> References: <20170216204137.30221-1-lersek@redhat.com> <20170216204137.30221-6-lersek@redhat.com> User-Agent: alot/0.5.1 Date: Fri, 17 Feb 2017 13:25:49 -0800 Subject: Re: [PATCH 5/5] OvmfPkg/AcpiPlatformDxe: replay QEMU_LOADER_WRITE_POINTER commands at S3 X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 17 Feb 2017 21:25:50 -0000 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable On 2017-02-16 12:41:37, Laszlo Ersek wrote: > Ultimately, each QEMU_LOADER_WRITE_POINTER command creates a guest memory > reference in some QEMU device. When the virtual machine is reset, the > device willfully forgets the guest address, since the guest memory is > wholly invalidated during platform reset. > = > ... Unless the reset is part of S3 resume. Then the guest memory is > preserved intact, and the firmware must reprogram those devices with the > original guest memory allocation addresses. > = > This patch accumulates the fw_cfg select, skip and write operations of > ProcessCmdWritePointer() in a validated / condensed form, and turns them > into an ACPI S3 Boot Script fragment at the very end of > InstallQemuFwCfgTables(). > = > Cc: Jordan Justen > Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=3D359 > Contributed-under: TianoCore Contribution Agreement 1.0 > Signed-off-by: Laszlo Ersek > --- > OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf | 2 + > OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpiPlatformDxe.inf | 2 + > OvmfPkg/AcpiPlatformDxe/AcpiPlatform.h | 27 ++ > OvmfPkg/AcpiPlatformDxe/BootScript.c | 414 +++++++++++++= +++++++ > OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c | 70 +++- > 5 files changed, 510 insertions(+), 5 deletions(-) > = > diff --git a/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf b/OvmfPkg/AcpiPl= atformDxe/AcpiPlatformDxe.inf > index 654d3a03905d..bb5f14e0fc7a 100644 > --- a/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf > +++ b/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf > @@ -31,10 +31,11 @@ [Sources] > Qemu.c > QemuFwCfgAcpi.c > Xen.c > EntryPoint.c > PciDecoding.c > + BootScript.c > = > [Packages] > MdePkg/MdePkg.dec > MdeModulePkg/MdeModulePkg.dec > OvmfPkg/OvmfPkg.dec > @@ -57,10 +58,11 @@ [LibraryClasses] > OrderedCollectionLib > = > [Protocols] > gEfiAcpiTableProtocolGuid # PROTOCOL ALWAYS_CONSUM= ED > gEfiPciIoProtocolGuid # PROTOCOL SOMETIMES_CON= SUMED > + gEfiS3SaveStateProtocolGuid # PROTOCOL SOMETIMES_CON= SUMED > = > [Guids] > gEfiXenInfoGuid > gRootBridgesConnectedEventGroupGuid > = > diff --git a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpiPlatformDxe.inf b/OvmfP= kg/AcpiPlatformDxe/QemuFwCfgAcpiPlatformDxe.inf > index d99f2d5a95c7..e550ff5a4714 100644 > --- a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpiPlatformDxe.inf > +++ b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpiPlatformDxe.inf > @@ -29,10 +29,11 @@ [Defines] > [Sources] > QemuFwCfgAcpiPlatform.c > QemuFwCfgAcpi.c > EntryPoint.c > PciDecoding.c > + BootScript.c > = > [Packages] > MdePkg/MdePkg.dec > MdeModulePkg/MdeModulePkg.dec > OvmfPkg/OvmfPkg.dec > @@ -47,10 +48,11 @@ [LibraryClasses] > UefiDriverEntryPoint > = > [Protocols] > gEfiAcpiTableProtocolGuid # PROTOCOL ALWAYS_CONSUM= ED > gEfiPciIoProtocolGuid # PROTOCOL SOMETIMES_CON= SUMED > + gEfiS3SaveStateProtocolGuid # PROTOCOL SOMETIMES_CON= SUMED > = > [Guids] > gRootBridgesConnectedEventGroupGuid > = > [Pcd] > diff --git a/OvmfPkg/AcpiPlatformDxe/AcpiPlatform.h b/OvmfPkg/AcpiPlatfor= mDxe/AcpiPlatform.h > index 08dd7f8f7dd7..0f035a0d5751 100644 > --- a/OvmfPkg/AcpiPlatformDxe/AcpiPlatform.h > +++ b/OvmfPkg/AcpiPlatformDxe/AcpiPlatform.h > @@ -31,10 +31,12 @@ > typedef struct { > EFI_PCI_IO_PROTOCOL *PciIo; > UINT64 PciAttributes; > } ORIGINAL_ATTRIBUTES; > = > +typedef struct S3_CONTEXT S3_CONTEXT; > + > EFI_STATUS > EFIAPI > InstallAcpiTable ( > IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol, > IN VOID *AcpiTableBuffer, > @@ -89,7 +91,32 @@ VOID > RestorePciDecoding ( > IN ORIGINAL_ATTRIBUTES *OriginalAttributes, > IN UINTN Count > ); > = > +EFI_STATUS > +AllocateS3Context ( > + OUT S3_CONTEXT **S3Context, > + IN UINTN WritePointerCount > + ); > + > +VOID > +ReleaseS3Context ( > + IN S3_CONTEXT *S3Context > + ); > + > +EFI_STATUS > +SaveCondensedWritePointerToS3Context ( > + IN OUT S3_CONTEXT *S3Context, > + IN UINT16 PointerItem, > + IN UINT8 PointerSize, > + IN UINT32 PointerOffset, > + IN UINT64 PointerValue > + ); > + > +EFI_STATUS > +TransferS3ContextToBootScript ( > + IN CONST S3_CONTEXT *S3Context > + ); > + > #endif > = > diff --git a/OvmfPkg/AcpiPlatformDxe/BootScript.c b/OvmfPkg/AcpiPlatformD= xe/BootScript.c > new file mode 100644 > index 000000000000..b7a7f270f223 > --- /dev/null > +++ b/OvmfPkg/AcpiPlatformDxe/BootScript.c > @@ -0,0 +1,414 @@ > +/** @file > + Append an ACPI S3 Boot Script fragment from the QEMU_LOADER_WRITE_POIN= TER > + 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 avai= lable > + under the terms and conditions of the BSD License which accompanies th= is > + 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 > +#include > +#include > + > +#include "AcpiPlatform.h" > + > + > +// > +// Condensed structure for capturing the fw_cfg operations -- select, sk= ip, > +// write -- inherent in executing a QEMU_LOADER_WRITE_POINTER command. > +// > +typedef struct { > + UINT16 PointerItem; // resolved from QEMU_LOADER_WRITE_POINTER.Point= erFile > + 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.Point= eeFile > +} 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, w= hen the > +// context structure is released, all pointed-to objects must be release= d too. > +// > +struct S3_CONTEXT { > + CONDENSED_WRITE_POINTER *WritePointers; // one array element per proce= ssed > + // QEMU_LOADER_WRITE_POINTER > + // command > + UINTN Allocated; // number of elements allocate= d for > + // WritePointers > + UINTN Used; // number of elements populate= d in > + // WritePointers > +}; > + > + > +// > +// Scratch buffer, allocated in EfiReservedMemoryType type memory, for t= he ACPI > +// S3 Boot Script opcodes to work on. We use the buffer to compose and to > +// replay several fw_cfg select+skip and write operations, using the DMA= access > +// method. The fw_cfg operations will implement the actions dictated by > +// CONDENSED_WRITE_POINTER objects. > +// > +#pragma pack (1) > +typedef struct { > + FW_CFG_DMA_ACCESS Access; // filled in from > + // CONDENSED_WRITE_POINTER.PointerIt= em, > + // CONDENSED_WRITE_POINTER.PointerSi= ze, > + // CONDENSED_WRITE_POINTER.PointerOf= fset > + UINT64 PointerValue; // filled in from > + // CONDENSED_WRITE_POINTER.PointerVa= lue > +} SCRATCH_BUFFER; > +#pragma pack () > + > + > +/** > + Allocate an S3_CONTEXT object. > + > + @param[out] S3Context The allocated S3_CONTEXT object is retur= ned > + through this parameter. > + > + @param[in] WritePointerCount Number of CONDENSED_WRITE_POINTER elemen= ts to > + allocate room for. WritePointerCount mus= t 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 =3D=3D 0) { > + return EFI_INVALID_PARAMETER; > + } > + > + Context =3D AllocateZeroPool (sizeof *Context); > + if (Context =3D=3D NULL) { > + return EFI_OUT_OF_RESOURCES; > + } > + > + Context->WritePointers =3D AllocatePool (WritePointerCount * > + sizeof *Context->WritePointers); > + if (Context->WritePointers =3D=3D NULL) { > + Status =3D EFI_OUT_OF_RESOURCES; > + goto FreeContext; > + } > + > + Context->Allocated =3D WritePointerCount; > + *S3Context =3D 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 perfor= med. > + > + @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 re= solved > + 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 / download= ed > + fw_cfg blob that is identified by > + QEMU_LOADER_WRITE_POINTER.PointeeFile. > + > + @retval EFI_SUCCESS The information derived from > + QEMU_LOADER_WRITE_POINTER has been succe= ssfully > + 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 =3D=3D S3Context->Allocated) { > + return EFI_OUT_OF_RESOURCES; > + } > + Condensed =3D S3Context->WritePointers + S3Context->Used; > + Condensed->PointerItem =3D PointerItem; > + Condensed->PointerSize =3D PointerSize; > + Condensed->PointerOffset =3D PointerOffset; > + Condensed->PointerValue =3D PointerValue; > + DEBUG ((DEBUG_VERBOSE, "%a: 0x%04x/[0x%08x+%d] :=3D 0x%Lx (%Lu)\n", > + __FUNCTION__, PointerItem, PointerOffset, PointerSize, PointerValue, > + (UINT64)S3Context->Used)); > + ++S3Context->Used; > + return EFI_SUCCESS; > +} > + > + > +/** > + 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 Bo= ot > + Script opcodes. > + > + @retval EFI_OUT_OF_RESOURCES Out of memory. > + > + @retval EFI_SUCCESS The translation of S3Context to ACPI S3 = Boot > + Script opcodes has been successful. > + > + @return Error codes from underlying functions. > +**/ > +EFI_STATUS > +TransferS3ContextToBootScript ( > + IN CONST S3_CONTEXT *S3Context > + ) > +{ > + EFI_STATUS Status; > + EFI_S3_SAVE_STATE_PROTOCOL *S3SaveState; > + SCRATCH_BUFFER *ScratchBuffer; > + FW_CFG_DMA_ACCESS *Access; > + UINT64 BigEndianAddressOfAccess; > + UINT32 ControlPollData; > + UINT32 ControlPollMask; > + UINTN Index; > + > + // > + // If the following protocol lookup fails, it shall not happen due to = an > + // unexpected DXE driver dispatch order. > + // > + // Namely, this function is only invoked on QEMU. Therefore it is only > + // reached after Platform BDS signals gRootBridgesConnectedEventGroupG= uid > + // (see OnRootBridgesConnected() in "EntryPoint.c"). Hence, because > + // TransferS3ContextToBootScript() is invoked in BDS, all DXE drivers, > + // including S3SaveStateDxe (producing EFI_S3_SAVE_STATE_PROTOCOL), ha= ve been > + // dispatched by the time we get here. (S3SaveStateDxe is not expected= to > + // have any stricter-than-TRUE DEPEX -- not a DEPEX that gets unblocke= d only > + // within BDS anyway.) > + // > + // Reaching this function also depends on QemuFwCfgS3Enabled(). That i= mplies > + // S3SaveStateDxe has not exited immediately due to S3 being disabled.= Thus > + // EFI_S3_SAVE_STATE_PROTOCOL can only be missing for genuinely unfore= seeable > + // reasons. > + // > + Status =3D gBS->LocateProtocol (&gEfiS3SaveStateProtocolGuid, > + NULL /* Registration */, (VOID **)&S3SaveState); > + if (EFI_ERROR (Status)) { > + DEBUG ((DEBUG_ERROR, "%a: LocateProtocol(): %r\n", __FUNCTION__, Sta= tus)); > + return Status; > + } > + > + ScratchBuffer =3D AllocateReservedPool (sizeof *ScratchBuffer); > + if (ScratchBuffer =3D=3D NULL) { > + return EFI_OUT_OF_RESOURCES; > + } > + > + // > + // Set up helper variables that we'll use identically for all > + // CONDENSED_WRITE_POINTER elements. > + // > + Access =3D &ScratchBuffer->Access; > + BigEndianAddressOfAccess =3D SwapBytes64 ((UINTN)Access); > + ControlPollData =3D 0; > + ControlPollMask =3D MAX_UINT32; > + > + // > + // For each CONDENSED_WRITE_POINTER, we need six ACPI S3 Boot Script o= pcodes: > + // (1) restore an FW_CFG_DMA_ACCESS object in reserved memory that sel= ects > + // the writeable fw_cfg file PointerFile (through PointerItem), an= d skips > + // to PointerOffset in it, > + // (2) call QEMU with the FW_CFG_DMA_ACCESS object, > + // (3) wait for the select+skip to finish, > + // (4) restore a SCRATCH_BUFFER object in reserved memory that writes > + // PointerValue (base address of the allocated / downloaded Pointe= eFile), > + // of size PointerSize, into the fw_cfg file selected in (1), at t= he > + // offset sought to in (1), > + // (5) call QEMU with the FW_CFG_DMA_ACCESS object, > + // (6) wait for the write to finish. > + // > + // EFI_S3_SAVE_STATE_PROTOCOL does not allow rolling back opcode addit= ions, > + // therefore we treat any failure here as fatal. > + // > + for (Index =3D 0; Index < S3Context->Used; ++Index) { > + CONST CONDENSED_WRITE_POINTER *Condensed; > + > + Condensed =3D &S3Context->WritePointers[Index]; > + > + // > + // (1) restore an FW_CFG_DMA_ACCESS object in reserved memory that s= elects > + // the writeable fw_cfg file PointerFile (through PointerItem), = and > + // skips to PointerOffset in it, > + // > + Access->Control =3D SwapBytes32 ((UINT32)Condensed->PointerItem << 1= 6 | > + FW_CFG_DMA_CTL_SELECT | FW_CFG_DMA_CTL_SKIP); > + Access->Length =3D SwapBytes32 (Condensed->PointerOffset); > + Access->Address =3D 0; > + Status =3D S3SaveState->Write ( > + S3SaveState, // This > + EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE, // OpCode > + EfiBootScriptWidthUint8, // Width > + (UINT64)(UINTN)Access, // Address > + sizeof *Access, // Count > + Access // Buffer > + ); > + if (EFI_ERROR (Status)) { > + DEBUG ((DEBUG_ERROR, "%a: Index %Lu opcode 1: %r\n", __FUNCTION__, > + (UINT64)Index, Status)); > + goto FatalError; > + } > + > + // > + // (2) call QEMU with the FW_CFG_DMA_ACCESS object, > + // > + Status =3D S3SaveState->Write ( > + S3SaveState, // This > + EFI_BOOT_SCRIPT_IO_WRITE_OPCODE, // OpCode > + EfiBootScriptWidthUint32, // Width > + (UINT64)0x514, // Address It's unfortunate that the boot script makes us add fw-cfg low level details into a module besides QemuFwCfgLib. We probably should add OvmfPkg/Include/IndustryStandard/QemuFwCfg.h to define the IA32/X64 I/O ports used by fw-cfg. Maybe move some other items from QemuFwCfgLib.h too. This is using the DMA access, right? Does something prevent adding this boot script entry when the DMA interface is not supported? -Jordan > + (UINTN)2, // Count > + &BigEndianAddressOfAccess // Buffer > + ); > + if (EFI_ERROR (Status)) { > + DEBUG ((DEBUG_ERROR, "%a: Index %Lu opcode 2: %r\n", __FUNCTION__, > + (UINT64)Index, Status)); > + goto FatalError; > + } > + > + // > + // (3) wait for the select+skip to finish, > + // > + Status =3D S3SaveState->Write ( > + S3SaveState, // This > + EFI_BOOT_SCRIPT_MEM_POLL_OPCODE, // OpCode > + EfiBootScriptWidthUint32, // Width > + (UINT64)(UINTN)&Access->Control, // Address > + &ControlPollData, // Data > + &ControlPollMask, // DataMask > + MAX_UINT64 // Delay > + ); > + if (EFI_ERROR (Status)) { > + DEBUG ((DEBUG_ERROR, "%a: Index %Lu opcode 3: %r\n", __FUNCTION__, > + (UINT64)Index, Status)); > + goto FatalError; > + } > + > + // > + // (4) restore a SCRATCH_BUFFER object in reserved memory that writes > + // PointerValue (base address of the allocated / downloaded > + // PointeeFile), of size PointerSize, into the fw_cfg file selec= ted in > + // (1), at the offset sought to in (1), > + // > + Access->Control =3D SwapBytes32 (FW_CFG_DMA_CTL_WRITE); > + Access->Length =3D SwapBytes32 (Condensed->PointerSize); > + Access->Address =3D SwapBytes64 ((UINTN)&ScratchBuffer->PointerValue= ); > + ScratchBuffer->PointerValue =3D Condensed->PointerValue; > + Status =3D S3SaveState->Write ( > + S3SaveState, // This > + EFI_BOOT_SCRIPT_MEM_WRITE_OPCODE, // OpCode > + EfiBootScriptWidthUint8, // Width > + (UINT64)(UINTN)ScratchBuffer, // Address > + sizeof *ScratchBuffer, // Count > + ScratchBuffer // Buffer > + ); > + if (EFI_ERROR (Status)) { > + DEBUG ((DEBUG_ERROR, "%a: Index %Lu opcode 4: %r\n", __FUNCTION__, > + (UINT64)Index, Status)); > + goto FatalError; > + } > + > + // > + // (5) call QEMU with the FW_CFG_DMA_ACCESS object, > + // > + Status =3D S3SaveState->Write ( > + S3SaveState, // This > + EFI_BOOT_SCRIPT_IO_WRITE_OPCODE, // OpCode > + EfiBootScriptWidthUint32, // Width > + (UINT64)0x514, // Address > + (UINTN)2, // Count > + &BigEndianAddressOfAccess // Buffer > + ); > + if (EFI_ERROR (Status)) { > + DEBUG ((DEBUG_ERROR, "%a: Index %Lu opcode 5: %r\n", __FUNCTION__, > + (UINT64)Index, Status)); > + goto FatalError; > + } > + > + // > + // (6) wait for the write to finish. > + // > + Status =3D S3SaveState->Write ( > + S3SaveState, // This > + EFI_BOOT_SCRIPT_MEM_POLL_OPCODE, // OpCode > + EfiBootScriptWidthUint32, // Width > + (UINT64)(UINTN)&Access->Control, // Address > + &ControlPollData, // Data > + &ControlPollMask, // DataMask > + MAX_UINT64 // Delay > + ); > + if (EFI_ERROR (Status)) { > + DEBUG ((DEBUG_ERROR, "%a: Index %Lu opcode 6: %r\n", __FUNCTION__, > + (UINT64)Index, Status)); > + goto FatalError; > + } > + } > + > + DEBUG ((DEBUG_VERBOSE, "%a: boot script fragment saved, ScratchBuffer= =3D%p\n", > + __FUNCTION__, (VOID *)ScratchBuffer)); > + return EFI_SUCCESS; > + > +FatalError: > + ASSERT (FALSE); > + CpuDeadLoop (); > + return Status; > +} > diff --git a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c b/OvmfPkg/AcpiPlatfo= rmDxe/QemuFwCfgAcpi.c > index de827c2df204..eadd690bef4e 100644 > --- a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c > +++ b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c > @@ -358,26 +358,39 @@ ProcessCmdAddChecksum ( > @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to pro= cess. > = > @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user > structures created thus far. > = > + @param[in,out] S3Context The S3_CONTEXT object capturing the fw_cfg a= ctions > + of successfully processed QEMU_LOADER_WRITE_= POINTER > + commands, to be replayed at S3 resume. S3Con= text > + may be NULL if S3 is disabled. > + > @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been fo= und in > WritePointer. Or, the WritePointer command > references a file unknown to Tracker or the > fw_cfg directory. Or, the pointer object to > rewrite has invalid location, size, or ini= tial > relative value. Or, the pointer value to s= tore > does not fit in the given pointer size. > = > @retval EFI_SUCCESS The pointer object inside the writeable fw= _cfg > - file has been written. > + file has been written. If S3Context is not= NULL, > + then WritePointer has been condensed into > + S3Context. > + > + @return Error codes propagated from > + SaveCondensedWritePointerToS3Context(). The > + pointer object inside the writeable fw_cfg= file > + has not been written. > **/ > STATIC > EFI_STATUS > ProcessCmdWritePointer ( > IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer, > - IN CONST ORDERED_COLLECTION *Tracker > + IN CONST ORDERED_COLLECTION *Tracker, > + IN OUT S3_CONTEXT *S3Context OPTIONAL > ) > { > RETURN_STATUS Status; > FIRMWARE_CONFIG_ITEM PointerItem; > UINTN PointerItemSize; > @@ -430,10 +443,29 @@ ProcessCmdWritePointer ( > DEBUG ((DEBUG_ERROR, "%a: pointer value unrepresentable in \"%a\"\n", > __FUNCTION__, WritePointer->PointerFile)); > return EFI_PROTOCOL_ERROR; > } > = > + // > + // If S3 is enabled, we have to capture the below fw_cfg actions in co= ndensed > + // form, to be replayed during S3 resume. > + // > + if (S3Context !=3D NULL) { > + EFI_STATUS SaveStatus; > + > + SaveStatus =3D SaveCondensedWritePointerToS3Context ( > + S3Context, > + (UINT16)PointerItem, > + WritePointer->PointerSize, > + WritePointer->PointerOffset, > + PointerValue > + ); > + if (EFI_ERROR (SaveStatus)) { > + return SaveStatus; > + } > + } > + > QemuFwCfgSelectItem (PointerItem); > QemuFwCfgSkipBytes (WritePointer->PointerOffset); > QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue); > = > // > @@ -699,10 +731,11 @@ InstallQemuFwCfgTables ( > QEMU_LOADER_ENTRY *LoaderStart; > CONST QEMU_LOADER_ENTRY *LoaderEntry, *LoaderEnd; > CONST QEMU_LOADER_ENTRY *WritePointerSubsetEnd; > ORIGINAL_ATTRIBUTES *OriginalPciAttributes; > UINTN OriginalPciAttributesCount; > + S3_CONTEXT *S3Context; > ORDERED_COLLECTION *Tracker; > UINTN *InstalledKey; > INT32 Installed; > ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2; > = > @@ -724,14 +757,26 @@ InstallQemuFwCfgTables ( > QemuFwCfgSelectItem (FwCfgItem); > QemuFwCfgReadBytes (FwCfgSize, LoaderStart); > RestorePciDecoding (OriginalPciAttributes, OriginalPciAttributesCount); > LoaderEnd =3D LoaderStart + FwCfgSize / sizeof *LoaderEntry; > = > + S3Context =3D NULL; > + if (QemuFwCfgS3Enabled ()) { > + // > + // Size the allocation pessimistically, assuming that all commands i= n the > + // script are QEMU_LOADER_WRITE_POINTER commands. > + // > + Status =3D AllocateS3Context (&S3Context, LoaderEnd - LoaderStart); > + if (EFI_ERROR (Status)) { > + goto FreeLoader; > + } > + } > + > Tracker =3D OrderedCollectionInit (BlobCompare, BlobKeyCompare); > if (Tracker =3D=3D NULL) { > Status =3D EFI_OUT_OF_RESOURCES; > - goto FreeLoader; > + goto FreeS3Context; > } > = > // > // first pass: process the commands > // > @@ -756,11 +801,11 @@ InstallQemuFwCfgTables ( > Tracker); > break; > = > case QemuLoaderCmdWritePointer: > Status =3D ProcessCmdWritePointer (&LoaderEntry->Command.WritePo= inter, > - Tracker); > + Tracker, S3Context); > if (!EFI_ERROR (Status)) { > WritePointerSubsetEnd =3D LoaderEntry + 1; > } > break; > = > @@ -788,15 +833,25 @@ InstallQemuFwCfgTables ( > for (LoaderEntry =3D LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEnt= ry) { > if (LoaderEntry->Type =3D=3D QemuLoaderCmdAddPointer) { > Status =3D Process2ndPassCmdAddPointer (&LoaderEntry->Command.AddP= ointer, > Tracker, AcpiProtocol, InstalledKey, &Installed); > if (EFI_ERROR (Status)) { > - break; > + goto UninstallAcpiTables; > } > } > } > = > + // > + // Translating the condensed QEMU_LOADER_WRITE_POINTER commands to ACP= I S3 > + // Boot Script opcodes has to be the last operation in this function, = because > + // if it succeeds, it cannot be undone. > + // > + if (S3Context !=3D NULL) { > + Status =3D TransferS3ContextToBootScript (S3Context); > + } > + > +UninstallAcpiTables: > if (EFI_ERROR (Status)) { > // > // roll back partial installation > // > while (Installed > 0) { > @@ -845,10 +900,15 @@ RollbackWritePointersAndFreeTracker: > } > FreePool (Blob); > } > OrderedCollectionUninit (Tracker); > = > +FreeS3Context: > + if (S3Context !=3D NULL) { > + ReleaseS3Context (S3Context); > + } > + > FreeLoader: > FreePool (LoaderStart); > = > return Status; > } > -- = > 2.9.3 >=20