On Tue, Mar 25, 2014 at 4:58 PM, Laszlo Ersek <[email protected]> wrote:
> Qemu v1.7.0 features an ACPI linker/loader interface, available over
> fw_cfg, written by Michael Tsirkin.
Can we add something like:
(See 1.7.0 compatibility information below.)
> 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").
Doesn't this cause a hang in certain situations? Could you more
clearly indicate the symptom seen on 1.7.0?
Depending on the symptom, we might want to update the README.
> - 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";
The name Func makes me think this might be a function pointer.
What about just using __FUNCTION__ in place of Func?
> + 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) {
PROTOCOL_ERROR seems odd here. Why does this situation get turned into
'success'?
Maybe just set Processed = TablesFileSize (with a comment) up above
before the break?
-Jordan
> + 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
------------------------------------------------------------------------------
_______________________________________________
edk2-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/edk2-devel