From 14b0faadfc87be12398964015ee0884d71bd0e7b Mon Sep 17 00:00:00 2001 From: Jordan Justen Date: Mon, 2 Feb 2015 19:08:57 +0000 Subject: OvmfPkg/AcpiPlatformDxe: Split QEMU fw-cfg into a new file The code left behind in Qemu.c has some PCAT dependencies, and might not be able to build on all platforms. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Jordan Justen Reviewed-by: Laszlo Ersek Signed-off-by: Laszlo Ersek git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16696 6f19259b-4bc3-4df7-8a09-765794883524 --- OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf | 1 + OvmfPkg/AcpiPlatformDxe/Qemu.c | 660 --------------------------- OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c | 685 ++++++++++++++++++++++++++++ 3 files changed, 686 insertions(+), 660 deletions(-) create mode 100644 OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c (limited to 'OvmfPkg') diff --git a/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf b/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf index c6dced21c1..53292bf150 100644 --- a/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf +++ b/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf @@ -29,6 +29,7 @@ [Sources] AcpiPlatform.c Qemu.c + QemuFwCfgAcpi.c Xen.c [Packages] diff --git a/OvmfPkg/AcpiPlatformDxe/Qemu.c b/OvmfPkg/AcpiPlatformDxe/Qemu.c index 59152487b8..90f06bba63 100644 --- a/OvmfPkg/AcpiPlatformDxe/Qemu.c +++ b/OvmfPkg/AcpiPlatformDxe/Qemu.c @@ -516,663 +516,3 @@ QemuInstallAcpiTable ( TableKey ); } - - -// -// The user structure for the ordered collection that will track the fw_cfg -// blobs under processing. -// -typedef struct { - UINT8 File[QEMU_LOADER_FNAME_SIZE]; // NUL-terminated name of the fw_cfg - // blob. This is the ordering / search - // key. - UINTN Size; // The number of bytes in this blob. - UINT8 *Base; // Pointer to the blob data. - BOOLEAN HostsOnlyTableData; // TRUE iff the blob has been found to - // only contain data that is directly - // part of ACPI tables. -} BLOB; - - -/** - Compare a standalone key against a user structure containing an embedded key. - - @param[in] StandaloneKey Pointer to the bare key. - - @param[in] UserStruct Pointer to the user structure with the embedded - key. - - @retval <0 If StandaloneKey compares less than UserStruct's key. - - @retval 0 If StandaloneKey compares equal to UserStruct's key. - - @retval >0 If StandaloneKey compares greater than UserStruct's key. -**/ -STATIC -INTN -EFIAPI -BlobKeyCompare ( - IN CONST VOID *StandaloneKey, - IN CONST VOID *UserStruct - ) -{ - CONST BLOB *Blob; - - Blob = UserStruct; - return AsciiStrCmp (StandaloneKey, (CONST CHAR8 *)Blob->File); -} - - -/** - Comparator function for two user structures. - - @param[in] UserStruct1 Pointer to the first user structure. - - @param[in] UserStruct2 Pointer to the second user structure. - - @retval <0 If UserStruct1 compares less than UserStruct2. - - @retval 0 If UserStruct1 compares equal to UserStruct2. - - @retval >0 If UserStruct1 compares greater than UserStruct2. -**/ -STATIC -INTN -EFIAPI -BlobCompare ( - IN CONST VOID *UserStruct1, - IN CONST VOID *UserStruct2 - ) -{ - CONST BLOB *Blob1; - - Blob1 = UserStruct1; - return BlobKeyCompare (Blob1->File, UserStruct2); -} - - -/** - Process a QEMU_LOADER_ALLOCATE command. - - @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. - - @retval EFI_SUCCESS An area of whole AcpiNVS pages has been - allocated for the blob contents, and the - contents have been saved. A BLOB object (user - structure) has been allocated from pool memory, - referencing the blob contents. The BLOB user - structure has been linked into Tracker. - - @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in - Allocate, or the Allocate command references a - file that is already known by Tracker. - - @retval EFI_UNSUPPORTED Unsupported alignment request has been found in - Allocate. - - @retval EFI_OUT_OF_RESOURCES Pool allocation failed. - - @return Error codes from QemuFwCfgFindFile() and - gBS->AllocatePages(). -**/ -STATIC -EFI_STATUS -EFIAPI -ProcessCmdAllocate ( - IN CONST QEMU_LOADER_ALLOCATE *Allocate, - IN OUT ORDERED_COLLECTION *Tracker - ) -{ - FIRMWARE_CONFIG_ITEM FwCfgItem; - UINTN FwCfgSize; - EFI_STATUS Status; - UINTN NumPages; - EFI_PHYSICAL_ADDRESS Address; - BLOB *Blob; - - if (Allocate->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') { - DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__)); - return EFI_PROTOCOL_ERROR; - } - - if (Allocate->Alignment > EFI_PAGE_SIZE) { - DEBUG ((EFI_D_ERROR, "%a: unsupported alignment 0x%x\n", __FUNCTION__, - Allocate->Alignment)); - return EFI_UNSUPPORTED; - } - - Status = QemuFwCfgFindFile ((CHAR8 *)Allocate->File, &FwCfgItem, &FwCfgSize); - if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "%a: QemuFwCfgFindFile(\"%a\"): %r\n", __FUNCTION__, - Allocate->File, Status)); - return Status; - } - - NumPages = EFI_SIZE_TO_PAGES (FwCfgSize); - Address = 0xFFFFFFFF; - Status = gBS->AllocatePages (AllocateMaxAddress, EfiACPIMemoryNVS, NumPages, - &Address); - if (EFI_ERROR (Status)) { - return Status; - } - - Blob = AllocatePool (sizeof *Blob); - if (Blob == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto FreePages; - } - CopyMem (Blob->File, Allocate->File, QEMU_LOADER_FNAME_SIZE); - Blob->Size = FwCfgSize; - Blob->Base = (VOID *)(UINTN)Address; - Blob->HostsOnlyTableData = TRUE; - - Status = OrderedCollectionInsert (Tracker, NULL, Blob); - if (Status == RETURN_ALREADY_STARTED) { - DEBUG ((EFI_D_ERROR, "%a: duplicated file \"%a\"\n", __FUNCTION__, - Allocate->File)); - Status = EFI_PROTOCOL_ERROR; - } - if (EFI_ERROR (Status)) { - goto FreeBlob; - } - - QemuFwCfgSelectItem (FwCfgItem); - QemuFwCfgReadBytes (FwCfgSize, Blob->Base); - ZeroMem (Blob->Base + Blob->Size, EFI_PAGES_TO_SIZE (NumPages) - Blob->Size); - - DEBUG ((EFI_D_VERBOSE, "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx " - "Address=0x%Lx\n", __FUNCTION__, Allocate->File, Allocate->Alignment, - Allocate->Zone, (UINT64)Blob->Size, (UINT64)(UINTN)Blob->Base)); - return EFI_SUCCESS; - -FreeBlob: - FreePool (Blob); - -FreePages: - gBS->FreePages (Address, NumPages); - - return Status; -} - - -/** - Process a QEMU_LOADER_ADD_POINTER command. - - @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process. - - @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user - structures created thus far. - - @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in - AddPointer, or the AddPointer command references - a file unknown to Tracker, or the pointer to - relocate has invalid location, size, or value, or - the relocated pointer value is not representable - in the given pointer size. - - @retval EFI_SUCCESS The pointer field inside the pointer blob has - been relocated. -**/ -STATIC -EFI_STATUS -EFIAPI -ProcessCmdAddPointer ( - IN CONST QEMU_LOADER_ADD_POINTER *AddPointer, - IN CONST ORDERED_COLLECTION *Tracker - ) -{ - ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2; - BLOB *Blob, *Blob2; - UINT8 *PointerField; - UINT64 PointerValue; - - if (AddPointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0' || - AddPointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') { - DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__)); - return EFI_PROTOCOL_ERROR; - } - - TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile); - TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile); - if (TrackerEntry == NULL || TrackerEntry2 == NULL) { - DEBUG ((EFI_D_ERROR, "%a: invalid blob reference(s) \"%a\" / \"%a\"\n", - __FUNCTION__, AddPointer->PointerFile, AddPointer->PointeeFile)); - return EFI_PROTOCOL_ERROR; - } - - Blob = OrderedCollectionUserStruct (TrackerEntry); - Blob2 = OrderedCollectionUserStruct (TrackerEntry2); - if ((AddPointer->PointerSize != 1 && AddPointer->PointerSize != 2 && - AddPointer->PointerSize != 4 && AddPointer->PointerSize != 8) || - Blob->Size < AddPointer->PointerSize || - Blob->Size - AddPointer->PointerSize < AddPointer->PointerOffset) { - DEBUG ((EFI_D_ERROR, "%a: invalid pointer location or size in \"%a\"\n", - __FUNCTION__, AddPointer->PointerFile)); - return EFI_PROTOCOL_ERROR; - } - - PointerField = Blob->Base + AddPointer->PointerOffset; - PointerValue = 0; - CopyMem (&PointerValue, PointerField, AddPointer->PointerSize); - if (PointerValue >= Blob2->Size) { - DEBUG ((EFI_D_ERROR, "%a: invalid pointer value in \"%a\"\n", __FUNCTION__, - AddPointer->PointerFile)); - 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)Blob2->Base <= MAX_ADDRESS - Blob2->Size); - - PointerValue += (UINT64)(UINTN)Blob2->Base; - if (RShiftU64 ( - RShiftU64 (PointerValue, AddPointer->PointerSize * 8 - 1), 1) != 0) { - DEBUG ((EFI_D_ERROR, "%a: relocated pointer value unrepresentable in " - "\"%a\"\n", __FUNCTION__, AddPointer->PointerFile)); - return EFI_PROTOCOL_ERROR; - } - - CopyMem (PointerField, &PointerValue, AddPointer->PointerSize); - - DEBUG ((EFI_D_VERBOSE, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" " - "PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__, - AddPointer->PointerFile, AddPointer->PointeeFile, - AddPointer->PointerOffset, AddPointer->PointerSize)); - return EFI_SUCCESS; -} - - -/** - Process a QEMU_LOADER_ADD_CHECKSUM command. - - @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process. - - @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user - structures created thus far. - - @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in - AddChecksum, or the AddChecksum command - references a file unknown to Tracker, or the - range to checksum is invalid. - - @retval EFI_SUCCESS The requested range has been checksummed. -**/ -STATIC -EFI_STATUS -EFIAPI -ProcessCmdAddChecksum ( - IN CONST QEMU_LOADER_ADD_CHECKSUM *AddChecksum, - IN CONST ORDERED_COLLECTION *Tracker - ) -{ - ORDERED_COLLECTION_ENTRY *TrackerEntry; - BLOB *Blob; - - if (AddChecksum->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') { - DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__)); - return EFI_PROTOCOL_ERROR; - } - - TrackerEntry = OrderedCollectionFind (Tracker, AddChecksum->File); - if (TrackerEntry == NULL) { - DEBUG ((EFI_D_ERROR, "%a: invalid blob reference \"%a\"\n", __FUNCTION__, - AddChecksum->File)); - return EFI_PROTOCOL_ERROR; - } - - Blob = OrderedCollectionUserStruct (TrackerEntry); - if (Blob->Size <= AddChecksum->ResultOffset || - Blob->Size < AddChecksum->Length || - Blob->Size - AddChecksum->Length < AddChecksum->Start) { - DEBUG ((EFI_D_ERROR, "%a: invalid checksum range in \"%a\"\n", - __FUNCTION__, AddChecksum->File)); - return EFI_PROTOCOL_ERROR; - } - - Blob->Base[AddChecksum->ResultOffset] = CalculateCheckSum8 ( - Blob->Base + AddChecksum->Start, - AddChecksum->Length - ); - DEBUG ((EFI_D_VERBOSE, "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x " - "Length=0x%x\n", __FUNCTION__, AddChecksum->File, - AddChecksum->ResultOffset, AddChecksum->Start, AddChecksum->Length)); - return EFI_SUCCESS; -} - - -// -// 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). -// -#define INSTALLED_TABLES_MAX 128 - -/** - Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte - 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. - - @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process. - - @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user - structures. - - @param[in] AcpiProtocol The ACPI table protocol used to install tables. - - @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN - elements, allocated by the caller. On output, - the function will have stored (appended) the - AcpiProtocol-internal key of the ACPI table that - the function has installed, if the AddPointer - command identified an ACPI table that is - different from RSDT and XSDT. - - @param[in,out] NumInstalled On input, the number of entries already used in - InstalledKey; it must be in [0, - INSTALLED_TABLES_MAX] inclusive. On output, the - parameter is incremented if the AddPointer - command identified an ACPI table that is - different from RSDT and XSDT. - - @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on - input. - - @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI - 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. - - @return Error codes returned by - AcpiProtocol->InstallAcpiTable(). -**/ -STATIC -EFI_STATUS -EFIAPI -Process2ndPassCmdAddPointer ( - IN CONST QEMU_LOADER_ADD_POINTER *AddPointer, - IN CONST ORDERED_COLLECTION *Tracker, - IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol, - IN OUT UINTN InstalledKey[INSTALLED_TABLES_MAX], - IN OUT INT32 *NumInstalled - ) -{ - CONST ORDERED_COLLECTION_ENTRY *TrackerEntry; - CONST ORDERED_COLLECTION_ENTRY *TrackerEntry2; - CONST BLOB *Blob; - BLOB *Blob2; - CONST UINT8 *PointerField; - UINT64 PointerValue; - UINTN Blob2Remaining; - UINTN TableSize; - CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs; - CONST EFI_ACPI_DESCRIPTION_HEADER *Header; - EFI_STATUS Status; - - if (*NumInstalled < 0 || *NumInstalled > INSTALLED_TABLES_MAX) { - return EFI_INVALID_PARAMETER; - } - - TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile); - TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile); - Blob = OrderedCollectionUserStruct (TrackerEntry); - Blob2 = OrderedCollectionUserStruct (TrackerEntry2); - PointerField = Blob->Base + AddPointer->PointerOffset; - PointerValue = 0; - CopyMem (&PointerValue, PointerField, AddPointer->PointerSize); - - // - // We assert that PointerValue falls inside Blob2's contents. This is ensured - // by the Blob2->Size check and later checks in ProcessCmdAddPointer(). - // - Blob2Remaining = (UINTN)Blob2->Base; - ASSERT(PointerValue >= Blob2Remaining); - Blob2Remaining += Blob2->Size; - ASSERT (PointerValue < Blob2Remaining); - - Blob2Remaining -= (UINTN) PointerValue; - DEBUG ((EFI_D_VERBOSE, "%a: checking for ACPI header in \"%a\" at 0x%Lx " - "(remaining: 0x%Lx): ", __FUNCTION__, AddPointer->PointeeFile, - PointerValue, (UINT64)Blob2Remaining)); - - TableSize = 0; - - // - // To make our job simple, the FACS has a custom header. Sigh. - // - if (sizeof *Facs <= Blob2Remaining) { - Facs = (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)PointerValue; - - if (Facs->Length >= sizeof *Facs && - Facs->Length <= Blob2Remaining && - Facs->Signature == - EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) { - DEBUG ((EFI_D_VERBOSE, "found \"%-4.4a\" size 0x%x\n", - (CONST CHAR8 *)&Facs->Signature, Facs->Length)); - TableSize = Facs->Length; - } - } - - // - // check for the uniform tables - // - if (TableSize == 0 && sizeof *Header <= Blob2Remaining) { - Header = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)PointerValue; - - if (Header->Length >= sizeof *Header && - Header->Length <= Blob2Remaining && - CalculateSum8 ((CONST UINT8 *)Header, Header->Length) == 0) { - // - // This looks very much like an ACPI table from QEMU: - // - Length field consistent with both ACPI and containing blob size - // - checksum is correct - // - DEBUG ((EFI_D_VERBOSE, "found \"%-4.4a\" size 0x%x\n", - (CONST CHAR8 *)&Header->Signature, Header->Length)); - TableSize = Header->Length; - - // - // Skip RSDT and XSDT because those are handled by - // EFI_ACPI_TABLE_PROTOCOL automatically. - if (Header->Signature == - EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE || - Header->Signature == - EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) { - return EFI_SUCCESS; - } - } - } - - if (TableSize == 0) { - DEBUG ((EFI_D_VERBOSE, "not found; marking fw_cfg blob as opaque\n")); - Blob2->HostsOnlyTableData = FALSE; - return EFI_SUCCESS; - } - - 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 = AcpiProtocol->InstallAcpiTable (AcpiProtocol, - (VOID *)(UINTN)PointerValue, TableSize, - &InstalledKey[*NumInstalled]); - if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "%a: InstallAcpiTable(): %r\n", __FUNCTION__, - Status)); - return Status; - } - ++*NumInstalled; - return EFI_SUCCESS; -} - - -/** - Download, process, and install ACPI table data from the QEMU loader - interface. - - @param[in] AcpiProtocol The ACPI table protocol used to install tables. - - @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU - loader command with unsupported parameters - has been found. - - @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg - files. - - @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than - INSTALLED_TABLES_MAX tables found. - - @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents. - - @return Status codes returned by - AcpiProtocol->InstallAcpiTable(). - -**/ -EFI_STATUS -EFIAPI -InstallAllQemuLinkedTables ( - IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol - ) -{ - EFI_STATUS Status; - FIRMWARE_CONFIG_ITEM FwCfgItem; - UINTN FwCfgSize; - QEMU_LOADER_ENTRY *LoaderStart; - CONST QEMU_LOADER_ENTRY *LoaderEntry, *LoaderEnd; - ORDERED_COLLECTION *Tracker; - UINTN *InstalledKey; - INT32 Installed; - ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2; - - Status = QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem, &FwCfgSize); - if (EFI_ERROR (Status)) { - return Status; - } - if (FwCfgSize % sizeof *LoaderEntry != 0) { - DEBUG ((EFI_D_ERROR, "%a: \"etc/table-loader\" has invalid size 0x%Lx\n", - __FUNCTION__, (UINT64)FwCfgSize)); - return EFI_PROTOCOL_ERROR; - } - - LoaderStart = AllocatePool (FwCfgSize); - if (LoaderStart == NULL) { - return EFI_OUT_OF_RESOURCES; - } - QemuFwCfgSelectItem (FwCfgItem); - QemuFwCfgReadBytes (FwCfgSize, LoaderStart); - LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry; - - Tracker = OrderedCollectionInit (BlobCompare, BlobKeyCompare); - if (Tracker == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto FreeLoader; - } - - // - // first pass: process the commands - // - for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) { - switch (LoaderEntry->Type) { - case QemuLoaderCmdAllocate: - Status = ProcessCmdAllocate (&LoaderEntry->Command.Allocate, Tracker); - break; - - case QemuLoaderCmdAddPointer: - Status = ProcessCmdAddPointer (&LoaderEntry->Command.AddPointer, - Tracker); - break; - - case QemuLoaderCmdAddChecksum: - Status = ProcessCmdAddChecksum (&LoaderEntry->Command.AddChecksum, - Tracker); - break; - - default: - DEBUG ((EFI_D_VERBOSE, "%a: unknown loader command: 0x%x\n", - __FUNCTION__, LoaderEntry->Type)); - break; - } - - if (EFI_ERROR (Status)) { - goto FreeTracker; - } - } - - InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey); - if (InstalledKey == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto FreeTracker; - } - - // - // second pass: identify and install ACPI tables - // - Installed = 0; - for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) { - if (LoaderEntry->Type == QemuLoaderCmdAddPointer) { - Status = Process2ndPassCmdAddPointer (&LoaderEntry->Command.AddPointer, - Tracker, AcpiProtocol, InstalledKey, &Installed); - if (EFI_ERROR (Status)) { - break; - } - } - } - - if (EFI_ERROR (Status)) { - // - // roll back partial installation - // - while (Installed > 0) { - --Installed; - AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]); - } - } else { - DEBUG ((EFI_D_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed)); - } - - FreePool (InstalledKey); - -FreeTracker: - // - // 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 - // not directly part of some ACPI table. - // - for (TrackerEntry = OrderedCollectionMin (Tracker); TrackerEntry != NULL; - TrackerEntry = TrackerEntry2) { - VOID *UserStruct; - BLOB *Blob; - - TrackerEntry2 = OrderedCollectionNext (TrackerEntry); - OrderedCollectionDelete (Tracker, TrackerEntry, &UserStruct); - Blob = UserStruct; - - if (EFI_ERROR (Status) || Blob->HostsOnlyTableData) { - DEBUG ((EFI_D_VERBOSE, "%a: freeing \"%a\"\n", __FUNCTION__, - Blob->File)); - gBS->FreePages ((UINTN)Blob->Base, EFI_SIZE_TO_PAGES (Blob->Size)); - } - FreePool (Blob); - } - OrderedCollectionUninit (Tracker); - -FreeLoader: - FreePool (LoaderStart); - - return Status; -} diff --git a/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c new file mode 100644 index 0000000000..70df284264 --- /dev/null +++ b/OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c @@ -0,0 +1,685 @@ +/** @file + OVMF ACPI support using QEMU's fw-cfg interface + + 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. + +**/ + +#include "AcpiPlatform.h" +#include "QemuLoader.h" +#include +#include +#include +#include +#include +#include +#include + + +// +// The user structure for the ordered collection that will track the fw_cfg +// blobs under processing. +// +typedef struct { + UINT8 File[QEMU_LOADER_FNAME_SIZE]; // NUL-terminated name of the fw_cfg + // blob. This is the ordering / search + // key. + UINTN Size; // The number of bytes in this blob. + UINT8 *Base; // Pointer to the blob data. + BOOLEAN HostsOnlyTableData; // TRUE iff the blob has been found to + // only contain data that is directly + // part of ACPI tables. +} BLOB; + + +/** + Compare a standalone key against a user structure containing an embedded key. + + @param[in] StandaloneKey Pointer to the bare key. + + @param[in] UserStruct Pointer to the user structure with the embedded + key. + + @retval <0 If StandaloneKey compares less than UserStruct's key. + + @retval 0 If StandaloneKey compares equal to UserStruct's key. + + @retval >0 If StandaloneKey compares greater than UserStruct's key. +**/ +STATIC +INTN +EFIAPI +BlobKeyCompare ( + IN CONST VOID *StandaloneKey, + IN CONST VOID *UserStruct + ) +{ + CONST BLOB *Blob; + + Blob = UserStruct; + return AsciiStrCmp (StandaloneKey, (CONST CHAR8 *)Blob->File); +} + + +/** + Comparator function for two user structures. + + @param[in] UserStruct1 Pointer to the first user structure. + + @param[in] UserStruct2 Pointer to the second user structure. + + @retval <0 If UserStruct1 compares less than UserStruct2. + + @retval 0 If UserStruct1 compares equal to UserStruct2. + + @retval >0 If UserStruct1 compares greater than UserStruct2. +**/ +STATIC +INTN +EFIAPI +BlobCompare ( + IN CONST VOID *UserStruct1, + IN CONST VOID *UserStruct2 + ) +{ + CONST BLOB *Blob1; + + Blob1 = UserStruct1; + return BlobKeyCompare (Blob1->File, UserStruct2); +} + + +/** + Process a QEMU_LOADER_ALLOCATE command. + + @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. + + @retval EFI_SUCCESS An area of whole AcpiNVS pages has been + allocated for the blob contents, and the + contents have been saved. A BLOB object (user + structure) has been allocated from pool memory, + referencing the blob contents. The BLOB user + structure has been linked into Tracker. + + @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in + Allocate, or the Allocate command references a + file that is already known by Tracker. + + @retval EFI_UNSUPPORTED Unsupported alignment request has been found in + Allocate. + + @retval EFI_OUT_OF_RESOURCES Pool allocation failed. + + @return Error codes from QemuFwCfgFindFile() and + gBS->AllocatePages(). +**/ +STATIC +EFI_STATUS +EFIAPI +ProcessCmdAllocate ( + IN CONST QEMU_LOADER_ALLOCATE *Allocate, + IN OUT ORDERED_COLLECTION *Tracker + ) +{ + FIRMWARE_CONFIG_ITEM FwCfgItem; + UINTN FwCfgSize; + EFI_STATUS Status; + UINTN NumPages; + EFI_PHYSICAL_ADDRESS Address; + BLOB *Blob; + + if (Allocate->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') { + DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__)); + return EFI_PROTOCOL_ERROR; + } + + if (Allocate->Alignment > EFI_PAGE_SIZE) { + DEBUG ((EFI_D_ERROR, "%a: unsupported alignment 0x%x\n", __FUNCTION__, + Allocate->Alignment)); + return EFI_UNSUPPORTED; + } + + Status = QemuFwCfgFindFile ((CHAR8 *)Allocate->File, &FwCfgItem, &FwCfgSize); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "%a: QemuFwCfgFindFile(\"%a\"): %r\n", __FUNCTION__, + Allocate->File, Status)); + return Status; + } + + NumPages = EFI_SIZE_TO_PAGES (FwCfgSize); + Address = 0xFFFFFFFF; + Status = gBS->AllocatePages (AllocateMaxAddress, EfiACPIMemoryNVS, NumPages, + &Address); + if (EFI_ERROR (Status)) { + return Status; + } + + Blob = AllocatePool (sizeof *Blob); + if (Blob == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto FreePages; + } + CopyMem (Blob->File, Allocate->File, QEMU_LOADER_FNAME_SIZE); + Blob->Size = FwCfgSize; + Blob->Base = (VOID *)(UINTN)Address; + Blob->HostsOnlyTableData = TRUE; + + Status = OrderedCollectionInsert (Tracker, NULL, Blob); + if (Status == RETURN_ALREADY_STARTED) { + DEBUG ((EFI_D_ERROR, "%a: duplicated file \"%a\"\n", __FUNCTION__, + Allocate->File)); + Status = EFI_PROTOCOL_ERROR; + } + if (EFI_ERROR (Status)) { + goto FreeBlob; + } + + QemuFwCfgSelectItem (FwCfgItem); + QemuFwCfgReadBytes (FwCfgSize, Blob->Base); + ZeroMem (Blob->Base + Blob->Size, EFI_PAGES_TO_SIZE (NumPages) - Blob->Size); + + DEBUG ((EFI_D_VERBOSE, "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx " + "Address=0x%Lx\n", __FUNCTION__, Allocate->File, Allocate->Alignment, + Allocate->Zone, (UINT64)Blob->Size, (UINT64)(UINTN)Blob->Base)); + return EFI_SUCCESS; + +FreeBlob: + FreePool (Blob); + +FreePages: + gBS->FreePages (Address, NumPages); + + return Status; +} + + +/** + Process a QEMU_LOADER_ADD_POINTER command. + + @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process. + + @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user + structures created thus far. + + @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in + AddPointer, or the AddPointer command references + a file unknown to Tracker, or the pointer to + relocate has invalid location, size, or value, or + the relocated pointer value is not representable + in the given pointer size. + + @retval EFI_SUCCESS The pointer field inside the pointer blob has + been relocated. +**/ +STATIC +EFI_STATUS +EFIAPI +ProcessCmdAddPointer ( + IN CONST QEMU_LOADER_ADD_POINTER *AddPointer, + IN CONST ORDERED_COLLECTION *Tracker + ) +{ + ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2; + BLOB *Blob, *Blob2; + UINT8 *PointerField; + UINT64 PointerValue; + + if (AddPointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0' || + AddPointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') { + DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__)); + return EFI_PROTOCOL_ERROR; + } + + TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile); + TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile); + if (TrackerEntry == NULL || TrackerEntry2 == NULL) { + DEBUG ((EFI_D_ERROR, "%a: invalid blob reference(s) \"%a\" / \"%a\"\n", + __FUNCTION__, AddPointer->PointerFile, AddPointer->PointeeFile)); + return EFI_PROTOCOL_ERROR; + } + + Blob = OrderedCollectionUserStruct (TrackerEntry); + Blob2 = OrderedCollectionUserStruct (TrackerEntry2); + if ((AddPointer->PointerSize != 1 && AddPointer->PointerSize != 2 && + AddPointer->PointerSize != 4 && AddPointer->PointerSize != 8) || + Blob->Size < AddPointer->PointerSize || + Blob->Size - AddPointer->PointerSize < AddPointer->PointerOffset) { + DEBUG ((EFI_D_ERROR, "%a: invalid pointer location or size in \"%a\"\n", + __FUNCTION__, AddPointer->PointerFile)); + return EFI_PROTOCOL_ERROR; + } + + PointerField = Blob->Base + AddPointer->PointerOffset; + PointerValue = 0; + CopyMem (&PointerValue, PointerField, AddPointer->PointerSize); + if (PointerValue >= Blob2->Size) { + DEBUG ((EFI_D_ERROR, "%a: invalid pointer value in \"%a\"\n", __FUNCTION__, + AddPointer->PointerFile)); + 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)Blob2->Base <= MAX_ADDRESS - Blob2->Size); + + PointerValue += (UINT64)(UINTN)Blob2->Base; + if (RShiftU64 ( + RShiftU64 (PointerValue, AddPointer->PointerSize * 8 - 1), 1) != 0) { + DEBUG ((EFI_D_ERROR, "%a: relocated pointer value unrepresentable in " + "\"%a\"\n", __FUNCTION__, AddPointer->PointerFile)); + return EFI_PROTOCOL_ERROR; + } + + CopyMem (PointerField, &PointerValue, AddPointer->PointerSize); + + DEBUG ((EFI_D_VERBOSE, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" " + "PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__, + AddPointer->PointerFile, AddPointer->PointeeFile, + AddPointer->PointerOffset, AddPointer->PointerSize)); + return EFI_SUCCESS; +} + + +/** + Process a QEMU_LOADER_ADD_CHECKSUM command. + + @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process. + + @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user + structures created thus far. + + @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in + AddChecksum, or the AddChecksum command + references a file unknown to Tracker, or the + range to checksum is invalid. + + @retval EFI_SUCCESS The requested range has been checksummed. +**/ +STATIC +EFI_STATUS +EFIAPI +ProcessCmdAddChecksum ( + IN CONST QEMU_LOADER_ADD_CHECKSUM *AddChecksum, + IN CONST ORDERED_COLLECTION *Tracker + ) +{ + ORDERED_COLLECTION_ENTRY *TrackerEntry; + BLOB *Blob; + + if (AddChecksum->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') { + DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__)); + return EFI_PROTOCOL_ERROR; + } + + TrackerEntry = OrderedCollectionFind (Tracker, AddChecksum->File); + if (TrackerEntry == NULL) { + DEBUG ((EFI_D_ERROR, "%a: invalid blob reference \"%a\"\n", __FUNCTION__, + AddChecksum->File)); + return EFI_PROTOCOL_ERROR; + } + + Blob = OrderedCollectionUserStruct (TrackerEntry); + if (Blob->Size <= AddChecksum->ResultOffset || + Blob->Size < AddChecksum->Length || + Blob->Size - AddChecksum->Length < AddChecksum->Start) { + DEBUG ((EFI_D_ERROR, "%a: invalid checksum range in \"%a\"\n", + __FUNCTION__, AddChecksum->File)); + return EFI_PROTOCOL_ERROR; + } + + Blob->Base[AddChecksum->ResultOffset] = CalculateCheckSum8 ( + Blob->Base + AddChecksum->Start, + AddChecksum->Length + ); + DEBUG ((EFI_D_VERBOSE, "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x " + "Length=0x%x\n", __FUNCTION__, AddChecksum->File, + AddChecksum->ResultOffset, AddChecksum->Start, AddChecksum->Length)); + return EFI_SUCCESS; +} + + +// +// 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). +// +#define INSTALLED_TABLES_MAX 128 + +/** + Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte + 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. + + @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process. + + @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user + structures. + + @param[in] AcpiProtocol The ACPI table protocol used to install tables. + + @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN + elements, allocated by the caller. On output, + the function will have stored (appended) the + AcpiProtocol-internal key of the ACPI table that + the function has installed, if the AddPointer + command identified an ACPI table that is + different from RSDT and XSDT. + + @param[in,out] NumInstalled On input, the number of entries already used in + InstalledKey; it must be in [0, + INSTALLED_TABLES_MAX] inclusive. On output, the + parameter is incremented if the AddPointer + command identified an ACPI table that is + different from RSDT and XSDT. + + @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on + input. + + @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI + 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. + + @return Error codes returned by + AcpiProtocol->InstallAcpiTable(). +**/ +STATIC +EFI_STATUS +EFIAPI +Process2ndPassCmdAddPointer ( + IN CONST QEMU_LOADER_ADD_POINTER *AddPointer, + IN CONST ORDERED_COLLECTION *Tracker, + IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol, + IN OUT UINTN InstalledKey[INSTALLED_TABLES_MAX], + IN OUT INT32 *NumInstalled + ) +{ + CONST ORDERED_COLLECTION_ENTRY *TrackerEntry; + CONST ORDERED_COLLECTION_ENTRY *TrackerEntry2; + CONST BLOB *Blob; + BLOB *Blob2; + CONST UINT8 *PointerField; + UINT64 PointerValue; + UINTN Blob2Remaining; + UINTN TableSize; + CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs; + CONST EFI_ACPI_DESCRIPTION_HEADER *Header; + EFI_STATUS Status; + + if (*NumInstalled < 0 || *NumInstalled > INSTALLED_TABLES_MAX) { + return EFI_INVALID_PARAMETER; + } + + TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile); + TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile); + Blob = OrderedCollectionUserStruct (TrackerEntry); + Blob2 = OrderedCollectionUserStruct (TrackerEntry2); + PointerField = Blob->Base + AddPointer->PointerOffset; + PointerValue = 0; + CopyMem (&PointerValue, PointerField, AddPointer->PointerSize); + + // + // We assert that PointerValue falls inside Blob2's contents. This is ensured + // by the Blob2->Size check and later checks in ProcessCmdAddPointer(). + // + Blob2Remaining = (UINTN)Blob2->Base; + ASSERT(PointerValue >= Blob2Remaining); + Blob2Remaining += Blob2->Size; + ASSERT (PointerValue < Blob2Remaining); + + Blob2Remaining -= (UINTN) PointerValue; + DEBUG ((EFI_D_VERBOSE, "%a: checking for ACPI header in \"%a\" at 0x%Lx " + "(remaining: 0x%Lx): ", __FUNCTION__, AddPointer->PointeeFile, + PointerValue, (UINT64)Blob2Remaining)); + + TableSize = 0; + + // + // To make our job simple, the FACS has a custom header. Sigh. + // + if (sizeof *Facs <= Blob2Remaining) { + Facs = (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)PointerValue; + + if (Facs->Length >= sizeof *Facs && + Facs->Length <= Blob2Remaining && + Facs->Signature == + EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) { + DEBUG ((EFI_D_VERBOSE, "found \"%-4.4a\" size 0x%x\n", + (CONST CHAR8 *)&Facs->Signature, Facs->Length)); + TableSize = Facs->Length; + } + } + + // + // check for the uniform tables + // + if (TableSize == 0 && sizeof *Header <= Blob2Remaining) { + Header = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)PointerValue; + + if (Header->Length >= sizeof *Header && + Header->Length <= Blob2Remaining && + CalculateSum8 ((CONST UINT8 *)Header, Header->Length) == 0) { + // + // This looks very much like an ACPI table from QEMU: + // - Length field consistent with both ACPI and containing blob size + // - checksum is correct + // + DEBUG ((EFI_D_VERBOSE, "found \"%-4.4a\" size 0x%x\n", + (CONST CHAR8 *)&Header->Signature, Header->Length)); + TableSize = Header->Length; + + // + // Skip RSDT and XSDT because those are handled by + // EFI_ACPI_TABLE_PROTOCOL automatically. + if (Header->Signature == + EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE || + Header->Signature == + EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) { + return EFI_SUCCESS; + } + } + } + + if (TableSize == 0) { + DEBUG ((EFI_D_VERBOSE, "not found; marking fw_cfg blob as opaque\n")); + Blob2->HostsOnlyTableData = FALSE; + return EFI_SUCCESS; + } + + 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 = AcpiProtocol->InstallAcpiTable (AcpiProtocol, + (VOID *)(UINTN)PointerValue, TableSize, + &InstalledKey[*NumInstalled]); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "%a: InstallAcpiTable(): %r\n", __FUNCTION__, + Status)); + return Status; + } + ++*NumInstalled; + return EFI_SUCCESS; +} + + +/** + Download, process, and install ACPI table data from the QEMU loader + interface. + + @param[in] AcpiProtocol The ACPI table protocol used to install tables. + + @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU + loader command with unsupported parameters + has been found. + + @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg + files. + + @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than + INSTALLED_TABLES_MAX tables found. + + @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents. + + @return Status codes returned by + AcpiProtocol->InstallAcpiTable(). + +**/ +EFI_STATUS +EFIAPI +InstallAllQemuLinkedTables ( + IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol + ) +{ + EFI_STATUS Status; + FIRMWARE_CONFIG_ITEM FwCfgItem; + UINTN FwCfgSize; + QEMU_LOADER_ENTRY *LoaderStart; + CONST QEMU_LOADER_ENTRY *LoaderEntry, *LoaderEnd; + ORDERED_COLLECTION *Tracker; + UINTN *InstalledKey; + INT32 Installed; + ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2; + + Status = QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem, &FwCfgSize); + if (EFI_ERROR (Status)) { + return Status; + } + if (FwCfgSize % sizeof *LoaderEntry != 0) { + DEBUG ((EFI_D_ERROR, "%a: \"etc/table-loader\" has invalid size 0x%Lx\n", + __FUNCTION__, (UINT64)FwCfgSize)); + return EFI_PROTOCOL_ERROR; + } + + LoaderStart = AllocatePool (FwCfgSize); + if (LoaderStart == NULL) { + return EFI_OUT_OF_RESOURCES; + } + QemuFwCfgSelectItem (FwCfgItem); + QemuFwCfgReadBytes (FwCfgSize, LoaderStart); + LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry; + + Tracker = OrderedCollectionInit (BlobCompare, BlobKeyCompare); + if (Tracker == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto FreeLoader; + } + + // + // first pass: process the commands + // + for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) { + switch (LoaderEntry->Type) { + case QemuLoaderCmdAllocate: + Status = ProcessCmdAllocate (&LoaderEntry->Command.Allocate, Tracker); + break; + + case QemuLoaderCmdAddPointer: + Status = ProcessCmdAddPointer (&LoaderEntry->Command.AddPointer, + Tracker); + break; + + case QemuLoaderCmdAddChecksum: + Status = ProcessCmdAddChecksum (&LoaderEntry->Command.AddChecksum, + Tracker); + break; + + default: + DEBUG ((EFI_D_VERBOSE, "%a: unknown loader command: 0x%x\n", + __FUNCTION__, LoaderEntry->Type)); + break; + } + + if (EFI_ERROR (Status)) { + goto FreeTracker; + } + } + + InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey); + if (InstalledKey == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto FreeTracker; + } + + // + // second pass: identify and install ACPI tables + // + Installed = 0; + for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) { + if (LoaderEntry->Type == QemuLoaderCmdAddPointer) { + Status = Process2ndPassCmdAddPointer (&LoaderEntry->Command.AddPointer, + Tracker, AcpiProtocol, InstalledKey, &Installed); + if (EFI_ERROR (Status)) { + break; + } + } + } + + if (EFI_ERROR (Status)) { + // + // roll back partial installation + // + while (Installed > 0) { + --Installed; + AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]); + } + } else { + DEBUG ((EFI_D_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed)); + } + + FreePool (InstalledKey); + +FreeTracker: + // + // 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 + // not directly part of some ACPI table. + // + for (TrackerEntry = OrderedCollectionMin (Tracker); TrackerEntry != NULL; + TrackerEntry = TrackerEntry2) { + VOID *UserStruct; + BLOB *Blob; + + TrackerEntry2 = OrderedCollectionNext (TrackerEntry); + OrderedCollectionDelete (Tracker, TrackerEntry, &UserStruct); + Blob = UserStruct; + + if (EFI_ERROR (Status) || Blob->HostsOnlyTableData) { + DEBUG ((EFI_D_VERBOSE, "%a: freeing \"%a\"\n", __FUNCTION__, + Blob->File)); + gBS->FreePages ((UINTN)Blob->Base, EFI_SIZE_TO_PAGES (Blob->Size)); + } + FreePool (Blob); + } + OrderedCollectionUninit (Tracker); + +FreeLoader: + FreePool (LoaderStart); + + return Status; +} -- cgit v1.2.3