]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg/XenAcpiPlatformDxe: create from AcpiPlatformDxe
authorLaszlo Ersek <lersek@redhat.com>
Wed, 26 May 2021 20:14:14 +0000 (22:14 +0200)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Fri, 4 Jun 2021 16:01:50 +0000 (16:01 +0000)
Create an almost verbatim copy of the
"OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf" driver for the OvmfXen
platform. We're going to trim the driver in subsequent patches.
Ultimately, the XenAcpiPlatformDxe driver will duplicate a negligible
amount of code that is currently present in the QemuFwCfgAcpiPlatformDxe
driver.

List the new driver in "Maintainers.txt", in the "OvmfPkg: Xen-related
modules" section.

Switch the OvmfXen platform to the new driver at once.

This patch should be reviewed with "git show --find-copies-harder".

Cc: Andrew Fish <afish@apple.com>
Cc: Anthony Perard <anthony.perard@citrix.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Julien Grall <julien@xen.org>
Cc: Leif Lindholm <leif@nuviainc.com>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Philippe Mathieu-Daudé <philmd@redhat.com>
Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2122
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Message-Id: <20210526201446.12554-12-lersek@redhat.com>
Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Leif Lindholm <leif@nuviainc.com>
12 files changed:
Maintainers.txt
OvmfPkg/OvmfXen.dsc
OvmfPkg/OvmfXen.fdf
OvmfPkg/XenAcpiPlatformDxe/AcpiPlatform.c [new file with mode: 0644]
OvmfPkg/XenAcpiPlatformDxe/AcpiPlatform.h [new file with mode: 0644]
OvmfPkg/XenAcpiPlatformDxe/BootScript.c [new file with mode: 0644]
OvmfPkg/XenAcpiPlatformDxe/EntryPoint.c [new file with mode: 0644]
OvmfPkg/XenAcpiPlatformDxe/PciDecoding.c [new file with mode: 0644]
OvmfPkg/XenAcpiPlatformDxe/Qemu.c [new file with mode: 0644]
OvmfPkg/XenAcpiPlatformDxe/QemuFwCfgAcpi.c [new file with mode: 0644]
OvmfPkg/XenAcpiPlatformDxe/Xen.c [new file with mode: 0644]
OvmfPkg/XenAcpiPlatformDxe/XenAcpiPlatformDxe.inf [new file with mode: 0644]

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