DF-5620 / gen_cst.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | #!/usr/bin/env python3 """ gen_cst.py -- craft a full RSDP->RSDT->[FACP->FACS+DSDT, MADT] chain carrying a crafted per-processor _CST package, for the acpi_cstate_machdep.c pass-2 verification (DF-5619 / DF-5620). Injection model (identical to the proven DF-5483/DF-5583 rig): - guest booted with loader kenv hint.acpi.0.rsdp=0x40000000 (sys/platform/pc64/acpica/acpi_sdt.c -- hinted RSDP accepted with NO signature and NO checksum validation) - QEMU -device loader,file=<blob>,addr=0x40000000,force-raw=on plants the chain in guest RAM before firmware handoff. The DSDT (compiled on the guest from the scenario dsdt_*.dsl with iasl) defines \\_SB.CPUS.C000..C005, each carrying a _CST package. Scenario 'control' : one valid C1 (HALT) state -- boots, hw.acpi.cpu.cst.cx_supported == "C1/1" Scenario 'nullenter' : one C2 state, GAS SystemIO/8/addr=0 -> acpi_bus_alloc_gas() rejects (Address==0, acpi.c:1280) -> cx->res stays NULL -> acpi_cst_md_cx_setup() *accepts* it (returns 0, acpi_cstate_machdep.c:91-93) with enter still NULL -> acpi_cst_idle(): cx_next->enter(cx_next) == call NULL (acpi_cpu_cstate.c:970) -> boot-time kernel panic Scenario 'zerostate' : one C2 state, GAS FixedHW (no MWAIT under TCG) -> mwait setup EOPNOTSUPP -> state skipped -> acpi_cst_cx_probe_cst() returns 0 with ZERO states -> idle reads the zeroed states[0] -> enter==NULL -> same panic via the caller's missing zero-state guard Scenario 'badhint' : valid C1 + C2 FixedHW/native with an INVALID mwait hint (0x99999971: cx_idx=(0x71>>4)+1=8 >= CPU_MWAIT_CX_MAX) -> "C2: invalid mwait hint 0x99999971", state rejected, BUT the done: block still runs cpu_mwait_cx_no_bmsts() twice + no_bmarb -> machdep.mwait.CX.c3_preamble drops 2 -> 0 system-wide (DF-5620); boot survives (C1 valid) """ import struct import sys BASE = 0x40000000 RSDP_OFF = 0x0000 RSDT_OFF = 0x0100 FADT_OFF = 0x0200 FACS_OFF = 0x0280 MADT_OFF = 0x0400 DSDT_OFF = 0x1000 def csum(b): return (-sum(b)) & 0xFF def sdth(sig, length, rev): h = bytearray(36) h[0:4] = sig struct.pack_into('<I', h, 4, length) h[8] = rev h[9] = 0 h[10:16] = b'DFBSD ' h[17:21] = b'PASS' struct.pack_into('<I', h, 21, 1) h[25:29] = b'IACT' struct.pack_into('<I', h, 29, 1) return h def build(dsdt, ncpu=6): # MADT: ncpu LAPIC entries (BSP apic id 0 first, all enabled) + the real # QEMU IOAPIC so that PCI interrupts keep working (as DF-5483's control). ent = b'' for i in range(ncpu): ent += struct.pack('<BBBBI', 0, 8, i, i, 1) # LAPIC ent += struct.pack('<BBBBII', 1, 12, 0, 0, 0xFEC00000, 0) # IOAPIC madt = sdth(b'APIC', 44 + len(ent), 3) + bytearray(8) struct.pack_into('<I', madt, 36, 0xFEE00000) struct.pack_into('<I', madt, 40, 1) madt += ent madt[9] = csum(madt) # FACS (64 bytes, everything zeroed) facs = bytearray(64) facs[0:4] = b'FACS' struct.pack_into('<I', facs, 4, 64) # FADT rev1 (128 bytes): PM registers cloned from the guest's real # SeaBIOS/QEMU FADT so AcpiEnableSubsystem() finds working PM1/GPE blocks; # DSDT pointer ours. P_LVL2/P_LVL3 latency 0xfff (>100/1000 => FADT # C2/C3 never offered; scenarios are all _CST-mode anyway). fadt = sdth(b'FACP', 128, 1) fadt += bytearray(128 - 36) struct.pack_into('<I', fadt, 36, BASE + FACS_OFF) # FIRMWARE_CTRL struct.pack_into('<I', fadt, 40, BASE + DSDT_OFF) # DSDT fadt[44] = 1 # INT_MODEL (QEMU) fadt[45] = 0 # profile struct.pack_into('<H', fadt, 46, 9) # SCI_INT struct.pack_into('<I', fadt, 48, 0xb2) # SMI_CMD (QEMU) fadt[52] = 0xf1 # ACPI_ENABLE fadt[53] = 0xf0 # ACPI_DISABLE struct.pack_into('<I', fadt, 56, 0x600) # PM1a_EVT_BLK struct.pack_into('<I', fadt, 64, 0x604) # PM1a_CNT_BLK struct.pack_into('<I', fadt, 76, 0x608) # PM_TMR_BLK struct.pack_into('<I', fadt, 80, 0xafe0) # GPE0_BLK fadt[88] = 4 # PM1_EVT_LEN fadt[89] = 2 # PM1_CNT_LEN fadt[91] = 4 # PM_TMR_LEN fadt[92] = 4 # GPE0_BLK_LEN struct.pack_into('<I', fadt, 96, 0x0fff) # P_LVL2_LAT struct.pack_into('<I', fadt, 100, 0x0fff) # P_LVL3_LAT fadt[9] = csum(fadt) # RSDT: FACP + APIC rsdt = sdth(b'RSDT', 36 + 8, 1) + bytearray(8) struct.pack_into('<I', rsdt, 36, BASE + FADT_OFF) struct.pack_into('<I', rsdt, 40, BASE + MADT_OFF) rsdt[9] = csum(rsdt) # RSDP v1 (20 bytes) rsdp = bytearray(20) rsdp[0:8] = b'RSD PTR ' rsdp[8] = 0 rsdp[9:15] = b'DFBSD ' rsdp[15] = 0 struct.pack_into('<I', rsdp, 16, BASE + RSDT_OFF) blob = bytearray(DSDT_OFF + len(dsdt)) blob[RSDP_OFF:RSDP_OFF + 20] = rsdp blob[RSDT_OFF:RSDT_OFF + len(rsdt)] = rsdt blob[FADT_OFF:FADT_OFF + len(fadt)] = fadt blob[FACS_OFF:FACS_OFF + len(facs)] = facs blob[MADT_OFF:MADT_OFF + len(madt)] = madt blob[DSDT_OFF:DSDT_OFF + len(dsdt)] = dsdt return bytes(blob) if __name__ == '__main__': aml_file, out = sys.argv[1], sys.argv[2] with open(aml_file, 'rb') as f: dsdt = f.read() assert dsdt[:4] == b'DSDT', 'not a DSDT' with open(out, 'wb') as f: f.write(build(dsdt)) sys.stderr.write('blob: %d bytes (DSDT %d)\n' % (DSDT_OFF + len(dsdt), len(dsdt))) |