DF-5527 / gpiofree.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 | /* * gpiofree.c -- DF-5527 production-code harness. * * Demonstrates on the STOCK kernel that gpio_cherryview_unmap_intr() * (gpio_cherryview.c:380-414) never clears the interrupt MASK bit: * releasing a mapped GPIO interrupt via GPIO_FREE_INTR *without* a * preceding GPIO_TEARDOWN_INTR leaves the community IRQ line armed * (CHV_GPIO_REG_MASK bit set) while its pin_intr_map is destroyed * (pin = -1, is_level zeroed). On real hardware a level-asserted pad * then drives gpio_cherryview_intr() into the edge-clear path in a * tight re-asserting loop -> interrupt storm / hard lockup. * * Runs on the DF-5518 planted-ACPI rig (fake INT33FF _UID 2 community, * RAM register image at 0xD0000+0x8000); stock drivers, no kernel * modifications. * * A/B experiment (single kldload): * line 5 / pin 8 : ALLOC -> SETUP -> FREE (skip teardown) * line 6 / pin 15: ALLOC -> SETUP -> TEARDOWN -> FREE (documented order) * Expected (defect): MASK bit5 == 1 with intrmaps[5].pin == -1 * Expected (control): MASK bit6 == 0 after teardown+free */ #include <sys/param.h> #include <sys/module.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/bus.h> #include <machine/vmparam.h> #include <vm/vm_page.h> #include "gpio_if.h" #include "gpio_intel_var.h" /* copied next to this file by build.sh */ /* Cherry-View register image offsets (gpio_cherryview.c:56-81) */ #define GPO_BASE 0x000D0000ul /* _CRS Memory32Fixed window */ #define CHV_IS 0x300 #define CHV_MASK 0x380 #define CHV_PINS 0x4400 #define PIN_CTL0(p) (GPO_BASE + CHV_PINS + ((p) / 15) * 0x400 + \ ((p) % 15) * 8) /* gpio_cherryview.c bit fields */ #define CTL0_GPIOEN 0x00008000u #define INTSEL_SHIFT 28 /* acrestyp.h */ #define TRIG_EDGE 1 /* ACPI_EDGE_SENSITIVE */ #define POL_HIGH 0 /* ACPI_ACTIVE_HIGH */ #define TERM_DEFAULT 0 /* ACPI_PIN_CONFIG_DEFAULT */ #define PIN_A 8 /* valid chv_n pin, family 0 member 8 */ #define PIN_B 15 /* valid chv_n pin, family 1 member 0 */ #define LINE_A 5 #define LINE_B 6 static uint32_t rd32(vm_paddr_t pa) { return (*(volatile uint32_t *)PHYS_TO_DMAP(pa)); } static void wr32(vm_paddr_t pa, uint32_t v) { *(volatile uint32_t *)PHYS_TO_DMAP(pa) = v; } static void dummy_intr(void *arg) { } static void dump_state(const char *tag, struct gpio_intel_softc *sc) { uint32_t is = rd32(GPO_BASE + CHV_IS); uint32_t mask = rd32(GPO_BASE + CHV_MASK); kprintf("gpiofree[%s]: IS=%08x MASK=%08x " "(bit5=%d bit6=%d) map5{pin=%d,intidx=%d,is_level=%d} " "map6{pin=%d,intidx=%d,is_level=%d}\n", tag, is, mask, !!(mask & (1u << LINE_A)), !!(mask & (1u << LINE_B)), sc->intrmaps[LINE_A].pin, sc->intrmaps[LINE_A].intidx, sc->intrmaps[LINE_A].is_level, sc->intrmaps[LINE_B].pin, sc->intrmaps[LINE_B].intidx, sc->intrmaps[LINE_B].is_level); } static int gpiofree_modevent(module_t mod, int type, void *arg) { devclass_t dc; device_t dev; struct gpio_intel_softc *sc; void *ca = NULL, *cb = NULL; int error; switch (type) { case MOD_LOAD: dc = devclass_find("gpio_intel"); dev = dc ? devclass_get_device(dc, 0) : NULL; if (dev == NULL) { kprintf("gpiofree: gpio_intel0 not found " "(is the DF-5518 rig booted?)\n"); return (ENXIO); } sc = device_get_softc(dev); kprintf("gpiofree: gpio_intel0 dev=%p sc=%p\n", dev, sc); /* * Plant two pads in the register image: * CTL0 = INTSEL<<28 | GPIOEN (GPIOCFG = 0: RX-capable) * CTL1 = INTCFG 2 (edge/rising, matches TRIG_EDGE/POL_HIGH) */ wr32(PIN_CTL0(PIN_A), (LINE_A << INTSEL_SHIFT) | CTL0_GPIOEN); wr32(PIN_CTL0(PIN_A) + 4, 2u); wr32(PIN_CTL0(PIN_B), (LINE_B << INTSEL_SHIFT) | CTL0_GPIOEN); wr32(PIN_CTL0(PIN_B) + 4, 2u); kprintf("gpiofree: planted pin%d CTL0=%08x CTL1=%08x; " "pin%d CTL0=%08x CTL1=%08x\n", PIN_A, rd32(PIN_CTL0(PIN_A)), rd32(PIN_CTL0(PIN_A) + 4), PIN_B, rd32(PIN_CTL0(PIN_B)), rd32(PIN_CTL0(PIN_B) + 4)); dump_state("before", sc); /* ---- A: ALLOC -> SETUP -> FREE (teardown SKIPPED) ---- */ error = GPIO_ALLOC_INTR(dev, PIN_A, TRIG_EDGE, POL_HIGH, TERM_DEFAULT, &ca); kprintf("gpiofree[A]: GPIO_ALLOC_INTR(pin%d line%d) = %d " "cookie=%p\n", PIN_A, LINE_A, error, ca); dump_state("A-alloc", sc); if (error == 0) { GPIO_SETUP_INTR(dev, ca, NULL, dummy_intr); kprintf("gpiofree[A]: after GPIO_SETUP_INTR " "(enable_intr unmasked line %d)\n", LINE_A); dump_state("A-setup", sc); GPIO_FREE_INTR(dev, ca); /* NO teardown */ kprintf("gpiofree[A]: after GPIO_FREE_INTR with NO " "teardown -> unmap_intr destroyed the mapping\n"); dump_state("A-free", sc); } /* ---- B: ALLOC -> SETUP -> TEARDOWN -> FREE ---- */ error = GPIO_ALLOC_INTR(dev, PIN_B, TRIG_EDGE, POL_HIGH, TERM_DEFAULT, &cb); kprintf("gpiofree[B]: GPIO_ALLOC_INTR(pin%d line%d) = %d " "cookie=%p\n", PIN_B, LINE_B, error, cb); if (error == 0) { GPIO_SETUP_INTR(dev, cb, NULL, dummy_intr); GPIO_TEARDOWN_INTR(dev, cb); GPIO_FREE_INTR(dev, cb); kprintf("gpiofree[B]: after SETUP -> TEARDOWN -> FREE " "(documented order)\n"); dump_state("B-done", sc); } kprintf("gpiofree: SUMMARY MASK=%08x: line%d armed-with-no-" "mapping=%d (DEFECT if 1), line%d left-armed=%d " "(control, expect 0)\n", rd32(GPO_BASE + CHV_MASK), LINE_A, !!(rd32(GPO_BASE + CHV_MASK) & (1u << LINE_A)) && sc->intrmaps[LINE_A].pin == -1, LINE_B, !!(rd32(GPO_BASE + CHV_MASK) & (1u << LINE_B))); break; case MOD_UNLOAD: kprintf("gpiofree: unloaded\n"); break; default: return (EOPNOTSUPP); } return (0); } static moduledata_t gpiofree_mod = { "gpiofree", gpiofree_modevent, NULL }; DECLARE_MODULE(gpiofree, gpiofree_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); MODULE_VERSION(gpiofree, 1); |