]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg: AcpiPlatformDxe: download ACPI tables from QEMU
authorLaszlo Ersek <lersek@redhat.com>
Mon, 31 Mar 2014 20:36:06 +0000 (20:36 +0000)
committerjljusten <jljusten@6f19259b-4bc3-4df7-8a09-765794883524>
Mon, 31 Mar 2014 20:36:06 +0000 (20:36 +0000)
Recent qemu versions compose all ACPI tables on the host side, according
to the target hardware configuration, and make the tables available to any
guest firmware over fw_cfg.

See version compatibility information below.

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 fw_cfg files:
- 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.

Qemu compatibility information:

--------------+---------------------+-------------------------------------
 qemu version | qemu machine type   | effects of the patch
--------------+---------------------+-------------------------------------
 up to 1.6.x  | any pc-i440fx       | None. OVMF's built-in ACPI tables
              |                     | are used.
--------------+---------------------+-------------------------------------
 any          | up to pc-i440fx-1.6 | None. OVMF's built-in ACPI tables
              |                     | are used.
--------------+---------------------+-------------------------------------
 1.7.0        | pc-i440fx-1.7       | Potential guest OS crash, dependent
              | (default for 1.7.0) | on guest RAM size.
              |                     |
              |                     | DO NOT RUN OVMF on the (1.7.0,
              |                     | pc-i440fx-1.7) qemu / machine type
              |                     | combination.
--------------+---------------------+-------------------------------------
 1.7.1        | pc-i440fx-1.7       | OVMF downloads valid ACPI tables
              | (default for 1.7.1) | from qemu and passes them to the
              |                     | guest OS.
--------------+---------------------+-------------------------------------
 2.0.0-rc0    | pc-i440fx-1.7 or    | OVMF downloads valid ACPI tables
              | later               | from qemu and passes them to the
              |                     | guest OS.
 -------------+---------------------+-------------------------------------

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@15420 6f19259b-4bc3-4df7-8a09-765794883524

OvmfPkg/AcpiPlatformDxe/AcpiPlatform.c
OvmfPkg/AcpiPlatformDxe/AcpiPlatform.h
OvmfPkg/AcpiPlatformDxe/Qemu.c

index 6e0b6100b50f79df0ea3f316297db44e7dbfe68b..084c3931d08cff4e6cb8fbee3b909f17862bcbf4 100644 (file)
@@ -256,16 +256,14 @@ AcpiPlatformEntryPoint (
 \r
   if (XenDetected ()) {\r
     Status = InstallXenTables (AcpiTable);\r
-    if (EFI_ERROR (Status)) {\r
-      Status = FindAcpiTablesInFv (AcpiTable);\r
-    }\r
   } else {\r
-    Status = FindAcpiTablesInFv (AcpiTable);\r
+    Status = InstallQemuLinkedTables (AcpiTable);\r
   }\r
+\r
   if (EFI_ERROR (Status)) {\r
-    return Status;\r
+    Status = FindAcpiTablesInFv (AcpiTable);\r
   }\r
 \r
-  return EFI_SUCCESS;\r
+  return Status;\r
 }\r
 \r
index 21107cd4ddb2931841c548cc6b09f5ac593b0a15..c643fa13d41c48137e183ee217a6c667efc2f475 100644 (file)
@@ -10,7 +10,7 @@
   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
 \r
-**/ \r
+**/\r
 \r
 #ifndef _ACPI_PLATFORM_H_INCLUDED_\r
 #define _ACPI_PLATFORM_H_INCLUDED_\r
@@ -61,5 +61,10 @@ InstallXenTables (
   IN   EFI_ACPI_TABLE_PROTOCOL       *AcpiProtocol\r
   );\r
 \r
+EFI_STATUS\r
+EFIAPI\r
+InstallQemuLinkedTables (\r
+  IN   EFI_ACPI_TABLE_PROTOCOL       *AcpiProtocol\r
+  );\r
 #endif\r
 \r
index 06bd46370e7fb88254fb44280bcf8919aa00e5bf..e62467a5bc3bafe02a3007a639c127b6b04f93e9 100644 (file)
@@ -515,3 +515,156 @@ QemuInstallAcpiTable (
            );\r
 }\r
 \r
+\r
+/**\r
+  Download the ACPI table data file from QEMU and interpret it.\r
+\r
+  @param[in] AcpiProtocol  The ACPI table protocol used to install tables.\r
+\r
+  @retval  EFI_UNSUPPORTED       Firmware configuration is unavailable.\r
+\r
+  @retval  EFI_NOT_FOUND         The host doesn't export the required fw_cfg\r
+                                 files.\r
+\r
+  @retval  EFI_OUT_OF_RESOURCES  Memory allocation failed.\r
+\r
+  @retval  EFI_PROTOCOL_ERROR    Found truncated or invalid ACPI table header\r
+                                 in the fw_cfg contents.\r
+\r
+  @return                        Status codes returned by\r
+                                 AcpiProtocol->InstallAcpiTable().\r
+\r
+**/\r
+\r
+//\r
+// We'll be saving the keys of installed tables so that we can roll them back\r
+// in case of failure. 128 tables should be enough for anyone (TM).\r
+//\r
+#define INSTALLED_TABLES_MAX 128\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+InstallQemuLinkedTables (\r
+  IN   EFI_ACPI_TABLE_PROTOCOL       *AcpiProtocol\r
+  )\r
+{\r
+  EFI_STATUS           Status;\r
+  FIRMWARE_CONFIG_ITEM TablesFile;\r
+  UINTN                TablesFileSize;\r
+  UINT8                *Tables;\r
+  UINTN                *InstalledKey;\r
+  UINTN                Processed;\r
+  INT32                Installed;\r
+\r
+  Status = QemuFwCfgFindFile ("etc/acpi/tables", &TablesFile, &TablesFileSize);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_INFO, "%a: \"etc/acpi/tables\" interface unavailable: %r\n",\r
+      __FUNCTION__, Status));\r
+    return Status;\r
+  }\r
+\r
+  Tables = AllocatePool (TablesFileSize);\r
+  if (Tables == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  QemuFwCfgSelectItem (TablesFile);\r
+  QemuFwCfgReadBytes (TablesFileSize, Tables);\r
+\r
+  InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);\r
+  if (InstalledKey == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto FreeTables;\r
+  }\r
+\r
+  Processed = 0;\r
+  Installed = 0;\r
+  while (Processed < TablesFileSize) {\r
+    UINTN                       Remaining;\r
+    EFI_ACPI_DESCRIPTION_HEADER *Probe;\r
+\r
+    Remaining = TablesFileSize - Processed;\r
+    if (Remaining < sizeof *Probe) {\r
+      Status = EFI_PROTOCOL_ERROR;\r
+      break;\r
+    }\r
+\r
+    Probe = (EFI_ACPI_DESCRIPTION_HEADER *) (Tables + Processed);\r
+    if (Remaining < Probe->Length || Probe->Length < sizeof *Probe) {\r
+      Status = EFI_PROTOCOL_ERROR;\r
+      break;\r
+    }\r
+\r
+    DEBUG ((EFI_D_VERBOSE, "%a: offset 0x%016Lx:"\r
+      " Signature=\"%-4.4a\" Length=0x%08x\n",\r
+      __FUNCTION__, (UINT64) Processed,\r
+      (CONST CHAR8 *) &Probe->Signature, Probe->Length));\r
+\r
+    //\r
+    // skip automatically handled "root" tables: RSDT, XSDT\r
+    //\r
+    if (Probe->Signature !=\r
+                        EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE &&\r
+        Probe->Signature !=\r
+                    EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {\r
+      if (Installed == INSTALLED_TABLES_MAX) {\r
+        DEBUG ((EFI_D_ERROR, "%a: can't install more than %d tables\n",\r
+          __FUNCTION__, INSTALLED_TABLES_MAX));\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+        break;\r
+      }\r
+\r
+      Status = AcpiProtocol->InstallAcpiTable (AcpiProtocol, Probe,\r
+                 Probe->Length, &InstalledKey[Installed]);\r
+      if (EFI_ERROR (Status)) {\r
+        DEBUG ((EFI_D_ERROR,\r
+          "%a: failed to install table \"%-4.4a\" at offset 0x%Lx: %r\n",\r
+          __FUNCTION__, (CONST CHAR8 *) &Probe->Signature, (UINT64) Processed,\r
+          Status));\r
+        break;\r
+      }\r
+\r
+      ++Installed;\r
+    }\r
+\r
+    Processed += Probe->Length;\r
+  }\r
+\r
+  //\r
+  // NUL-padding at the end is accepted\r
+  //\r
+  if (Status == EFI_PROTOCOL_ERROR) {\r
+    UINTN ErrorLocation;\r
+\r
+    ErrorLocation = Processed;\r
+    while (Processed < TablesFileSize && Tables[Processed] == '\0') {\r
+      ++Processed;\r
+    }\r
+    if (Processed < TablesFileSize) {\r
+      DEBUG ((EFI_D_ERROR, "%a: truncated or invalid ACPI table header at "\r
+        "offset 0x%Lx\n", __FUNCTION__, (UINT64) ErrorLocation));\r
+    }\r
+  }\r
+\r
+  if (Processed == TablesFileSize) {\r
+    DEBUG ((EFI_D_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));\r
+    Status = EFI_SUCCESS;\r
+  } else {\r
+    ASSERT (EFI_ERROR (Status));\r
+\r
+    //\r
+    // Roll back partial installation.\r
+    //\r
+    while (Installed > 0) {\r
+      --Installed;\r
+      AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]);\r
+    }\r
+  }\r
+\r
+  FreePool (InstalledKey);\r
+\r
+FreeTables:\r
+  FreePool (Tables);\r
+\r
+  return Status;\r
+}\r