X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;ds=sidebyside;f=OvmfPkg%2FAcpiPlatformDxe%2FQemuFwCfgAcpi.c;h=bc1a891dbaf16aa801589de504b37faf47fbd6f7;hb=591b8cb7f3d026d2fa4483c59f3d5fb14be181bf;hp=70df2842642284daef16e736d68f3a5fc10fe761;hpb=14b0faadfc87be12398964015ee0884d71bd0e7b;p=mirror_edk2.git
diff --git a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
index 70df284264..bc1a891dba 100644
--- a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
+++ b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
@@ -4,13 +4,7 @@
Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.
Copyright (C) 2012-2014, 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.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
**/
@@ -19,6 +13,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -98,13 +93,205 @@ BlobCompare (
}
+/**
+ Comparator function for two opaque pointers, ordering on (unsigned) pointer
+ value itself.
+ Can be used as both Key and UserStruct comparator.
+
+ @param[in] Pointer1 First pointer.
+
+ @param[in] Pointer2 Second pointer.
+
+ @retval <0 If Pointer1 compares less than Pointer2.
+
+ @retval 0 If Pointer1 compares equal to Pointer2.
+
+ @retval >0 If Pointer1 compares greater than Pointer2.
+**/
+STATIC
+INTN
+EFIAPI
+PointerCompare (
+ IN CONST VOID *Pointer1,
+ IN CONST VOID *Pointer2
+ )
+{
+ if (Pointer1 == Pointer2) {
+ return 0;
+ }
+ if ((UINTN)Pointer1 < (UINTN)Pointer2) {
+ return -1;
+ }
+ return 1;
+}
+
+
+/**
+ Comparator function for two ASCII strings. Can be used as both Key and
+ UserStruct comparator.
+
+ This function exists solely so we can avoid casting &AsciiStrCmp to
+ ORDERED_COLLECTION_USER_COMPARE and ORDERED_COLLECTION_KEY_COMPARE.
+
+ @param[in] AsciiString1 Pointer to the first ASCII string.
+
+ @param[in] AsciiString2 Pointer to the second ASCII string.
+
+ @return The return value of AsciiStrCmp (AsciiString1, AsciiString2).
+**/
+STATIC
+INTN
+EFIAPI
+AsciiStringCompare (
+ IN CONST VOID *AsciiString1,
+ IN CONST VOID *AsciiString2
+ )
+{
+ return AsciiStrCmp (AsciiString1, AsciiString2);
+}
+
+
+/**
+ Release the ORDERED_COLLECTION structure populated by
+ CollectAllocationsRestrictedTo32Bit() (below).
+
+ This function may be called by CollectAllocationsRestrictedTo32Bit() itself,
+ on the error path.
+
+ @param[in] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION structure to
+ release.
+**/
+STATIC
+VOID
+ReleaseAllocationsRestrictedTo32Bit (
+ IN ORDERED_COLLECTION *AllocationsRestrictedTo32Bit
+)
+{
+ ORDERED_COLLECTION_ENTRY *Entry, *Entry2;
+
+ for (Entry = OrderedCollectionMin (AllocationsRestrictedTo32Bit);
+ Entry != NULL;
+ Entry = Entry2) {
+ Entry2 = OrderedCollectionNext (Entry);
+ OrderedCollectionDelete (AllocationsRestrictedTo32Bit, Entry, NULL);
+ }
+ OrderedCollectionUninit (AllocationsRestrictedTo32Bit);
+}
+
+
+/**
+ Iterate over the linker/loader script, and collect the names of the fw_cfg
+ blobs that are referenced by QEMU_LOADER_ADD_POINTER.PointeeFile fields, such
+ that QEMU_LOADER_ADD_POINTER.PointerSize is less than 8. This means that the
+ pointee blob's address will have to be patched into a narrower-than-8 byte
+ pointer field, hence the pointee blob must not be allocated from 64-bit
+ address space.
+
+ @param[out] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION structure
+ linking (not copying / owning) such
+ QEMU_LOADER_ADD_POINTER.PointeeFile
+ fields that name the blobs
+ restricted from 64-bit allocation.
+
+ @param[in] LoaderStart Points to the first entry in the
+ linker/loader script.
+
+ @param[in] LoaderEnd Points one past the last entry in
+ the linker/loader script.
+
+ @retval EFI_SUCCESS AllocationsRestrictedTo32Bit has been
+ populated.
+
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
+
+ @retval EFI_PROTOCOL_ERROR Invalid linker/loader script contents.
+**/
+STATIC
+EFI_STATUS
+CollectAllocationsRestrictedTo32Bit (
+ OUT ORDERED_COLLECTION **AllocationsRestrictedTo32Bit,
+ IN CONST QEMU_LOADER_ENTRY *LoaderStart,
+ IN CONST QEMU_LOADER_ENTRY *LoaderEnd
+)
+{
+ ORDERED_COLLECTION *Collection;
+ CONST QEMU_LOADER_ENTRY *LoaderEntry;
+ EFI_STATUS Status;
+
+ Collection = OrderedCollectionInit (AsciiStringCompare, AsciiStringCompare);
+ if (Collection == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
+ CONST QEMU_LOADER_ADD_POINTER *AddPointer;
+
+ if (LoaderEntry->Type != QemuLoaderCmdAddPointer) {
+ continue;
+ }
+ AddPointer = &LoaderEntry->Command.AddPointer;
+
+ if (AddPointer->PointerSize >= 8) {
+ continue;
+ }
+
+ if (AddPointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {
+ DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));
+ Status = EFI_PROTOCOL_ERROR;
+ goto RollBack;
+ }
+
+ Status = OrderedCollectionInsert (
+ Collection,
+ NULL, // Entry
+ (VOID *)AddPointer->PointeeFile
+ );
+ switch (Status) {
+ case EFI_SUCCESS:
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: restricting blob \"%a\" from 64-bit allocation\n",
+ __FUNCTION__,
+ AddPointer->PointeeFile
+ ));
+ break;
+ case EFI_ALREADY_STARTED:
+ //
+ // The restriction has been recorded already.
+ //
+ break;
+ case EFI_OUT_OF_RESOURCES:
+ goto RollBack;
+ default:
+ ASSERT (FALSE);
+ }
+ }
+
+ *AllocationsRestrictedTo32Bit = Collection;
+ return EFI_SUCCESS;
+
+RollBack:
+ ReleaseAllocationsRestrictedTo32Bit (Collection);
+ return Status;
+}
+
+
/**
Process a QEMU_LOADER_ALLOCATE command.
- @param[in] Allocate The QEMU_LOADER_ALLOCATE command to process.
+ @param[in] Allocate The QEMU_LOADER_ALLOCATE command to
+ process.
- @param[in,out] Tracker The ORDERED_COLLECTION tracking the BLOB user
- structures created thus far.
+ @param[in,out] Tracker The ORDERED_COLLECTION tracking the
+ BLOB user structures created thus
+ far.
+
+ @param[in] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION populated by
+ the function
+ CollectAllocationsRestrictedTo32Bit,
+ naming the fw_cfg blobs that must
+ not be allocated from 64-bit address
+ space.
@retval EFI_SUCCESS An area of whole AcpiNVS pages has been
allocated for the blob contents, and the
@@ -130,7 +317,8 @@ EFI_STATUS
EFIAPI
ProcessCmdAllocate (
IN CONST QEMU_LOADER_ALLOCATE *Allocate,
- IN OUT ORDERED_COLLECTION *Tracker
+ IN OUT ORDERED_COLLECTION *Tracker,
+ IN ORDERED_COLLECTION *AllocationsRestrictedTo32Bit
)
{
FIRMWARE_CONFIG_ITEM FwCfgItem;
@@ -159,7 +347,13 @@ ProcessCmdAllocate (
}
NumPages = EFI_SIZE_TO_PAGES (FwCfgSize);
- Address = 0xFFFFFFFF;
+ Address = MAX_UINT64;
+ if (OrderedCollectionFind (
+ AllocationsRestrictedTo32Bit,
+ Allocate->File
+ ) != NULL) {
+ Address = MAX_UINT32;
+ }
Status = gBS->AllocatePages (AllocateMaxAddress, EfiACPIMemoryNVS, NumPages,
&Address);
if (EFI_ERROR (Status)) {
@@ -277,8 +471,8 @@ ProcessCmdAddPointer (
ASSERT ((UINTN)Blob2->Base <= MAX_ADDRESS - Blob2->Size);
PointerValue += (UINT64)(UINTN)Blob2->Base;
- if (RShiftU64 (
- RShiftU64 (PointerValue, AddPointer->PointerSize * 8 - 1), 1) != 0) {
+ if (AddPointer->PointerSize < 8 &&
+ RShiftU64 (PointerValue, AddPointer->PointerSize * 8) != 0) {
DEBUG ((EFI_D_ERROR, "%a: relocated pointer value unrepresentable in "
"\"%a\"\n", __FUNCTION__, AddPointer->PointerFile));
return EFI_PROTOCOL_ERROR;
@@ -352,6 +546,175 @@ ProcessCmdAddChecksum (
}
+/**
+ Process a QEMU_LOADER_WRITE_POINTER command.
+
+ @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to process.
+
+ @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 actions
+ of successfully processed QEMU_LOADER_WRITE_POINTER
+ commands, to be replayed at S3 resume. S3Context
+ may be NULL if S3 is disabled.
+
+ @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found 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 initial
+ relative value. Or, the pointer value to store
+ does not fit in the given pointer size.
+
+ @retval EFI_SUCCESS The pointer object inside the writeable fw_cfg
+ 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 OUT S3_CONTEXT *S3Context OPTIONAL
+ )
+{
+ RETURN_STATUS Status;
+ FIRMWARE_CONFIG_ITEM PointerItem;
+ UINTN PointerItemSize;
+ ORDERED_COLLECTION_ENTRY *PointeeEntry;
+ BLOB *PointeeBlob;
+ UINT64 PointerValue;
+
+ if (WritePointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0' ||
+ WritePointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {
+ DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ Status = QemuFwCfgFindFile ((CONST CHAR8 *)WritePointer->PointerFile,
+ &PointerItem, &PointerItemSize);
+ PointeeEntry = OrderedCollectionFind (Tracker, WritePointer->PointeeFile);
+ if (RETURN_ERROR (Status) || PointeeEntry == NULL) {
+ DEBUG ((DEBUG_ERROR,
+ "%a: invalid fw_cfg file or blob reference \"%a\" / \"%a\"\n",
+ __FUNCTION__, WritePointer->PointerFile, WritePointer->PointeeFile));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ if ((WritePointer->PointerSize != 1 && WritePointer->PointerSize != 2 &&
+ WritePointer->PointerSize != 4 && WritePointer->PointerSize != 8) ||
+ (PointerItemSize < WritePointer->PointerSize) ||
+ (PointerItemSize - WritePointer->PointerSize <
+ WritePointer->PointerOffset)) {
+ DEBUG ((DEBUG_ERROR, "%a: invalid pointer location or size in \"%a\"\n",
+ __FUNCTION__, WritePointer->PointerFile));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ PointeeBlob = OrderedCollectionUserStruct (PointeeEntry);
+ PointerValue = WritePointer->PointeeOffset;
+ if (PointerValue >= PointeeBlob->Size) {
+ DEBUG ((DEBUG_ERROR, "%a: invalid PointeeOffset\n", __FUNCTION__));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ //
+ // The memory allocation system ensures that the address of the byte past the
+ // last byte of any allocated object is expressible (no wraparound).
+ //
+ ASSERT ((UINTN)PointeeBlob->Base <= MAX_ADDRESS - PointeeBlob->Size);
+
+ PointerValue += (UINT64)(UINTN)PointeeBlob->Base;
+ if (WritePointer->PointerSize < 8 &&
+ RShiftU64 (PointerValue, WritePointer->PointerSize * 8) != 0) {
+ 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 condensed
+ // form, to be replayed during S3 resume.
+ //
+ if (S3Context != NULL) {
+ EFI_STATUS SaveStatus;
+
+ SaveStatus = SaveCondensedWritePointerToS3Context (
+ S3Context,
+ (UINT16)PointerItem,
+ WritePointer->PointerSize,
+ WritePointer->PointerOffset,
+ PointerValue
+ );
+ if (EFI_ERROR (SaveStatus)) {
+ return SaveStatus;
+ }
+ }
+
+ QemuFwCfgSelectItem (PointerItem);
+ QemuFwCfgSkipBytes (WritePointer->PointerOffset);
+ QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);
+
+ //
+ // Because QEMU has now learned PointeeBlob->Base, we must mark PointeeBlob
+ // as unreleasable, for the case when the whole linker/loader script is
+ // handled successfully.
+ //
+ PointeeBlob->HostsOnlyTableData = FALSE;
+
+ DEBUG ((DEBUG_VERBOSE, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
+ "PointerOffset=0x%x PointeeOffset=0x%x PointerSize=%d\n", __FUNCTION__,
+ WritePointer->PointerFile, WritePointer->PointeeFile,
+ WritePointer->PointerOffset, WritePointer->PointeeOffset,
+ WritePointer->PointerSize));
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Undo a QEMU_LOADER_WRITE_POINTER command.
+
+ This function revokes (zeroes out) a guest memory reference communicated to
+ QEMU earlier. The caller is responsible for invoking this function only on
+ such QEMU_LOADER_WRITE_POINTER commands that have been successfully processed
+ by ProcessCmdWritePointer().
+
+ @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to undo.
+**/
+STATIC
+VOID
+UndoCmdWritePointer (
+ IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer
+ )
+{
+ RETURN_STATUS Status;
+ FIRMWARE_CONFIG_ITEM PointerItem;
+ UINTN PointerItemSize;
+ UINT64 PointerValue;
+
+ Status = QemuFwCfgFindFile ((CONST CHAR8 *)WritePointer->PointerFile,
+ &PointerItem, &PointerItemSize);
+ ASSERT_RETURN_ERROR (Status);
+
+ PointerValue = 0;
+ QemuFwCfgSelectItem (PointerItem);
+ QemuFwCfgSkipBytes (WritePointer->PointerOffset);
+ QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);
+
+ DEBUG ((DEBUG_VERBOSE,
+ "%a: PointerFile=\"%a\" PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__,
+ WritePointer->PointerFile, WritePointer->PointerOffset,
+ WritePointer->PointerSize));
+}
+
+
//
// We'll be saving the keys of installed tables so that we can roll them back
// in case of failure. 128 tables should be enough for anyone (TM).
@@ -363,7 +726,7 @@ ProcessCmdAddChecksum (
array is an ACPI table, and if so, install it.
This function assumes that the entire QEMU linker/loader command file has
- been processed successfuly in a prior first pass.
+ been processed successfully in a prior first pass.
@param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
@@ -387,6 +750,16 @@ ProcessCmdAddChecksum (
command identified an ACPI table that is
different from RSDT and XSDT.
+ @param[in,out] SeenPointers The ORDERED_COLLECTION tracking the absolute
+ target addresses that have been pointed-to by
+ QEMU_LOADER_ADD_POINTER commands thus far. If a
+ target address is encountered for the first
+ time, and it identifies an ACPI table that is
+ different from RDST and XSDT, the table is
+ installed. If a target address is seen for the
+ second or later times, it is skipped without
+ taking any action.
+
@retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on
input.
@@ -394,14 +767,15 @@ ProcessCmdAddChecksum (
table different from RSDT and XSDT, but there
was no more room in InstalledKey.
- @retval EFI_SUCCESS AddPointer has been processed. Either an ACPI
- table different from RSDT and XSDT has been
- installed (reflected by InstalledKey and
- NumInstalled), or RSDT or XSDT has been
- identified but not installed, or the fw_cfg
- blob pointed-into by AddPointer has been
- marked as hosting something else than just
- direct ACPI table contents.
+ @retval EFI_SUCCESS AddPointer has been processed. Either its
+ absolute target address has been encountered
+ before, or an ACPI table different from RSDT
+ and XSDT has been installed (reflected by
+ InstalledKey and NumInstalled), or RSDT or
+ XSDT has been identified but not installed, or
+ the fw_cfg blob pointed-into by AddPointer has
+ been marked as hosting something else than
+ just direct ACPI table contents.
@return Error codes returned by
AcpiProtocol->InstallAcpiTable().
@@ -414,11 +788,13 @@ Process2ndPassCmdAddPointer (
IN CONST ORDERED_COLLECTION *Tracker,
IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,
IN OUT UINTN InstalledKey[INSTALLED_TABLES_MAX],
- IN OUT INT32 *NumInstalled
+ IN OUT INT32 *NumInstalled,
+ IN OUT ORDERED_COLLECTION *SeenPointers
)
{
CONST ORDERED_COLLECTION_ENTRY *TrackerEntry;
CONST ORDERED_COLLECTION_ENTRY *TrackerEntry2;
+ ORDERED_COLLECTION_ENTRY *SeenPointerEntry;
CONST BLOB *Blob;
BLOB *Blob2;
CONST UINT8 *PointerField;
@@ -450,6 +826,27 @@ Process2ndPassCmdAddPointer (
Blob2Remaining += Blob2->Size;
ASSERT (PointerValue < Blob2Remaining);
+ Status = OrderedCollectionInsert (
+ SeenPointers,
+ &SeenPointerEntry, // for reverting insertion in error case
+ (VOID *)(UINTN)PointerValue
+ );
+ if (EFI_ERROR (Status)) {
+ if (Status == RETURN_ALREADY_STARTED) {
+ //
+ // Already seen this pointer, don't try to process it again.
+ //
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: PointerValue=0x%Lx already processed, skipping.\n",
+ __FUNCTION__,
+ PointerValue
+ ));
+ Status = EFI_SUCCESS;
+ }
+ return Status;
+ }
+
Blob2Remaining -= (UINTN) PointerValue;
DEBUG ((EFI_D_VERBOSE, "%a: checking for ACPI header in \"%a\" at 0x%Lx "
"(remaining: 0x%Lx): ", __FUNCTION__, AddPointer->PointeeFile,
@@ -512,7 +909,8 @@ Process2ndPassCmdAddPointer (
if (*NumInstalled == INSTALLED_TABLES_MAX) {
DEBUG ((EFI_D_ERROR, "%a: can't install more than %d tables\n",
__FUNCTION__, INSTALLED_TABLES_MAX));
- return EFI_OUT_OF_RESOURCES;
+ Status = EFI_OUT_OF_RESOURCES;
+ goto RollbackSeenPointer;
}
Status = AcpiProtocol->InstallAcpiTable (AcpiProtocol,
@@ -521,10 +919,14 @@ Process2ndPassCmdAddPointer (
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "%a: InstallAcpiTable(): %r\n", __FUNCTION__,
Status));
- return Status;
+ goto RollbackSeenPointer;
}
++*NumInstalled;
return EFI_SUCCESS;
+
+RollbackSeenPointer:
+ OrderedCollectionDelete (SeenPointers, SeenPointerEntry, NULL);
+ return Status;
}
@@ -552,7 +954,7 @@ Process2ndPassCmdAddPointer (
**/
EFI_STATUS
EFIAPI
-InstallAllQemuLinkedTables (
+InstallQemuFwCfgTables (
IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol
)
{
@@ -561,10 +963,17 @@ InstallAllQemuLinkedTables (
UINTN FwCfgSize;
QEMU_LOADER_ENTRY *LoaderStart;
CONST QEMU_LOADER_ENTRY *LoaderEntry, *LoaderEnd;
+ CONST QEMU_LOADER_ENTRY *WritePointerSubsetEnd;
+ ORIGINAL_ATTRIBUTES *OriginalPciAttributes;
+ UINTN OriginalPciAttributesCount;
+ ORDERED_COLLECTION *AllocationsRestrictedTo32Bit;
+ S3_CONTEXT *S3Context;
ORDERED_COLLECTION *Tracker;
UINTN *InstalledKey;
INT32 Installed;
ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;
+ ORDERED_COLLECTION *SeenPointers;
+ ORDERED_COLLECTION_ENTRY *SeenPointerEntry, *SeenPointerEntry2;
Status = QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem, &FwCfgSize);
if (EFI_ERROR (Status)) {
@@ -580,23 +989,56 @@ InstallAllQemuLinkedTables (
if (LoaderStart == NULL) {
return EFI_OUT_OF_RESOURCES;
}
+ EnablePciDecoding (&OriginalPciAttributes, &OriginalPciAttributesCount);
QemuFwCfgSelectItem (FwCfgItem);
QemuFwCfgReadBytes (FwCfgSize, LoaderStart);
+ RestorePciDecoding (OriginalPciAttributes, OriginalPciAttributesCount);
LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry;
+ AllocationsRestrictedTo32Bit = NULL;
+ Status = CollectAllocationsRestrictedTo32Bit (
+ &AllocationsRestrictedTo32Bit,
+ LoaderStart,
+ LoaderEnd
+ );
+ if (EFI_ERROR (Status)) {
+ goto FreeLoader;
+ }
+
+ S3Context = NULL;
+ if (QemuFwCfgS3Enabled ()) {
+ //
+ // Size the allocation pessimistically, assuming that all commands in the
+ // script are QEMU_LOADER_WRITE_POINTER commands.
+ //
+ Status = AllocateS3Context (&S3Context, LoaderEnd - LoaderStart);
+ if (EFI_ERROR (Status)) {
+ goto FreeAllocationsRestrictedTo32Bit;
+ }
+ }
+
Tracker = OrderedCollectionInit (BlobCompare, BlobKeyCompare);
if (Tracker == NULL) {
Status = EFI_OUT_OF_RESOURCES;
- goto FreeLoader;
+ goto FreeS3Context;
}
//
// first pass: process the commands
//
+ // "WritePointerSubsetEnd" points one past the last successful
+ // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first
+ // pass, no such command has been encountered yet.
+ //
+ WritePointerSubsetEnd = LoaderStart;
for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
switch (LoaderEntry->Type) {
case QemuLoaderCmdAllocate:
- Status = ProcessCmdAllocate (&LoaderEntry->Command.Allocate, Tracker);
+ Status = ProcessCmdAllocate (
+ &LoaderEntry->Command.Allocate,
+ Tracker,
+ AllocationsRestrictedTo32Bit
+ );
break;
case QemuLoaderCmdAddPointer:
@@ -609,6 +1051,14 @@ InstallAllQemuLinkedTables (
Tracker);
break;
+ case QemuLoaderCmdWritePointer:
+ Status = ProcessCmdWritePointer (&LoaderEntry->Command.WritePointer,
+ Tracker, S3Context);
+ if (!EFI_ERROR (Status)) {
+ WritePointerSubsetEnd = LoaderEntry + 1;
+ }
+ break;
+
default:
DEBUG ((EFI_D_VERBOSE, "%a: unknown loader command: 0x%x\n",
__FUNCTION__, LoaderEntry->Type));
@@ -616,14 +1066,20 @@ InstallAllQemuLinkedTables (
}
if (EFI_ERROR (Status)) {
- goto FreeTracker;
+ goto RollbackWritePointersAndFreeTracker;
}
}
InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);
if (InstalledKey == NULL) {
Status = EFI_OUT_OF_RESOURCES;
- goto FreeTracker;
+ goto RollbackWritePointersAndFreeTracker;
+ }
+
+ SeenPointers = OrderedCollectionInit (PointerCompare, PointerCompare);
+ if (SeenPointers == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto FreeKeys;
}
//
@@ -632,14 +1088,37 @@ InstallAllQemuLinkedTables (
Installed = 0;
for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
if (LoaderEntry->Type == QemuLoaderCmdAddPointer) {
- Status = Process2ndPassCmdAddPointer (&LoaderEntry->Command.AddPointer,
- Tracker, AcpiProtocol, InstalledKey, &Installed);
+ Status = Process2ndPassCmdAddPointer (
+ &LoaderEntry->Command.AddPointer,
+ Tracker,
+ AcpiProtocol,
+ InstalledKey,
+ &Installed,
+ SeenPointers
+ );
if (EFI_ERROR (Status)) {
- break;
+ goto UninstallAcpiTables;
}
}
}
+ //
+ // Translating the condensed QEMU_LOADER_WRITE_POINTER commands to ACPI S3
+ // Boot Script opcodes has to be the last operation in this function, because
+ // if it succeeds, it cannot be undone.
+ //
+ if (S3Context != NULL) {
+ Status = TransferS3ContextToBootScript (S3Context);
+ if (EFI_ERROR (Status)) {
+ goto UninstallAcpiTables;
+ }
+ //
+ // Ownership of S3Context has been transfered.
+ //
+ S3Context = NULL;
+ }
+
+UninstallAcpiTables:
if (EFI_ERROR (Status)) {
//
// roll back partial installation
@@ -652,9 +1131,32 @@ InstallAllQemuLinkedTables (
DEBUG ((EFI_D_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));
}
+ for (SeenPointerEntry = OrderedCollectionMin (SeenPointers);
+ SeenPointerEntry != NULL;
+ SeenPointerEntry = SeenPointerEntry2) {
+ SeenPointerEntry2 = OrderedCollectionNext (SeenPointerEntry);
+ OrderedCollectionDelete (SeenPointers, SeenPointerEntry, NULL);
+ }
+ OrderedCollectionUninit (SeenPointers);
+
+FreeKeys:
FreePool (InstalledKey);
-FreeTracker:
+RollbackWritePointersAndFreeTracker:
+ //
+ // In case of failure, revoke any allocation addresses that were communicated
+ // to QEMU previously, before we release all the blobs.
+ //
+ if (EFI_ERROR (Status)) {
+ LoaderEntry = WritePointerSubsetEnd;
+ while (LoaderEntry > LoaderStart) {
+ --LoaderEntry;
+ if (LoaderEntry->Type == QemuLoaderCmdWritePointer) {
+ UndoCmdWritePointer (&LoaderEntry->Command.WritePointer);
+ }
+ }
+ }
+
//
// Tear down the tracker infrastructure. Each fw_cfg blob will be left in
// place only if we're exiting with success and the blob hosts data that is
@@ -678,6 +1180,14 @@ FreeTracker:
}
OrderedCollectionUninit (Tracker);
+FreeS3Context:
+ if (S3Context != NULL) {
+ ReleaseS3Context (S3Context);
+ }
+
+FreeAllocationsRestrictedTo32Bit:
+ ReleaseAllocationsRestrictedTo32Bit (AllocationsRestrictedTo32Bit);
+
FreeLoader:
FreePool (LoaderStart);