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