]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/AcpiPlatformDxe/Qemu.c
OvmfPkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / OvmfPkg / AcpiPlatformDxe / Qemu.c
index 70887339054cdd5688c0e9eb5512b8f0bf9a1fac..f60e00f58af3b72fc153ae497a605ab8e7670cee 100644 (file)
@@ -1,14 +1,11 @@
 /** @file\r
   OVMF ACPI QEMU support\r
 \r
-  Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR>\r
-  This program and the accompanying materials\r
-  are licensed and made available under the terms and conditions of the BSD License\r
-  which accompanies this distribution.  The full text of the license may be found at\r
-  http://opensource.org/licenses/bsd-license.php\r
+  Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR>\r
 \r
-  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+  Copyright (C) 2012-2014, Red Hat, Inc.\r
+\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
 #include <Library/BaseMemoryLib.h>\r
 #include <Library/MemoryAllocationLib.h>\r
 #include <Library/QemuFwCfgLib.h>\r
-\r
+#include <Library/DxeServicesTableLib.h>\r
+#include <Library/PcdLib.h>\r
+#include <Library/OrderedCollectionLib.h>\r
+#include <IndustryStandard/Acpi.h>\r
 \r
 BOOLEAN\r
 QemuDetected (\r
@@ -31,6 +31,27 @@ QemuDetected (
 }\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
@@ -41,58 +62,120 @@ QemuInstallAcpiMadtTable (
   OUT  UINTN                         *TableKey\r
   )\r
 {\r
-  EFI_STATUS                                   Status;\r
-  UINTN                                        Count;\r
-  UINTN                                        Loop;\r
-  EFI_ACPI_DESCRIPTION_HEADER                  *Hdr;\r
-  UINTN                                        NewBufferSize;\r
-  EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC_STRUCTURE  *LocalApic;\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
-  Count = (UINTN) QemuFwCfgRead16 ();\r
-  ASSERT (Count >= 1);\r
-\r
-  if (Count == 1) {\r
-    //\r
-    // The pre-built MADT table covers the single CPU case\r
-    //\r
-    return InstallAcpiTable (\r
-             AcpiProtocol,\r
-             AcpiTableBuffer,\r
-             AcpiTableBufferSize,\r
-             TableKey\r
-             );\r
-  }\r
+  CpuCount = QemuFwCfgRead16 ();\r
+  ASSERT (CpuCount >= 1);\r
 \r
   //\r
-  // We need to add additional Local APIC entries to the MADT\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
-  NewBufferSize = AcpiTableBufferSize + ((Count - 1) * sizeof (*LocalApic));\r
-  Hdr = (EFI_ACPI_DESCRIPTION_HEADER*) AllocatePool (NewBufferSize);\r
-  ASSERT (Hdr != NULL);\r
+  PciLinkIsoCount = CountBits16 (PcdGet16 (Pcd8259LegacyModeEdgeLevel));\r
 \r
-  CopyMem (Hdr, AcpiTableBuffer, AcpiTableBufferSize);\r
+  NewBufferSize = 1                     * sizeof (*Madt) +\r
+                  CpuCount              * sizeof (*LocalApic) +\r
+                  1                     * sizeof (*IoApic) +\r
+                  (1 + PciLinkIsoCount) * sizeof (*Iso) +\r
+                  1                     * sizeof (*LocalApicNmi);\r
 \r
-  LocalApic = (EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC_STRUCTURE*)\r
-                (((UINT8*) Hdr) + AcpiTableBufferSize);\r
+  Madt = AllocatePool (NewBufferSize);\r
+  if (Madt == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
 \r
-  //\r
-  // Add Local APIC entries for the APs to the MADT\r
-  //\r
-  for (Loop = 1; Loop < Count; Loop++) {\r
-    LocalApic->Type = EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC;\r
-    LocalApic->Length = sizeof (*LocalApic);\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;\r
-    LocalApic++;\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
-  Hdr->Length = (UINT32) NewBufferSize;\r
+  //\r
+  // Set Level-tiggered, 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-tiggered, 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
-  Status = InstallAcpiTable (AcpiProtocol, Hdr, NewBufferSize, TableKey);\r
+  ASSERT ((UINTN) ((UINT8 *)Ptr - (UINT8 *)Madt) == NewBufferSize);\r
+  Status = InstallAcpiTable (AcpiProtocol, Madt, NewBufferSize, TableKey);\r
 \r
-  FreePool (Hdr);\r
+  FreePool (Madt);\r
 \r
   return Status;\r
 }\r
@@ -111,6 +194,23 @@ typedef struct {
   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
@@ -121,7 +221,172 @@ PopulateFwData(
   OUT  FIRMWARE_DATA *FwData\r
   )\r
 {\r
-  return EFI_SUCCESS;\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
@@ -142,10 +407,16 @@ QemuInstallAcpiSsdtTable (
 \r
   FwData = AllocateReservedPool (sizeof (*FwData));\r
   if (FwData != NULL) {\r
-    UINTN SsdtSize;\r
-    UINT8 *Ssdt;\r
-\r
-    SsdtSize = AcpiTableBufferSize + 17;\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
@@ -182,8 +453,16 @@ QemuInstallAcpiSsdtTable (
         *(UINT32*) SsdtPtr = sizeof (*FwData);\r
         SsdtPtr += 4;\r
 \r
-        ASSERT(SsdtPtr - Ssdt == SsdtSize);\r
-        ((EFI_ACPI_DESCRIPTION_HEADER *) Ssdt)->Length = SsdtSize;\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
@@ -230,4 +509,3 @@ QemuInstallAcpiTable (
            TableKey\r
            );\r
 }\r
-\r