X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=OvmfPkg%2FAcpiPlatformDxe%2FQemuFwCfgAcpi.c;h=bc1a891dbaf16aa801589de504b37faf47fbd6f7;hb=631195044ff01c9d9b35749d44cc04475da119e6;hp=d75394c0f8a887aafe5687cd0634700c07e882a5;hpb=903f52297f5d08d6e53bf9b48aafbbdd055e8e27;p=mirror_edk2.git diff --git a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c index d75394c0f8..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,46 +1180,16 @@ FreeTracker: } OrderedCollectionUninit (Tracker); -FreeLoader: - FreePool (LoaderStart); - - return Status; -} - - -/** - Entrypoint of QEMU fw-cfg Acpi Platform driver. - - @param ImageHandle - @param SystemTable - - @return EFI_SUCCESS - @return EFI_LOAD_ERROR - @return EFI_OUT_OF_RESOURCES +FreeS3Context: + if (S3Context != NULL) { + ReleaseS3Context (S3Context); + } -**/ -EFI_STATUS -EFIAPI -QemuFwCfgAcpiPlatformEntryPoint ( - IN EFI_HANDLE ImageHandle, - IN EFI_SYSTEM_TABLE *SystemTable - ) -{ - EFI_STATUS Status; - EFI_ACPI_TABLE_PROTOCOL *AcpiTable; +FreeAllocationsRestrictedTo32Bit: + ReleaseAllocationsRestrictedTo32Bit (AllocationsRestrictedTo32Bit); - // - // Find the AcpiTable protocol - // - Status = gBS->LocateProtocol ( - &gEfiAcpiTableProtocolGuid, - NULL, - (VOID**)&AcpiTable - ); - if (EFI_ERROR (Status)) { - return EFI_ABORTED; - } +FreeLoader: + FreePool (LoaderStart); - Status = InstallAllQemuLinkedTables (AcpiTable); return Status; }