DF-5617 / df5617.c
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 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | /* * DF-5617 harness โ ig4 ACPI attach-error unwinder re-enters the serializer * leaked by ig4_iic.c's bus_setup_intr error path (ACPI twin of DF-5601). * * Target chain (all production code except the failure injection): * ig4_acpi.c:104 lwkt_serialize_init(&sc->slz) * ig4_acpi.c:108 sc->version = IG4_ATOM * ig4_acpi.c:127 sc->pci_attached = 1 * ig4_acpi.c:132 error = ig4iic_attach(sc) (REAL, stock ig4.ko) * ig4_iic.c:648 lwkt_serialize_enter(&sc->slz) * ig4_iic.c:649 error = bus_setup_intr(...) <- injected EINVAL * ig4_iic.c:651-655 if (error) goto done; <-- returns with the * serializer STILL HELD (no lwkt_serialize_exit) * ig4_acpi.c:133-134 if (error) ig4iic_acpi_detach(dev) * ig4_acpi.c:146-147 pci_attached -> ig4iic_detach(sc) (REAL) * ig4_iic.c:678 lwkt_serialize_enter(&sc->slz) <-- re-entry! * sys/serialize.h:41-42: the serializer is * "not safe from deadlocks nor is it recursive" * * On X86_64_GENERIC (options INVARIANTS) lwkt_serialize_enter's * ASSERT_NOT_SERIALIZED KKASSERT-panics; without INVARIANTS the same path * self-deadlocks in atomic_intr_cond_enter(). * * The bus_setup_intr failure is the only synthetic input: on real ACPI * hardware it is any rman_activate_resource()/register_int() failure * (nexus.c:519-532) โ e.g. an exclusive-owner conflict on the GSI. The * planted-ACPI runs in this evidence pack (ssdt_ig4p.aml, dance logs) drove * the REAL ig4iic_acpi_probe/_CRS-translation/ig4iic_acpi_attach path end to * end and show every other step is reachable and live on this guest. * * Run: kldload ig4; kldload df56.ko; kldload df5617.ko * Success criterion (baseline): panic in lwkt_serialize_enter <- * ig4iic_detach <- df5617_attach, with the dmesg trail showing * "Unable to setup irq" then "serializer is HELD". * With the fixed ig4.ko (ig4_iic.c error path releases the serializer): * ig4iic_detach() returns 0, guest stays alive. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <sys/bus.h> #include <sys/malloc.h> #include <sys/serialize.h> #include <sys/kthread.h> #include "ig4_reg.h" #include "ig4_var.h" static volatile int df5617_mirror_run = 1; static struct thread *df5617_mirror_td; /* * Mirror kthread: emulates the DW ENABLE_STATUS latch by reflecting the * IG4_I2C_ENABLE bit written to I2C_EN (0x6c) into ENABLE_STATUS (0x9c). * set_controller() polls with 1-tick tsleeps and 100 retries, so a 1-tick * mirror cadence always keeps up. */ static void df5617_mirror(void *arg) { volatile uint32_t *f = arg; kprintf("DF5617: mirror thread up (emulates ENABLE_STATUS latch)\n"); while (df5617_mirror_run) { f[IG4_REG_ENABLE_STATUS / 4] = f[IG4_REG_I2C_EN / 4] & IG4_I2C_ENABLE; tsleep(&df5617_mirror_run, 0, "df56mir", 1); } kprintf("DF5617: mirror thread exiting\n"); lwkt_exit(); /* NOT REACHED */ } static int df5617_probe(device_t dev) { device_set_desc(dev, "DF-5617 ig4 ACPI attach-error unwinder scenario"); return (0); } /* * Replay of ig4_acpi.c's unwinder body (ig4iic_acpi_detach, lines 141-169), * which is static in ig4_acpi.c so it cannot be called directly. Everything * up to ig4iic_detach()'s lwkt_serialize_enter is identical; the panic (or, * on a fixed kernel, the clean return) happens INSIDE the production * ig4iic_detach() before any of the later release lines matter, so the * harness skips the bus_release_resource()/D3 tail to stay * allocation-neutral on a fixed kernel. */ static int df5617_acpi_detach_replay(device_t dev, ig4iic_softc_t *sc) { int error; if (sc->pci_attached) { /* ig4_acpi.c:146 */ error = ig4iic_detach(sc); /* ig4_acpi.c:147 (REAL) */ if (error) return error; sc->pci_attached = 0; } /* ig4_acpi.c:153-166: release intr_res/regs_res, D3 โ after the * decision point; intentionally not replayed (fake resources). */ return (0); } static int df5617_attach(device_t dev) { ig4iic_softc_t *sc; volatile uint32_t *f; int error; f = kmalloc(0x1000, M_DEVBUF, M_WAITOK | M_ZERO); sc = kmalloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); if (f == NULL || sc == NULL) return (ENOMEM); f[0x0F4 / 4] = 0x0000003F; /* COMP_PARAM1 */ f[0x0F8 / 4] = 0x7FFFFFFF; /* COMP_VER (>= 0x3131352A) */ f[0x0FC / 4] = 0x7FFFFFFF; /* COMP_TYPE */ /* ---- ig4iic_acpi_attach() prologue replay (ig4_acpi.c:99-127) ---- */ /* ig4_acpi.c: no bzero in the ACPI glue (softc is M_ZERO-allocated * by newbus, subr_bus.c:1953) โ the DF-5600 twin-check */ lwkt_serialize_init(&sc->slz); /* ig4_acpi.c:104 */ sc->dev = dev; /* ig4_acpi.c:106 */ sc->version = IG4_ATOM; /* ig4_acpi.c:108 */ sc->regs_rid = 0; /* ig4_acpi.c:109 */ /* ig4_acpi.c:110-116: bus_alloc_resource_any(SYS_RES_MEMORY) */ sc->regs_t = 1; /* mem space; tag unused */ sc->regs_h = (bus_space_handle_t)f; sc->intr_rid = 0; /* ig4_acpi.c:117 */ sc->intr_res = (struct resource *)dev; /* ig4_acpi.c:118; never * dereferenced: the * pseudo-bus setup_intr * fails before touching it */ sc->regs_t = 1; /* ig4_acpi.c:125 */ sc->regs_h = (bus_space_handle_t)f; /* ig4_acpi.c:126 */ sc->pci_attached = 1; /* ig4_acpi.c:127 */ kthread_create(df5617_mirror, __DEVOLATILE(void *, f), &df5617_mirror_td, "df5617mir"); kprintf("DF5617: replaying ig4_acpi.c:132 ig4iic_attach() ...\n"); error = ig4iic_attach(sc); /* ig4_acpi.c:132 (REAL) */ kprintf("DF5617: ig4iic_attach() = %d (EINVAL from bus_setup_intr)\n", error); kprintf("DF5617: serializer is %s by the ig4_iic.c:648-655 error path " "(IS_SERIALIZED: %d)\n", IS_SERIALIZED(&sc->slz) ? "HELD" : "released", IS_SERIALIZED(&sc->slz)); kprintf("DF5617: replaying ig4_acpi.c:133-134 unwinder -> " "ig4iic_acpi_detach() -> ig4_acpi.c:146-147 ig4iic_detach(). " "If the serializer is held, lwkt_serialize_enter at ig4_iic.c:678 " "self-deadlocks/panics NOW.\n"); error = df5617_acpi_detach_replay(dev, sc); /* ig4_acpi.c:134 */ kprintf("DF5617: ig4iic_acpi_detach replay = %d -- returned cleanly, " "NO PANIC (fix effective)\n", error); df5617_mirror_run = 0; wakeup(&df5617_mirror_run); return (0); } static device_method_t df5617_methods[] = { DEVMETHOD(device_probe, df5617_probe), DEVMETHOD(device_attach, df5617_attach), DEVMETHOD_END }; static driver_t df5617_driver = { "df5617ig4", df5617_methods, 0 }; static devclass_t df5617_devclass; DRIVER_MODULE(df5617ig4, df56bus, df5617_driver, df5617_devclass, NULL, NULL); static int df5617_load(struct module *m, int what, void *arg) { device_t bus; device_t dev; int error; switch (what) { case MOD_LOAD: bus = devclass_get_device(devclass_find("df56bus"), 0); if (bus == NULL) { kprintf("DF5617: df56bus0 not found; load df56.ko " "first\n"); return (ENXIO); } kprintf("DF5617: scenario armed under %s\n", device_get_nameunit(bus)); dev = device_add_child(bus, "df5617ig4", -1); if (dev == NULL) return (ENOMEM); error = device_probe_and_attach(dev); kprintf("DF5617: scenario device attach = %d\n", error); break; default: break; } return (0); } static moduledata_t df5617_mod = { "df5617", df5617_load, NULL }; DECLARE_MODULE(df5617, df5617_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); |