[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