summaryrefslogtreecommitdiffstats
path: root/OvmfPkg/XenAcpiPlatformDxe
diff options
context:
space:
mode:
Diffstat (limited to 'OvmfPkg/XenAcpiPlatformDxe')
-rw-r--r--OvmfPkg/XenAcpiPlatformDxe/AcpiPlatform.c268
-rw-r--r--OvmfPkg/XenAcpiPlatformDxe/AcpiPlatform.h102
-rw-r--r--OvmfPkg/XenAcpiPlatformDxe/BootScript.c269
-rw-r--r--OvmfPkg/XenAcpiPlatformDxe/EntryPoint.c95
-rw-r--r--OvmfPkg/XenAcpiPlatformDxe/PciDecoding.c194
-rw-r--r--OvmfPkg/XenAcpiPlatformDxe/Qemu.c511
-rw-r--r--OvmfPkg/XenAcpiPlatformDxe/QemuFwCfgAcpi.c1196
-rw-r--r--OvmfPkg/XenAcpiPlatformDxe/Xen.c316
-rw-r--r--OvmfPkg/XenAcpiPlatformDxe/XenAcpiPlatformDxe.inf70
9 files changed, 3021 insertions, 0 deletions
diff --git a/OvmfPkg/XenAcpiPlatformDxe/AcpiPlatform.c b/OvmfPkg/XenAcpiPlatformDxe/AcpiPlatform.c
new file mode 100644
index 0000000000..2b2dc57675
--- /dev/null
+++ b/OvmfPkg/XenAcpiPlatformDxe/AcpiPlatform.c
@@ -0,0 +1,268 @@
+/** @file
+ OVMF ACPI Platform Driver for Xen guests
+
+ Copyright (C) 2021, Red Hat, Inc.
+ Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/DebugLib.h> // ASSERT_EFI_ERROR()
+#include <Library/UefiBootServicesTableLib.h> // gBS
+#include <Library/XenPlatformLib.h> // XenDetected()
+#include <Protocol/FirmwareVolume2.h> // gEfiFirmwareVolume2Protocol...
+
+#include "AcpiPlatform.h"
+
+EFI_STATUS
+EFIAPI
+InstallAcpiTable (
+ IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,
+ IN VOID *AcpiTableBuffer,
+ IN UINTN AcpiTableBufferSize,
+ OUT UINTN *TableKey
+ )
+{
+ return AcpiProtocol->InstallAcpiTable (
+ AcpiProtocol,
+ AcpiTableBuffer,
+ AcpiTableBufferSize,
+ TableKey
+ );
+}
+
+
+/**
+ Locate the first instance of a protocol. If the protocol requested is an
+ FV protocol, then it will return the first FV that contains the ACPI table
+ storage file.
+
+ @param Instance Return pointer to the first instance of the protocol
+
+ @return EFI_SUCCESS The function completed successfully.
+ @return EFI_NOT_FOUND The protocol could not be located.
+ @return EFI_OUT_OF_RESOURCES There are not enough resources to find the protocol.
+
+**/
+EFI_STATUS
+LocateFvInstanceWithTables (
+ OUT EFI_FIRMWARE_VOLUME2_PROTOCOL **Instance
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE *HandleBuffer;
+ UINTN NumberOfHandles;
+ EFI_FV_FILETYPE FileType;
+ UINT32 FvStatus;
+ EFI_FV_FILE_ATTRIBUTES Attributes;
+ UINTN Size;
+ UINTN Index;
+ EFI_FIRMWARE_VOLUME2_PROTOCOL *FvInstance;
+
+ FvStatus = 0;
+
+ //
+ // Locate protocol.
+ //
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiFirmwareVolume2ProtocolGuid,
+ NULL,
+ &NumberOfHandles,
+ &HandleBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // Defined errors at this time are not found and out of resources.
+ //
+ return Status;
+ }
+
+ //
+ // Looking for FV with ACPI storage file
+ //
+ for (Index = 0; Index < NumberOfHandles; Index++) {
+ //
+ // Get the protocol on this handle
+ // This should not fail because of LocateHandleBuffer
+ //
+ Status = gBS->HandleProtocol (
+ HandleBuffer[Index],
+ &gEfiFirmwareVolume2ProtocolGuid,
+ (VOID**) &FvInstance
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // See if it has the ACPI storage file
+ //
+ Status = FvInstance->ReadFile (
+ FvInstance,
+ (EFI_GUID*)PcdGetPtr (PcdAcpiTableStorageFile),
+ NULL,
+ &Size,
+ &FileType,
+ &Attributes,
+ &FvStatus
+ );
+
+ //
+ // If we found it, then we are done
+ //
+ if (Status == EFI_SUCCESS) {
+ *Instance = FvInstance;
+ break;
+ }
+ }
+
+ //
+ // Our exit status is determined by the success of the previous operations
+ // If the protocol was found, Instance already points to it.
+ //
+
+ //
+ // Free any allocated buffers
+ //
+ gBS->FreePool (HandleBuffer);
+
+ return Status;
+}
+
+
+/**
+ Find ACPI tables in an FV and install them.
+
+ This is now a fall-back path. Normally, we will search for tables provided
+ by the VMM first.
+
+ If that fails, we use this function to load the ACPI tables from an FV. The
+ sources for the FV based tables is located under OvmfPkg/AcpiTables.
+
+ @param AcpiTable Protocol instance pointer
+
+**/
+EFI_STATUS
+EFIAPI
+InstallOvmfFvTables (
+ IN EFI_ACPI_TABLE_PROTOCOL *AcpiTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_FIRMWARE_VOLUME2_PROTOCOL *FwVol;
+ INTN Instance;
+ EFI_ACPI_COMMON_HEADER *CurrentTable;
+ UINTN TableHandle;
+ UINT32 FvStatus;
+ UINTN TableSize;
+ UINTN Size;
+ EFI_ACPI_TABLE_INSTALL_ACPI_TABLE TableInstallFunction;
+
+ Instance = 0;
+ CurrentTable = NULL;
+ TableHandle = 0;
+
+ if (QemuDetected ()) {
+ TableInstallFunction = QemuInstallAcpiTable;
+ } else {
+ TableInstallFunction = InstallAcpiTable;
+ }
+
+ //
+ // set FwVol (and use an ASSERT() below) to suppress incorrect
+ // compiler/analyzer warnings
+ //
+ FwVol = NULL;
+ //
+ // Locate the firmware volume protocol
+ //
+ Status = LocateFvInstanceWithTables (&FwVol);
+ if (EFI_ERROR (Status)) {
+ return EFI_ABORTED;
+ }
+ ASSERT (FwVol != NULL);
+
+ //
+ // Read tables from the storage file.
+ //
+ while (Status == EFI_SUCCESS) {
+
+ Status = FwVol->ReadSection (
+ FwVol,
+ (EFI_GUID*)PcdGetPtr (PcdAcpiTableStorageFile),
+ EFI_SECTION_RAW,
+ Instance,
+ (VOID**) &CurrentTable,
+ &Size,
+ &FvStatus
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // Add the table
+ //
+ TableHandle = 0;
+
+ TableSize = ((EFI_ACPI_DESCRIPTION_HEADER *) CurrentTable)->Length;
+ ASSERT (Size >= TableSize);
+
+ //
+ // Install ACPI table
+ //
+ Status = TableInstallFunction (
+ AcpiTable,
+ CurrentTable,
+ TableSize,
+ &TableHandle
+ );
+
+ //
+ // Free memory allocated by ReadSection
+ //
+ gBS->FreePool (CurrentTable);
+
+ if (EFI_ERROR (Status)) {
+ return EFI_ABORTED;
+ }
+
+ //
+ // Increment the instance
+ //
+ Instance++;
+ CurrentTable = NULL;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Effective entrypoint of Acpi Platform driver.
+
+ @param ImageHandle
+ @param SystemTable
+
+ @return EFI_SUCCESS
+ @return EFI_LOAD_ERROR
+ @return EFI_OUT_OF_RESOURCES
+
+**/
+EFI_STATUS
+EFIAPI
+InstallAcpiTables (
+ IN EFI_ACPI_TABLE_PROTOCOL *AcpiTable
+ )
+{
+ EFI_STATUS Status;
+
+ if (XenDetected ()) {
+ Status = InstallXenTables (AcpiTable);
+ } else {
+ Status = InstallQemuFwCfgTables (AcpiTable);
+ }
+
+ if (EFI_ERROR (Status)) {
+ Status = InstallOvmfFvTables (AcpiTable);
+ }
+
+ return Status;
+}
+
diff --git a/OvmfPkg/XenAcpiPlatformDxe/AcpiPlatform.h b/OvmfPkg/XenAcpiPlatformDxe/AcpiPlatform.h
new file mode 100644
index 0000000000..6259697c4b
--- /dev/null
+++ b/OvmfPkg/XenAcpiPlatformDxe/AcpiPlatform.h
@@ -0,0 +1,102 @@
+/** @file
+ OVMF ACPI Platform Driver for Xen guests
+
+ Copyright (C) 2021, Red Hat, Inc.
+ Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef ACPI_PLATFORM_H_
+#define ACPI_PLATFORM_H_
+
+#include <Protocol/AcpiTable.h> // EFI_ACPI_TABLE_PROTOCOL
+#include <Protocol/PciIo.h> // EFI_PCI_IO_PROTOCOL
+
+typedef struct {
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ UINT64 PciAttributes;
+} ORIGINAL_ATTRIBUTES;
+
+typedef struct S3_CONTEXT S3_CONTEXT;
+
+EFI_STATUS
+EFIAPI
+InstallAcpiTable (
+ IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,
+ IN VOID *AcpiTableBuffer,
+ IN UINTN AcpiTableBufferSize,
+ OUT UINTN *TableKey
+ );
+
+BOOLEAN
+QemuDetected (
+ VOID
+ );
+
+EFI_STATUS
+EFIAPI
+QemuInstallAcpiTable (
+ IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,
+ IN VOID *AcpiTableBuffer,
+ IN UINTN AcpiTableBufferSize,
+ OUT UINTN *TableKey
+ );
+
+EFI_STATUS
+EFIAPI
+InstallXenTables (
+ IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol
+ );
+
+EFI_STATUS
+EFIAPI
+InstallQemuFwCfgTables (
+ IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol
+ );
+
+EFI_STATUS
+EFIAPI
+InstallAcpiTables (
+ IN EFI_ACPI_TABLE_PROTOCOL *AcpiTable
+ );
+
+VOID
+EnablePciDecoding (
+ OUT ORIGINAL_ATTRIBUTES **OriginalAttributes,
+ OUT UINTN *Count
+ );
+
+VOID
+RestorePciDecoding (
+ IN ORIGINAL_ATTRIBUTES *OriginalAttributes,
+ IN UINTN Count
+ );
+
+EFI_STATUS
+AllocateS3Context (
+ OUT S3_CONTEXT **S3Context,
+ IN UINTN WritePointerCount
+ );
+
+VOID
+ReleaseS3Context (
+ IN S3_CONTEXT *S3Context
+ );
+
+EFI_STATUS
+SaveCondensedWritePointerToS3Context (
+ IN OUT S3_CONTEXT *S3Context,
+ IN UINT16 PointerItem,
+ IN UINT8 PointerSize,
+ IN UINT32 PointerOffset,
+ IN UINT64 PointerValue
+ );
+
+EFI_STATUS
+TransferS3ContextToBootScript (
+ IN S3_CONTEXT *S3Context
+ );
+
+#endif
+
diff --git a/OvmfPkg/XenAcpiPlatformDxe/BootScript.c b/OvmfPkg/XenAcpiPlatformDxe/BootScript.c
new file mode 100644
index 0000000000..14d1e68694
--- /dev/null
+++ b/OvmfPkg/XenAcpiPlatformDxe/BootScript.c
@@ -0,0 +1,269 @@
+/** @file
+ Append an ACPI S3 Boot Script fragment from the QEMU_LOADER_WRITE_POINTER
+ commands of QEMU's fully processed table linker/loader script.
+
+ Copyright (C) 2017-2021, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/BaseLib.h> // CpuDeadLoop()
+#include <Library/DebugLib.h> // DEBUG()
+#include <Library/MemoryAllocationLib.h> // AllocatePool()
+#include <Library/QemuFwCfgS3Lib.h> // QemuFwCfgS3ScriptSkipBytes()
+
+#include "AcpiPlatform.h"
+
+
+//
+// Condensed structure for capturing the fw_cfg operations -- select, skip,
+// write -- inherent in executing a QEMU_LOADER_WRITE_POINTER command.
+//
+typedef struct {
+ UINT16 PointerItem; // resolved from QEMU_LOADER_WRITE_POINTER.PointerFile
+ UINT8 PointerSize; // copied as-is from QEMU_LOADER_WRITE_POINTER
+ UINT32 PointerOffset; // copied as-is from QEMU_LOADER_WRITE_POINTER
+ UINT64 PointerValue; // resolved from QEMU_LOADER_WRITE_POINTER.PointeeFile
+ // and QEMU_LOADER_WRITE_POINTER.PointeeOffset
+} CONDENSED_WRITE_POINTER;
+
+
+//
+// Context structure to accumulate CONDENSED_WRITE_POINTER objects from
+// QEMU_LOADER_WRITE_POINTER commands.
+//
+// Any pointers in this structure own the pointed-to objects; that is, when the
+// context structure is released, all pointed-to objects must be released too.
+//
+struct S3_CONTEXT {
+ CONDENSED_WRITE_POINTER *WritePointers; // one array element per processed
+ // QEMU_LOADER_WRITE_POINTER
+ // command
+ UINTN Allocated; // number of elements allocated for
+ // WritePointers
+ UINTN Used; // number of elements populated in
+ // WritePointers
+};
+
+
+//
+// Scratch buffer, allocated in EfiReservedMemoryType type memory, for the ACPI
+// S3 Boot Script opcodes to work on.
+//
+#pragma pack (1)
+typedef union {
+ UINT64 PointerValue; // filled in from CONDENSED_WRITE_POINTER.PointerValue
+} SCRATCH_BUFFER;
+#pragma pack ()
+
+
+/**
+ Allocate an S3_CONTEXT object.
+
+ @param[out] S3Context The allocated S3_CONTEXT object is returned
+ through this parameter.
+
+ @param[in] WritePointerCount Number of CONDENSED_WRITE_POINTER elements to
+ allocate room for. WritePointerCount must be
+ positive.
+
+ @retval EFI_SUCCESS Allocation successful.
+
+ @retval EFI_OUT_OF_RESOURCES Out of memory.
+
+ @retval EFI_INVALID_PARAMETER WritePointerCount is zero.
+**/
+EFI_STATUS
+AllocateS3Context (
+ OUT S3_CONTEXT **S3Context,
+ IN UINTN WritePointerCount
+ )
+{
+ EFI_STATUS Status;
+ S3_CONTEXT *Context;
+
+ if (WritePointerCount == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Context = AllocateZeroPool (sizeof *Context);
+ if (Context == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Context->WritePointers = AllocatePool (WritePointerCount *
+ sizeof *Context->WritePointers);
+ if (Context->WritePointers == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto FreeContext;
+ }
+
+ Context->Allocated = WritePointerCount;
+ *S3Context = Context;
+ return EFI_SUCCESS;
+
+FreeContext:
+ FreePool (Context);
+
+ return Status;
+}
+
+
+/**
+ Release an S3_CONTEXT object.
+
+ @param[in] S3Context The object to release.
+**/
+VOID
+ReleaseS3Context (
+ IN S3_CONTEXT *S3Context
+ )
+{
+ FreePool (S3Context->WritePointers);
+ FreePool (S3Context);
+}
+
+
+/**
+ Save the information necessary to replicate a QEMU_LOADER_WRITE_POINTER
+ command during S3 resume, in condensed format.
+
+ This function is to be called from ProcessCmdWritePointer(), after all the
+ sanity checks have passed, and before the fw_cfg operations are performed.
+
+ @param[in,out] S3Context The S3_CONTEXT object into which the caller wants
+ to save the information that was derived from
+ QEMU_LOADER_WRITE_POINTER.
+
+ @param[in] PointerItem The FIRMWARE_CONFIG_ITEM that
+ QEMU_LOADER_WRITE_POINTER.PointerFile was resolved
+ to, expressed as a UINT16 value.
+
+ @param[in] PointerSize Copied directly from
+ QEMU_LOADER_WRITE_POINTER.PointerSize.
+
+ @param[in] PointerOffset Copied directly from
+ QEMU_LOADER_WRITE_POINTER.PointerOffset.
+
+ @param[in] PointerValue The base address of the allocated / downloaded
+ fw_cfg blob that is identified by
+ QEMU_LOADER_WRITE_POINTER.PointeeFile, plus
+ QEMU_LOADER_WRITE_POINTER.PointeeOffset.
+
+ @retval EFI_SUCCESS The information derived from
+ QEMU_LOADER_WRITE_POINTER has been successfully
+ absorbed into S3Context.
+
+ @retval EFI_OUT_OF_RESOURCES No room available in S3Context.
+**/
+EFI_STATUS
+SaveCondensedWritePointerToS3Context (
+ IN OUT S3_CONTEXT *S3Context,
+ IN UINT16 PointerItem,
+ IN UINT8 PointerSize,
+ IN UINT32 PointerOffset,
+ IN UINT64 PointerValue
+ )
+{
+ CONDENSED_WRITE_POINTER *Condensed;
+
+ if (S3Context->Used == S3Context->Allocated) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Condensed = S3Context->WritePointers + S3Context->Used;
+ Condensed->PointerItem = PointerItem;
+ Condensed->PointerSize = PointerSize;
+ Condensed->PointerOffset = PointerOffset;
+ Condensed->PointerValue = PointerValue;
+ DEBUG ((DEBUG_VERBOSE, "%a: 0x%04x/[0x%08x+%d] := 0x%Lx (%Lu)\n",
+ __FUNCTION__, PointerItem, PointerOffset, PointerSize, PointerValue,
+ (UINT64)S3Context->Used));
+ ++S3Context->Used;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION provided to QemuFwCfgS3Lib.
+**/
+STATIC
+VOID
+EFIAPI
+AppendFwCfgBootScript (
+ IN OUT VOID *Context, OPTIONAL
+ IN OUT VOID *ExternalScratchBuffer
+ )
+{
+ S3_CONTEXT *S3Context;
+ SCRATCH_BUFFER *ScratchBuffer;
+ UINTN Index;
+
+ S3Context = Context;
+ ScratchBuffer = ExternalScratchBuffer;
+
+ for (Index = 0; Index < S3Context->Used; ++Index) {
+ CONST CONDENSED_WRITE_POINTER *Condensed;
+ RETURN_STATUS Status;
+
+ Condensed = &S3Context->WritePointers[Index];
+
+ Status = QemuFwCfgS3ScriptSkipBytes (Condensed->PointerItem,
+ Condensed->PointerOffset);
+ if (RETURN_ERROR (Status)) {
+ goto FatalError;
+ }
+
+ ScratchBuffer->PointerValue = Condensed->PointerValue;
+ Status = QemuFwCfgS3ScriptWriteBytes (-1, Condensed->PointerSize);
+ if (RETURN_ERROR (Status)) {
+ goto FatalError;
+ }
+ }
+
+ DEBUG ((DEBUG_VERBOSE, "%a: boot script fragment saved\n", __FUNCTION__));
+
+ ReleaseS3Context (S3Context);
+ return;
+
+FatalError:
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+}
+
+
+/**
+ Translate and append the information from an S3_CONTEXT object to the ACPI S3
+ Boot Script.
+
+ The effects of a successful call to this function cannot be undone.
+
+ @param[in] S3Context The S3_CONTEXT object to translate to ACPI S3 Boot
+ Script opcodes. If the function returns successfully,
+ the caller must set the S3Context pointer -- originally
+ returned by AllocateS3Context() -- immediately to NULL,
+ because the ownership of S3Context has been transferred.
+
+ @retval EFI_SUCCESS The translation of S3Context to ACPI S3 Boot Script
+ opcodes has been successfully executed or queued. (This
+ includes the case when S3Context was empty on input and
+ no ACPI S3 Boot Script opcodes have been necessary to
+ produce.)
+
+ @return Error codes from underlying functions.
+**/
+EFI_STATUS
+TransferS3ContextToBootScript (
+ IN S3_CONTEXT *S3Context
+ )
+{
+ RETURN_STATUS Status;
+
+ if (S3Context->Used == 0) {
+ ReleaseS3Context (S3Context);
+ return EFI_SUCCESS;
+ }
+
+ Status = QemuFwCfgS3CallWhenBootScriptReady (AppendFwCfgBootScript,
+ S3Context, sizeof (SCRATCH_BUFFER));
+ return (EFI_STATUS)Status;
+}
diff --git a/OvmfPkg/XenAcpiPlatformDxe/EntryPoint.c b/OvmfPkg/XenAcpiPlatformDxe/EntryPoint.c
new file mode 100644
index 0000000000..b6d0835fe3
--- /dev/null
+++ b/OvmfPkg/XenAcpiPlatformDxe/EntryPoint.c
@@ -0,0 +1,95 @@
+/** @file
+ Entry point of OVMF ACPI Platform Driver for Xen guests
+
+ Copyright (C) 2015-2021, Red Hat, Inc.
+ Copyright (c) 2008 - 2015, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Guid/RootBridgesConnectedEventGroup.h> // gRootBridgesConnectedEve...
+#include <Library/DebugLib.h> // DEBUG()
+#include <Library/PcdLib.h> // PcdGetBool()
+#include <Library/UefiBootServicesTableLib.h> // gBS
+#include <Protocol/AcpiTable.h> // EFI_ACPI_TABLE_PROTOCOL
+
+#include "AcpiPlatform.h"
+
+STATIC
+EFI_ACPI_TABLE_PROTOCOL *
+FindAcpiTableProtocol (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_ACPI_TABLE_PROTOCOL *AcpiTable;
+
+ Status = gBS->LocateProtocol (
+ &gEfiAcpiTableProtocolGuid,
+ NULL,
+ (VOID**)&AcpiTable
+ );
+ ASSERT_EFI_ERROR (Status);
+ return AcpiTable;
+}
+
+
+STATIC
+VOID
+EFIAPI
+OnRootBridgesConnected (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+
+ DEBUG ((DEBUG_INFO,
+ "%a: root bridges have been connected, installing ACPI tables\n",
+ __FUNCTION__));
+ Status = InstallAcpiTables (FindAcpiTableProtocol ());
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: InstallAcpiTables: %r\n", __FUNCTION__, Status));
+ }
+ gBS->CloseEvent (Event);
+}
+
+
+EFI_STATUS
+EFIAPI
+AcpiPlatformEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_EVENT RootBridgesConnected;
+
+ //
+ // If the platform doesn't support PCI, or PCI enumeration has been disabled,
+ // install the tables at once, and let the entry point's return code reflect
+ // the full functionality.
+ //
+ if (PcdGetBool (PcdPciDisableBusEnumeration)) {
+ DEBUG ((DEBUG_INFO, "%a: PCI or its enumeration disabled, installing "
+ "ACPI tables\n", __FUNCTION__));
+ return InstallAcpiTables (FindAcpiTableProtocol ());
+ }
+
+ //
+ // Otherwise, delay installing the ACPI tables until root bridges are
+ // connected. The entry point's return status will only reflect the callback
+ // setup. (Note that we're a DXE_DRIVER; our entry point function is invoked
+ // strictly before BDS is entered and can connect the root bridges.)
+ //
+ Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
+ OnRootBridgesConnected, NULL /* Context */,
+ &gRootBridgesConnectedEventGroupGuid, &RootBridgesConnected);
+ if (!EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_INFO,
+ "%a: waiting for root bridges to be connected, registered callback\n",
+ __FUNCTION__));
+ }
+
+ return Status;
+}
diff --git a/OvmfPkg/XenAcpiPlatformDxe/PciDecoding.c b/OvmfPkg/XenAcpiPlatformDxe/PciDecoding.c
new file mode 100644
index 0000000000..00fc57eb13
--- /dev/null
+++ b/OvmfPkg/XenAcpiPlatformDxe/PciDecoding.c
@@ -0,0 +1,194 @@
+/** @file
+ Temporarily enable IO and MMIO decoding for all PCI devices while QEMU
+ regenerates the ACPI tables.
+
+ Copyright (C) 2016-2021, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/DebugLib.h> // DEBUG()
+#include <Library/MemoryAllocationLib.h> // AllocatePool()
+#include <Library/UefiBootServicesTableLib.h> // gBS
+
+#include "AcpiPlatform.h"
+
+
+/**
+ Collect all PciIo protocol instances in the system. Save their original
+ attributes, and enable IO and MMIO decoding for each.
+
+ This is a best effort function; it doesn't return status codes. Its
+ caller is supposed to proceed even if this function fails.
+
+ @param[out] OriginalAttributes On output, a dynamically allocated array of
+ ORIGINAL_ATTRIBUTES elements. The array lists
+ the PciIo protocol instances found in the
+ system at the time of the call, plus the
+ original PCI attributes for each.
+
+ Before returning, the function enables IO and
+ MMIO decoding for each PciIo instance it
+ finds.
+
+ On error, or when no such instances are
+ found, OriginalAttributes is set to NULL.
+
+ @param[out] Count On output, the number of elements in
+ OriginalAttributes. On error it is set to
+ zero.
+**/
+VOID
+EnablePciDecoding (
+ OUT ORIGINAL_ATTRIBUTES **OriginalAttributes,
+ OUT UINTN *Count
+ )
+{
+ EFI_STATUS Status;
+ UINTN NoHandles;
+ EFI_HANDLE *Handles;
+ ORIGINAL_ATTRIBUTES *OrigAttrs;
+ UINTN Idx;
+
+ *OriginalAttributes = NULL;
+ *Count = 0;
+
+ if (PcdGetBool (PcdPciDisableBusEnumeration)) {
+ //
+ // The platform downloads ACPI tables from QEMU in general, but there are
+ // no root bridges in this execution. We're done.
+ //
+ return;
+ }
+
+ Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiPciIoProtocolGuid,
+ NULL /* SearchKey */, &NoHandles, &Handles);
+ if (Status == EFI_NOT_FOUND) {
+ //
+ // No PCI devices were found on either of the root bridges. We're done.
+ //
+ return;
+ }
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_WARN, "%a: LocateHandleBuffer(): %r\n", __FUNCTION__,
+ Status));
+ return;
+ }
+
+ OrigAttrs = AllocatePool (NoHandles * sizeof *OrigAttrs);
+ if (OrigAttrs == NULL) {
+ DEBUG ((DEBUG_WARN, "%a: AllocatePool(): out of resources\n",
+ __FUNCTION__));
+ goto FreeHandles;
+ }
+
+ for (Idx = 0; Idx < NoHandles; ++Idx) {
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ UINT64 Attributes;
+
+ //
+ // Look up PciIo on the handle and stash it
+ //
+ Status = gBS->HandleProtocol (Handles[Idx], &gEfiPciIoProtocolGuid,
+ (VOID**)&PciIo);
+ ASSERT_EFI_ERROR (Status);
+ OrigAttrs[Idx].PciIo = PciIo;
+
+ //
+ // Stash the current attributes
+ //
+ Status = PciIo->Attributes (PciIo, EfiPciIoAttributeOperationGet, 0,
+ &OrigAttrs[Idx].PciAttributes);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_WARN, "%a: EfiPciIoAttributeOperationGet: %r\n",
+ __FUNCTION__, Status));
+ goto RestoreAttributes;
+ }
+
+ //
+ // Retrieve supported attributes
+ //
+ Status = PciIo->Attributes (PciIo, EfiPciIoAttributeOperationSupported, 0,
+ &Attributes);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_WARN, "%a: EfiPciIoAttributeOperationSupported: %r\n",
+ __FUNCTION__, Status));
+ goto RestoreAttributes;
+ }
+
+ //
+ // Enable IO and MMIO decoding
+ //
+ Attributes &= EFI_PCI_IO_ATTRIBUTE_IO | EFI_PCI_IO_ATTRIBUTE_MEMORY;
+ Status = PciIo->Attributes (PciIo, EfiPciIoAttributeOperationEnable,
+ Attributes, NULL);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_WARN, "%a: EfiPciIoAttributeOperationEnable: %r\n",
+ __FUNCTION__, Status));
+ goto RestoreAttributes;
+ }
+ }
+
+ //
+ // Success
+ //
+ FreePool (Handles);
+ *OriginalAttributes = OrigAttrs;
+ *Count = NoHandles;
+ return;
+
+RestoreAttributes:
+ while (Idx > 0) {
+ --Idx;
+ OrigAttrs[Idx].PciIo->Attributes (OrigAttrs[Idx].PciIo,
+ EfiPciIoAttributeOperationSet,
+ OrigAttrs[Idx].PciAttributes,
+ NULL
+ );
+ }
+ FreePool (OrigAttrs);
+
+FreeHandles:
+ FreePool (Handles);
+}
+
+
+/**
+ Restore the original PCI attributes saved with EnablePciDecoding().
+
+ @param[in] OriginalAttributes The array allocated and populated by
+ EnablePciDecoding(). This parameter may be
+ NULL. If OriginalAttributes is NULL, then the
+ function is a no-op; otherwise the PciIo
+ attributes will be restored, and the
+ OriginalAttributes array will be freed.
+
+ @param[in] Count The Count value stored by EnablePciDecoding(),
+ the number of elements in OriginalAttributes.
+ Count may be zero if and only if
+ OriginalAttributes is NULL.
+**/
+VOID
+RestorePciDecoding (
+ IN ORIGINAL_ATTRIBUTES *OriginalAttributes,
+ IN UINTN Count
+ )
+{
+ UINTN Idx;
+
+ ASSERT ((OriginalAttributes == NULL) == (Count == 0));
+ if (OriginalAttributes == NULL) {
+ return;
+ }
+
+ for (Idx = 0; Idx < Count; ++Idx) {
+ OriginalAttributes[Idx].PciIo->Attributes (
+ OriginalAttributes[Idx].PciIo,
+ EfiPciIoAttributeOperationSet,
+ OriginalAttributes[Idx].PciAttributes,
+ NULL
+ );
+ }
+ FreePool (OriginalAttributes);
+}
diff --git a/OvmfPkg/XenAcpiPlatformDxe/Qemu.c b/OvmfPkg/XenAcpiPlatformDxe/Qemu.c
new file mode 100644
index 0000000000..b4a407c41b
--- /dev/null
+++ b/OvmfPkg/XenAcpiPlatformDxe/Qemu.c
@@ -0,0 +1,511 @@
+/** @file
+ OVMF ACPI QEMU support
+
+ Copyright (C) 2012-2021, Red Hat, Inc.
+ Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <IndustryStandard/Acpi.h> // EFI_ACPI_1_0_IO_APIC_STRUCTURE
+#include <Library/BaseMemoryLib.h> // CopyMem()
+#include <Library/DebugLib.h> // DEBUG()
+#include <Library/DxeServicesTableLib.h> // gDS
+#include <Library/MemoryAllocationLib.h> // AllocatePool()
+#include <Library/PcdLib.h> // PcdGet16()
+#include <Library/QemuFwCfgLib.h> // QemuFwCfgIsAvailable()
+
+#include "AcpiPlatform.h"
+
+BOOLEAN
+QemuDetected (
+ VOID
+ )
+{
+ if (!QemuFwCfgIsAvailable ()) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+STATIC
+UINTN
+CountBits16 (
+ UINT16 Mask
+ )
+{
+ //
+ // For all N >= 1, N bits are enough to represent the number of bits set
+ // among N bits. It's true for N == 1. When adding a new bit (N := N+1),
+ // the maximum number of possibly set bits increases by one, while the
+ // representable maximum doubles.
+ //
+ Mask = ((Mask & 0xAAAA) >> 1) + (Mask & 0x5555);
+ Mask = ((Mask & 0xCCCC) >> 2) + (Mask & 0x3333);
+ Mask = ((Mask & 0xF0F0) >> 4) + (Mask & 0x0F0F);
+ Mask = ((Mask & 0xFF00) >> 8) + (Mask & 0x00FF);
+
+ return Mask;
+}
+
+
+STATIC
+EFI_STATUS
+EFIAPI
+QemuInstallAcpiMadtTable (
+ IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,
+ IN VOID *AcpiTableBuffer,
+ IN UINTN AcpiTableBufferSize,
+ OUT UINTN *TableKey
+ )
+{
+ UINTN CpuCount;
+ UINTN PciLinkIsoCount;
+ UINTN NewBufferSize;
+ EFI_ACPI_1_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER *Madt;
+ EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC_STRUCTURE *LocalApic;
+ EFI_ACPI_1_0_IO_APIC_STRUCTURE *IoApic;
+ EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE *Iso;
+ EFI_ACPI_1_0_LOCAL_APIC_NMI_STRUCTURE *LocalApicNmi;
+ VOID *Ptr;
+ UINTN Loop;
+ EFI_STATUS Status;
+
+ ASSERT (AcpiTableBufferSize >= sizeof (EFI_ACPI_DESCRIPTION_HEADER));
+
+ QemuFwCfgSelectItem (QemuFwCfgItemSmpCpuCount);
+ CpuCount = QemuFwCfgRead16 ();
+ ASSERT (CpuCount >= 1);
+
+ //
+ // Set Level-tiggered, Active High for these identity mapped IRQs. The bitset
+ // corresponds to the union of all possible interrupt assignments for the LNKA,
+ // LNKB, LNKC, LNKD PCI interrupt lines. See the DSDT.
+ //
+ PciLinkIsoCount = CountBits16 (PcdGet16 (Pcd8259LegacyModeEdgeLevel));
+
+ NewBufferSize = 1 * sizeof (*Madt) +
+ CpuCount * sizeof (*LocalApic) +
+ 1 * sizeof (*IoApic) +
+ (1 + PciLinkIsoCount) * sizeof (*Iso) +
+ 1 * sizeof (*LocalApicNmi);
+
+ Madt = AllocatePool (NewBufferSize);
+ if (Madt == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (&(Madt->Header), AcpiTableBuffer, sizeof (EFI_ACPI_DESCRIPTION_HEADER));
+ Madt->Header.Length = (UINT32) NewBufferSize;
+ Madt->LocalApicAddress = PcdGet32 (PcdCpuLocalApicBaseAddress);
+ Madt->Flags = EFI_ACPI_1_0_PCAT_COMPAT;
+ Ptr = Madt + 1;
+
+ LocalApic = Ptr;
+ for (Loop = 0; Loop < CpuCount; ++Loop) {
+ LocalApic->Type = EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC;
+ LocalApic->Length = sizeof (*LocalApic);
+ LocalApic->AcpiProcessorId = (UINT8) Loop;
+ LocalApic->ApicId = (UINT8) Loop;
+ LocalApic->Flags = 1; // enabled
+ ++LocalApic;
+ }
+ Ptr = LocalApic;
+
+ IoApic = Ptr;
+ IoApic->Type = EFI_ACPI_1_0_IO_APIC;
+ IoApic->Length = sizeof (*IoApic);
+ IoApic->IoApicId = (UINT8) CpuCount;
+ IoApic->Reserved = EFI_ACPI_RESERVED_BYTE;
+ IoApic->IoApicAddress = 0xFEC00000;
+ IoApic->SystemVectorBase = 0x00000000;
+ Ptr = IoApic + 1;
+
+ //
+ // IRQ0 (8254 Timer) => IRQ2 (PIC) Interrupt Source Override Structure
+ //
+ Iso = Ptr;
+ Iso->Type = EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE;
+ Iso->Length = sizeof (*Iso);
+ Iso->Bus = 0x00; // ISA
+ Iso->Source = 0x00; // IRQ0
+ Iso->GlobalSystemInterruptVector = 0x00000002;
+ Iso->Flags = 0x0000; // Conforms to specs of the bus
+ ++Iso;
+
+ //
+ // Set Level-triggered, Active High for all possible PCI link targets.
+ //
+ for (Loop = 0; Loop < 16; ++Loop) {
+ if ((PcdGet16 (Pcd8259LegacyModeEdgeLevel) & (1 << Loop)) == 0) {
+ continue;
+ }
+ Iso->Type = EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE;
+ Iso->Length = sizeof (*Iso);
+ Iso->Bus = 0x00; // ISA
+ Iso->Source = (UINT8) Loop;
+ Iso->GlobalSystemInterruptVector = (UINT32) Loop;
+ Iso->Flags = 0x000D; // Level-triggered, Active High
+ ++Iso;
+ }
+ ASSERT (
+ (UINTN) (Iso - (EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE *)Ptr) ==
+ 1 + PciLinkIsoCount
+ );
+ Ptr = Iso;
+
+ LocalApicNmi = Ptr;
+ LocalApicNmi->Type = EFI_ACPI_1_0_LOCAL_APIC_NMI;
+ LocalApicNmi->Length = sizeof (*LocalApicNmi);
+ LocalApicNmi->AcpiProcessorId = 0xFF; // applies to all processors
+ //
+ // polarity and trigger mode of the APIC I/O input signals conform to the
+ // specifications of the bus
+ //
+ LocalApicNmi->Flags = 0x0000;
+ //
+ // Local APIC interrupt input LINTn to which NMI is connected.
+ //
+ LocalApicNmi->LocalApicInti = 0x01;
+ Ptr = LocalApicNmi + 1;
+
+ ASSERT ((UINTN) ((UINT8 *)Ptr - (UINT8 *)Madt) == NewBufferSize);
+ Status = InstallAcpiTable (AcpiProtocol, Madt, NewBufferSize, TableKey);
+
+ FreePool (Madt);
+
+ return Status;
+}
+
+
+#pragma pack(1)
+
+typedef struct {
+ UINT64 Base;
+ UINT64 End;
+ UINT64 Length;
+} PCI_WINDOW;
+
+typedef struct {
+ PCI_WINDOW PciWindow32;
+ PCI_WINDOW PciWindow64;
+} FIRMWARE_DATA;
+
+typedef struct {
+ UINT8 BytePrefix;
+ UINT8 ByteValue;
+} AML_BYTE;
+
+typedef struct {
+ UINT8 NameOp;
+ UINT8 RootChar;
+ UINT8 NameChar[4];
+ UINT8 PackageOp;
+ UINT8 PkgLength;
+ UINT8 NumElements;
+ AML_BYTE Pm1aCntSlpTyp;
+ AML_BYTE Pm1bCntSlpTyp;
+ AML_BYTE Reserved[2];
+} SYSTEM_STATE_PACKAGE;
+
+#pragma pack()
+
+
+STATIC
+EFI_STATUS
+EFIAPI
+PopulateFwData(
+ OUT FIRMWARE_DATA *FwData
+ )
+{
+ EFI_STATUS Status;
+ UINTN NumDesc;
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR *AllDesc;
+
+ Status = gDS->GetMemorySpaceMap (&NumDesc, &AllDesc);
+ if (Status == EFI_SUCCESS) {
+ UINT64 NonMmio32MaxExclTop;
+ UINT64 Mmio32MinBase;
+ UINT64 Mmio32MaxExclTop;
+ UINTN CurDesc;
+
+ Status = EFI_UNSUPPORTED;
+
+ NonMmio32MaxExclTop = 0;
+ Mmio32MinBase = BASE_4GB;
+ Mmio32MaxExclTop = 0;
+
+ for (CurDesc = 0; CurDesc < NumDesc; ++CurDesc) {
+ CONST EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Desc;
+ UINT64 ExclTop;
+
+ Desc = &AllDesc[CurDesc];
+ ExclTop = Desc->BaseAddress + Desc->Length;
+
+ if (ExclTop <= (UINT64) PcdGet32 (PcdOvmfFdBaseAddress)) {
+ switch (Desc->GcdMemoryType) {
+ case EfiGcdMemoryTypeNonExistent:
+ break;
+
+ case EfiGcdMemoryTypeReserved:
+ case EfiGcdMemoryTypeSystemMemory:
+ if (NonMmio32MaxExclTop < ExclTop) {
+ NonMmio32MaxExclTop = ExclTop;
+ }
+ break;
+
+ case EfiGcdMemoryTypeMemoryMappedIo:
+ if (Mmio32MinBase > Desc->BaseAddress) {
+ Mmio32MinBase = Desc->BaseAddress;
+ }
+ if (Mmio32MaxExclTop < ExclTop) {
+ Mmio32MaxExclTop = ExclTop;
+ }
+ break;
+
+ default:
+ ASSERT(0);
+ }
+ }
+ }
+
+ if (Mmio32MinBase < NonMmio32MaxExclTop) {
+ Mmio32MinBase = NonMmio32MaxExclTop;
+ }
+
+ if (Mmio32MinBase < Mmio32MaxExclTop) {
+ FwData->PciWindow32.Base = Mmio32MinBase;
+ FwData->PciWindow32.End = Mmio32MaxExclTop - 1;
+ FwData->PciWindow32.Length = Mmio32MaxExclTop - Mmio32MinBase;
+
+ FwData->PciWindow64.Base = 0;
+ FwData->PciWindow64.End = 0;
+ FwData->PciWindow64.Length = 0;
+
+ Status = EFI_SUCCESS;
+ }
+
+ FreePool (AllDesc);
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "ACPI PciWindow32: Base=0x%08lx End=0x%08lx Length=0x%08lx\n",
+ FwData->PciWindow32.Base,
+ FwData->PciWindow32.End,
+ FwData->PciWindow32.Length
+ ));
+ DEBUG ((
+ DEBUG_INFO,
+ "ACPI PciWindow64: Base=0x%08lx End=0x%08lx Length=0x%08lx\n",
+ FwData->PciWindow64.Base,
+ FwData->PciWindow64.End,
+ FwData->PciWindow64.Length
+ ));
+
+ return Status;
+}
+
+
+STATIC
+VOID
+EFIAPI
+GetSuspendStates (
+ UINTN *SuspendToRamSize,
+ SYSTEM_STATE_PACKAGE *SuspendToRam,
+ UINTN *SuspendToDiskSize,
+ SYSTEM_STATE_PACKAGE *SuspendToDisk
+ )
+{
+ STATIC CONST SYSTEM_STATE_PACKAGE Template = {
+ 0x08, // NameOp
+ '\\', // RootChar
+ { '_', 'S', 'x', '_' }, // NameChar[4]
+ 0x12, // PackageOp
+ 0x0A, // PkgLength
+ 0x04, // NumElements
+ { 0x0A, 0x00 }, // Pm1aCntSlpTyp
+ { 0x0A, 0x00 }, // Pm1bCntSlpTyp -- we don't support it
+ { // Reserved[2]
+ { 0x0A, 0x00 },
+ { 0x0A, 0x00 }
+ }
+ };
+ RETURN_STATUS Status;
+ FIRMWARE_CONFIG_ITEM FwCfgItem;
+ UINTN FwCfgSize;
+ UINT8 SystemStates[6];
+
+ //
+ // configure defaults
+ //
+ *SuspendToRamSize = sizeof Template;
+ CopyMem (SuspendToRam, &Template, sizeof Template);
+ SuspendToRam->NameChar[2] = '3'; // S3
+ SuspendToRam->Pm1aCntSlpTyp.ByteValue = 1; // PIIX4: STR
+
+ *SuspendToDiskSize = sizeof Template;
+ CopyMem (SuspendToDisk, &Template, sizeof Template);
+ SuspendToDisk->NameChar[2] = '4'; // S4
+ SuspendToDisk->Pm1aCntSlpTyp.ByteValue = 2; // PIIX4: POSCL
+
+ //
+ // check for overrides
+ //
+ Status = QemuFwCfgFindFile ("etc/system-states", &FwCfgItem, &FwCfgSize);
+ if (Status != RETURN_SUCCESS || FwCfgSize != sizeof SystemStates) {
+ DEBUG ((DEBUG_INFO, "ACPI using S3/S4 defaults\n"));
+ return;
+ }
+ QemuFwCfgSelectItem (FwCfgItem);
+ QemuFwCfgReadBytes (sizeof SystemStates, SystemStates);
+
+ //
+ // Each byte corresponds to a system state. In each byte, the MSB tells us
+ // whether the given state is enabled. If so, the three LSBs specify the
+ // value to be written to the PM control register's SUS_TYP bits.
+ //
+ if (SystemStates[3] & BIT7) {
+ SuspendToRam->Pm1aCntSlpTyp.ByteValue =
+ SystemStates[3] & (BIT2 | BIT1 | BIT0);
+ DEBUG ((DEBUG_INFO, "ACPI S3 value: %d\n",
+ SuspendToRam->Pm1aCntSlpTyp.ByteValue));
+ } else {
+ *SuspendToRamSize = 0;
+ DEBUG ((DEBUG_INFO, "ACPI S3 disabled\n"));
+ }
+
+ if (SystemStates[4] & BIT7) {
+ SuspendToDisk->Pm1aCntSlpTyp.ByteValue =
+ SystemStates[4] & (BIT2 | BIT1 | BIT0);
+ DEBUG ((DEBUG_INFO, "ACPI S4 value: %d\n",
+ SuspendToDisk->Pm1aCntSlpTyp.ByteValue));
+ } else {
+ *SuspendToDiskSize = 0;
+ DEBUG ((DEBUG_INFO, "ACPI S4 disabled\n"));
+ }
+}
+
+
+STATIC
+EFI_STATUS
+EFIAPI
+QemuInstallAcpiSsdtTable (
+ IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,
+ IN VOID *AcpiTableBuffer,
+ IN UINTN AcpiTableBufferSize,
+ OUT UINTN *TableKey
+ )
+{
+ EFI_STATUS Status;
+ FIRMWARE_DATA *FwData;
+
+ Status = EFI_OUT_OF_RESOURCES;
+
+ FwData = AllocateReservedPool (sizeof (*FwData));
+ if (FwData != NULL) {
+ UINTN SuspendToRamSize;
+ SYSTEM_STATE_PACKAGE SuspendToRam;
+ UINTN SuspendToDiskSize;
+ SYSTEM_STATE_PACKAGE SuspendToDisk;
+ UINTN SsdtSize;
+ UINT8 *Ssdt;
+
+ GetSuspendStates (&SuspendToRamSize, &SuspendToRam,
+ &SuspendToDiskSize, &SuspendToDisk);
+ SsdtSize = AcpiTableBufferSize + 17 + SuspendToRamSize + SuspendToDiskSize;
+ Ssdt = AllocatePool (SsdtSize);
+
+ if (Ssdt != NULL) {
+ Status = PopulateFwData (FwData);
+
+ if (Status == EFI_SUCCESS) {
+ UINT8 *SsdtPtr;
+
+ SsdtPtr = Ssdt;
+
+ CopyMem (SsdtPtr, AcpiTableBuffer, AcpiTableBufferSize);
+ SsdtPtr += AcpiTableBufferSize;
+
+ //
+ // build "OperationRegion(FWDT, SystemMemory, 0x12345678, 0x87654321)"
+ //
+ *(SsdtPtr++) = 0x5B; // ExtOpPrefix
+ *(SsdtPtr++) = 0x80; // OpRegionOp
+ *(SsdtPtr++) = 'F';
+ *(SsdtPtr++) = 'W';
+ *(SsdtPtr++) = 'D';
+ *(SsdtPtr++) = 'T';
+ *(SsdtPtr++) = 0x00; // SystemMemory
+ *(SsdtPtr++) = 0x0C; // DWordPrefix
+
+ //
+ // no virtual addressing yet, take the four least significant bytes
+ //
+ CopyMem(SsdtPtr, &FwData, 4);
+ SsdtPtr += 4;
+
+ *(SsdtPtr++) = 0x0C; // DWordPrefix
+
+ *(UINT32*) SsdtPtr = sizeof (*FwData);
+ SsdtPtr += 4;
+
+ //
+ // add suspend system states
+ //
+ CopyMem (SsdtPtr, &SuspendToRam, SuspendToRamSize);
+ SsdtPtr += SuspendToRamSize;
+ CopyMem (SsdtPtr, &SuspendToDisk, SuspendToDiskSize);
+ SsdtPtr += SuspendToDiskSize;
+
+ ASSERT((UINTN) (SsdtPtr - Ssdt) == SsdtSize);
+ ((EFI_ACPI_DESCRIPTION_HEADER *) Ssdt)->Length = (UINT32) SsdtSize;
+ Status = InstallAcpiTable (AcpiProtocol, Ssdt, SsdtSize, TableKey);
+ }
+
+ FreePool(Ssdt);
+ }
+
+ if (Status != EFI_SUCCESS) {
+ FreePool(FwData);
+ }
+ }
+
+ return Status;
+}
+
+
+EFI_STATUS
+EFIAPI
+QemuInstallAcpiTable (
+ IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,
+ IN VOID *AcpiTableBuffer,
+ IN UINTN AcpiTableBufferSize,
+ OUT UINTN *TableKey
+ )
+{
+ EFI_ACPI_DESCRIPTION_HEADER *Hdr;
+ EFI_ACPI_TABLE_INSTALL_ACPI_TABLE TableInstallFunction;
+
+ Hdr = (EFI_ACPI_DESCRIPTION_HEADER*) AcpiTableBuffer;
+ switch (Hdr->Signature) {
+ case EFI_ACPI_1_0_APIC_SIGNATURE:
+ TableInstallFunction = QemuInstallAcpiMadtTable;
+ break;
+ case EFI_ACPI_1_0_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE:
+ TableInstallFunction = QemuInstallAcpiSsdtTable;
+ break;
+ default:
+ TableInstallFunction = InstallAcpiTable;
+ }
+
+ return TableInstallFunction (
+ AcpiProtocol,
+ AcpiTableBuffer,
+ AcpiTableBufferSize,
+ TableKey
+ );
+}
diff --git a/OvmfPkg/XenAcpiPlatformDxe/QemuFwCfgAcpi.c b/OvmfPkg/XenAcpiPlatformDxe/QemuFwCfgAcpi.c
new file mode 100644
index 0000000000..521c06cf54
--- /dev/null
+++ b/OvmfPkg/XenAcpiPlatformDxe/QemuFwCfgAcpi.c
@@ -0,0 +1,1196 @@
+/** @file
+ OVMF ACPI support using QEMU's fw-cfg interface
+
+ Copyright (C) 2012-2021, Red Hat, Inc.
+ Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <IndustryStandard/Acpi.h> // EFI_ACPI_DESCRIPTION_HEADER
+#include <IndustryStandard/QemuLoader.h> // QEMU_LOADER_FNAME_SIZE
+#include <Library/BaseLib.h> // AsciiStrCmp()
+#include <Library/BaseMemoryLib.h> // CopyMem()
+#include <Library/DebugLib.h> // DEBUG()
+#include <Library/MemoryAllocationLib.h> // AllocatePool()
+#include <Library/OrderedCollectionLib.h> // OrderedCollectionMin()
+#include <Library/QemuFwCfgLib.h> // QemuFwCfgFindFile()
+#include <Library/QemuFwCfgS3Lib.h> // QemuFwCfgS3Enabled()
+#include <Library/UefiBootServicesTableLib.h> // gBS
+
+#include "AcpiPlatform.h"
+
+//
+// The user structure for the ordered collection that will track the fw_cfg
+// blobs under processing.
+//
+typedef struct {
+ UINT8 File[QEMU_LOADER_FNAME_SIZE]; // NUL-terminated name of the fw_cfg
+ // blob. This is the ordering / search
+ // key.
+ UINTN Size; // The number of bytes in this blob.
+ UINT8 *Base; // Pointer to the blob data.
+ BOOLEAN HostsOnlyTableData; // TRUE iff the blob has been found to
+ // only contain data that is directly
+ // part of ACPI tables.
+} BLOB;
+
+
+/**
+ Compare a standalone key against a user structure containing an embedded key.
+
+ @param[in] StandaloneKey Pointer to the bare key.
+
+ @param[in] UserStruct Pointer to the user structure with the embedded
+ key.
+
+ @retval <0 If StandaloneKey compares less than UserStruct's key.
+
+ @retval 0 If StandaloneKey compares equal to UserStruct's key.
+
+ @retval >0 If StandaloneKey compares greater than UserStruct's key.
+**/
+STATIC
+INTN
+EFIAPI
+BlobKeyCompare (
+ IN CONST VOID *StandaloneKey,
+ IN CONST VOID *UserStruct
+ )
+{
+ CONST BLOB *Blob;
+
+ Blob = UserStruct;
+ return AsciiStrCmp (StandaloneKey, (CONST CHAR8 *)Blob->File);
+}
+
+
+/**
+ Comparator function for two user structures.
+
+ @param[in] UserStruct1 Pointer to the first user structure.
+
+ @param[in] UserStruct2 Pointer to the second user structure.
+
+ @retval <0 If UserStruct1 compares less than UserStruct2.
+
+ @retval 0 If UserStruct1 compares equal to UserStruct2.
+
+ @retval >0 If UserStruct1 compares greater than UserStruct2.
+**/
+STATIC
+INTN
+EFIAPI
+BlobCompare (
+ IN CONST VOID *UserStruct1,
+ IN CONST VOID *UserStruct2
+ )
+{
+ CONST BLOB *Blob1;
+
+ Blob1 = UserStruct1;
+ return BlobKeyCompare (Blob1->File, UserStruct2);
+}
+
+
+/**
+ Comparator function for two opaque pointers, ordering on (unsigned) pointer
+ value itself.
+ Can be used as both Key and UserStruct comparator.
+
+ @param[in] Pointer1 First pointer.
+
+ @param[in] Pointer2 Second pointer.
+
+ @retval <0 If Pointer1 compares less than Pointer2.
+
+ @retval 0 If Pointer1 compares equal to Pointer2.
+
+ @retval >0 If Pointer1 compares greater than Pointer2.
+**/
+STATIC
+INTN
+EFIAPI
+PointerCompare (
+ IN CONST VOID *Pointer1,
+ IN CONST VOID *Pointer2
+ )
+{
+ if (Pointer1 == Pointer2) {
+ return 0;
+ }
+ if ((UINTN)Pointer1 < (UINTN)Pointer2) {
+ return -1;
+ }
+ return 1;
+}
+
+
+/**
+ Comparator function for two ASCII strings. Can be used as both Key and
+ UserStruct comparator.
+
+ This function exists solely so we can avoid casting &AsciiStrCmp to
+ ORDERED_COLLECTION_USER_COMPARE and ORDERED_COLLECTION_KEY_COMPARE.
+
+ @param[in] AsciiString1 Pointer to the first ASCII string.
+
+ @param[in] AsciiString2 Pointer to the second ASCII string.
+
+ @return The return value of AsciiStrCmp (AsciiString1, AsciiString2).
+**/
+STATIC
+INTN
+EFIAPI
+AsciiStringCompare (
+ IN CONST VOID *AsciiString1,
+ IN CONST VOID *AsciiString2
+ )
+{
+ return AsciiStrCmp (AsciiString1, AsciiString2);
+}
+
+
+/**
+ Release the ORDERED_COLLECTION structure populated by
+ CollectAllocationsRestrictedTo32Bit() (below).
+
+ This function may be called by CollectAllocationsRestrictedTo32Bit() itself,
+ on the error path.
+
+ @param[in] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION structure to
+ release.
+**/
+STATIC
+VOID
+ReleaseAllocationsRestrictedTo32Bit (
+ IN ORDERED_COLLECTION *AllocationsRestrictedTo32Bit
+)
+{
+ ORDERED_COLLECTION_ENTRY *Entry, *Entry2;
+
+ for (Entry = OrderedCollectionMin (AllocationsRestrictedTo32Bit);
+ Entry != NULL;
+ Entry = Entry2) {
+ Entry2 = OrderedCollectionNext (Entry);
+ OrderedCollectionDelete (AllocationsRestrictedTo32Bit, Entry, NULL);
+ }
+ OrderedCollectionUninit (AllocationsRestrictedTo32Bit);
+}
+
+
+/**
+ Iterate over the linker/loader script, and collect the names of the fw_cfg
+ blobs that are referenced by QEMU_LOADER_ADD_POINTER.PointeeFile fields, such
+ that QEMU_LOADER_ADD_POINTER.PointerSize is less than 8. This means that the
+ pointee blob's address will have to be patched into a narrower-than-8 byte
+ pointer field, hence the pointee blob must not be allocated from 64-bit
+ address space.
+
+ @param[out] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION structure
+ linking (not copying / owning) such
+ QEMU_LOADER_ADD_POINTER.PointeeFile
+ fields that name the blobs
+ restricted from 64-bit allocation.
+
+ @param[in] LoaderStart Points to the first entry in the
+ linker/loader script.
+
+ @param[in] LoaderEnd Points one past the last entry in
+ the linker/loader script.
+
+ @retval EFI_SUCCESS AllocationsRestrictedTo32Bit has been
+ populated.
+
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
+
+ @retval EFI_PROTOCOL_ERROR Invalid linker/loader script contents.
+**/
+STATIC
+EFI_STATUS
+CollectAllocationsRestrictedTo32Bit (
+ OUT ORDERED_COLLECTION **AllocationsRestrictedTo32Bit,
+ IN CONST QEMU_LOADER_ENTRY *LoaderStart,
+ IN CONST QEMU_LOADER_ENTRY *LoaderEnd
+)
+{
+ ORDERED_COLLECTION *Collection;
+ CONST QEMU_LOADER_ENTRY *LoaderEntry;
+ EFI_STATUS Status;
+
+ Collection = OrderedCollectionInit (AsciiStringCompare, AsciiStringCompare);
+ if (Collection == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
+ CONST QEMU_LOADER_ADD_POINTER *AddPointer;
+
+ if (LoaderEntry->Type != QemuLoaderCmdAddPointer) {
+ continue;
+ }
+ AddPointer = &LoaderEntry->Command.AddPointer;
+
+ if (AddPointer->PointerSize >= 8) {
+ continue;
+ }
+
+ if (AddPointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {
+ DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));
+ Status = EFI_PROTOCOL_ERROR;
+ goto RollBack;
+ }
+
+ Status = OrderedCollectionInsert (
+ Collection,
+ NULL, // Entry
+ (VOID *)AddPointer->PointeeFile
+ );
+ switch (Status) {
+ case EFI_SUCCESS:
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: restricting blob \"%a\" from 64-bit allocation\n",
+ __FUNCTION__,
+ AddPointer->PointeeFile
+ ));
+ break;
+ case EFI_ALREADY_STARTED:
+ //
+ // The restriction has been recorded already.
+ //
+ break;
+ case EFI_OUT_OF_RESOURCES:
+ goto RollBack;
+ default:
+ ASSERT (FALSE);
+ }
+ }
+
+ *AllocationsRestrictedTo32Bit = Collection;
+ return EFI_SUCCESS;
+
+RollBack:
+ ReleaseAllocationsRestrictedTo32Bit (Collection);
+ return Status;
+}
+
+
+/**
+ Process a QEMU_LOADER_ALLOCATE command.
+
+ @param[in] Allocate The QEMU_LOADER_ALLOCATE command to
+ process.
+
+ @param[in,out] Tracker The ORDERED_COLLECTION tracking the
+ BLOB user structures created thus
+ far.
+
+ @param[in] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION populated by
+ the function
+ CollectAllocationsRestrictedTo32Bit,
+ naming the fw_cfg blobs that must
+ not be allocated from 64-bit address
+ space.
+
+ @retval EFI_SUCCESS An area of whole AcpiNVS pages has been
+ allocated for the blob contents, and the
+ contents have been saved. A BLOB object (user
+ structure) has been allocated from pool memory,
+ referencing the blob contents. The BLOB user
+ structure has been linked into Tracker.
+
+ @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
+ Allocate, or the Allocate command references a
+ file that is already known by Tracker.
+
+ @retval EFI_UNSUPPORTED Unsupported alignment request has been found in
+ Allocate.
+
+ @retval EFI_OUT_OF_RESOURCES Pool allocation failed.
+
+ @return Error codes from QemuFwCfgFindFile() and
+ gBS->AllocatePages().
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ProcessCmdAllocate (
+ IN CONST QEMU_LOADER_ALLOCATE *Allocate,
+ IN OUT ORDERED_COLLECTION *Tracker,
+ IN ORDERED_COLLECTION *AllocationsRestrictedTo32Bit
+ )
+{
+ FIRMWARE_CONFIG_ITEM FwCfgItem;
+ UINTN FwCfgSize;
+ EFI_STATUS Status;
+ UINTN NumPages;
+ EFI_PHYSICAL_ADDRESS Address;
+ BLOB *Blob;
+
+ if (Allocate->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {
+ DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ if (Allocate->Alignment > EFI_PAGE_SIZE) {
+ DEBUG ((DEBUG_ERROR, "%a: unsupported alignment 0x%x\n", __FUNCTION__,
+ Allocate->Alignment));
+ return EFI_UNSUPPORTED;
+ }
+
+ Status = QemuFwCfgFindFile ((CHAR8 *)Allocate->File, &FwCfgItem, &FwCfgSize);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: QemuFwCfgFindFile(\"%a\"): %r\n", __FUNCTION__,
+ Allocate->File, Status));
+ return Status;
+ }
+
+ NumPages = EFI_SIZE_TO_PAGES (FwCfgSize);
+ Address = MAX_UINT64;
+ if (OrderedCollectionFind (
+ AllocationsRestrictedTo32Bit,
+ Allocate->File
+ ) != NULL) {
+ Address = MAX_UINT32;
+ }
+ Status = gBS->AllocatePages (AllocateMaxAddress, EfiACPIMemoryNVS, NumPages,
+ &Address);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Blob = AllocatePool (sizeof *Blob);
+ if (Blob == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto FreePages;
+ }
+ CopyMem (Blob->File, Allocate->File, QEMU_LOADER_FNAME_SIZE);
+ Blob->Size = FwCfgSize;
+ Blob->Base = (VOID *)(UINTN)Address;
+ Blob->HostsOnlyTableData = TRUE;
+
+ Status = OrderedCollectionInsert (Tracker, NULL, Blob);
+ if (Status == RETURN_ALREADY_STARTED) {
+ DEBUG ((DEBUG_ERROR, "%a: duplicated file \"%a\"\n", __FUNCTION__,
+ Allocate->File));
+ Status = EFI_PROTOCOL_ERROR;
+ }
+ if (EFI_ERROR (Status)) {
+ goto FreeBlob;
+ }
+
+ QemuFwCfgSelectItem (FwCfgItem);
+ QemuFwCfgReadBytes (FwCfgSize, Blob->Base);
+ ZeroMem (Blob->Base + Blob->Size, EFI_PAGES_TO_SIZE (NumPages) - Blob->Size);
+
+ DEBUG ((DEBUG_VERBOSE, "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx "
+ "Address=0x%Lx\n", __FUNCTION__, Allocate->File, Allocate->Alignment,
+ Allocate->Zone, (UINT64)Blob->Size, (UINT64)(UINTN)Blob->Base));
+ return EFI_SUCCESS;
+
+FreeBlob:
+ FreePool (Blob);
+
+FreePages:
+ gBS->FreePages (Address, NumPages);
+
+ return Status;
+}
+
+
+/**
+ Process a QEMU_LOADER_ADD_POINTER command.
+
+ @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
+
+ @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
+ structures created thus far.
+
+ @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
+ AddPointer, or the AddPointer command references
+ a file unknown to Tracker, or the pointer to
+ relocate has invalid location, size, or value, or
+ the relocated pointer value is not representable
+ in the given pointer size.
+
+ @retval EFI_SUCCESS The pointer field inside the pointer blob has
+ been relocated.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ProcessCmdAddPointer (
+ IN CONST QEMU_LOADER_ADD_POINTER *AddPointer,
+ IN CONST ORDERED_COLLECTION *Tracker
+ )
+{
+ ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;
+ BLOB *Blob, *Blob2;
+ UINT8 *PointerField;
+ UINT64 PointerValue;
+
+ if (AddPointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0' ||
+ AddPointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {
+ DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile);
+ TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile);
+ if (TrackerEntry == NULL || TrackerEntry2 == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a: invalid blob reference(s) \"%a\" / \"%a\"\n",
+ __FUNCTION__, AddPointer->PointerFile, AddPointer->PointeeFile));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ Blob = OrderedCollectionUserStruct (TrackerEntry);
+ Blob2 = OrderedCollectionUserStruct (TrackerEntry2);
+ if ((AddPointer->PointerSize != 1 && AddPointer->PointerSize != 2 &&
+ AddPointer->PointerSize != 4 && AddPointer->PointerSize != 8) ||
+ Blob->Size < AddPointer->PointerSize ||
+ Blob->Size - AddPointer->PointerSize < AddPointer->PointerOffset) {
+ DEBUG ((DEBUG_ERROR, "%a: invalid pointer location or size in \"%a\"\n",
+ __FUNCTION__, AddPointer->PointerFile));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ PointerField = Blob->Base + AddPointer->PointerOffset;
+ PointerValue = 0;
+ CopyMem (&PointerValue, PointerField, AddPointer->PointerSize);
+ if (PointerValue >= Blob2->Size) {
+ DEBUG ((DEBUG_ERROR, "%a: invalid pointer value in \"%a\"\n", __FUNCTION__,
+ AddPointer->PointerFile));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ //
+ // The memory allocation system ensures that the address of the byte past the
+ // last byte of any allocated object is expressible (no wraparound).
+ //
+ ASSERT ((UINTN)Blob2->Base <= MAX_ADDRESS - Blob2->Size);
+
+ PointerValue += (UINT64)(UINTN)Blob2->Base;
+ if (AddPointer->PointerSize < 8 &&
+ RShiftU64 (PointerValue, AddPointer->PointerSize * 8) != 0) {
+ DEBUG ((DEBUG_ERROR, "%a: relocated pointer value unrepresentable in "
+ "\"%a\"\n", __FUNCTION__, AddPointer->PointerFile));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ CopyMem (PointerField, &PointerValue, AddPointer->PointerSize);
+
+ DEBUG ((DEBUG_VERBOSE, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
+ "PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__,
+ AddPointer->PointerFile, AddPointer->PointeeFile,
+ AddPointer->PointerOffset, AddPointer->PointerSize));
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Process a QEMU_LOADER_ADD_CHECKSUM command.
+
+ @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process.
+
+ @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
+ structures created thus far.
+
+ @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
+ AddChecksum, or the AddChecksum command
+ references a file unknown to Tracker, or the
+ range to checksum is invalid.
+
+ @retval EFI_SUCCESS The requested range has been checksummed.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ProcessCmdAddChecksum (
+ IN CONST QEMU_LOADER_ADD_CHECKSUM *AddChecksum,
+ IN CONST ORDERED_COLLECTION *Tracker
+ )
+{
+ ORDERED_COLLECTION_ENTRY *TrackerEntry;
+ BLOB *Blob;
+
+ if (AddChecksum->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {
+ DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ TrackerEntry = OrderedCollectionFind (Tracker, AddChecksum->File);
+ if (TrackerEntry == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a: invalid blob reference \"%a\"\n", __FUNCTION__,
+ AddChecksum->File));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ Blob = OrderedCollectionUserStruct (TrackerEntry);
+ if (Blob->Size <= AddChecksum->ResultOffset ||
+ Blob->Size < AddChecksum->Length ||
+ Blob->Size - AddChecksum->Length < AddChecksum->Start) {
+ DEBUG ((DEBUG_ERROR, "%a: invalid checksum range in \"%a\"\n",
+ __FUNCTION__, AddChecksum->File));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ Blob->Base[AddChecksum->ResultOffset] = CalculateCheckSum8 (
+ Blob->Base + AddChecksum->Start,
+ AddChecksum->Length
+ );
+ DEBUG ((DEBUG_VERBOSE, "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x "
+ "Length=0x%x\n", __FUNCTION__, AddChecksum->File,
+ AddChecksum->ResultOffset, AddChecksum->Start, AddChecksum->Length));
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Process a QEMU_LOADER_WRITE_POINTER command.
+
+ @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to process.
+
+ @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
+ structures created thus far.
+
+ @param[in,out] S3Context The S3_CONTEXT object capturing the fw_cfg actions
+ of successfully processed QEMU_LOADER_WRITE_POINTER
+ commands, to be replayed at S3 resume. S3Context
+ may be NULL if S3 is disabled.
+
+ @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
+ WritePointer. Or, the WritePointer command
+ references a file unknown to Tracker or the
+ fw_cfg directory. Or, the pointer object to
+ rewrite has invalid location, size, or initial
+ relative value. Or, the pointer value to store
+ does not fit in the given pointer size.
+
+ @retval EFI_SUCCESS The pointer object inside the writeable fw_cfg
+ file has been written. If S3Context is not NULL,
+ then WritePointer has been condensed into
+ S3Context.
+
+ @return Error codes propagated from
+ SaveCondensedWritePointerToS3Context(). The
+ pointer object inside the writeable fw_cfg file
+ has not been written.
+**/
+STATIC
+EFI_STATUS
+ProcessCmdWritePointer (
+ IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer,
+ IN CONST ORDERED_COLLECTION *Tracker,
+ IN OUT S3_CONTEXT *S3Context OPTIONAL
+ )
+{
+ RETURN_STATUS Status;
+ FIRMWARE_CONFIG_ITEM PointerItem;
+ UINTN PointerItemSize;
+ ORDERED_COLLECTION_ENTRY *PointeeEntry;
+ BLOB *PointeeBlob;
+ UINT64 PointerValue;
+
+ if (WritePointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0' ||
+ WritePointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {
+ DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ Status = QemuFwCfgFindFile ((CONST CHAR8 *)WritePointer->PointerFile,
+ &PointerItem, &PointerItemSize);
+ PointeeEntry = OrderedCollectionFind (Tracker, WritePointer->PointeeFile);
+ if (RETURN_ERROR (Status) || PointeeEntry == NULL) {
+ DEBUG ((DEBUG_ERROR,
+ "%a: invalid fw_cfg file or blob reference \"%a\" / \"%a\"\n",
+ __FUNCTION__, WritePointer->PointerFile, WritePointer->PointeeFile));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ if ((WritePointer->PointerSize != 1 && WritePointer->PointerSize != 2 &&
+ WritePointer->PointerSize != 4 && WritePointer->PointerSize != 8) ||
+ (PointerItemSize < WritePointer->PointerSize) ||
+ (PointerItemSize - WritePointer->PointerSize <
+ WritePointer->PointerOffset)) {
+ DEBUG ((DEBUG_ERROR, "%a: invalid pointer location or size in \"%a\"\n",
+ __FUNCTION__, WritePointer->PointerFile));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ PointeeBlob = OrderedCollectionUserStruct (PointeeEntry);
+ PointerValue = WritePointer->PointeeOffset;
+ if (PointerValue >= PointeeBlob->Size) {
+ DEBUG ((DEBUG_ERROR, "%a: invalid PointeeOffset\n", __FUNCTION__));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ //
+ // The memory allocation system ensures that the address of the byte past the
+ // last byte of any allocated object is expressible (no wraparound).
+ //
+ ASSERT ((UINTN)PointeeBlob->Base <= MAX_ADDRESS - PointeeBlob->Size);
+
+ PointerValue += (UINT64)(UINTN)PointeeBlob->Base;
+ if (WritePointer->PointerSize < 8 &&
+ RShiftU64 (PointerValue, WritePointer->PointerSize * 8) != 0) {
+ DEBUG ((DEBUG_ERROR, "%a: pointer value unrepresentable in \"%a\"\n",
+ __FUNCTION__, WritePointer->PointerFile));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ //
+ // If S3 is enabled, we have to capture the below fw_cfg actions in condensed
+ // form, to be replayed during S3 resume.
+ //
+ if (S3Context != NULL) {
+ EFI_STATUS SaveStatus;
+
+ SaveStatus = SaveCondensedWritePointerToS3Context (
+ S3Context,
+ (UINT16)PointerItem,
+ WritePointer->PointerSize,
+ WritePointer->PointerOffset,
+ PointerValue
+ );
+ if (EFI_ERROR (SaveStatus)) {
+ return SaveStatus;
+ }
+ }
+
+ QemuFwCfgSelectItem (PointerItem);
+ QemuFwCfgSkipBytes (WritePointer->PointerOffset);
+ QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);
+
+ //
+ // Because QEMU has now learned PointeeBlob->Base, we must mark PointeeBlob
+ // as unreleasable, for the case when the whole linker/loader script is
+ // handled successfully.
+ //
+ PointeeBlob->HostsOnlyTableData = FALSE;
+
+ DEBUG ((DEBUG_VERBOSE, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
+ "PointerOffset=0x%x PointeeOffset=0x%x PointerSize=%d\n", __FUNCTION__,
+ WritePointer->PointerFile, WritePointer->PointeeFile,
+ WritePointer->PointerOffset, WritePointer->PointeeOffset,
+ WritePointer->PointerSize));
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Undo a QEMU_LOADER_WRITE_POINTER command.
+
+ This function revokes (zeroes out) a guest memory reference communicated to
+ QEMU earlier. The caller is responsible for invoking this function only on
+ such QEMU_LOADER_WRITE_POINTER commands that have been successfully processed
+ by ProcessCmdWritePointer().
+
+ @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to undo.
+**/
+STATIC
+VOID
+UndoCmdWritePointer (
+ IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer
+ )
+{
+ RETURN_STATUS Status;
+ FIRMWARE_CONFIG_ITEM PointerItem;
+ UINTN PointerItemSize;
+ UINT64 PointerValue;
+
+ Status = QemuFwCfgFindFile ((CONST CHAR8 *)WritePointer->PointerFile,
+ &PointerItem, &PointerItemSize);
+ ASSERT_RETURN_ERROR (Status);
+
+ PointerValue = 0;
+ QemuFwCfgSelectItem (PointerItem);
+ QemuFwCfgSkipBytes (WritePointer->PointerOffset);
+ QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);
+
+ DEBUG ((DEBUG_VERBOSE,
+ "%a: PointerFile=\"%a\" PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__,
+ WritePointer->PointerFile, WritePointer->PointerOffset,
+ WritePointer->PointerSize));
+}
+
+
+//
+// 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
+
+/**
+ Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte
+ array is an ACPI table, and if so, install it.
+
+ This function assumes that the entire QEMU linker/loader command file has
+ been processed successfully in a prior first pass.
+
+ @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
+
+ @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
+ structures.
+
+ @param[in] AcpiProtocol The ACPI table protocol used to install tables.
+
+ @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN
+ elements, allocated by the caller. On output,
+ the function will have stored (appended) the
+ AcpiProtocol-internal key of the ACPI table that
+ the function has installed, if the AddPointer
+ command identified an ACPI table that is
+ different from RSDT and XSDT.
+
+ @param[in,out] NumInstalled On input, the number of entries already used in
+ InstalledKey; it must be in [0,
+ INSTALLED_TABLES_MAX] inclusive. On output, the
+ parameter is incremented if the AddPointer
+ command identified an ACPI table that is
+ different from RSDT and XSDT.
+
+ @param[in,out] SeenPointers The ORDERED_COLLECTION tracking the absolute
+ target addresses that have been pointed-to by
+ QEMU_LOADER_ADD_POINTER commands thus far. If a
+ target address is encountered for the first
+ time, and it identifies an ACPI table that is
+ different from RDST and XSDT, the table is
+ installed. If a target address is seen for the
+ second or later times, it is skipped without
+ taking any action.
+
+ @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on
+ input.
+
+ @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI
+ table different from RSDT and XSDT, but there
+ was no more room in InstalledKey.
+
+ @retval EFI_SUCCESS AddPointer has been processed. Either its
+ absolute target address has been encountered
+ before, or an ACPI table different from RSDT
+ and XSDT has been installed (reflected by
+ InstalledKey and NumInstalled), or RSDT or
+ XSDT has been identified but not installed, or
+ the fw_cfg blob pointed-into by AddPointer has
+ been marked as hosting something else than
+ just direct ACPI table contents.
+
+ @return Error codes returned by
+ AcpiProtocol->InstallAcpiTable().
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+Process2ndPassCmdAddPointer (
+ IN CONST QEMU_LOADER_ADD_POINTER *AddPointer,
+ IN CONST ORDERED_COLLECTION *Tracker,
+ IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,
+ IN OUT UINTN InstalledKey[INSTALLED_TABLES_MAX],
+ IN OUT INT32 *NumInstalled,
+ IN OUT ORDERED_COLLECTION *SeenPointers
+ )
+{
+ CONST ORDERED_COLLECTION_ENTRY *TrackerEntry;
+ CONST ORDERED_COLLECTION_ENTRY *TrackerEntry2;
+ ORDERED_COLLECTION_ENTRY *SeenPointerEntry;
+ CONST BLOB *Blob;
+ BLOB *Blob2;
+ CONST UINT8 *PointerField;
+ UINT64 PointerValue;
+ UINTN Blob2Remaining;
+ UINTN TableSize;
+ CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;
+ CONST EFI_ACPI_DESCRIPTION_HEADER *Header;
+ EFI_STATUS Status;
+
+ if (*NumInstalled < 0 || *NumInstalled > INSTALLED_TABLES_MAX) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile);
+ TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile);
+ Blob = OrderedCollectionUserStruct (TrackerEntry);
+ Blob2 = OrderedCollectionUserStruct (TrackerEntry2);
+ PointerField = Blob->Base + AddPointer->PointerOffset;
+ PointerValue = 0;
+ CopyMem (&PointerValue, PointerField, AddPointer->PointerSize);
+
+ //
+ // We assert that PointerValue falls inside Blob2's contents. This is ensured
+ // by the Blob2->Size check and later checks in ProcessCmdAddPointer().
+ //
+ Blob2Remaining = (UINTN)Blob2->Base;
+ ASSERT(PointerValue >= Blob2Remaining);
+ Blob2Remaining += Blob2->Size;
+ ASSERT (PointerValue < Blob2Remaining);
+
+ Status = OrderedCollectionInsert (
+ SeenPointers,
+ &SeenPointerEntry, // for reverting insertion in error case
+ (VOID *)(UINTN)PointerValue
+ );
+ if (EFI_ERROR (Status)) {
+ if (Status == RETURN_ALREADY_STARTED) {
+ //
+ // Already seen this pointer, don't try to process it again.
+ //
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: PointerValue=0x%Lx already processed, skipping.\n",
+ __FUNCTION__,
+ PointerValue
+ ));
+ Status = EFI_SUCCESS;
+ }
+ return Status;
+ }
+
+ Blob2Remaining -= (UINTN) PointerValue;
+ DEBUG ((DEBUG_VERBOSE, "%a: checking for ACPI header in \"%a\" at 0x%Lx "
+ "(remaining: 0x%Lx): ", __FUNCTION__, AddPointer->PointeeFile,
+ PointerValue, (UINT64)Blob2Remaining));
+
+ TableSize = 0;
+
+ //
+ // To make our job simple, the FACS has a custom header. Sigh.
+ //
+ if (sizeof *Facs <= Blob2Remaining) {
+ Facs = (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)PointerValue;
+
+ if (Facs->Length >= sizeof *Facs &&
+ Facs->Length <= Blob2Remaining &&
+ Facs->Signature ==
+ EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) {
+ DEBUG ((DEBUG_VERBOSE, "found \"%-4.4a\" size 0x%x\n",
+ (CONST CHAR8 *)&Facs->Signature, Facs->Length));
+ TableSize = Facs->Length;
+ }
+ }
+
+ //
+ // check for the uniform tables
+ //
+ if (TableSize == 0 && sizeof *Header <= Blob2Remaining) {
+ Header = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)PointerValue;
+
+ if (Header->Length >= sizeof *Header &&
+ Header->Length <= Blob2Remaining &&
+ CalculateSum8 ((CONST UINT8 *)Header, Header->Length) == 0) {
+ //
+ // This looks very much like an ACPI table from QEMU:
+ // - Length field consistent with both ACPI and containing blob size
+ // - checksum is correct
+ //
+ DEBUG ((DEBUG_VERBOSE, "found \"%-4.4a\" size 0x%x\n",
+ (CONST CHAR8 *)&Header->Signature, Header->Length));
+ TableSize = Header->Length;
+
+ //
+ // Skip RSDT and XSDT because those are handled by
+ // EFI_ACPI_TABLE_PROTOCOL automatically.
+ if (Header->Signature ==
+ EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE ||
+ Header->Signature ==
+ EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {
+ return EFI_SUCCESS;
+ }
+ }
+ }
+
+ if (TableSize == 0) {
+ DEBUG ((DEBUG_VERBOSE, "not found; marking fw_cfg blob as opaque\n"));
+ Blob2->HostsOnlyTableData = FALSE;
+ return EFI_SUCCESS;
+ }
+
+ if (*NumInstalled == INSTALLED_TABLES_MAX) {
+ DEBUG ((DEBUG_ERROR, "%a: can't install more than %d tables\n",
+ __FUNCTION__, INSTALLED_TABLES_MAX));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto RollbackSeenPointer;
+ }
+
+ Status = AcpiProtocol->InstallAcpiTable (AcpiProtocol,
+ (VOID *)(UINTN)PointerValue, TableSize,
+ &InstalledKey[*NumInstalled]);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: InstallAcpiTable(): %r\n", __FUNCTION__,
+ Status));
+ goto RollbackSeenPointer;
+ }
+ ++*NumInstalled;
+ return EFI_SUCCESS;
+
+RollbackSeenPointer:
+ OrderedCollectionDelete (SeenPointers, SeenPointerEntry, NULL);
+ return Status;
+}
+
+
+/**
+ Download, process, and install ACPI table data from the QEMU loader
+ interface.
+
+ @param[in] AcpiProtocol The ACPI table protocol used to install tables.
+
+ @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU
+ loader command with unsupported parameters
+ has been found.
+
+ @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg
+ files.
+
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than
+ INSTALLED_TABLES_MAX tables found.
+
+ @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents.
+
+ @return Status codes returned by
+ AcpiProtocol->InstallAcpiTable().
+
+**/
+EFI_STATUS
+EFIAPI
+InstallQemuFwCfgTables (
+ IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol
+ )
+{
+ EFI_STATUS Status;
+ FIRMWARE_CONFIG_ITEM FwCfgItem;
+ UINTN FwCfgSize;
+ QEMU_LOADER_ENTRY *LoaderStart;
+ CONST QEMU_LOADER_ENTRY *LoaderEntry, *LoaderEnd;
+ CONST QEMU_LOADER_ENTRY *WritePointerSubsetEnd;
+ ORIGINAL_ATTRIBUTES *OriginalPciAttributes;
+ UINTN OriginalPciAttributesCount;
+ ORDERED_COLLECTION *AllocationsRestrictedTo32Bit;
+ S3_CONTEXT *S3Context;
+ ORDERED_COLLECTION *Tracker;
+ UINTN *InstalledKey;
+ INT32 Installed;
+ ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;
+ ORDERED_COLLECTION *SeenPointers;
+ ORDERED_COLLECTION_ENTRY *SeenPointerEntry, *SeenPointerEntry2;
+
+ Status = QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem, &FwCfgSize);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ if (FwCfgSize % sizeof *LoaderEntry != 0) {
+ DEBUG ((DEBUG_ERROR, "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",
+ __FUNCTION__, (UINT64)FwCfgSize));
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ LoaderStart = AllocatePool (FwCfgSize);
+ if (LoaderStart == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ EnablePciDecoding (&OriginalPciAttributes, &OriginalPciAttributesCount);
+ QemuFwCfgSelectItem (FwCfgItem);
+ QemuFwCfgReadBytes (FwCfgSize, LoaderStart);
+ RestorePciDecoding (OriginalPciAttributes, OriginalPciAttributesCount);
+ LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry;
+
+ AllocationsRestrictedTo32Bit = NULL;
+ Status = CollectAllocationsRestrictedTo32Bit (
+ &AllocationsRestrictedTo32Bit,
+ LoaderStart,
+ LoaderEnd
+ );
+ if (EFI_ERROR (Status)) {
+ goto FreeLoader;
+ }
+
+ S3Context = NULL;
+ if (QemuFwCfgS3Enabled ()) {
+ //
+ // Size the allocation pessimistically, assuming that all commands in the
+ // script are QEMU_LOADER_WRITE_POINTER commands.
+ //
+ Status = AllocateS3Context (&S3Context, LoaderEnd - LoaderStart);
+ if (EFI_ERROR (Status)) {
+ goto FreeAllocationsRestrictedTo32Bit;
+ }
+ }
+
+ Tracker = OrderedCollectionInit (BlobCompare, BlobKeyCompare);
+ if (Tracker == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto FreeS3Context;
+ }
+
+ //
+ // first pass: process the commands
+ //
+ // "WritePointerSubsetEnd" points one past the last successful
+ // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first
+ // pass, no such command has been encountered yet.
+ //
+ WritePointerSubsetEnd = LoaderStart;
+ for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
+ switch (LoaderEntry->Type) {
+ case QemuLoaderCmdAllocate:
+ Status = ProcessCmdAllocate (
+ &LoaderEntry->Command.Allocate,
+ Tracker,
+ AllocationsRestrictedTo32Bit
+ );
+ break;
+
+ case QemuLoaderCmdAddPointer:
+ Status = ProcessCmdAddPointer (&LoaderEntry->Command.AddPointer,
+ Tracker);
+ break;
+
+ case QemuLoaderCmdAddChecksum:
+ Status = ProcessCmdAddChecksum (&LoaderEntry->Command.AddChecksum,
+ Tracker);
+ break;
+
+ case QemuLoaderCmdWritePointer:
+ Status = ProcessCmdWritePointer (&LoaderEntry->Command.WritePointer,
+ Tracker, S3Context);
+ if (!EFI_ERROR (Status)) {
+ WritePointerSubsetEnd = LoaderEntry + 1;
+ }
+ break;
+
+ default:
+ DEBUG ((DEBUG_VERBOSE, "%a: unknown loader command: 0x%x\n",
+ __FUNCTION__, LoaderEntry->Type));
+ break;
+ }
+
+ if (EFI_ERROR (Status)) {
+ goto RollbackWritePointersAndFreeTracker;
+ }
+ }
+
+ InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);
+ if (InstalledKey == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto RollbackWritePointersAndFreeTracker;
+ }
+
+ SeenPointers = OrderedCollectionInit (PointerCompare, PointerCompare);
+ if (SeenPointers == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto FreeKeys;
+ }
+
+ //
+ // second pass: identify and install ACPI tables
+ //
+ Installed = 0;
+ for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
+ if (LoaderEntry->Type == QemuLoaderCmdAddPointer) {
+ Status = Process2ndPassCmdAddPointer (
+ &LoaderEntry->Command.AddPointer,
+ Tracker,
+ AcpiProtocol,
+ InstalledKey,
+ &Installed,
+ SeenPointers
+ );
+ if (EFI_ERROR (Status)) {
+ goto UninstallAcpiTables;
+ }
+ }
+ }
+
+ //
+ // Translating the condensed QEMU_LOADER_WRITE_POINTER commands to ACPI S3
+ // Boot Script opcodes has to be the last operation in this function, because
+ // if it succeeds, it cannot be undone.
+ //
+ if (S3Context != NULL) {
+ Status = TransferS3ContextToBootScript (S3Context);
+ if (EFI_ERROR (Status)) {
+ goto UninstallAcpiTables;
+ }
+ //
+ // Ownership of S3Context has been transferred.
+ //
+ S3Context = NULL;
+ }
+
+UninstallAcpiTables:
+ if (EFI_ERROR (Status)) {
+ //
+ // roll back partial installation
+ //
+ while (Installed > 0) {
+ --Installed;
+ AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]);
+ }
+ } else {
+ DEBUG ((DEBUG_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));
+ }
+
+ for (SeenPointerEntry = OrderedCollectionMin (SeenPointers);
+ SeenPointerEntry != NULL;
+ SeenPointerEntry = SeenPointerEntry2) {
+ SeenPointerEntry2 = OrderedCollectionNext (SeenPointerEntry);
+ OrderedCollectionDelete (SeenPointers, SeenPointerEntry, NULL);
+ }
+ OrderedCollectionUninit (SeenPointers);
+
+FreeKeys:
+ FreePool (InstalledKey);
+
+RollbackWritePointersAndFreeTracker:
+ //
+ // In case of failure, revoke any allocation addresses that were communicated
+ // to QEMU previously, before we release all the blobs.
+ //
+ if (EFI_ERROR (Status)) {
+ LoaderEntry = WritePointerSubsetEnd;
+ while (LoaderEntry > LoaderStart) {
+ --LoaderEntry;
+ if (LoaderEntry->Type == QemuLoaderCmdWritePointer) {
+ UndoCmdWritePointer (&LoaderEntry->Command.WritePointer);
+ }
+ }
+ }
+
+ //
+ // Tear down the tracker infrastructure. Each fw_cfg blob will be left in
+ // place only if we're exiting with success and the blob hosts data that is
+ // not directly part of some ACPI table.
+ //
+ for (TrackerEntry = OrderedCollectionMin (Tracker); TrackerEntry != NULL;
+ TrackerEntry = TrackerEntry2) {
+ VOID *UserStruct;
+ BLOB *Blob;
+
+ TrackerEntry2 = OrderedCollectionNext (TrackerEntry);
+ OrderedCollectionDelete (Tracker, TrackerEntry, &UserStruct);
+ Blob = UserStruct;
+
+ if (EFI_ERROR (Status) || Blob->HostsOnlyTableData) {
+ DEBUG ((DEBUG_VERBOSE, "%a: freeing \"%a\"\n", __FUNCTION__,
+ Blob->File));
+ gBS->FreePages ((UINTN)Blob->Base, EFI_SIZE_TO_PAGES (Blob->Size));
+ }
+ FreePool (Blob);
+ }
+ OrderedCollectionUninit (Tracker);
+
+FreeS3Context:
+ if (S3Context != NULL) {
+ ReleaseS3Context (S3Context);
+ }
+
+FreeAllocationsRestrictedTo32Bit:
+ ReleaseAllocationsRestrictedTo32Bit (AllocationsRestrictedTo32Bit);
+
+FreeLoader:
+ FreePool (LoaderStart);
+
+ return Status;
+}
diff --git a/OvmfPkg/XenAcpiPlatformDxe/Xen.c b/OvmfPkg/XenAcpiPlatformDxe/Xen.c
new file mode 100644
index 0000000000..e8395db548
--- /dev/null
+++ b/OvmfPkg/XenAcpiPlatformDxe/Xen.c
@@ -0,0 +1,316 @@
+/** @file
+ OVMF ACPI Xen support
+
+ Copyright (C) 2021, Red Hat, Inc.
+ Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2012, Bei Guan <gbtju85@gmail.com>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h> // CpuDeadLoop()
+#include <Library/DebugLib.h> // DEBUG()
+#include <Library/XenPlatformLib.h> // XenGetInfoHOB()
+
+#include "AcpiPlatform.h"
+
+#define XEN_ACPI_PHYSICAL_ADDRESS 0x000EA020
+#define XEN_BIOS_PHYSICAL_END 0x000FFFFF
+
+EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *XenAcpiRsdpStructurePtr = NULL;
+
+/**
+ Get the address of Xen ACPI Root System Description Pointer (RSDP)
+ structure.
+
+ @param RsdpStructurePtr Return pointer to RSDP structure
+
+ @return EFI_SUCCESS Find Xen RSDP structure successfully.
+ @return EFI_NOT_FOUND Don't find Xen RSDP structure.
+ @return EFI_ABORTED Find Xen RSDP structure, but it's not integrated.
+
+**/
+EFI_STATUS
+EFIAPI
+GetXenAcpiRsdp (
+ OUT EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER **RsdpPtr
+ )
+{
+ EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *RsdpStructurePtr;
+ UINT8 *XenAcpiPtr;
+ UINT8 Sum;
+ EFI_XEN_INFO *XenInfo;
+
+ //
+ // Detect the RSDP structure
+ //
+
+ //
+ // First look for PVH one
+ //
+ XenInfo = XenGetInfoHOB ();
+ ASSERT (XenInfo != NULL);
+ if (XenInfo->RsdpPvh != NULL) {
+ DEBUG ((DEBUG_INFO, "%a: Use ACPI RSDP table at 0x%p\n",
+ gEfiCallerBaseName, XenInfo->RsdpPvh));
+ *RsdpPtr = XenInfo->RsdpPvh;
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Otherwise, look for the HVM one
+ //
+ for (XenAcpiPtr = (UINT8*)(UINTN) XEN_ACPI_PHYSICAL_ADDRESS;
+ XenAcpiPtr < (UINT8*)(UINTN) XEN_BIOS_PHYSICAL_END;
+ XenAcpiPtr += 0x10) {
+
+ RsdpStructurePtr = (EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *)
+ (UINTN) XenAcpiPtr;
+
+ if (!AsciiStrnCmp ((CHAR8 *) &RsdpStructurePtr->Signature, "RSD PTR ", 8)) {
+ //
+ // RSDP ACPI 1.0 checksum for 1.0/2.0/3.0 table.
+ // This is only the first 20 bytes of the structure
+ //
+ Sum = CalculateSum8 (
+ (CONST UINT8 *)RsdpStructurePtr,
+ sizeof (EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER)
+ );
+ if (Sum != 0) {
+ return EFI_ABORTED;
+ }
+
+ if (RsdpStructurePtr->Revision >= 2) {
+ //
+ // RSDP ACPI 2.0/3.0 checksum, this is the entire table
+ //
+ Sum = CalculateSum8 (
+ (CONST UINT8 *)RsdpStructurePtr,
+ sizeof (EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER)
+ );
+ if (Sum != 0) {
+ return EFI_ABORTED;
+ }
+ }
+ *RsdpPtr = RsdpStructurePtr;
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Get Xen Acpi tables from the RSDP structure. And installs Xen ACPI tables
+ into the RSDT/XSDT using InstallAcpiTable. Some signature of the installed
+ ACPI tables are: FACP, APIC, HPET, WAET, SSDT, FACS, DSDT.
+
+ @param AcpiProtocol Protocol instance pointer.
+
+ @return EFI_SUCCESS The table was successfully inserted.
+ @return EFI_INVALID_PARAMETER Either AcpiTableBuffer is NULL, TableHandle is
+ NULL, or AcpiTableBufferSize and the size
+ field embedded in the ACPI table pointed to
+ by AcpiTableBuffer are not in sync.
+ @return EFI_OUT_OF_RESOURCES Insufficient resources exist to complete the request.
+
+**/
+EFI_STATUS
+EFIAPI
+InstallXenTables (
+ IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol
+ )
+{
+ EFI_STATUS Status;
+ UINTN TableHandle;
+
+ EFI_ACPI_DESCRIPTION_HEADER *Rsdt;
+ EFI_ACPI_DESCRIPTION_HEADER *Xsdt;
+ VOID *CurrentTableEntry;
+ UINTN CurrentTablePointer;
+ EFI_ACPI_DESCRIPTION_HEADER *CurrentTable;
+ UINTN Index;
+ UINTN NumberOfTableEntries;
+ EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt2Table;
+ EFI_ACPI_1_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt1Table;
+ EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs2Table;
+ EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs1Table;
+ EFI_ACPI_DESCRIPTION_HEADER *DsdtTable;
+
+ Fadt2Table = NULL;
+ Fadt1Table = NULL;
+ Facs2Table = NULL;
+ Facs1Table = NULL;
+ DsdtTable = NULL;
+ TableHandle = 0;
+ NumberOfTableEntries = 0;
+
+ //
+ // Try to find Xen ACPI tables
+ //
+ Status = GetXenAcpiRsdp (&XenAcpiRsdpStructurePtr);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // If XSDT table is find, just install its tables.
+ // Otherwise, try to find and install the RSDT tables.
+ //
+ if (XenAcpiRsdpStructurePtr->XsdtAddress) {
+ //
+ // Retrieve the addresses of XSDT and
+ // calculate the number of its table entries.
+ //
+ Xsdt = (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN)
+ XenAcpiRsdpStructurePtr->XsdtAddress;
+ NumberOfTableEntries = (Xsdt->Length -
+ sizeof (EFI_ACPI_DESCRIPTION_HEADER)) /
+ sizeof (UINT64);
+
+ //
+ // Install ACPI tables found in XSDT.
+ //
+ for (Index = 0; Index < NumberOfTableEntries; Index++) {
+ //
+ // Get the table entry from XSDT
+ //
+ CurrentTableEntry = (VOID *) ((UINT8 *) Xsdt +
+ sizeof (EFI_ACPI_DESCRIPTION_HEADER) +
+ Index * sizeof (UINT64));
+ CurrentTablePointer = (UINTN) *(UINT64 *)CurrentTableEntry;
+ CurrentTable = (EFI_ACPI_DESCRIPTION_HEADER *) CurrentTablePointer;
+
+ //
+ // Install the XSDT tables
+ //
+ Status = InstallAcpiTable (
+ AcpiProtocol,
+ CurrentTable,
+ CurrentTable->Length,
+ &TableHandle
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Get the FACS and DSDT table address from the table FADT
+ //
+ if (!AsciiStrnCmp ((CHAR8 *) &CurrentTable->Signature, "FACP", 4)) {
+ Fadt2Table = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *)
+ (UINTN) CurrentTablePointer;
+ Facs2Table = (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)
+ (UINTN) Fadt2Table->FirmwareCtrl;
+ DsdtTable = (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN) Fadt2Table->Dsdt;
+ }
+ }
+ }
+ else if (XenAcpiRsdpStructurePtr->RsdtAddress) {
+ //
+ // Retrieve the addresses of RSDT and
+ // calculate the number of its table entries.
+ //
+ Rsdt = (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN)
+ XenAcpiRsdpStructurePtr->RsdtAddress;
+ NumberOfTableEntries = (Rsdt->Length -
+ sizeof (EFI_ACPI_DESCRIPTION_HEADER)) /
+ sizeof (UINT32);
+
+ //
+ // Install ACPI tables found in XSDT.
+ //
+ for (Index = 0; Index < NumberOfTableEntries; Index++) {
+ //
+ // Get the table entry from RSDT
+ //
+ CurrentTableEntry = (UINT32 *) ((UINT8 *) Rsdt +
+ sizeof (EFI_ACPI_DESCRIPTION_HEADER) +
+ Index * sizeof (UINT32));
+ CurrentTablePointer = *(UINT32 *)CurrentTableEntry;
+ CurrentTable = (EFI_ACPI_DESCRIPTION_HEADER *) CurrentTablePointer;
+
+ //
+ // Install the RSDT tables
+ //
+ Status = InstallAcpiTable (
+ AcpiProtocol,
+ CurrentTable,
+ CurrentTable->Length,
+ &TableHandle
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Get the FACS and DSDT table address from the table FADT
+ //
+ if (!AsciiStrnCmp ((CHAR8 *) &CurrentTable->Signature, "FACP", 4)) {
+ Fadt1Table = (EFI_ACPI_1_0_FIXED_ACPI_DESCRIPTION_TABLE *)
+ (UINTN) CurrentTablePointer;
+ Facs1Table = (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)
+ (UINTN) Fadt1Table->FirmwareCtrl;
+ DsdtTable = (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN) Fadt1Table->Dsdt;
+ }
+ }
+ }
+
+ //
+ // Install the FACS table.
+ //
+ if (Fadt2Table) {
+ //
+ // FACS 2.0
+ //
+ Status = InstallAcpiTable (
+ AcpiProtocol,
+ Facs2Table,
+ Facs2Table->Length,
+ &TableHandle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+ else if (Fadt1Table) {
+ //
+ // FACS 1.0
+ //
+ Status = InstallAcpiTable (
+ AcpiProtocol,
+ Facs1Table,
+ Facs1Table->Length,
+ &TableHandle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ //
+ // Install DSDT table. If we reached this point without finding the DSDT,
+ // then we're out of sync with the hypervisor, and cannot continue.
+ //
+ if (DsdtTable == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a: no DSDT found\n", __FUNCTION__));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+
+ Status = InstallAcpiTable (
+ AcpiProtocol,
+ DsdtTable,
+ DsdtTable->Length,
+ &TableHandle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
diff --git a/OvmfPkg/XenAcpiPlatformDxe/XenAcpiPlatformDxe.inf b/OvmfPkg/XenAcpiPlatformDxe/XenAcpiPlatformDxe.inf
new file mode 100644
index 0000000000..379b5d56d5
--- /dev/null
+++ b/OvmfPkg/XenAcpiPlatformDxe/XenAcpiPlatformDxe.inf
@@ -0,0 +1,70 @@
+## @file
+# OVMF ACPI Platform Driver for Xen guests
+#
+# Copyright (C) 2021, Red Hat, Inc.
+# Copyright (c) 2008 - 2019, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = XenAcpiPlatform
+ FILE_GUID = fa0a48ac-767d-4c88-b70c-ec54c8b900c4
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = AcpiPlatformEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ AcpiPlatform.c
+ AcpiPlatform.h
+ BootScript.c
+ EntryPoint.c
+ PciDecoding.c
+ Qemu.c
+ QemuFwCfgAcpi.c
+ Xen.c
+
+[Packages]
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+ UefiCpuPkg/UefiCpuPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ DxeServicesTableLib
+ MemoryAllocationLib
+ OrderedCollectionLib
+ PcdLib
+ QemuFwCfgLib
+ QemuFwCfgS3Lib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ XenPlatformLib
+
+[Protocols]
+ gEfiAcpiTableProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiFirmwareVolume2ProtocolGuid # PROTOCOL SOMETIMES_CONSUMED
+ gEfiPciIoProtocolGuid # PROTOCOL SOMETIMES_CONSUMED
+
+[Guids]
+ gRootBridgesConnectedEventGroupGuid
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiTableStorageFile
+ gEfiMdeModulePkgTokenSpaceGuid.PcdPciDisableBusEnumeration
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress
+ gUefiOvmfPkgTokenSpaceGuid.Pcd8259LegacyModeEdgeLevel
+ gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
+
+[Depex]
+ gEfiAcpiTableProtocolGuid