OvmfPkg: generate full MADT dynamically, synchronize contents with qemu

Represent the set of possible PCI link target IRQs with
Pcd8259LegacyModeEdgeLevel. This ensures that the 8259 Interrupt
Controller code in PcAtChipsetPkg will treat them as level-triggered too.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>

git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@13628 6f19259b-4bc3-4df7-8a09-765794883524
diff --git a/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf b/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf
index 04eb495..acc2247 100644
--- a/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf
+++ b/OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf
@@ -35,6 +35,8 @@
   MdePkg/MdePkg.dec

   MdeModulePkg/MdeModulePkg.dec

   OvmfPkg/OvmfPkg.dec

+  UefiCpuPkg/UefiCpuPkg.dec

+  PcAtChipsetPkg/PcAtChipsetPkg.dec

 

 [LibraryClasses]

   UefiLib

@@ -58,6 +60,8 @@
 

 [Pcd]

   gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiTableStorageFile

+  gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress

+  gPcAtChipsetPkgTokenSpaceGuid.Pcd8259LegacyModeEdgeLevel

 

 [Depex]

   gEfiAcpiTableProtocolGuid

diff --git a/OvmfPkg/AcpiPlatformDxe/Qemu.c b/OvmfPkg/AcpiPlatformDxe/Qemu.c
index 4c88c32..1ac443f 100644
--- a/OvmfPkg/AcpiPlatformDxe/Qemu.c
+++ b/OvmfPkg/AcpiPlatformDxe/Qemu.c
@@ -17,7 +17,8 @@
 #include <Library/MemoryAllocationLib.h>

 #include <Library/QemuFwCfgLib.h>

 #include <Library/DxeServicesTableLib.h>

-

+#include <Library/PcdLib.h>

+#include <IndustryStandard/Acpi.h>

 

 BOOLEAN

 QemuDetected (

@@ -33,6 +34,27 @@
 

 

 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 (

@@ -42,58 +64,120 @@
   OUT  UINTN                         *TableKey

   )

 {

-  EFI_STATUS                                   Status;

-  UINTN                                        Count;

-  UINTN                                        Loop;

-  EFI_ACPI_DESCRIPTION_HEADER                  *Hdr;

-  UINTN                                        NewBufferSize;

-  EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC_STRUCTURE  *LocalApic;

+  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);

-  Count = (UINTN) QemuFwCfgRead16 ();

-  ASSERT (Count >= 1);

+  CpuCount = QemuFwCfgRead16 ();

+  ASSERT (CpuCount >= 1);

 

-  if (Count == 1) {

-    //

-    // The pre-built MADT table covers the single CPU case

-    //

-    return InstallAcpiTable (

-             AcpiProtocol,

-             AcpiTableBuffer,

-             AcpiTableBufferSize,

-             TableKey

-             );

+  //

+  // 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;

   }

 

-  //

-  // We need to add additional Local APIC entries to the MADT

-  //

-  NewBufferSize = AcpiTableBufferSize + ((Count - 1) * sizeof (*LocalApic));

-  Hdr = (EFI_ACPI_DESCRIPTION_HEADER*) AllocatePool (NewBufferSize);

-  ASSERT (Hdr != NULL);

+  Madt->Header           = *(EFI_ACPI_DESCRIPTION_HEADER *) AcpiTableBuffer;

+  Madt->Header.Length    = NewBufferSize;

+  Madt->LocalApicAddress = PcdGet32 (PcdCpuLocalApicBaseAddress);

+  Madt->Flags            = EFI_ACPI_1_0_PCAT_COMPAT;

+  Ptr = Madt + 1;

 

-  CopyMem (Hdr, AcpiTableBuffer, AcpiTableBufferSize);

-

-  LocalApic = (EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC_STRUCTURE*)

-                (((UINT8*) Hdr) + AcpiTableBufferSize);

-

-  //

-  // Add Local APIC entries for the APs to the MADT

-  //

-  for (Loop = 1; Loop < Count; 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;

-    LocalApic++;

+  LocalApic = Ptr;

+  for (Loop = 0; Loop < CpuCount; ++Loop) {

+    LocalApic->Type            = EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC;

+    LocalApic->Length          = sizeof (*LocalApic);

+    LocalApic->AcpiProcessorId = Loop;

+    LocalApic->ApicId          = Loop;

+    LocalApic->Flags           = 1; // enabled

+    ++LocalApic;

   }

+  Ptr = LocalApic;

 

-  Hdr->Length = (UINT32) NewBufferSize;

+  IoApic = Ptr;

+  IoApic->Type             = EFI_ACPI_1_0_IO_APIC;

+  IoApic->Length           = sizeof (*IoApic);

+  IoApic->IoApicId         = CpuCount;

+  IoApic->Reserved         = EFI_ACPI_RESERVED_BYTE;

+  IoApic->IoApicAddress    = 0xFEC00000;

+  IoApic->SystemVectorBase = 0x00000000;

+  Ptr = IoApic + 1;

 

-  Status = InstallAcpiTable (AcpiProtocol, Hdr, NewBufferSize, TableKey);

+  //

+  // 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;

 

-  FreePool (Hdr);

+  //

+  // Set Level-tiggered, 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                      = Loop;

+    Iso->GlobalSystemInterruptVector = Loop;

+    Iso->Flags                       = 0x000D; // Level-tiggered, Active High

+    ++Iso;

+  }

+  ASSERT (

+    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 ((UINT8 *)Ptr - (UINT8 *)Madt == NewBufferSize);

+  Status = InstallAcpiTable (AcpiProtocol, Madt, NewBufferSize, TableKey);

+

+  FreePool (Madt);

 

   return Status;

 }

diff --git a/OvmfPkg/AcpiTables/Madt.aslc b/OvmfPkg/AcpiTables/Madt.aslc
index f5b71a6..62bf2c7 100644
--- a/OvmfPkg/AcpiTables/Madt.aslc
+++ b/OvmfPkg/AcpiTables/Madt.aslc
@@ -16,11 +16,7 @@
 **/

 

 #include <IndustryStandard/Acpi.h>

-

-//

-// MADT Definitions

-//

-#define EFI_ACPI_OEM_MADT_REVISION 0x00000000 // TBD

+#include <Platform.h>

 

 //

 // Local APIC address

@@ -74,21 +70,12 @@
   EFI_ACPI_1_0_APIC_SIGNATURE,

   sizeof (EFI_ACPI_1_0_MULTIPLE_APIC_DESCRIPTION_TABLE),

   EFI_ACPI_1_0_MULTIPLE_APIC_DESCRIPTION_TABLE_REVISION,

-

-  //

-  // Checksum will be updated at runtime

-  //

-  0x00,

-

-  //

-  // It is expected that these values will be programmed at runtime

-  //

-  ' ', ' ', ' ', ' ', ' ', ' ',

-

-  0,

-  EFI_ACPI_OEM_MADT_REVISION,

-  0,

-  0,

+  0x00,                                                     // Checksum will be updated at runtime

+  EFI_ACPI_OEM_ID,

+  EFI_ACPI_OEM_TABLE_ID,

+  EFI_ACPI_OEM_REVISION,

+  EFI_ACPI_CREATOR_ID,

+  EFI_ACPI_CREATOR_REVISION,

 

   //

   // MADT specific fields

diff --git a/OvmfPkg/AcpiTables/Platform.h b/OvmfPkg/AcpiTables/Platform.h
index 4b03897..e8fae3c 100644
--- a/OvmfPkg/AcpiTables/Platform.h
+++ b/OvmfPkg/AcpiTables/Platform.h
@@ -23,9 +23,9 @@
 //

 #define EFI_ACPI_OEM_ID           'O','V','M','F',' ',' '   // OEMID 6 bytes long

 #define EFI_ACPI_OEM_TABLE_ID     SIGNATURE_64('O','V','M','F','E','D','K','2') // OEM table id 8 bytes long

-#define EFI_ACPI_OEM_REVISION     0x02000820

+#define EFI_ACPI_OEM_REVISION     0x20120804

 #define EFI_ACPI_CREATOR_ID       SIGNATURE_32('O','V','M','F')

-#define EFI_ACPI_CREATOR_REVISION 0x00000097

+#define EFI_ACPI_CREATOR_REVISION 0x00000098

 

 #define INT_MODEL       0x01

 #define SCI_INT_VECTOR  0x0009

diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
index ffedcbf..453a578 100644
--- a/OvmfPkg/OvmfPkgIa32.dsc
+++ b/OvmfPkg/OvmfPkgIa32.dsc
@@ -302,6 +302,9 @@
 !endif

 !endif

 

+  # IRQs 5, 9, 10, 11 are level-triggered

+  gPcAtChipsetPkgTokenSpaceGuid.Pcd8259LegacyModeEdgeLevel|0x0E20

+

 !if $(SECURE_BOOT_ENABLE) == TRUE

   # override the default values from SecurityPkg to ensure images from all sources are verified in secure boot

   gEfiSecurityPkgTokenSpaceGuid.PcdOptionRomImageVerificationPolicy|0x05

diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
index 123ac31..73e8c98 100644
--- a/OvmfPkg/OvmfPkgIa32X64.dsc
+++ b/OvmfPkg/OvmfPkgIa32X64.dsc
@@ -302,6 +302,9 @@
 !endif

 !endif

 

+  # IRQs 5, 9, 10, 11 are level-triggered

+  gPcAtChipsetPkgTokenSpaceGuid.Pcd8259LegacyModeEdgeLevel|0x0E20

+

 [PcdsFixedAtBuild.X64]

 !if $(SECURE_BOOT_ENABLE) == TRUE

   # override the default values from SecurityPkg to ensure images from all sources are verified in secure boot

diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index 1fce7a7..1c2aec0 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -302,6 +302,9 @@
 !endif

 !endif

 

+  # IRQs 5, 9, 10, 11 are level-triggered

+  gPcAtChipsetPkgTokenSpaceGuid.Pcd8259LegacyModeEdgeLevel|0x0E20

+

 !if $(SECURE_BOOT_ENABLE) == TRUE

   # override the default values from SecurityPkg to ensure images from all sources are verified in secure boot

   gEfiSecurityPkgTokenSpaceGuid.PcdOptionRomImageVerificationPolicy|0x05