⬢ DragonFlyBSD Kernel Audit
DF-5518 / gen_gpio.py
← back to finding ↓ download raw
#!/usr/bin/env python3
"""
gen_gpio.py -- craft the RSDP->RSDT->[FACP->FACS+DSDT, MADT] chain for the
DF-5518 rig (sys/bus/gpio/gpio_acpi/gpio_acpi.c: in-flight GPIO address-space
handler vs. driver detach).

Injection model: identical to the proven madt-batch / DF-5483 vector:
  guest booted with loader kenv hint.acpi.0.rsdp=0x40000000
  (sys/platform/pc64/acpica/acpi_sdt.c -- hinted RSDP accepted with NO
  signature/checksum validation), chain planted by
  QEMU -device loader,file=<blob>,addr=0x40000000,force-raw=on.

FADT (rev1, 128 bytes) cloned PM blocks from the guest's SeaBIOS FADT so
AcpiEnableSubsystem() finds working PM1/GPE blocks; DSDT pointer is ours.
"""
import struct
import sys

BASE     = 0x40000000
RSDP_OFF = 0x0000
RSDT_OFF = 0x0100
FADT_OFF = 0x0200
FACS_OFF = 0x0280
MADT_OFF = 0x0400
DSDT_OFF = 0x1000

NITER = 200000     # hammer iterations inside _TMP (after register plant)

# chv_n_ranges (gpio_cherryview.c, _UID=2): all valid pins, ascending
PINS = (list(range(0, 9)) + list(range(15, 28)) + list(range(30, 42)) +
        list(range(45, 57)) + list(range(60, 73)))
assert len(PINS) == 59

# FLD0 result bit i = RXSTATE of PINS[i]; gpoke plants RXSTATE=1 on even
# physical pins -> magic has bit i set iff PINS[i] is even.
MAGIC = 0
for i, p in enumerate(PINS):
    if p % 2 == 0:
        MAGIC |= 1 << i


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: LAPIC entries + the real QEMU IOAPIC (keeps PCI ints working)
    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
    facs = bytearray(64)
    facs[0:4] = b'FACS'
    struct.pack_into('<I', facs, 4, 64)

    # FADT rev1, PM blocks cloned from SeaBIOS/QEMU
    fadt = sdth(b'FACP', 128, 1)
    fadt += bytearray(128 - 36)
    struct.pack_into('<I', fadt, 36, BASE + FACS_OFF)
    struct.pack_into('<I', fadt, 40, BASE + DSDT_OFF)
    fadt[44] = 1
    fadt[45] = 0
    struct.pack_into('<H', fadt, 46, 9)          # SCI_INT
    struct.pack_into('<I', fadt, 48, 0xb2)       # SMI_CMD
    fadt[52] = 0xf1
    fadt[53] = 0xf0
    struct.pack_into('<I', fadt, 56, 0x600)      # PM1a_EVT
    struct.pack_into('<I', fadt, 64, 0x604)      # PM1a_CNT
    struct.pack_into('<I', fadt, 76, 0x608)      # PM_TMR
    struct.pack_into('<I', fadt, 80, 0xafe0)     # GPE0
    fadt[88] = 4
    fadt[89] = 2
    fadt[91] = 4
    fadt[92] = 4
    struct.pack_into('<I', fadt, 96, 0x0fff)
    struct.pack_into('<I', fadt, 100, 0x0fff)
    fadt[9] = csum(fadt)

    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 = 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__':
    if sys.argv[1] == 'subst':
        d = open(sys.argv[2]).read()
        d = d.replace('@ITER@', str(NITER)).replace('@MAGIC@', hex(MAGIC))
        open(sys.argv[3], 'w').write(d)
        sys.stderr.write('substituted: N=%d magic=0x%X\n' % (NITER, MAGIC))
        sys.exit(0)
    aml_file, out = sys.argv[2], sys.argv[3]
    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, hammer N=%d, magic=0x%X)\n' %
                     (DSDT_OFF + len(dsdt), len(dsdt), NITER, MAGIC))