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
# 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
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
--- /dev/null
+/** @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
--- /dev/null
+/** @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
--- /dev/null
+/** @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
--- /dev/null
+/** @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
--- /dev/null
+/** @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
--- /dev/null
+/** @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
--- /dev/null
+/** @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
--- /dev/null
+/** @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
--- /dev/null
+## @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