This driver currently relies on a 2nd pass processing of the ADD_POINTER commands to identify potential ACPI tables in the pointed-to blobs.
In order to tell apart ACPI tables from other operation region-like areas within pointed-to blobs, we employ a heuristic called "ACPI SDT header probe" at the target locations of the ADD_POINTER commands. While all ACPI tables generated by QEMU satisfy this check (i.e., there are no false negatives), blob content that is *not* an ACPI table has a very slight chance to pass the test as well (i.e., there is a small chance for false positives).
In order to suppress this small chance, in QEMU we've historically formatted opregion-like areas in blobs with a fixed size zero prefix (see e.g. "docs/specs/vmgenid.txt"), which guarantees that the probe in OvmfPkg/AcpiPlatformDxe will fail. However, this "suppressor prefix" has had to be taken into account explicitly in generated AML code -- the prefix size has had to be added to the patched integer object in AML, at runtime --, leading to awkwardness.
QEMU is introducing a new hint for the ALLOCATE command, as the most significant bit of the UINT8 "Zone" field, for disabling the ACPI SDT header probe in OvmfPkg/AcpiPlatformDxe, for all the pointers that point into the blob downloaded with the ALLOCATE command. When the bit is set, the blob is guaranteed to contain no ACPI tables. When the bit is clear, the behavior is left unchanged.
In ProcessCmdAllocate(), save the hint for later.
In Process2ndPassCmdAddPointer(), consult the saved hint. If QEMU reported the blob as containing no ACPI table data, then omit the ACPI SDT header probing and mark the pointed-to blob as unreleasable.
Cc: "Michael S. Tsirkin" mst@redhat.com Cc: Ard Biesheuvel ard.biesheuvel@linaro.org Cc: Ben Warren ben@skyportsystems.com Cc: Dongjiu Geng gengdongjiu@huawei.com Cc: Igor Mammedov imammedo@redhat.com Cc: Jordan Justen jordan.l.justen@intel.com Cc: Leif Lindholm leif.lindholm@linaro.org Cc: Shannon Zhao zhaoshenglong@huawei.com Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Laszlo Ersek lersek@redhat.com --- OvmfPkg/AcpiPlatformDxe/QemuLoader.h | 9 +++++- OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c | 29 +++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-)
diff --git a/OvmfPkg/AcpiPlatformDxe/QemuLoader.h b/OvmfPkg/AcpiPlatformDxe/QemuLoader.h index 437776d86d9a..fa558540e62b 100644 --- a/OvmfPkg/AcpiPlatformDxe/QemuLoader.h +++ b/OvmfPkg/AcpiPlatformDxe/QemuLoader.h @@ -34,19 +34,26 @@ typedef enum { typedef enum { QemuLoaderAllocHigh = 1, QemuLoaderAllocFSeg } QEMU_LOADER_ALLOC_ZONE;
+typedef enum { + QemuLoaderAllocContentMixed = 0x00, + QemuLoaderAllocContentNoAcpi = 0x80, +} QEMU_LOADER_ALLOC_CONTENT; + #pragma pack (1) // // QemuLoaderCmdAllocate: download the fw_cfg file named File, to a buffer // allocated in the zone specified by Zone, aligned at a multiple of Alignment. // typedef struct { UINT8 File[QEMU_LOADER_FNAME_SIZE]; // NUL-terminated UINT32 Alignment; // power of two - UINT8 Zone; // QEMU_LOADER_ALLOC_ZONE values + UINT8 Zone; // One QEMU_LOADER_ALLOC_ZONE value + // OR-ed together with one + // QEMU_LOADER_ALLOC_CONTENT value } QEMU_LOADER_ALLOCATE;
// // QemuLoaderCmdAddPointer: the bytes at // [PointerOffset..PointerOffset+PointerSize) in the file PointerFile contain a diff --git a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c index 4a7b051288bc..23d543ffe361 100644 --- a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c +++ b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c @@ -36,10 +36,12 @@ typedef struct { // key. UINTN Size; // The number of bytes in this blob. UINT8 *Base; // Pointer to the blob data. BOOLEAN Releasable; // TRUE iff the blob should be released // at the end of processing. + BOOLEAN AcpiTablesExcluded; // TRUE iff QEMU guarantees that the + // blob contains no ACPI tables } BLOB;
/** Compare a standalone key against a user structure containing an embedded key. @@ -167,10 +169,12 @@ ProcessCmdAllocate ( ) { FIRMWARE_CONFIG_ITEM FwCfgItem; UINTN FwCfgSize; EFI_STATUS Status; + UINT32 Zone; + BOOLEAN AcpiTablesExcluded; UINTN NumPages; EFI_PHYSICAL_ADDRESS Address; BLOB *Blob;
if (Allocate->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') { @@ -189,10 +193,18 @@ ProcessCmdAllocate ( DEBUG ((EFI_D_ERROR, "%a: QemuFwCfgFindFile("%a"): %r\n", __FUNCTION__, Allocate->File, Status)); return Status; }
+ Zone = Allocate->Zone; + if ((Zone & QemuLoaderAllocContentNoAcpi) != 0) { + Zone &= ~(UINT32)QemuLoaderAllocContentNoAcpi; + AcpiTablesExcluded = TRUE; + } else { + AcpiTablesExcluded = FALSE; + } + NumPages = EFI_SIZE_TO_PAGES (FwCfgSize); Address = 0xFFFFFFFF; Status = gBS->AllocatePages (AllocateMaxAddress, EfiACPIMemoryNVS, NumPages, &Address); if (EFI_ERROR (Status)) { @@ -206,10 +218,11 @@ ProcessCmdAllocate ( } CopyMem (Blob->File, Allocate->File, QEMU_LOADER_FNAME_SIZE); Blob->Size = FwCfgSize; Blob->Base = (VOID *)(UINTN)Address; Blob->Releasable = TRUE; + Blob->AcpiTablesExcluded = AcpiTablesExcluded;
Status = OrderedCollectionInsert (Tracker, NULL, Blob); if (Status == RETURN_ALREADY_STARTED) { DEBUG ((EFI_D_ERROR, "%a: duplicated file "%a"\n", __FUNCTION__, Allocate->File)); @@ -595,11 +608,13 @@ UndoCmdWritePointer ( 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. + taking any action. Target addresses that fall + into fw_cfg blobs that QEMU reported in advance + as holding no ACPI content are not even tracked.
@retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on input.
@retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI @@ -651,10 +666,22 @@ Process2ndPassCmdAddPointer (
TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile); TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile); Blob = OrderedCollectionUserStruct (TrackerEntry); Blob2 = OrderedCollectionUserStruct (TrackerEntry2); + + if (Blob2->AcpiTablesExcluded) { + DEBUG (( + DEBUG_VERBOSE, + "%a: marking blob "%a" with no ACPI content as unreleasable\n", + __FUNCTION__, + AddPointer->PointeeFile + )); + Blob2->Releasable = FALSE; + return EFI_SUCCESS; + } + PointerField = Blob->Base + AddPointer->PointerOffset; PointerValue = 0; CopyMem (&PointerValue, PointerField, AddPointer->PointerSize);
//