Skip to contentSkip to conversation

acpi: DSDT/SSDT runtime patching

PATCH In review Michael S. Tsirkin 2 participants 7 messages Nov 20, 2011 – Nov 23, 2011

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

v4 cover acpi: DSDT/SSDT runtime patching

Here's an updated revision of acpi runtime patching patchset. Lightly tested.

changes in v4:
- split PCI hotplug handling out to a separate SSDT

Changes in v3:
- change ssdt generation code to get rid of hardcoded offsets
- enhancements to acpi_extract: add more extract methods ACPI_EXTRACT_NAME_WORD_CONST - extract a Word Const object from Name() ACPI_EXTRACT_NAME_BYTE_CONST - extract a Byte Const object from Name() ACPI_EXTRACT_PROCESSOR_START - start of Processor() block ACPI_EXTRACT_PROCESSOR_STRING - extract a NameString from Processor() ACPI_EXTRACT_PROCESSOR_END - offset at last byte of Processor() + 1

Changes in v2:
- tools rewritten in python
- Original ASL retains _EJ0 methods, BIOS patches that to EJ0_
- generic ACP_EXTRACT infrastructure that can match Method and Name Operators
- instead of matching specific method name, insert tags in original DSL source and match that to AML

-----

Here's a bug: guest thinks it can eject VGA device and ISA bridge.

[root@dhcp74-172 ~]#lspci
00:00.0 Host bridge: Intel Corporation 440FX - 82441FX PMC [Natoma] (rev 02) 00:01.0 ISA bridge: Intel Corporation 82371SB PIIX3 ISA [Natoma/Triton II] 00:01.1 IDE interface: Intel Corporation 82371SB PIIX3 IDE [Natoma/Triton II] 00:01.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 03)
00:02.0 VGA compatible controller: Cirrus Logic GD 5446
00:03.0 PCI bridge: Red Hat, Inc. Device 0001
00:04.0 Ethernet controller: Qumranet, Inc. Virtio network device
00:05.0 SCSI storage controller: Qumranet, Inc. Virtio block device
01:00.0 Ethernet controller: Intel Corporation 82540EM Gigabit Ethernet Controller (rev 03)

[root@dhcp74-172 ~]# ls /sys/bus/pci/slots/1/
adapter address attention latch module power
[root@dhcp74-172 ~]# ls /sys/bus/pci/slots/2/
adapter address attention latch module power

[root@dhcp74-172 ~]# echo 0 > /sys/bus/pci/slots/2/power
[root@dhcp74-172 ~]# lspci
00:00.0 Host bridge: Intel Corporation 440FX - 82441FX PMC [Natoma] (rev 02) 00:01.0 ISA bridge: Intel Corporation 82371SB PIIX3 ISA [Natoma/Triton II] 00:01.1 IDE interface: Intel Corporation 82371SB PIIX3 IDE [Natoma/Triton II] 00:01.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 03)
00:03.0 PCI bridge: Red Hat, Inc. Device 0001
00:04.0 Ethernet controller: Qumranet, Inc. Virtio network device
00:05.0 SCSI storage controller: Qumranet, Inc. Virtio block device
01:00.0 Ethernet controller: Intel Corporation 82540EM Gigabit Ethernet Controller (rev 03)

This is wrong because slots 1 and 2 are marked as not hotpluggable in qemu.

The reason is that our acpi tables declare both _RMV with value 0, and _EJ0 method for these slots. What happens in this case is undocumented by ACPI spec, so linux ignores _RMV, and windows seems to ignore _EJ0.

The correct way to suppress hotplug is not to have _EJ0, so this is what this patch does: it probes PIIX and modifies DSDT to match.

With these patches applied, we get:

[root@dhcp74-172 ~]# ls /sys/bus/pci/slots/1/
address
[root@dhcp74-172 ~]# ls /sys/bus/pci/slots/2/
address

Michael S. Tsirkin (3):
acpi: add ssdt for pci hotplug
acpi: EJ0 method name patching
acpi: remove _RMV

 Makefile           |    2 +-
 src/acpi-dsdt.dsl  |  132 +--------------------------------------------------
 src/acpi.c         |   40 ++++++++++++++++
 src/ssdt-pcihp.dsl |   98 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 142 insertions(+), 130 deletions(-)
 create mode 100644 src/ssdt-pcihp.dsl
Signature · 1 line
1.7.5.53.gc233e
#1 lore raw

PATCH v4 1/3 acpi: add ssdt for pci hotplug

The point of this split is to make runtime patching easier.

DSDT is required to supply: PCI0 - PCI root device object; PCEJ - Method object to eject a PCI slot.
Additionally, SSDT is required to supply PCNT - Method object to notify OSPM of a PCI slot event.

---
 Makefile           |    2 +-
 src/acpi-dsdt.dsl  |  117 +--------------------------------------------
 src/acpi.c         |   12 +++++
 src/ssdt-pcihp.dsl |  132 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 148 insertions(+), 115 deletions(-)
 create mode 100644 src/ssdt-pcihp.dsl
Makefile +1 -1
index 540f1ea..ef8e15d 100644
@@ -200,7 +200,7 @@ src/%.hex: src/%.dsl ./tools/acpi_extract_preprocess.py ./tools/acpi_extract.py
 	$(Q)./tools/acpi_extract.py $(OUT)$*.lst > $(OUT)$*.off
 	$(Q)cat $(OUT)$*.off > $@
 
-$(OUT)ccode32flat.o: src/acpi-dsdt.hex src/ssdt-proc.hex
+$(OUT)ccode32flat.o: src/acpi-dsdt.hex src/ssdt-proc.hex src/ssdt-pcihp.hex
 
 ####### Kconfig rules
 export HOSTCC             := $(CC)
src/acpi-dsdt.dsl +3 -114

135 lines not on this page · raw message

src/acpi.c +12 -0
index a3770ae..c5147d9 100644
@@ -406,6 +406,7 @@ encodeLen(u8 *ssdt_ptr, int length, int bytes)
 #define SD_PROC (ssdp_proc_aml + *ssdt_proc_start)
 
 #define SSDT_SIGNATURE 0x54445353 // SSDT
+
 static void*
 build_ssdt(void)
 {
@@ -483,6 +484,14 @@ build_ssdt(void)
     return ssdt;
 }
 
+static void*
+copy_table(void *table, int size)
+{
+    u8 *copy = malloc_high(size);
+    memcpy(copy, table, size);
+    return copy;
+}
+
 #define HPET_SIGNATURE 0x54455048 // HPET
 static void*
 build_hpet(void)
@@ -633,6 +642,8 @@ static const struct pci_device_id acpi_find_tbl[] = {
 
 struct rsdp_descriptor *RsdpAddr;
 
+#include "ssdt-pcihp.hex"
+
 #define MAX_ACPI_TABLES 20
 void
 acpi_bios_init(void)
@@ -664,6 +675,7 @@ acpi_bios_init(void)
     ACPI_INIT_TABLE(build_madt());
     ACPI_INIT_TABLE(build_hpet());
     ACPI_INIT_TABLE(build_srat());
+    ACPI_INIT_TABLE(copy_table(ssdp_pcihp_aml, sizeof ssdp_pcihp_aml));
 
     u16 i, external_tables = qemu_cfg_acpi_additional_tables();
 
src/ssdt-pcihp.dsl new file +132 -0

132 lines not on this page · raw message

Signature · 1 line
1.7.5.53.gc233e
#2 lore raw

PATCH v4 2/3 acpi: EJ0 method name patching

In reply to Michael S. Tsirkin

Modify ACPI to only supply _EJ0 methods for PCI slots that support hotplug.

This is done by runtime patching:
- Instrument SSDT ASL code with ACPI_EXTRACT directives tagging _EJ0 and _ADR fields.
- At compile time, tools/acpi_extract.py looks for these methods in ASL source finds the matching AML, and stores the offsets of these methods in tables named aml_ej0_name and aml_adr_dword.
- At run time, go over aml_ej0_name, use aml_adr_dword to get slot information and check which slots support hotplug.

If hotplug is disabled, we patch the _EJ0 NameString in ACPI table, replacing _EJ0 with EJ0_.

Note that this has the same checksum, but is ignored by OSPM.

Note: the method used is robust in that we don't need
to change any offsets manually in case of ASL code changes. As all parsing is done at compile time, any unexpected input causes build failure, not a runtime failure.

---
 src/acpi.c         |   44 ++++++++++++++++++++++++++++++++++++--------
 src/ssdt-pcihp.dsl |    6 ++++++
 2 files changed, 42 insertions(+), 8 deletions(-)
src/acpi.c +36 -8
index c5147d9..107469f 100644
@@ -484,12 +484,42 @@ build_ssdt(void)
     return ssdt;
 }
 
-static void*
-copy_table(void *table, int size)
+#include "ssdt-pcihp.hex"
+
+#define PCI_RMV_BASE 0xae0c
+
+extern void link_time_assertion(void);
+
+static void* build_pcihp(void)
 {
-    u8 *copy = malloc_high(size);
-    memcpy(copy, table, size);
-    return copy;
+    u32 rmvc_pcrm;
+    int i;
+
+    u8 *ssdt = malloc_high(sizeof ssdp_pcihp_aml);
+    memcpy(ssdt, ssdp_pcihp_aml, sizeof ssdp_pcihp_aml);
+
+    /* Runtime patching of EJ0: to disable hotplug for a slot,
+     * replace the method name: _EJ0 by EJ0_. */
+    if (ARRAY_SIZE(aml_ej0_name) != ARRAY_SIZE(aml_adr_dword)) {
+        link_time_assertion();
+    }
+
+    rmvc_pcrm = inl(PCI_RMV_BASE);
+    for (i = 0; i < ARRAY_SIZE(aml_ej0_name); ++i) {
+        /* Slot is in byte 2 in _ADR */
+        u8 slot = ssdp_pcihp_aml[aml_adr_dword[i] + 2] & 0x1F;
+	/* Sanity check */
+        if (memcmp(ssdp_pcihp_aml + aml_ej0_name[i], "_EJ0", 4)) {
+            warn_internalerror();
+            free(ssdt);
+            return NULL;
+        }
+        if (!(rmvc_pcrm & (0x1 << slot))) {
+            memcpy(ssdt + aml_ej0_name[i], "EJ0_", 4);
+        }
+    }
+
+    return ssdt;
 }
 
 #define HPET_SIGNATURE 0x54455048 // HPET
@@ -642,8 +672,6 @@ static const struct pci_device_id acpi_find_tbl[] = {
 
 struct rsdp_descriptor *RsdpAddr;
 
-#include "ssdt-pcihp.hex"
-
 #define MAX_ACPI_TABLES 20
 void
 acpi_bios_init(void)
@@ -675,7 +703,7 @@ acpi_bios_init(void)
     ACPI_INIT_TABLE(build_madt());
     ACPI_INIT_TABLE(build_hpet());
     ACPI_INIT_TABLE(build_srat());
-    ACPI_INIT_TABLE(copy_table(ssdp_pcihp_aml, sizeof ssdp_pcihp_aml));
+    ACPI_INIT_TABLE(build_pcihp());
 
     u16 i, external_tables = qemu_cfg_acpi_additional_tables();
 
src/ssdt-pcihp.dsl +6 -0
index 72d1bb7..cc96ffc 100644
@@ -53,9 +53,15 @@ DefinitionBlock ("ssdt-pcihp.aml", "SSDT", 0x01, "BXPC", "BXSSDTPCIHP", 0x1)
         gen_pci_device(1f)
 
         /* Bulk generated PCI hotplug devices */
+        // Method _EJ0 can be patched by BIOS to EJ0_
+        // at runtime, if the slot is detected to not support hotplug.
+        // Extract the offset of the address dword and the
+        // _EJ0 name to allow this patching.
 #define hotplug_slot(slot)                              \
         Device (S##slot) {                              \
+           ACPI_EXTRACT_NAME_DWORD_CONST aml_adr_dword  \
            Name (_ADR, 0x##slot##0000)                  \
+           ACPI_EXTRACT_METHOD_STRING aml_ej0_name      \
            Method (_EJ0, 1) { Return(PCEJ(0x##slot)) }  \
            Name (_SUN, 0x##slot)                        \
         }
Signature · 1 line
1.7.5.53.gc233e
#3 lore raw

PATCH v4 3/3 acpi: remove _RMV

In reply to Michael S. Tsirkin

The macro gen_pci_device is used to add _RMV method to a slot device so it is no longer needed:
presence of _EJ0 now indicates that the slot is ejectable. It is also placing two devices with the same _ADR on the same bus, which isn't defined by the ACPI spec. So let's remove it.

---
 src/acpi-dsdt.dsl  |   15 ---------------
 src/ssdt-pcihp.dsl |   40 ----------------------------------------
 2 files changed, 0 insertions(+), 55 deletions(-)
src/acpi-dsdt.dsl +0 -15
index f97d463..aff3f48 100644
@@ -132,12 +132,6 @@ DefinitionBlock (
                 B0EJ, 32,
             }
 
-            OperationRegion(RMVC, SystemIO, 0xae0c, 0x04)
-            Field(RMVC, DWordAcc, NoLock, WriteAsZeros)
-            {
-                PCRM, 32,
-            }
-
             Name (_CRS, ResourceTemplate ()
             {
                 WordBusNumber (ResourceProducer, MinFixed, MaxFixed, PosDecode,
@@ -239,7 +233,6 @@ DefinitionBlock (
                                  Return (0x00)
                          }
                  }
-                 Method(_RMV) { Return (0x00) }
         }
     }
 
@@ -251,7 +244,6 @@ DefinitionBlock (
     Scope(\_SB.PCI0) {
         Device (ISA) {
             Name (_ADR, 0x00010000)
-            Method(_RMV) { Return (0x00) }
 
             /* PIIX PCI to ISA irq remapping */
             OperationRegion (P40C, PCI_Config, 0x60, 0x04)
@@ -466,13 +458,6 @@ DefinitionBlock (
 
     Scope(\_SB.PCI0) {
         /* Methods called by bulk generated PCI devices below */
-        Method (PRMV, 1, NotSerialized) {
-            // _RMV method - check if device can be removed
-            If (And(\_SB.PCI0.PCRM, ShiftLeft(1, Arg0))) {
-                Return (0x1)
-            }
-            Return (0x0)
-        }
 
         /* Methods called by hotplug devices */
         Method (PCEJ, 1, NotSerialized) {
src/ssdt-pcihp.dsl +0 -40
index cc96ffc..4b435b8 100644
@@ -9,49 +9,9 @@ DefinitionBlock ("ssdt-pcihp.aml", "SSDT", 0x01, "BXPC", "BXSSDTPCIHP", 0x1)
 
     /* Objects supplied by DSDT */
     External (\_SB.PCI0, DeviceObj)
-    External (\_SB.PCI0.PRMV, MethodObj)
     External (\_SB.PCI0.PCEJ, MethodObj)
 
     Scope(\_SB.PCI0) {
-
-#define gen_pci_device(slot)                                    \
-        Device(SL##slot) {                                      \
-            Name (_ADR, 0x##slot##0000)                         \
-            Method (_RMV) { Return (PRMV(0x##slot)) }           \
-            Name (_SUN, 0x##slot)                               \
-        }
-
-        /* VGA (slot 1) and ISA bus (slot 2) defined in DSDT */
-        gen_pci_device(03)
-        gen_pci_device(04)
-        gen_pci_device(05)
-        gen_pci_device(06)
-        gen_pci_device(07)
-        gen_pci_device(08)
-        gen_pci_device(09)
-        gen_pci_device(0a)
-        gen_pci_device(0b)
-        gen_pci_device(0c)
-        gen_pci_device(0d)
-        gen_pci_device(0e)
-        gen_pci_device(0f)
-        gen_pci_device(10)
-        gen_pci_device(11)
-        gen_pci_device(12)
-        gen_pci_device(13)
-        gen_pci_device(14)
-        gen_pci_device(15)
-        gen_pci_device(16)
-        gen_pci_device(17)
-        gen_pci_device(18)
-        gen_pci_device(19)
-        gen_pci_device(1a)
-        gen_pci_device(1b)
-        gen_pci_device(1c)
-        gen_pci_device(1d)
-        gen_pci_device(1e)
-        gen_pci_device(1f)
-
         /* Bulk generated PCI hotplug devices */
         // Method _EJ0 can be patched by BIOS to EJ0_
         // at runtime, if the slot is detected to not support hotplug.
Signature · 1 line
1.7.5.53.gc233e
#5 lore raw
Quoted 7 lines · Kevin O'Connor
Quoted 2 lines · Michael S. Tsirkin

Here's an updated revision of acpi runtime patching patchset. Lightly tested.

It looks good to me.

-Kevin

Run some linux and windows tests, things seem to work smoothly. Pls apply.

Signature · 1 line
MST
#6 lore raw
Quoted 11 lines · Michael S. Tsirkin
Quoted 7 lines · Kevin O'Connor
Quoted 2 lines · Michael S. Tsirkin

Here's an updated revision of acpi runtime patching patchset. Lightly tested.

It looks good to me.

-Kevin

Run some linux and windows tests, things seem to work smoothly. Pls apply.

I committed this series.

Thanks,
-Kevin