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 <ard.biesheuvel@linaro.org>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Igor Mammedov <imammedo@redhat.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Suggested-by: Igor Mammedov <imammedo@redhat.com>
Suggested-by: Gerd Hoffmann <kraxel@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Tested-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
This commit is contained in:
Laszlo Ersek 2017-06-03 16:11:08 +02:00
parent b941c34ef8
commit 4275f38507
1 changed files with 190 additions and 7 deletions

View File

@ -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. 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 @param[in,out] Tracker The ORDERED_COLLECTION tracking the
structures created thus far. 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 @retval EFI_SUCCESS An area of whole AcpiNVS pages has been
allocated for the blob contents, and the allocated for the blob contents, and the
@ -164,7 +323,8 @@ EFI_STATUS
EFIAPI EFIAPI
ProcessCmdAllocate ( ProcessCmdAllocate (
IN CONST QEMU_LOADER_ALLOCATE *Allocate, IN CONST QEMU_LOADER_ALLOCATE *Allocate,
IN OUT ORDERED_COLLECTION *Tracker IN OUT ORDERED_COLLECTION *Tracker,
IN ORDERED_COLLECTION *AllocationsRestrictedTo32Bit
) )
{ {
FIRMWARE_CONFIG_ITEM FwCfgItem; FIRMWARE_CONFIG_ITEM FwCfgItem;
@ -193,7 +353,13 @@ ProcessCmdAllocate (
} }
NumPages = EFI_SIZE_TO_PAGES (FwCfgSize); 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, Status = gBS->AllocatePages (AllocateMaxAddress, EfiACPIMemoryNVS, NumPages,
&Address); &Address);
if (EFI_ERROR (Status)) { if (EFI_ERROR (Status)) {
@ -806,6 +972,7 @@ InstallQemuFwCfgTables (
CONST QEMU_LOADER_ENTRY *WritePointerSubsetEnd; CONST QEMU_LOADER_ENTRY *WritePointerSubsetEnd;
ORIGINAL_ATTRIBUTES *OriginalPciAttributes; ORIGINAL_ATTRIBUTES *OriginalPciAttributes;
UINTN OriginalPciAttributesCount; UINTN OriginalPciAttributesCount;
ORDERED_COLLECTION *AllocationsRestrictedTo32Bit;
S3_CONTEXT *S3Context; S3_CONTEXT *S3Context;
ORDERED_COLLECTION *Tracker; ORDERED_COLLECTION *Tracker;
UINTN *InstalledKey; UINTN *InstalledKey;
@ -834,6 +1001,15 @@ InstallQemuFwCfgTables (
RestorePciDecoding (OriginalPciAttributes, OriginalPciAttributesCount); RestorePciDecoding (OriginalPciAttributes, OriginalPciAttributesCount);
LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry; LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry;
Status = CollectAllocationsRestrictedTo32Bit (
&AllocationsRestrictedTo32Bit,
LoaderStart,
LoaderEnd
);
if (EFI_ERROR (Status)) {
goto FreeLoader;
}
S3Context = NULL; S3Context = NULL;
if (QemuFwCfgS3Enabled ()) { if (QemuFwCfgS3Enabled ()) {
// //
@ -842,7 +1018,7 @@ InstallQemuFwCfgTables (
// //
Status = AllocateS3Context (&S3Context, LoaderEnd - LoaderStart); Status = AllocateS3Context (&S3Context, LoaderEnd - LoaderStart);
if (EFI_ERROR (Status)) { if (EFI_ERROR (Status)) {
goto FreeLoader; goto FreeAllocationsRestrictedTo32Bit;
} }
} }
@ -863,7 +1039,11 @@ InstallQemuFwCfgTables (
for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) { for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
switch (LoaderEntry->Type) { switch (LoaderEntry->Type) {
case QemuLoaderCmdAllocate: case QemuLoaderCmdAllocate:
Status = ProcessCmdAllocate (&LoaderEntry->Command.Allocate, Tracker); Status = ProcessCmdAllocate (
&LoaderEntry->Command.Allocate,
Tracker,
AllocationsRestrictedTo32Bit
);
break; break;
case QemuLoaderCmdAddPointer: case QemuLoaderCmdAddPointer:
@ -1010,6 +1190,9 @@ FreeS3Context:
ReleaseS3Context (S3Context); ReleaseS3Context (S3Context);
} }
FreeAllocationsRestrictedTo32Bit:
ReleaseAllocationsRestrictedTo32Bit (AllocationsRestrictedTo32Bit);
FreeLoader: FreeLoader:
FreePool (LoaderStart); FreePool (LoaderStart);