summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJordan Justen <jordan.l.justen@intel.com>2015-02-02 19:08:57 +0000
committerlersek <lersek@Edk2>2015-02-02 19:08:57 +0000
commit14b0faadfc87be12398964015ee0884d71bd0e7b (patch)
treedf01005babb022b0409d2778806f44c002a3b74b
parent9054e55a22cb7408bd57f3c3f6c85c7fab930abb (diff)
downloadedk2-14b0faadfc87be12398964015ee0884d71bd0e7b.tar.gz
edk2-14b0faadfc87be12398964015ee0884d71bd0e7b.tar.bz2
edk2-14b0faadfc87be12398964015ee0884d71bd0e7b.zip
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 <jordan.l.justen@intel.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Laszlo Ersek <lersek@redhat.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16696 6f19259b-4bc3-4df7-8a09-765794883524
-rw-r--r--OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf1
-rw-r--r--OvmfPkg/AcpiPlatformDxe/Qemu.c660
-rw-r--r--OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c685
3 files changed, 686 insertions, 660 deletions
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.<BR>
+ 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 <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/PcdLib.h>
+#include <Library/OrderedCollectionLib.h>
+#include <IndustryStandard/Acpi.h>
+
+
+//
+// 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;
+}