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