From 4275f38507a4a44260555495dfb6da1d8a307307 Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Sat, 3 Jun 2017 16:11:08 +0200 Subject: OvmfPkg/AcpiPlatformDxe: alloc blobs from 64-bit space unless restricted ... by narrower than 8-byte ADD_POINTER references. Introduce the CollectAllocationsRestrictedTo32Bit() function, which iterates over the linker/loader script, and collects 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. In ProcessCmdAllocate(), consult these restrictions when setting the maximum address for gBS->AllocatePages(). The default is now MAX_UINT64, unless restricted like described above to the pre-patch MAX_UINT32 limit. In combination with Ard's QEMU commit cb51ac2ffe36 ("hw/arm/virt: generate 64-bit addressable ACPI objects", 2017-04-10), this patch enables OvmfPkg/AcpiPlatformDxe to work entirely above the 4GB mark. (An upcoming / planned aarch64 QEMU machine type will have no RAM under 4GB at all. Plus, moving the allocations higher is beneficial to the current "virt" machine type as well; in Ard's words: "having all firmware allocations inside the same 1 GB (or 512 MB for 64k pages) frame reduces the TLB footprint".) Cc: Ard Biesheuvel Cc: Gerd Hoffmann Cc: Igor Mammedov Cc: Jordan Justen Suggested-by: Igor Mammedov Suggested-by: Gerd Hoffmann Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Laszlo Ersek Tested-by: Ard Biesheuvel Reviewed-by: Jordan Justen --- OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c | 197 ++++++++++++++++++++++++++++++-- 1 file changed, 190 insertions(+), 7 deletions(-) (limited to 'OvmfPkg/AcpiPlatformDxe') diff --git a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c index 1bc5fe297a..98be36c64b 100644 --- a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c +++ b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c @@ -132,13 +132,172 @@ PointerCompare ( } +/** + 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 @@ -164,7 +323,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; @@ -193,7 +353,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)) { @@ -806,6 +972,7 @@ InstallQemuFwCfgTables ( CONST QEMU_LOADER_ENTRY *WritePointerSubsetEnd; ORIGINAL_ATTRIBUTES *OriginalPciAttributes; UINTN OriginalPciAttributesCount; + ORDERED_COLLECTION *AllocationsRestrictedTo32Bit; S3_CONTEXT *S3Context; ORDERED_COLLECTION *Tracker; UINTN *InstalledKey; @@ -834,6 +1001,15 @@ InstallQemuFwCfgTables ( RestorePciDecoding (OriginalPciAttributes, OriginalPciAttributesCount); LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry; + Status = CollectAllocationsRestrictedTo32Bit ( + &AllocationsRestrictedTo32Bit, + LoaderStart, + LoaderEnd + ); + if (EFI_ERROR (Status)) { + goto FreeLoader; + } + S3Context = NULL; if (QemuFwCfgS3Enabled ()) { // @@ -842,7 +1018,7 @@ InstallQemuFwCfgTables ( // Status = AllocateS3Context (&S3Context, LoaderEnd - LoaderStart); if (EFI_ERROR (Status)) { - goto FreeLoader; + goto FreeAllocationsRestrictedTo32Bit; } } @@ -863,7 +1039,11 @@ InstallQemuFwCfgTables ( 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: @@ -1010,6 +1190,9 @@ FreeS3Context: ReleaseS3Context (S3Context); } +FreeAllocationsRestrictedTo32Bit: + ReleaseAllocationsRestrictedTo32Bit (AllocationsRestrictedTo32Bit); + FreeLoader: FreePool (LoaderStart); -- cgit v1.2.3