qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [edk2 PATCH 1/1] OvmfPkg/AcpiPlatformDxe: download ACPI tab


From: Laszlo Ersek
Subject: [Qemu-devel] [edk2 PATCH 1/1] OvmfPkg/AcpiPlatformDxe: download ACPI tables from QEMU
Date: Tue, 12 Nov 2013 16:11:50 +0100

Qemu v1.7.0-rc0 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.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <address@hidden>
---
 OvmfPkg/AcpiPlatformDxe/AcpiPlatform.h |   7 +-
 OvmfPkg/AcpiPlatformDxe/AcpiPlatform.c |  12 +-
 OvmfPkg/AcpiPlatformDxe/Qemu.c         | 204 +++++++++++++++++++++++++++++++++
 3 files changed, 215 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..c572f8a 100644
--- a/OvmfPkg/AcpiPlatformDxe/Qemu.c
+++ b/OvmfPkg/AcpiPlatformDxe/Qemu.c
@@ -515,3 +515,207 @@ QemuInstallAcpiTable (
            );
 }
 
+
+/**
+  Download the ACPI table data file from QEMU and interpret it.
+
+  Starting with v1.7.0-rc0, 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 files have been filled in by qemu, but two kinds of
+  fields are incomplete in each table: pointers to other tables, and checksums
+  (which depend on the pointers). The pointers are pre-initialized with
+  *relative* offsets, but their final values depend on where the actual files
+  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 into 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 some file, with the
+    dynamic base address of the same (or another) file.
+
+    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" file, determining the boundaries of each
+  table and installing 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 "etc/acpi/tables"
+                                 fw_cfg file.
+
+  @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
+
+//
+// This macro produces three arguments for DEBUG(), for the format string
+// "%-*.*a" -- left-justify, take field width, take number of characters to
+// consume, ASCII character string. The argument must be a valid pointer.
+//
+#define DBGSTR(ptr) (sizeof *(ptr)), (sizeof *(ptr)), ((CONST CHAR8 *) (ptr))
+
+//
+// Introduce a macro for the format string as well, bracketed by embedded
+// double quotes.
+//
+#define DBGFMT "\"%-*.*a\""
+
+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=" DBGFMT
+      " Length=0x%08x"
+      " Revision=0x%02x"
+      " OemId=" DBGFMT
+      " OemTableId=" DBGFMT
+      " OemRevision=0x%08x"
+      " CreatorId=" DBGFMT
+      " CreatorRevision=0x%08x\n",
+      Func, (UINT64) Processed,
+      DBGSTR (&Probe->Signature),
+      Probe->Length,
+      Probe->Revision,
+      DBGSTR (&Probe->OemId),
+      DBGSTR (&Probe->OemTableId),
+      Probe->OemRevision,
+      DBGSTR (&Probe->CreatorId),
+      Probe->CreatorRevision));
+
+    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 " DBGFMT " at offset 0x%Lx: %r\n", Func,
+          DBGSTR (&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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]