DF-5617 / df56.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 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 | /* * DF-5600 harness - ig4_pci.c probe/attach version wipe. * * Target: sys/bus/smbus/ichiic/ig4_pci.c * ig4iic_pci_probe() ig4_pci.c:151 sc->version = <table row> * ig4iic_pci_attach() ig4_pci.c:167 bzero(sc, sizeof(*sc)) <-- WIPES IT * ig4iic_pci_attach() ig4_pci.c:193 ig4iic_attach(sc) consumes version * * The ACPI sibling does NOT have the bug: ig4_acpi.c:104-108 sets * sc->version = IG4_ATOM in attach, after init, with no bzero. * * Consequence, executed through the REAL production ig4iic_attach() (from * the stock /boot/kernel/ig4.ko): a Cannon Lake-LP controller (devid * 0x9dc58086 -> IG4_CANNONLAKE per ig4_pci.c:128) is programmed as a * Haswell: * - ig4_iic.c:513-518 IG4_REG_GENERAL (0x808, HSW-only register) gets * IG4_GENERAL_SWMODE (0x4) written into it * - ig4_iic.c:522-526 SW/AUTO LTR regs 0x810/0x814 read (HSW offsets) * - ig4_iic.c:534-541 COMP_VER gate applied to a generation upstream * restricts to HSW/ATOM * * Run A replays the exact production statement order (probe stores * version, attach prologue bzero's it) and calls the real ig4iic_attach() * on a fake MMIO register file. Run B is the control: identical except * sc->version is restored after the bzero (the fix / the ig4_acpi.c * ordering). Success: run A's fake file shows GENERAL(0x808)=0x4 (SWMODE * written on non-Haswell silicon); run B's stays 0x0. * * Rig notes (no ig4 LPSS controller exists in QEMU): fake MMIO register * file is kmalloc'd memory; on x86_64 the bus_space tag is unused for * memory space (X86_64_BUS_SPACE_MEM == 1) and the handle is dereferenced * directly, so regs_t=1 + regs_h=<kva> works. The harness device lives * on a pseudo-bus parented to the root bus (root_print_child is a safe * no-op; nexus_print_child would deref nexus ivars that raw * device_add_child() children lack). */ #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 <bus/pci/pcireg.h> #include "ig4_reg.h" #include "ig4_var.h" /* ig4iic_attach / ig4iic_detach are declared in ig4_var.h */ #define FAKE_DEVID 0x9dc58086 /* Cannon Lake-LP I2C Controller-0 */ static device_t df56_bus; /* ======================= pseudo bus: df56bus ======================= */ static int df56bus_probe(device_t dev) { device_set_desc(dev, "DF-56xx pseudo bus (bus_setup_intr -> EINVAL)"); return (0); } static int df56bus_attach(device_t dev) { /* children are created by module glue */ return (0); } static int df56bus_setup_intr(device_t bus, device_t child, struct resource *r, int flags, void (*ihand)(void *), void *arg, void **cookiep, lwkt_serialize_t serializer, const char *desc) { kprintf("DF5601: pseudo-bus bus_setup_intr() -> EINVAL (stands in " "for rman_activate_resource()/register_int() failure on real " "hardware)\n"); (void)bus; (void)child; (void)r; (void)flags; (void)ihand; (void)arg; (void)cookiep; (void)serializer; (void)desc; return (EINVAL); } static device_method_t df56bus_methods[] = { DEVMETHOD(device_probe, df56bus_probe), DEVMETHOD(device_attach, df56bus_attach), DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_setup_intr, df56bus_setup_intr), DEVMETHOD_END }; static driver_t df56bus_driver = { "df56bus", df56bus_methods, 0 }; static devclass_t df56bus_devclass; /* root bus: root_print_child() is a safe no-op, unlike nexus_print_child * which derefs nexus ivars that raw device_add_child() children lack. */ DRIVER_MODULE(df56bus, root, df56bus_driver, df56bus_devclass, NULL, NULL); /* =================== DF-5600: version wipe scenario =================== */ static int df5600_devid_version(uint32_t devid) { /* the ig4iic_pci_devices[] row for 0x9dc58086 (ig4_pci.c:128) */ if (devid == FAKE_DEVID) return (IG4_CANNONLAKE); return (IG4_HASWELL); } static void df5600_dump_file(const char *tag, volatile uint32_t *f) { int i; kprintf("DF5600[%s]: fake-MMIO write map (offset=value):", tag); for (i = 0; i < 0x1000 / 4; i++) { if (f[i] != 0) kprintf(" %03x=%08x", i * 4, f[i]); } kprintf("\n"); } static int df5600_run_scenario(device_t dev, int control) { ig4iic_softc_t *sc; volatile uint32_t *f; const char *tag = control ? "B-fixed" : "A-wiped"; int error; int vprobe; 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); /* fake controller identity: designware core regs */ f[0x0F4 / 4] = 0x0000003F; /* COMP_PARAM1 */ f[0x0F8 / 4] = 0x7FFFFFFF; /* COMP_VER: passes any gate */ f[0x0FC / 4] = 0x7FFFFFFF; /* COMP_TYPE */ f[0x09C / 4] = 0; /* ENABLE_STATUS: enable never * latches -> attach-2 ENXIO */ /* ---- ig4iic_pci_probe() replay (ig4_pci.c:147-153) ---- */ sc->version = df5600_devid_version(FAKE_DEVID); vprobe = sc->version; /* ---- ig4iic_pci_attach() prologue replay (ig4_pci.c:167-191) ---- */ bzero(sc, sizeof(*sc)); /* ig4_pci.c:167 */ lwkt_serialize_init(&sc->slz); /* ig4_pci.c:169 */ sc->dev = dev; /* ig4_pci.c:171 */ sc->regs_rid = PCIR_BAR(0); /* ig4_pci.c:172 */ sc->regs_t = 1; /* mem space; tag unused on x86 */ sc->regs_h = (bus_space_handle_t)f; /* bus_alloc_resource_any faked */ sc->intr_type = 0; /* PCI_INTR_TYPE_LEGACY */ sc->pci_attached = 1; /* ig4_pci.c:191 */ if (control) sc->version = vprobe; /* THE FIX: re-set after bzero */ kprintf("DF5600[%s]: probe stored version=%d; after attach prologue " "sc->version=%d -> %s\n", tag, vprobe, sc->version, (sc->version == vprobe) ? "preserved" : "WIPED to 0 == IG4_HASWELL"); error = ig4iic_attach(sc); /* ig4_pci.c:193 */ kprintf("DF5600[%s]: ig4iic_attach() = %d (ENXIO expected: fake " "ENABLE_STATUS never latches)\n", tag, error); kprintf("DF5600[%s]: GENERAL(0x808)=%08x -> Haswell-only SWMODE " "write %s this non-Haswell part\n", tag, f[0x808 / 4], (f[0x808 / 4] & IG4_GENERAL_SWMODE) ? "HAPPENED ON" : "did not happen on"); df5600_dump_file(tag, f); /* production teardown path (what ig4_pci.c:208 does) */ error = ig4iic_detach(sc); kprintf("DF5600[%s]: ig4iic_detach() = %d\n", tag, error); kfree(__DEVOLATILE(uint32_t *, f), M_DEVBUF); kfree(sc, M_DEVBUF); return (0); } static int df5600_probe(device_t dev) { device_set_desc(dev, "DF-5600 ig4 version-wipe harness"); return (0); } static int df5600_attach(device_t dev) { device_printf(dev, "run A: production ordering (probe sets version, " "attach bzero wipes it)\n"); df5600_run_scenario(dev, 0); device_printf(dev, "run B: control (version restored after bzero)\n"); df5600_run_scenario(dev, 1); return (0); } static device_method_t df5600_methods[] = { DEVMETHOD(device_probe, df5600_probe), DEVMETHOD(device_attach, df5600_attach), DEVMETHOD_END }; static driver_t df5600_driver = { "df56f1", df5600_methods, 0 }; static devclass_t df5600_devclass; DRIVER_MODULE(df56f1, df56bus, df5600_driver, df5600_devclass, NULL, NULL); /* ============================ module glue ============================ */ static int df56_load(struct module *m, int what, void *arg) { device_t dev; int error; switch (what) { case MOD_LOAD: df56_bus = device_add_child(root_bus, "df56bus", -1); if (df56_bus == NULL) return (ENOMEM); error = device_probe_and_attach(df56_bus); if (error) { kprintf("DF56xx: pseudo-bus attach failed: %d\n", error); return (error); } /* DF-5600 runs immediately */ dev = device_add_child(df56_bus, "df56f1", -1); if (dev == NULL) return (ENOMEM); error = device_probe_and_attach(dev); if (error) kprintf("DF5600: harness device attach failed: %d\n", error); kprintf("DF56xx: loaded; DF-5600 done; load df56f2.ko to " "arm the DF-5601 scenario\n"); break; default: break; } return (0); } static moduledata_t df56_mod = { "df56", df56_load, NULL }; DECLARE_MODULE(df56, df56_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); MODULE_DEPEND(df56, ig4iic, 1, 1, 1); /* ig4iic_attach / ig4iic_detach */ |