Qemu v1.7.0 features an ACPI linker/loader interface, available over
fw_cfg, written by Michael Tsirkin.

Qemu composes all ACPI tables on the host side, according to the target
hardware configuration, and makes the tables available to any guest
firmware over fw_cfg.

The feature moves the burden of keeping ACPI tables up-to-date from boot
firmware to qemu (which is the source of hardware configuration anyway).

This patch adds client code for this feature. Benefits of the
qemu-provided ACPI tables include PCI hotplug for example.

Qemu provides the following three files for 1.7+ machine types:
- etc/acpi/rsdp
- etc/acpi/tables
- etc/table-loader

"etc/acpi/rsdp" and "etc/acpi/tables" are similar, they are only kept
separate because they have different allocation requirements in SeaBIOS.

Both of these fw_cfg files contain preformatted ACPI payload.
"etc/acpi/rsdp" contains only the RSDP table, while "etc/acpi/tables"
contains all other tables, concatenated.

The tables in these two fw_cfg files are filled in by qemu, but two kinds
of fields are left incomplete in each table: pointers to other tables, and
checksums (which depend on the pointers).

Qemu initializes each pointer with a relative offset into the fw_cfg file
that contains the pointed-to ACPI table. The final pointer values depend
on where the fw_cfg files, holding the pointed-to ACPI tables, will be
placed in memory by the guest. That is, the pointer fields need to be
"relocated" (incremented) by the base addresses of where "/etc/acpi/rsdp"
and "/etc/acpi/tables" will be placed in guest memory.

This is where the third file, "/etc/table-loader" comes in the picture. It
is a linker/loader script that has several command types:

  One command type instructs the guest to download the other two files.

  Another command type instructs the guest to increment ("absolutize") a
  pointer field (having a relative initial value) in the pointing ACPI
  table, present in some fw_cfg file, with the dynamic base address of the
  same (or another) fw_cfg file, holding the pointed-to ACPI table.

  The third command type instructs the guest to compute checksums over
  ranges and to store them.

In edk2, EFI_ACPI_TABLE_PROTOCOL knows about table relationships -- it
handles linkage automatically when a table is installed. The protocol
takes care of checksumming too. RSDP is installed automatically. Hence we
only need to care about the "etc/acpi/tables" fw_cfg file, determining the
boundaries of each ACPI table inside it, and installing those tables.

Compatibility information:

- The patch has no effect when OVMF runs on qemu versions up to v1.6.x.
  OVMF's built-in ACPI tables are used.

- The patch has no effect when OVMF runs on any qemu version, and a
  machine type up to "pc-i440fx-1.6" is selected. OVMF's built-in ACPI
  tables are used.

- The patch causes a regression in PCI resource allocation for some guest
  RAM sizes (eg. 2560MB) when OVMF runs on qemu v1.7.0 precisely, and the
  "pc-i440fx-1.7" (ie. default) machine type is selected. This bug has
  been fixed in qemu v1.7.1 (commit 03bc4f6 -- "piix: fix 32bit pci
  hole").

- The patch has the intended effect when OVMF runs on qemu versions
  v1.7.1+ (including the current development tree, v2.0.0-rc0) and a
  pc-i440fx-1.7+ machine type is selected.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <[email protected]>
---

Notes:
    v1: http://thread.gmane.org/gmane.comp.bios.tianocore.devel/4881
    v2: http://thread.gmane.org/gmane.comp.bios.tianocore.devel/5103
    v3: this posting
    
    v1->v2 changes:
    - move large comment block from "OvmfPkg/AcpiPlatformDxe/Qemu.c" to
      commit message
    - fall back to builtin tables if 32-bit PCI window has not been found in
      the PEI phase (and consequently could not cause PCI resource
      allocation to match qemu's ACPI tables)
    - trim excessive logging
    
    v2->v3:
    - Support for the "etc/pci-info" fw_cfg file has been revoked in qemu
      v1.7.0 (hence there is no official qemu release that provdies it). The
      bottom of the 32-bit PCI hole that is exported in qemu's DSDT/SSDTs
      has been fixed in qemu v1.7.1 (qemu commit 03bc4f6), thus we don't
      need "etc/pci-info" any longer.
    
    Testing: been running my Linux and Windows guests with this patch for
    months.

 OvmfPkg/AcpiPlatformDxe/AcpiPlatform.h |   7 +-
 OvmfPkg/AcpiPlatformDxe/AcpiPlatform.c |  12 ++-
 OvmfPkg/AcpiPlatformDxe/Qemu.c         | 138 +++++++++++++++++++++++++++++++++
 3 files changed, 149 insertions(+), 8 deletions(-)

diff --git a/OvmfPkg/AcpiPlatformDxe/AcpiPlatform.h 
b/OvmfPkg/AcpiPlatformDxe/AcpiPlatform.h
index 21107cd..c643fa1 100644
--- a/OvmfPkg/AcpiPlatformDxe/AcpiPlatform.h
+++ b/OvmfPkg/AcpiPlatformDxe/AcpiPlatform.h
@@ -10,7 +10,7 @@
   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 
-**/ 
+**/
 
 #ifndef _ACPI_PLATFORM_H_INCLUDED_
 #define _ACPI_PLATFORM_H_INCLUDED_
@@ -61,5 +61,10 @@ InstallXenTables (
   IN   EFI_ACPI_TABLE_PROTOCOL       *AcpiProtocol
   );
 
+EFI_STATUS
+EFIAPI
+InstallQemuLinkedTables (
+  IN   EFI_ACPI_TABLE_PROTOCOL       *AcpiProtocol
+  );
 #endif
 
diff --git a/OvmfPkg/AcpiPlatformDxe/AcpiPlatform.c 
b/OvmfPkg/AcpiPlatformDxe/AcpiPlatform.c
index 6e0b610..084c393 100644
--- a/OvmfPkg/AcpiPlatformDxe/AcpiPlatform.c
+++ b/OvmfPkg/AcpiPlatformDxe/AcpiPlatform.c
@@ -256,16 +256,14 @@ AcpiPlatformEntryPoint (
 
   if (XenDetected ()) {
     Status = InstallXenTables (AcpiTable);
-    if (EFI_ERROR (Status)) {
-      Status = FindAcpiTablesInFv (AcpiTable);
-    }
   } else {
+    Status = InstallQemuLinkedTables (AcpiTable);
+  }
+
+  if (EFI_ERROR (Status)) {
     Status = FindAcpiTablesInFv (AcpiTable);
   }
-  if (EFI_ERROR (Status)) {
-    return Status;
-  }
 
-  return EFI_SUCCESS;
+  return Status;
 }
 
diff --git a/OvmfPkg/AcpiPlatformDxe/Qemu.c b/OvmfPkg/AcpiPlatformDxe/Qemu.c
index 06bd463..91f6db5 100644
--- a/OvmfPkg/AcpiPlatformDxe/Qemu.c
+++ b/OvmfPkg/AcpiPlatformDxe/Qemu.c
@@ -515,3 +515,141 @@ QemuInstallAcpiTable (
            );
 }
 
+
+/**
+  Download the ACPI table data file from QEMU and interpret it.
+
+  @param[in] AcpiProtocol  The ACPI table protocol used to install tables.
+
+  @retval  EFI_UNSUPPORTED       Firmware configuration is unavailable.
+
+  @retval  EFI_NOT_FOUND         The host doesn't export the required fw_cfg
+                                 files.
+
+  @retval  EFI_OUT_OF_RESOURCES  Memory allocation failed.
+
+  @return                        Status codes returned by
+                                 AcpiProtocol->InstallAcpiTable().
+
+**/
+
+//
+// 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
+
+EFI_STATUS
+EFIAPI
+InstallQemuLinkedTables (
+  IN   EFI_ACPI_TABLE_PROTOCOL       *AcpiProtocol
+  )
+{
+  STATIC CONST CHAR8   Func[] = "InstallQemuLinkedTables";
+  EFI_STATUS           Status;
+  FIRMWARE_CONFIG_ITEM TablesFile;
+  UINTN                TablesFileSize;
+  UINT8                *Tables;
+  UINTN                *InstalledKey;
+  UINTN                Processed;
+  INT32                Installed;
+
+  Status = QemuFwCfgFindFile ("etc/acpi/tables", &TablesFile, &TablesFileSize);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((EFI_D_INFO, "%a: \"etc/acpi/tables\" interface unavailable: %r\n",
+      Func, Status));
+    return Status;
+  }
+
+  Tables = AllocatePool (TablesFileSize);
+  if (Tables == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  QemuFwCfgSelectItem (TablesFile);
+  QemuFwCfgReadBytes (TablesFileSize, Tables);
+
+  InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);
+  if (InstalledKey == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto FreeTables;
+  }
+
+  Processed = 0;
+  Installed = 0;
+  while (Processed < TablesFileSize) {
+    UINTN                       Remaining;
+    EFI_ACPI_DESCRIPTION_HEADER *Probe;
+
+    Remaining = TablesFileSize - Processed;
+    if (Remaining < sizeof *Probe) {
+      DEBUG ((EFI_D_VERBOSE, "%a: truncated ACPI table header at offset "
+        "0x%Lx\n", Func, (UINT64) Processed));
+      Status = EFI_PROTOCOL_ERROR;
+      break;
+    }
+
+    Probe = (EFI_ACPI_DESCRIPTION_HEADER *) (Tables + Processed);
+    DEBUG ((EFI_D_VERBOSE, "%a: offset 0x%016Lx:"
+      " Signature=\"%-4.4a\" Length=0x%08x\n",
+      Func, (UINT64) Processed,
+      (CONST CHAR8 *) &Probe->Signature, Probe->Length));
+
+    if (Remaining < Probe->Length || Probe->Length < sizeof *Probe) {
+      DEBUG ((EFI_D_VERBOSE, "%a: invalid ACPI table header at offset 0x%Lx\n",
+        Func, (UINT64) Processed));
+      Status = EFI_PROTOCOL_ERROR;
+      break;
+    }
+
+    //
+    // skip automatically handled "root" tables: RSDT, XSDT
+    //
+    if (Probe->Signature !=
+                        EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE &&
+        Probe->Signature !=
+                    EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {
+      if (Installed == INSTALLED_TABLES_MAX) {
+        DEBUG ((EFI_D_ERROR, "%a: can't install more than %d tables\n", Func,
+          INSTALLED_TABLES_MAX));
+        Status = EFI_OUT_OF_RESOURCES;
+        break;
+      }
+
+      Status = AcpiProtocol->InstallAcpiTable (AcpiProtocol, Probe,
+                 Probe->Length, &InstalledKey[Installed]);
+      if (EFI_ERROR (Status)) {
+        DEBUG ((EFI_D_ERROR,
+          "%a: failed to install table \"%-4.4a\" at offset 0x%Lx: %r\n", Func,
+          (CONST CHAR8 *) &Probe->Signature, (UINT64) Processed, Status));
+        break;
+      }
+
+      ++Installed;
+    }
+
+    Processed += Probe->Length;
+  }
+
+  if (Processed == TablesFileSize || Status == EFI_PROTOCOL_ERROR) {
+    DEBUG ((EFI_D_INFO, "%a: installed %d tables\n", Func, Installed));
+    Status = EFI_SUCCESS;
+  } else {
+    ASSERT (EFI_ERROR (Status));
+
+    //
+    // Roll back partial installation.
+    //
+    while (Installed > 0) {
+      --Installed;
+      AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]);
+    }
+  }
+
+  FreePool (InstalledKey);
+
+FreeTables:
+  FreePool (Tables);
+
+  return Status;
+}
-- 
1.8.3.1


------------------------------------------------------------------------------
Learn Graph Databases - Download FREE O'Reilly Book
"Graph Databases" is the definitive new guide to graph databases and their
applications. Written by three acclaimed leaders in the field,
this first edition is now available. Download your free book today!
http://p.sf.net/sfu/13534_NeoTech
_______________________________________________
edk2-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/edk2-devel

Reply via email to