OvmfPkg/XenAcpiPlatformDxe: create from AcpiPlatformDxe

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>
This commit is contained in:
Laszlo Ersek 2021-05-26 22:14:14 +02:00 committed by mergify[bot]
parent cc302b799e
commit c9bba52fc7
12 changed files with 3024 additions and 2 deletions

View File

@ -497,6 +497,7 @@ F: OvmfPkg/PlatformPei/MemDetect.c
F: OvmfPkg/PlatformPei/Platform.*
F: OvmfPkg/PlatformPei/Xen.*
F: OvmfPkg/SmbiosPlatformDxe/*Xen.c
F: OvmfPkg/XenAcpiPlatformDxe/
F: OvmfPkg/XenBusDxe/
F: OvmfPkg/XenIoPciDxe/
F: OvmfPkg/XenIoPvhDxe/

View File

@ -652,7 +652,7 @@
# ACPI Support
#
MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf
OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf
OvmfPkg/XenAcpiPlatformDxe/XenAcpiPlatformDxe.inf
OvmfPkg/AcpiTables/AcpiTables.inf
MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.inf
MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.inf

View File

@ -352,7 +352,7 @@ INF MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf
INF OvmfPkg/SmbiosPlatformDxe/SmbiosPlatformDxe.inf
INF MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf
INF OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf
INF OvmfPkg/XenAcpiPlatformDxe/XenAcpiPlatformDxe.inf
INF RuleOverride=ACPITABLE OvmfPkg/AcpiTables/AcpiTables.inf
INF MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.inf
INF MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.inf

View File

@ -0,0 +1,268 @@
/** @file
OVMF ACPI Platform Driver for Xen guests
Copyright (C) 2021, Red Hat, Inc.
Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Library/DebugLib.h> // ASSERT_EFI_ERROR()
#include <Library/UefiBootServicesTableLib.h> // gBS
#include <Library/XenPlatformLib.h> // XenDetected()
#include <Protocol/FirmwareVolume2.h> // gEfiFirmwareVolume2Protocol...
#include "AcpiPlatform.h"
EFI_STATUS
EFIAPI
InstallAcpiTable (
IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,
IN VOID *AcpiTableBuffer,
IN UINTN AcpiTableBufferSize,
OUT UINTN *TableKey
)
{
return AcpiProtocol->InstallAcpiTable (
AcpiProtocol,
AcpiTableBuffer,
AcpiTableBufferSize,
TableKey
);
}
/**
Locate the first instance of a protocol. If the protocol requested is an
FV protocol, then it will return the first FV that contains the ACPI table
storage file.
@param Instance Return pointer to the first instance of the protocol
@return EFI_SUCCESS The function completed successfully.
@return EFI_NOT_FOUND The protocol could not be located.
@return EFI_OUT_OF_RESOURCES There are not enough resources to find the protocol.
**/
EFI_STATUS
LocateFvInstanceWithTables (
OUT EFI_FIRMWARE_VOLUME2_PROTOCOL **Instance
)
{
EFI_STATUS Status;
EFI_HANDLE *HandleBuffer;
UINTN NumberOfHandles;
EFI_FV_FILETYPE FileType;
UINT32 FvStatus;
EFI_FV_FILE_ATTRIBUTES Attributes;
UINTN Size;
UINTN Index;
EFI_FIRMWARE_VOLUME2_PROTOCOL *FvInstance;
FvStatus = 0;
//
// Locate protocol.
//
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiFirmwareVolume2ProtocolGuid,
NULL,
&NumberOfHandles,
&HandleBuffer
);
if (EFI_ERROR (Status)) {
//
// Defined errors at this time are not found and out of resources.
//
return Status;
}
//
// Looking for FV with ACPI storage file
//
for (Index = 0; Index < NumberOfHandles; Index++) {
//
// Get the protocol on this handle
// This should not fail because of LocateHandleBuffer
//
Status = gBS->HandleProtocol (
HandleBuffer[Index],
&gEfiFirmwareVolume2ProtocolGuid,
(VOID**) &FvInstance
);
ASSERT_EFI_ERROR (Status);
//
// See if it has the ACPI storage file
//
Status = FvInstance->ReadFile (
FvInstance,
(EFI_GUID*)PcdGetPtr (PcdAcpiTableStorageFile),
NULL,
&Size,
&FileType,
&Attributes,
&FvStatus
);
//
// If we found it, then we are done
//
if (Status == EFI_SUCCESS) {
*Instance = FvInstance;
break;
}
}
//
// Our exit status is determined by the success of the previous operations
// If the protocol was found, Instance already points to it.
//
//
// Free any allocated buffers
//
gBS->FreePool (HandleBuffer);
return Status;
}
/**
Find ACPI tables in an FV and install them.
This is now a fall-back path. Normally, we will search for tables provided
by the VMM first.
If that fails, we use this function to load the ACPI tables from an FV. The
sources for the FV based tables is located under OvmfPkg/AcpiTables.
@param AcpiTable Protocol instance pointer
**/
EFI_STATUS
EFIAPI
InstallOvmfFvTables (
IN EFI_ACPI_TABLE_PROTOCOL *AcpiTable
)
{
EFI_STATUS Status;
EFI_FIRMWARE_VOLUME2_PROTOCOL *FwVol;
INTN Instance;
EFI_ACPI_COMMON_HEADER *CurrentTable;
UINTN TableHandle;
UINT32 FvStatus;
UINTN TableSize;
UINTN Size;
EFI_ACPI_TABLE_INSTALL_ACPI_TABLE TableInstallFunction;
Instance = 0;
CurrentTable = NULL;
TableHandle = 0;
if (QemuDetected ()) {
TableInstallFunction = QemuInstallAcpiTable;
} else {
TableInstallFunction = InstallAcpiTable;
}
//
// set FwVol (and use an ASSERT() below) to suppress incorrect
// compiler/analyzer warnings
//
FwVol = NULL;
//
// Locate the firmware volume protocol
//
Status = LocateFvInstanceWithTables (&FwVol);
if (EFI_ERROR (Status)) {
return EFI_ABORTED;
}
ASSERT (FwVol != NULL);
//
// Read tables from the storage file.
//
while (Status == EFI_SUCCESS) {
Status = FwVol->ReadSection (
FwVol,
(EFI_GUID*)PcdGetPtr (PcdAcpiTableStorageFile),
EFI_SECTION_RAW,
Instance,
(VOID**) &CurrentTable,
&Size,
&FvStatus
);
if (!EFI_ERROR (Status)) {
//
// Add the table
//
TableHandle = 0;
TableSize = ((EFI_ACPI_DESCRIPTION_HEADER *) CurrentTable)->Length;
ASSERT (Size >= TableSize);
//
// Install ACPI table
//
Status = TableInstallFunction (
AcpiTable,
CurrentTable,
TableSize,
&TableHandle
);
//
// Free memory allocated by ReadSection
//
gBS->FreePool (CurrentTable);
if (EFI_ERROR (Status)) {
return EFI_ABORTED;
}
//
// Increment the instance
//
Instance++;
CurrentTable = NULL;
}
}
return EFI_SUCCESS;
}
/**
Effective entrypoint of Acpi Platform driver.
@param ImageHandle
@param SystemTable
@return EFI_SUCCESS
@return EFI_LOAD_ERROR
@return EFI_OUT_OF_RESOURCES
**/
EFI_STATUS
EFIAPI
InstallAcpiTables (
IN EFI_ACPI_TABLE_PROTOCOL *AcpiTable
)
{
EFI_STATUS Status;
if (XenDetected ()) {
Status = InstallXenTables (AcpiTable);
} else {
Status = InstallQemuFwCfgTables (AcpiTable);
}
if (EFI_ERROR (Status)) {
Status = InstallOvmfFvTables (AcpiTable);
}
return Status;
}

View File

@ -0,0 +1,102 @@
/** @file
OVMF ACPI Platform Driver for Xen guests
Copyright (C) 2021, Red Hat, Inc.
Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef ACPI_PLATFORM_H_
#define ACPI_PLATFORM_H_
#include <Protocol/AcpiTable.h> // EFI_ACPI_TABLE_PROTOCOL
#include <Protocol/PciIo.h> // EFI_PCI_IO_PROTOCOL
typedef struct {
EFI_PCI_IO_PROTOCOL *PciIo;
UINT64 PciAttributes;
} ORIGINAL_ATTRIBUTES;
typedef struct S3_CONTEXT S3_CONTEXT;
EFI_STATUS
EFIAPI
InstallAcpiTable (
IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,
IN VOID *AcpiTableBuffer,
IN UINTN AcpiTableBufferSize,
OUT UINTN *TableKey
);
BOOLEAN
QemuDetected (
VOID
);
EFI_STATUS
EFIAPI
QemuInstallAcpiTable (
IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,
IN VOID *AcpiTableBuffer,
IN UINTN AcpiTableBufferSize,
OUT UINTN *TableKey
);
EFI_STATUS
EFIAPI
InstallXenTables (
IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol
);
EFI_STATUS
EFIAPI
InstallQemuFwCfgTables (
IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol
);
EFI_STATUS
EFIAPI
InstallAcpiTables (
IN EFI_ACPI_TABLE_PROTOCOL *AcpiTable
);
VOID
EnablePciDecoding (
OUT ORIGINAL_ATTRIBUTES **OriginalAttributes,
OUT UINTN *Count
);
VOID
RestorePciDecoding (
IN ORIGINAL_ATTRIBUTES *OriginalAttributes,
IN UINTN Count
);
EFI_STATUS
AllocateS3Context (
OUT S3_CONTEXT **S3Context,
IN UINTN WritePointerCount
);
VOID
ReleaseS3Context (
IN S3_CONTEXT *S3Context
);
EFI_STATUS
SaveCondensedWritePointerToS3Context (
IN OUT S3_CONTEXT *S3Context,
IN UINT16 PointerItem,
IN UINT8 PointerSize,
IN UINT32 PointerOffset,
IN UINT64 PointerValue
);
EFI_STATUS
TransferS3ContextToBootScript (
IN S3_CONTEXT *S3Context
);
#endif

View File

@ -0,0 +1,269 @@
/** @file
Append an ACPI S3 Boot Script fragment from the QEMU_LOADER_WRITE_POINTER
commands of QEMU's fully processed table linker/loader script.
Copyright (C) 2017-2021, Red Hat, Inc.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Library/BaseLib.h> // CpuDeadLoop()
#include <Library/DebugLib.h> // DEBUG()
#include <Library/MemoryAllocationLib.h> // AllocatePool()
#include <Library/QemuFwCfgS3Lib.h> // QemuFwCfgS3ScriptSkipBytes()
#include "AcpiPlatform.h"
//
// Condensed structure for capturing the fw_cfg operations -- select, skip,
// write -- inherent in executing a QEMU_LOADER_WRITE_POINTER command.
//
typedef struct {
UINT16 PointerItem; // resolved from QEMU_LOADER_WRITE_POINTER.PointerFile
UINT8 PointerSize; // copied as-is from QEMU_LOADER_WRITE_POINTER
UINT32 PointerOffset; // copied as-is from QEMU_LOADER_WRITE_POINTER
UINT64 PointerValue; // resolved from QEMU_LOADER_WRITE_POINTER.PointeeFile
// and QEMU_LOADER_WRITE_POINTER.PointeeOffset
} CONDENSED_WRITE_POINTER;
//
// Context structure to accumulate CONDENSED_WRITE_POINTER objects from
// QEMU_LOADER_WRITE_POINTER commands.
//
// Any pointers in this structure own the pointed-to objects; that is, when the
// context structure is released, all pointed-to objects must be released too.
//
struct S3_CONTEXT {
CONDENSED_WRITE_POINTER *WritePointers; // one array element per processed
// QEMU_LOADER_WRITE_POINTER
// command
UINTN Allocated; // number of elements allocated for
// WritePointers
UINTN Used; // number of elements populated in
// WritePointers
};
//
// Scratch buffer, allocated in EfiReservedMemoryType type memory, for the ACPI
// S3 Boot Script opcodes to work on.
//
#pragma pack (1)
typedef union {
UINT64 PointerValue; // filled in from CONDENSED_WRITE_POINTER.PointerValue
} SCRATCH_BUFFER;
#pragma pack ()
/**
Allocate an S3_CONTEXT object.
@param[out] S3Context The allocated S3_CONTEXT object is returned
through this parameter.
@param[in] WritePointerCount Number of CONDENSED_WRITE_POINTER elements to
allocate room for. WritePointerCount must be
positive.
@retval EFI_SUCCESS Allocation successful.
@retval EFI_OUT_OF_RESOURCES Out of memory.
@retval EFI_INVALID_PARAMETER WritePointerCount is zero.
**/
EFI_STATUS
AllocateS3Context (
OUT S3_CONTEXT **S3Context,
IN UINTN WritePointerCount
)
{
EFI_STATUS Status;
S3_CONTEXT *Context;
if (WritePointerCount == 0) {
return EFI_INVALID_PARAMETER;
}
Context = AllocateZeroPool (sizeof *Context);
if (Context == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Context->WritePointers = AllocatePool (WritePointerCount *
sizeof *Context->WritePointers);
if (Context->WritePointers == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto FreeContext;
}
Context->Allocated = WritePointerCount;
*S3Context = Context;
return EFI_SUCCESS;
FreeContext:
FreePool (Context);
return Status;
}
/**
Release an S3_CONTEXT object.
@param[in] S3Context The object to release.
**/
VOID
ReleaseS3Context (
IN S3_CONTEXT *S3Context
)
{
FreePool (S3Context->WritePointers);
FreePool (S3Context);
}
/**
Save the information necessary to replicate a QEMU_LOADER_WRITE_POINTER
command during S3 resume, in condensed format.
This function is to be called from ProcessCmdWritePointer(), after all the
sanity checks have passed, and before the fw_cfg operations are performed.
@param[in,out] S3Context The S3_CONTEXT object into which the caller wants
to save the information that was derived from
QEMU_LOADER_WRITE_POINTER.
@param[in] PointerItem The FIRMWARE_CONFIG_ITEM that
QEMU_LOADER_WRITE_POINTER.PointerFile was resolved
to, expressed as a UINT16 value.
@param[in] PointerSize Copied directly from
QEMU_LOADER_WRITE_POINTER.PointerSize.
@param[in] PointerOffset Copied directly from
QEMU_LOADER_WRITE_POINTER.PointerOffset.
@param[in] PointerValue The base address of the allocated / downloaded
fw_cfg blob that is identified by
QEMU_LOADER_WRITE_POINTER.PointeeFile, plus
QEMU_LOADER_WRITE_POINTER.PointeeOffset.
@retval EFI_SUCCESS The information derived from
QEMU_LOADER_WRITE_POINTER has been successfully
absorbed into S3Context.
@retval EFI_OUT_OF_RESOURCES No room available in S3Context.
**/
EFI_STATUS
SaveCondensedWritePointerToS3Context (
IN OUT S3_CONTEXT *S3Context,
IN UINT16 PointerItem,
IN UINT8 PointerSize,
IN UINT32 PointerOffset,
IN UINT64 PointerValue
)
{
CONDENSED_WRITE_POINTER *Condensed;
if (S3Context->Used == S3Context->Allocated) {
return EFI_OUT_OF_RESOURCES;
}
Condensed = S3Context->WritePointers + S3Context->Used;
Condensed->PointerItem = PointerItem;
Condensed->PointerSize = PointerSize;
Condensed->PointerOffset = PointerOffset;
Condensed->PointerValue = PointerValue;
DEBUG ((DEBUG_VERBOSE, "%a: 0x%04x/[0x%08x+%d] := 0x%Lx (%Lu)\n",
__FUNCTION__, PointerItem, PointerOffset, PointerSize, PointerValue,
(UINT64)S3Context->Used));
++S3Context->Used;
return EFI_SUCCESS;
}
/**
FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION provided to QemuFwCfgS3Lib.
**/
STATIC
VOID
EFIAPI
AppendFwCfgBootScript (
IN OUT VOID *Context, OPTIONAL
IN OUT VOID *ExternalScratchBuffer
)
{
S3_CONTEXT *S3Context;
SCRATCH_BUFFER *ScratchBuffer;
UINTN Index;
S3Context = Context;
ScratchBuffer = ExternalScratchBuffer;
for (Index = 0; Index < S3Context->Used; ++Index) {
CONST CONDENSED_WRITE_POINTER *Condensed;
RETURN_STATUS Status;
Condensed = &S3Context->WritePointers[Index];
Status = QemuFwCfgS3ScriptSkipBytes (Condensed->PointerItem,
Condensed->PointerOffset);
if (RETURN_ERROR (Status)) {
goto FatalError;
}
ScratchBuffer->PointerValue = Condensed->PointerValue;
Status = QemuFwCfgS3ScriptWriteBytes (-1, Condensed->PointerSize);
if (RETURN_ERROR (Status)) {
goto FatalError;
}
}
DEBUG ((DEBUG_VERBOSE, "%a: boot script fragment saved\n", __FUNCTION__));
ReleaseS3Context (S3Context);
return;
FatalError:
ASSERT (FALSE);
CpuDeadLoop ();
}
/**
Translate and append the information from an S3_CONTEXT object to the ACPI S3
Boot Script.
The effects of a successful call to this function cannot be undone.
@param[in] S3Context The S3_CONTEXT object to translate to ACPI S3 Boot
Script opcodes. If the function returns successfully,
the caller must set the S3Context pointer -- originally
returned by AllocateS3Context() -- immediately to NULL,
because the ownership of S3Context has been transferred.
@retval EFI_SUCCESS The translation of S3Context to ACPI S3 Boot Script
opcodes has been successfully executed or queued. (This
includes the case when S3Context was empty on input and
no ACPI S3 Boot Script opcodes have been necessary to
produce.)
@return Error codes from underlying functions.
**/
EFI_STATUS
TransferS3ContextToBootScript (
IN S3_CONTEXT *S3Context
)
{
RETURN_STATUS Status;
if (S3Context->Used == 0) {
ReleaseS3Context (S3Context);
return EFI_SUCCESS;
}
Status = QemuFwCfgS3CallWhenBootScriptReady (AppendFwCfgBootScript,
S3Context, sizeof (SCRATCH_BUFFER));
return (EFI_STATUS)Status;
}

View File

@ -0,0 +1,95 @@
/** @file
Entry point of OVMF ACPI Platform Driver for Xen guests
Copyright (C) 2015-2021, Red Hat, Inc.
Copyright (c) 2008 - 2015, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Guid/RootBridgesConnectedEventGroup.h> // gRootBridgesConnectedEve...
#include <Library/DebugLib.h> // DEBUG()
#include <Library/PcdLib.h> // PcdGetBool()
#include <Library/UefiBootServicesTableLib.h> // gBS
#include <Protocol/AcpiTable.h> // EFI_ACPI_TABLE_PROTOCOL
#include "AcpiPlatform.h"
STATIC
EFI_ACPI_TABLE_PROTOCOL *
FindAcpiTableProtocol (
VOID
)
{
EFI_STATUS Status;
EFI_ACPI_TABLE_PROTOCOL *AcpiTable;
Status = gBS->LocateProtocol (
&gEfiAcpiTableProtocolGuid,
NULL,
(VOID**)&AcpiTable
);
ASSERT_EFI_ERROR (Status);
return AcpiTable;
}
STATIC
VOID
EFIAPI
OnRootBridgesConnected (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
DEBUG ((DEBUG_INFO,
"%a: root bridges have been connected, installing ACPI tables\n",
__FUNCTION__));
Status = InstallAcpiTables (FindAcpiTableProtocol ());
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "%a: InstallAcpiTables: %r\n", __FUNCTION__, Status));
}
gBS->CloseEvent (Event);
}
EFI_STATUS
EFIAPI
AcpiPlatformEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_EVENT RootBridgesConnected;
//
// If the platform doesn't support PCI, or PCI enumeration has been disabled,
// install the tables at once, and let the entry point's return code reflect
// the full functionality.
//
if (PcdGetBool (PcdPciDisableBusEnumeration)) {
DEBUG ((DEBUG_INFO, "%a: PCI or its enumeration disabled, installing "
"ACPI tables\n", __FUNCTION__));
return InstallAcpiTables (FindAcpiTableProtocol ());
}
//
// Otherwise, delay installing the ACPI tables until root bridges are
// connected. The entry point's return status will only reflect the callback
// setup. (Note that we're a DXE_DRIVER; our entry point function is invoked
// strictly before BDS is entered and can connect the root bridges.)
//
Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
OnRootBridgesConnected, NULL /* Context */,
&gRootBridgesConnectedEventGroupGuid, &RootBridgesConnected);
if (!EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO,
"%a: waiting for root bridges to be connected, registered callback\n",
__FUNCTION__));
}
return Status;
}

View File

@ -0,0 +1,194 @@
/** @file
Temporarily enable IO and MMIO decoding for all PCI devices while QEMU
regenerates the ACPI tables.
Copyright (C) 2016-2021, Red Hat, Inc.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Library/DebugLib.h> // DEBUG()
#include <Library/MemoryAllocationLib.h> // AllocatePool()
#include <Library/UefiBootServicesTableLib.h> // gBS
#include "AcpiPlatform.h"
/**
Collect all PciIo protocol instances in the system. Save their original
attributes, and enable IO and MMIO decoding for each.
This is a best effort function; it doesn't return status codes. Its
caller is supposed to proceed even if this function fails.
@param[out] OriginalAttributes On output, a dynamically allocated array of
ORIGINAL_ATTRIBUTES elements. The array lists
the PciIo protocol instances found in the
system at the time of the call, plus the
original PCI attributes for each.
Before returning, the function enables IO and
MMIO decoding for each PciIo instance it
finds.
On error, or when no such instances are
found, OriginalAttributes is set to NULL.
@param[out] Count On output, the number of elements in
OriginalAttributes. On error it is set to
zero.
**/
VOID
EnablePciDecoding (
OUT ORIGINAL_ATTRIBUTES **OriginalAttributes,
OUT UINTN *Count
)
{
EFI_STATUS Status;
UINTN NoHandles;
EFI_HANDLE *Handles;
ORIGINAL_ATTRIBUTES *OrigAttrs;
UINTN Idx;
*OriginalAttributes = NULL;
*Count = 0;
if (PcdGetBool (PcdPciDisableBusEnumeration)) {
//
// The platform downloads ACPI tables from QEMU in general, but there are
// no root bridges in this execution. We're done.
//
return;
}
Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiPciIoProtocolGuid,
NULL /* SearchKey */, &NoHandles, &Handles);
if (Status == EFI_NOT_FOUND) {
//
// No PCI devices were found on either of the root bridges. We're done.
//
return;
}
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "%a: LocateHandleBuffer(): %r\n", __FUNCTION__,
Status));
return;
}
OrigAttrs = AllocatePool (NoHandles * sizeof *OrigAttrs);
if (OrigAttrs == NULL) {
DEBUG ((DEBUG_WARN, "%a: AllocatePool(): out of resources\n",
__FUNCTION__));
goto FreeHandles;
}
for (Idx = 0; Idx < NoHandles; ++Idx) {
EFI_PCI_IO_PROTOCOL *PciIo;
UINT64 Attributes;
//
// Look up PciIo on the handle and stash it
//
Status = gBS->HandleProtocol (Handles[Idx], &gEfiPciIoProtocolGuid,
(VOID**)&PciIo);
ASSERT_EFI_ERROR (Status);
OrigAttrs[Idx].PciIo = PciIo;
//
// Stash the current attributes
//
Status = PciIo->Attributes (PciIo, EfiPciIoAttributeOperationGet, 0,
&OrigAttrs[Idx].PciAttributes);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "%a: EfiPciIoAttributeOperationGet: %r\n",
__FUNCTION__, Status));
goto RestoreAttributes;
}
//
// Retrieve supported attributes
//
Status = PciIo->Attributes (PciIo, EfiPciIoAttributeOperationSupported, 0,
&Attributes);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "%a: EfiPciIoAttributeOperationSupported: %r\n",
__FUNCTION__, Status));
goto RestoreAttributes;
}
//
// Enable IO and MMIO decoding
//
Attributes &= EFI_PCI_IO_ATTRIBUTE_IO | EFI_PCI_IO_ATTRIBUTE_MEMORY;
Status = PciIo->Attributes (PciIo, EfiPciIoAttributeOperationEnable,
Attributes, NULL);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "%a: EfiPciIoAttributeOperationEnable: %r\n",
__FUNCTION__, Status));
goto RestoreAttributes;
}
}
//
// Success
//
FreePool (Handles);
*OriginalAttributes = OrigAttrs;
*Count = NoHandles;
return;
RestoreAttributes:
while (Idx > 0) {
--Idx;
OrigAttrs[Idx].PciIo->Attributes (OrigAttrs[Idx].PciIo,
EfiPciIoAttributeOperationSet,
OrigAttrs[Idx].PciAttributes,
NULL
);
}
FreePool (OrigAttrs);
FreeHandles:
FreePool (Handles);
}
/**
Restore the original PCI attributes saved with EnablePciDecoding().
@param[in] OriginalAttributes The array allocated and populated by
EnablePciDecoding(). This parameter may be
NULL. If OriginalAttributes is NULL, then the
function is a no-op; otherwise the PciIo
attributes will be restored, and the
OriginalAttributes array will be freed.
@param[in] Count The Count value stored by EnablePciDecoding(),
the number of elements in OriginalAttributes.
Count may be zero if and only if
OriginalAttributes is NULL.
**/
VOID
RestorePciDecoding (
IN ORIGINAL_ATTRIBUTES *OriginalAttributes,
IN UINTN Count
)
{
UINTN Idx;
ASSERT ((OriginalAttributes == NULL) == (Count == 0));
if (OriginalAttributes == NULL) {
return;
}
for (Idx = 0; Idx < Count; ++Idx) {
OriginalAttributes[Idx].PciIo->Attributes (
OriginalAttributes[Idx].PciIo,
EfiPciIoAttributeOperationSet,
OriginalAttributes[Idx].PciAttributes,
NULL
);
}
FreePool (OriginalAttributes);
}

View File

@ -0,0 +1,511 @@
/** @file
OVMF ACPI QEMU support
Copyright (C) 2012-2021, Red Hat, Inc.
Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <IndustryStandard/Acpi.h> // EFI_ACPI_1_0_IO_APIC_STRUCTURE
#include <Library/BaseMemoryLib.h> // CopyMem()
#include <Library/DebugLib.h> // DEBUG()
#include <Library/DxeServicesTableLib.h> // gDS
#include <Library/MemoryAllocationLib.h> // AllocatePool()
#include <Library/PcdLib.h> // PcdGet16()
#include <Library/QemuFwCfgLib.h> // QemuFwCfgIsAvailable()
#include "AcpiPlatform.h"
BOOLEAN
QemuDetected (
VOID
)
{
if (!QemuFwCfgIsAvailable ()) {
return FALSE;
}
return TRUE;
}
STATIC
UINTN
CountBits16 (
UINT16 Mask
)
{
//
// For all N >= 1, N bits are enough to represent the number of bits set
// among N bits. It's true for N == 1. When adding a new bit (N := N+1),
// the maximum number of possibly set bits increases by one, while the
// representable maximum doubles.
//
Mask = ((Mask & 0xAAAA) >> 1) + (Mask & 0x5555);
Mask = ((Mask & 0xCCCC) >> 2) + (Mask & 0x3333);
Mask = ((Mask & 0xF0F0) >> 4) + (Mask & 0x0F0F);
Mask = ((Mask & 0xFF00) >> 8) + (Mask & 0x00FF);
return Mask;
}
STATIC
EFI_STATUS
EFIAPI
QemuInstallAcpiMadtTable (
IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,
IN VOID *AcpiTableBuffer,
IN UINTN AcpiTableBufferSize,
OUT UINTN *TableKey
)
{
UINTN CpuCount;
UINTN PciLinkIsoCount;
UINTN NewBufferSize;
EFI_ACPI_1_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER *Madt;
EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC_STRUCTURE *LocalApic;
EFI_ACPI_1_0_IO_APIC_STRUCTURE *IoApic;
EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE *Iso;
EFI_ACPI_1_0_LOCAL_APIC_NMI_STRUCTURE *LocalApicNmi;
VOID *Ptr;
UINTN Loop;
EFI_STATUS Status;
ASSERT (AcpiTableBufferSize >= sizeof (EFI_ACPI_DESCRIPTION_HEADER));
QemuFwCfgSelectItem (QemuFwCfgItemSmpCpuCount);
CpuCount = QemuFwCfgRead16 ();
ASSERT (CpuCount >= 1);
//
// Set Level-tiggered, Active High for these identity mapped IRQs. The bitset
// corresponds to the union of all possible interrupt assignments for the LNKA,
// LNKB, LNKC, LNKD PCI interrupt lines. See the DSDT.
//
PciLinkIsoCount = CountBits16 (PcdGet16 (Pcd8259LegacyModeEdgeLevel));
NewBufferSize = 1 * sizeof (*Madt) +
CpuCount * sizeof (*LocalApic) +
1 * sizeof (*IoApic) +
(1 + PciLinkIsoCount) * sizeof (*Iso) +
1 * sizeof (*LocalApicNmi);
Madt = AllocatePool (NewBufferSize);
if (Madt == NULL) {
return EFI_OUT_OF_RESOURCES;
}
CopyMem (&(Madt->Header), AcpiTableBuffer, sizeof (EFI_ACPI_DESCRIPTION_HEADER));
Madt->Header.Length = (UINT32) NewBufferSize;
Madt->LocalApicAddress = PcdGet32 (PcdCpuLocalApicBaseAddress);
Madt->Flags = EFI_ACPI_1_0_PCAT_COMPAT;
Ptr = Madt + 1;
LocalApic = Ptr;
for (Loop = 0; Loop < CpuCount; ++Loop) {
LocalApic->Type = EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC;
LocalApic->Length = sizeof (*LocalApic);
LocalApic->AcpiProcessorId = (UINT8) Loop;
LocalApic->ApicId = (UINT8) Loop;
LocalApic->Flags = 1; // enabled
++LocalApic;
}
Ptr = LocalApic;
IoApic = Ptr;
IoApic->Type = EFI_ACPI_1_0_IO_APIC;
IoApic->Length = sizeof (*IoApic);
IoApic->IoApicId = (UINT8) CpuCount;
IoApic->Reserved = EFI_ACPI_RESERVED_BYTE;
IoApic->IoApicAddress = 0xFEC00000;
IoApic->SystemVectorBase = 0x00000000;
Ptr = IoApic + 1;
//
// IRQ0 (8254 Timer) => IRQ2 (PIC) Interrupt Source Override Structure
//
Iso = Ptr;
Iso->Type = EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE;
Iso->Length = sizeof (*Iso);
Iso->Bus = 0x00; // ISA
Iso->Source = 0x00; // IRQ0
Iso->GlobalSystemInterruptVector = 0x00000002;
Iso->Flags = 0x0000; // Conforms to specs of the bus
++Iso;
//
// Set Level-triggered, Active High for all possible PCI link targets.
//
for (Loop = 0; Loop < 16; ++Loop) {
if ((PcdGet16 (Pcd8259LegacyModeEdgeLevel) & (1 << Loop)) == 0) {
continue;
}
Iso->Type = EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE;
Iso->Length = sizeof (*Iso);
Iso->Bus = 0x00; // ISA
Iso->Source = (UINT8) Loop;
Iso->GlobalSystemInterruptVector = (UINT32) Loop;
Iso->Flags = 0x000D; // Level-triggered, Active High
++Iso;
}
ASSERT (
(UINTN) (Iso - (EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE *)Ptr) ==
1 + PciLinkIsoCount
);
Ptr = Iso;
LocalApicNmi = Ptr;
LocalApicNmi->Type = EFI_ACPI_1_0_LOCAL_APIC_NMI;
LocalApicNmi->Length = sizeof (*LocalApicNmi);
LocalApicNmi->AcpiProcessorId = 0xFF; // applies to all processors
//
// polarity and trigger mode of the APIC I/O input signals conform to the
// specifications of the bus
//
LocalApicNmi->Flags = 0x0000;
//
// Local APIC interrupt input LINTn to which NMI is connected.
//
LocalApicNmi->LocalApicInti = 0x01;
Ptr = LocalApicNmi + 1;
ASSERT ((UINTN) ((UINT8 *)Ptr - (UINT8 *)Madt) == NewBufferSize);
Status = InstallAcpiTable (AcpiProtocol, Madt, NewBufferSize, TableKey);
FreePool (Madt);
return Status;
}
#pragma pack(1)
typedef struct {
UINT64 Base;
UINT64 End;
UINT64 Length;
} PCI_WINDOW;
typedef struct {
PCI_WINDOW PciWindow32;
PCI_WINDOW PciWindow64;
} FIRMWARE_DATA;
typedef struct {
UINT8 BytePrefix;
UINT8 ByteValue;
} AML_BYTE;
typedef struct {
UINT8 NameOp;
UINT8 RootChar;
UINT8 NameChar[4];
UINT8 PackageOp;
UINT8 PkgLength;
UINT8 NumElements;
AML_BYTE Pm1aCntSlpTyp;
AML_BYTE Pm1bCntSlpTyp;
AML_BYTE Reserved[2];
} SYSTEM_STATE_PACKAGE;
#pragma pack()
STATIC
EFI_STATUS
EFIAPI
PopulateFwData(
OUT FIRMWARE_DATA *FwData
)
{
EFI_STATUS Status;
UINTN NumDesc;
EFI_GCD_MEMORY_SPACE_DESCRIPTOR *AllDesc;
Status = gDS->GetMemorySpaceMap (&NumDesc, &AllDesc);
if (Status == EFI_SUCCESS) {
UINT64 NonMmio32MaxExclTop;
UINT64 Mmio32MinBase;
UINT64 Mmio32MaxExclTop;
UINTN CurDesc;
Status = EFI_UNSUPPORTED;
NonMmio32MaxExclTop = 0;
Mmio32MinBase = BASE_4GB;
Mmio32MaxExclTop = 0;
for (CurDesc = 0; CurDesc < NumDesc; ++CurDesc) {
CONST EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Desc;
UINT64 ExclTop;
Desc = &AllDesc[CurDesc];
ExclTop = Desc->BaseAddress + Desc->Length;
if (ExclTop <= (UINT64) PcdGet32 (PcdOvmfFdBaseAddress)) {
switch (Desc->GcdMemoryType) {
case EfiGcdMemoryTypeNonExistent:
break;
case EfiGcdMemoryTypeReserved:
case EfiGcdMemoryTypeSystemMemory:
if (NonMmio32MaxExclTop < ExclTop) {
NonMmio32MaxExclTop = ExclTop;
}
break;
case EfiGcdMemoryTypeMemoryMappedIo:
if (Mmio32MinBase > Desc->BaseAddress) {
Mmio32MinBase = Desc->BaseAddress;
}
if (Mmio32MaxExclTop < ExclTop) {
Mmio32MaxExclTop = ExclTop;
}
break;
default:
ASSERT(0);
}
}
}
if (Mmio32MinBase < NonMmio32MaxExclTop) {
Mmio32MinBase = NonMmio32MaxExclTop;
}
if (Mmio32MinBase < Mmio32MaxExclTop) {
FwData->PciWindow32.Base = Mmio32MinBase;
FwData->PciWindow32.End = Mmio32MaxExclTop - 1;
FwData->PciWindow32.Length = Mmio32MaxExclTop - Mmio32MinBase;
FwData->PciWindow64.Base = 0;
FwData->PciWindow64.End = 0;
FwData->PciWindow64.Length = 0;
Status = EFI_SUCCESS;
}
FreePool (AllDesc);
}
DEBUG ((
DEBUG_INFO,
"ACPI PciWindow32: Base=0x%08lx End=0x%08lx Length=0x%08lx\n",
FwData->PciWindow32.Base,
FwData->PciWindow32.End,
FwData->PciWindow32.Length
));
DEBUG ((
DEBUG_INFO,
"ACPI PciWindow64: Base=0x%08lx End=0x%08lx Length=0x%08lx\n",
FwData->PciWindow64.Base,
FwData->PciWindow64.End,
FwData->PciWindow64.Length
));
return Status;
}
STATIC
VOID
EFIAPI
GetSuspendStates (
UINTN *SuspendToRamSize,
SYSTEM_STATE_PACKAGE *SuspendToRam,
UINTN *SuspendToDiskSize,
SYSTEM_STATE_PACKAGE *SuspendToDisk
)
{
STATIC CONST SYSTEM_STATE_PACKAGE Template = {
0x08, // NameOp
'\\', // RootChar
{ '_', 'S', 'x', '_' }, // NameChar[4]
0x12, // PackageOp
0x0A, // PkgLength
0x04, // NumElements
{ 0x0A, 0x00 }, // Pm1aCntSlpTyp
{ 0x0A, 0x00 }, // Pm1bCntSlpTyp -- we don't support it
{ // Reserved[2]
{ 0x0A, 0x00 },
{ 0x0A, 0x00 }
}
};
RETURN_STATUS Status;
FIRMWARE_CONFIG_ITEM FwCfgItem;
UINTN FwCfgSize;
UINT8 SystemStates[6];
//
// configure defaults
//
*SuspendToRamSize = sizeof Template;
CopyMem (SuspendToRam, &Template, sizeof Template);
SuspendToRam->NameChar[2] = '3'; // S3
SuspendToRam->Pm1aCntSlpTyp.ByteValue = 1; // PIIX4: STR
*SuspendToDiskSize = sizeof Template;
CopyMem (SuspendToDisk, &Template, sizeof Template);
SuspendToDisk->NameChar[2] = '4'; // S4
SuspendToDisk->Pm1aCntSlpTyp.ByteValue = 2; // PIIX4: POSCL
//
// check for overrides
//
Status = QemuFwCfgFindFile ("etc/system-states", &FwCfgItem, &FwCfgSize);
if (Status != RETURN_SUCCESS || FwCfgSize != sizeof SystemStates) {
DEBUG ((DEBUG_INFO, "ACPI using S3/S4 defaults\n"));
return;
}
QemuFwCfgSelectItem (FwCfgItem);
QemuFwCfgReadBytes (sizeof SystemStates, SystemStates);
//
// Each byte corresponds to a system state. In each byte, the MSB tells us
// whether the given state is enabled. If so, the three LSBs specify the
// value to be written to the PM control register's SUS_TYP bits.
//
if (SystemStates[3] & BIT7) {
SuspendToRam->Pm1aCntSlpTyp.ByteValue =
SystemStates[3] & (BIT2 | BIT1 | BIT0);
DEBUG ((DEBUG_INFO, "ACPI S3 value: %d\n",
SuspendToRam->Pm1aCntSlpTyp.ByteValue));
} else {
*SuspendToRamSize = 0;
DEBUG ((DEBUG_INFO, "ACPI S3 disabled\n"));
}
if (SystemStates[4] & BIT7) {
SuspendToDisk->Pm1aCntSlpTyp.ByteValue =
SystemStates[4] & (BIT2 | BIT1 | BIT0);
DEBUG ((DEBUG_INFO, "ACPI S4 value: %d\n",
SuspendToDisk->Pm1aCntSlpTyp.ByteValue));
} else {
*SuspendToDiskSize = 0;
DEBUG ((DEBUG_INFO, "ACPI S4 disabled\n"));
}
}
STATIC
EFI_STATUS
EFIAPI
QemuInstallAcpiSsdtTable (
IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,
IN VOID *AcpiTableBuffer,
IN UINTN AcpiTableBufferSize,
OUT UINTN *TableKey
)
{
EFI_STATUS Status;
FIRMWARE_DATA *FwData;
Status = EFI_OUT_OF_RESOURCES;
FwData = AllocateReservedPool (sizeof (*FwData));
if (FwData != NULL) {
UINTN SuspendToRamSize;
SYSTEM_STATE_PACKAGE SuspendToRam;
UINTN SuspendToDiskSize;
SYSTEM_STATE_PACKAGE SuspendToDisk;
UINTN SsdtSize;
UINT8 *Ssdt;
GetSuspendStates (&SuspendToRamSize, &SuspendToRam,
&SuspendToDiskSize, &SuspendToDisk);
SsdtSize = AcpiTableBufferSize + 17 + SuspendToRamSize + SuspendToDiskSize;
Ssdt = AllocatePool (SsdtSize);
if (Ssdt != NULL) {
Status = PopulateFwData (FwData);
if (Status == EFI_SUCCESS) {
UINT8 *SsdtPtr;
SsdtPtr = Ssdt;
CopyMem (SsdtPtr, AcpiTableBuffer, AcpiTableBufferSize);
SsdtPtr += AcpiTableBufferSize;
//
// build "OperationRegion(FWDT, SystemMemory, 0x12345678, 0x87654321)"
//
*(SsdtPtr++) = 0x5B; // ExtOpPrefix
*(SsdtPtr++) = 0x80; // OpRegionOp
*(SsdtPtr++) = 'F';
*(SsdtPtr++) = 'W';
*(SsdtPtr++) = 'D';
*(SsdtPtr++) = 'T';
*(SsdtPtr++) = 0x00; // SystemMemory
*(SsdtPtr++) = 0x0C; // DWordPrefix
//
// no virtual addressing yet, take the four least significant bytes
//
CopyMem(SsdtPtr, &FwData, 4);
SsdtPtr += 4;
*(SsdtPtr++) = 0x0C; // DWordPrefix
*(UINT32*) SsdtPtr = sizeof (*FwData);
SsdtPtr += 4;
//
// add suspend system states
//
CopyMem (SsdtPtr, &SuspendToRam, SuspendToRamSize);
SsdtPtr += SuspendToRamSize;
CopyMem (SsdtPtr, &SuspendToDisk, SuspendToDiskSize);
SsdtPtr += SuspendToDiskSize;
ASSERT((UINTN) (SsdtPtr - Ssdt) == SsdtSize);
((EFI_ACPI_DESCRIPTION_HEADER *) Ssdt)->Length = (UINT32) SsdtSize;
Status = InstallAcpiTable (AcpiProtocol, Ssdt, SsdtSize, TableKey);
}
FreePool(Ssdt);
}
if (Status != EFI_SUCCESS) {
FreePool(FwData);
}
}
return Status;
}
EFI_STATUS
EFIAPI
QemuInstallAcpiTable (
IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,
IN VOID *AcpiTableBuffer,
IN UINTN AcpiTableBufferSize,
OUT UINTN *TableKey
)
{
EFI_ACPI_DESCRIPTION_HEADER *Hdr;
EFI_ACPI_TABLE_INSTALL_ACPI_TABLE TableInstallFunction;
Hdr = (EFI_ACPI_DESCRIPTION_HEADER*) AcpiTableBuffer;
switch (Hdr->Signature) {
case EFI_ACPI_1_0_APIC_SIGNATURE:
TableInstallFunction = QemuInstallAcpiMadtTable;
break;
case EFI_ACPI_1_0_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE:
TableInstallFunction = QemuInstallAcpiSsdtTable;
break;
default:
TableInstallFunction = InstallAcpiTable;
}
return TableInstallFunction (
AcpiProtocol,
AcpiTableBuffer,
AcpiTableBufferSize,
TableKey
);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,316 @@
/** @file
OVMF ACPI Xen support
Copyright (C) 2021, Red Hat, Inc.
Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR>
Copyright (c) 2012, Bei Guan <gbtju85@gmail.com>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Library/BaseLib.h> // CpuDeadLoop()
#include <Library/DebugLib.h> // DEBUG()
#include <Library/XenPlatformLib.h> // XenGetInfoHOB()
#include "AcpiPlatform.h"
#define XEN_ACPI_PHYSICAL_ADDRESS 0x000EA020
#define XEN_BIOS_PHYSICAL_END 0x000FFFFF
EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *XenAcpiRsdpStructurePtr = NULL;
/**
Get the address of Xen ACPI Root System Description Pointer (RSDP)
structure.
@param RsdpStructurePtr Return pointer to RSDP structure
@return EFI_SUCCESS Find Xen RSDP structure successfully.
@return EFI_NOT_FOUND Don't find Xen RSDP structure.
@return EFI_ABORTED Find Xen RSDP structure, but it's not integrated.
**/
EFI_STATUS
EFIAPI
GetXenAcpiRsdp (
OUT EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER **RsdpPtr
)
{
EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *RsdpStructurePtr;
UINT8 *XenAcpiPtr;
UINT8 Sum;
EFI_XEN_INFO *XenInfo;
//
// Detect the RSDP structure
//
//
// First look for PVH one
//
XenInfo = XenGetInfoHOB ();
ASSERT (XenInfo != NULL);
if (XenInfo->RsdpPvh != NULL) {
DEBUG ((DEBUG_INFO, "%a: Use ACPI RSDP table at 0x%p\n",
gEfiCallerBaseName, XenInfo->RsdpPvh));
*RsdpPtr = XenInfo->RsdpPvh;
return EFI_SUCCESS;
}
//
// Otherwise, look for the HVM one
//
for (XenAcpiPtr = (UINT8*)(UINTN) XEN_ACPI_PHYSICAL_ADDRESS;
XenAcpiPtr < (UINT8*)(UINTN) XEN_BIOS_PHYSICAL_END;
XenAcpiPtr += 0x10) {
RsdpStructurePtr = (EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *)
(UINTN) XenAcpiPtr;
if (!AsciiStrnCmp ((CHAR8 *) &RsdpStructurePtr->Signature, "RSD PTR ", 8)) {
//
// RSDP ACPI 1.0 checksum for 1.0/2.0/3.0 table.
// This is only the first 20 bytes of the structure
//
Sum = CalculateSum8 (
(CONST UINT8 *)RsdpStructurePtr,
sizeof (EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER)
);
if (Sum != 0) {
return EFI_ABORTED;
}
if (RsdpStructurePtr->Revision >= 2) {
//
// RSDP ACPI 2.0/3.0 checksum, this is the entire table
//
Sum = CalculateSum8 (
(CONST UINT8 *)RsdpStructurePtr,
sizeof (EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER)
);
if (Sum != 0) {
return EFI_ABORTED;
}
}
*RsdpPtr = RsdpStructurePtr;
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}
/**
Get Xen Acpi tables from the RSDP structure. And installs Xen ACPI tables
into the RSDT/XSDT using InstallAcpiTable. Some signature of the installed
ACPI tables are: FACP, APIC, HPET, WAET, SSDT, FACS, DSDT.
@param AcpiProtocol Protocol instance pointer.
@return EFI_SUCCESS The table was successfully inserted.
@return EFI_INVALID_PARAMETER Either AcpiTableBuffer is NULL, TableHandle is
NULL, or AcpiTableBufferSize and the size
field embedded in the ACPI table pointed to
by AcpiTableBuffer are not in sync.
@return EFI_OUT_OF_RESOURCES Insufficient resources exist to complete the request.
**/
EFI_STATUS
EFIAPI
InstallXenTables (
IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol
)
{
EFI_STATUS Status;
UINTN TableHandle;
EFI_ACPI_DESCRIPTION_HEADER *Rsdt;
EFI_ACPI_DESCRIPTION_HEADER *Xsdt;
VOID *CurrentTableEntry;
UINTN CurrentTablePointer;
EFI_ACPI_DESCRIPTION_HEADER *CurrentTable;
UINTN Index;
UINTN NumberOfTableEntries;
EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt2Table;
EFI_ACPI_1_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt1Table;
EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs2Table;
EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs1Table;
EFI_ACPI_DESCRIPTION_HEADER *DsdtTable;
Fadt2Table = NULL;
Fadt1Table = NULL;
Facs2Table = NULL;
Facs1Table = NULL;
DsdtTable = NULL;
TableHandle = 0;
NumberOfTableEntries = 0;
//
// Try to find Xen ACPI tables
//
Status = GetXenAcpiRsdp (&XenAcpiRsdpStructurePtr);
if (EFI_ERROR (Status)) {
return Status;
}
//
// If XSDT table is find, just install its tables.
// Otherwise, try to find and install the RSDT tables.
//
if (XenAcpiRsdpStructurePtr->XsdtAddress) {
//
// Retrieve the addresses of XSDT and
// calculate the number of its table entries.
//
Xsdt = (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN)
XenAcpiRsdpStructurePtr->XsdtAddress;
NumberOfTableEntries = (Xsdt->Length -
sizeof (EFI_ACPI_DESCRIPTION_HEADER)) /
sizeof (UINT64);
//
// Install ACPI tables found in XSDT.
//
for (Index = 0; Index < NumberOfTableEntries; Index++) {
//
// Get the table entry from XSDT
//
CurrentTableEntry = (VOID *) ((UINT8 *) Xsdt +
sizeof (EFI_ACPI_DESCRIPTION_HEADER) +
Index * sizeof (UINT64));
CurrentTablePointer = (UINTN) *(UINT64 *)CurrentTableEntry;
CurrentTable = (EFI_ACPI_DESCRIPTION_HEADER *) CurrentTablePointer;
//
// Install the XSDT tables
//
Status = InstallAcpiTable (
AcpiProtocol,
CurrentTable,
CurrentTable->Length,
&TableHandle
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Get the FACS and DSDT table address from the table FADT
//
if (!AsciiStrnCmp ((CHAR8 *) &CurrentTable->Signature, "FACP", 4)) {
Fadt2Table = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *)
(UINTN) CurrentTablePointer;
Facs2Table = (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)
(UINTN) Fadt2Table->FirmwareCtrl;
DsdtTable = (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN) Fadt2Table->Dsdt;
}
}
}
else if (XenAcpiRsdpStructurePtr->RsdtAddress) {
//
// Retrieve the addresses of RSDT and
// calculate the number of its table entries.
//
Rsdt = (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN)
XenAcpiRsdpStructurePtr->RsdtAddress;
NumberOfTableEntries = (Rsdt->Length -
sizeof (EFI_ACPI_DESCRIPTION_HEADER)) /
sizeof (UINT32);
//
// Install ACPI tables found in XSDT.
//
for (Index = 0; Index < NumberOfTableEntries; Index++) {
//
// Get the table entry from RSDT
//
CurrentTableEntry = (UINT32 *) ((UINT8 *) Rsdt +
sizeof (EFI_ACPI_DESCRIPTION_HEADER) +
Index * sizeof (UINT32));
CurrentTablePointer = *(UINT32 *)CurrentTableEntry;
CurrentTable = (EFI_ACPI_DESCRIPTION_HEADER *) CurrentTablePointer;
//
// Install the RSDT tables
//
Status = InstallAcpiTable (
AcpiProtocol,
CurrentTable,
CurrentTable->Length,
&TableHandle
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Get the FACS and DSDT table address from the table FADT
//
if (!AsciiStrnCmp ((CHAR8 *) &CurrentTable->Signature, "FACP", 4)) {
Fadt1Table = (EFI_ACPI_1_0_FIXED_ACPI_DESCRIPTION_TABLE *)
(UINTN) CurrentTablePointer;
Facs1Table = (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)
(UINTN) Fadt1Table->FirmwareCtrl;
DsdtTable = (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN) Fadt1Table->Dsdt;
}
}
}
//
// Install the FACS table.
//
if (Fadt2Table) {
//
// FACS 2.0
//
Status = InstallAcpiTable (
AcpiProtocol,
Facs2Table,
Facs2Table->Length,
&TableHandle
);
if (EFI_ERROR (Status)) {
return Status;
}
}
else if (Fadt1Table) {
//
// FACS 1.0
//
Status = InstallAcpiTable (
AcpiProtocol,
Facs1Table,
Facs1Table->Length,
&TableHandle
);
if (EFI_ERROR (Status)) {
return Status;
}
}
//
// Install DSDT table. If we reached this point without finding the DSDT,
// then we're out of sync with the hypervisor, and cannot continue.
//
if (DsdtTable == NULL) {
DEBUG ((DEBUG_ERROR, "%a: no DSDT found\n", __FUNCTION__));
ASSERT (FALSE);
CpuDeadLoop ();
}
Status = InstallAcpiTable (
AcpiProtocol,
DsdtTable,
DsdtTable->Length,
&TableHandle
);
if (EFI_ERROR (Status)) {
return Status;
}
return EFI_SUCCESS;
}

View File

@ -0,0 +1,70 @@
## @file
# OVMF ACPI Platform Driver for Xen guests
#
# Copyright (C) 2021, Red Hat, Inc.
# Copyright (c) 2008 - 2019, Intel Corporation. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = XenAcpiPlatform
FILE_GUID = fa0a48ac-767d-4c88-b70c-ec54c8b900c4
MODULE_TYPE = DXE_DRIVER
VERSION_STRING = 1.0
ENTRY_POINT = AcpiPlatformEntryPoint
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = IA32 X64 EBC
#
[Sources]
AcpiPlatform.c
AcpiPlatform.h
BootScript.c
EntryPoint.c
PciDecoding.c
Qemu.c
QemuFwCfgAcpi.c
Xen.c
[Packages]
MdeModulePkg/MdeModulePkg.dec
MdePkg/MdePkg.dec
OvmfPkg/OvmfPkg.dec
UefiCpuPkg/UefiCpuPkg.dec
[LibraryClasses]
BaseLib
BaseMemoryLib
DebugLib
DxeServicesTableLib
MemoryAllocationLib
OrderedCollectionLib
PcdLib
QemuFwCfgLib
QemuFwCfgS3Lib
UefiBootServicesTableLib
UefiDriverEntryPoint
XenPlatformLib
[Protocols]
gEfiAcpiTableProtocolGuid # PROTOCOL ALWAYS_CONSUMED
gEfiFirmwareVolume2ProtocolGuid # PROTOCOL SOMETIMES_CONSUMED
gEfiPciIoProtocolGuid # PROTOCOL SOMETIMES_CONSUMED
[Guids]
gRootBridgesConnectedEventGroupGuid
[Pcd]
gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiTableStorageFile
gEfiMdeModulePkgTokenSpaceGuid.PcdPciDisableBusEnumeration
gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress
gUefiOvmfPkgTokenSpaceGuid.Pcd8259LegacyModeEdgeLevel
gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
[Depex]
gEfiAcpiTableProtocolGuid