DF-4961 / agi2c_probe.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 | /* * DF-4961 probe KLD - amdgpu_i2c.c bit-bang adapters have NULL * adapter->lock_ops, so every i2c_transfer() on them calls * adapter->lock_ops->lock_bus() through a NULL pointer -> kernel panic. * * This module compiles the REAL, UNMODIFIED audited files * sys/dev/drm/amd/amdgpu/amdgpu_i2c.c (the audited file) * sys/dev/drm/linux_i2c.c (the DragonFly linux-compat i2c * core where i2c_transfer() does * adapter->lock_ops->lock_bus()) * from the guest's own /usr/src/sys (sha256-verified identical to the audit * tree by build.sh) and drives the real production chain: * * amdgpu_i2c_create() amdgpu_i2c.c:160-219 * rec->hw_capable == false -> bit branch amdgpu_i2c.c:193-212 * -> i2c_bit_add_bus() linux_i2c.c:128-134 * (sets adapter->algo = &i2c_bit_algo; DOES NOT set lock_ops) * amdgpu_i2c_router_select_ddc_port() amdgpu_i2c.c:341-366 * -> amdgpu_i2c_get_byte() amdgpu_i2c.c:284-316 * -> i2c_transfer() linux_i2c.c:92-104 * -> adapter->lock_ops->lock_bus() linux_i2c.c:99 <- NULL fp * * The NULL dereference happens BEFORE any hardware access (lock_bus precedes * master_xfer), so no AMD GPU is required. Production reachability of the * exact same i2c_transfer() call: drm_get_edid(&ddc_bus->adapter) * (amdgpu_connectors.c:310-313) -> drm_do_probe_ddc_edid (drm_edid.c:1552), * plus this file's own router_select entry points * (amdgpu_display.c:463, amdgpu_connectors.c:292, dce_v8_0.c:3229, * dce_v10_0.c:3341, dce_v11_0.c:3467). * * Reference proof that the compat layer REQUIRES i2c_add_adapter() for * lock_ops: radeon's identical bit branch carries an explicit * "#ifdef __DragonFly__ / i2c_add_adapter(&i2c->adapter); / #endif" * with the comment "setup lock_ops" (radeon_i2c.c:970-972), and the DP * aux core sets its own lock_ops (drm_dp_helper.c:1124) - only * amdgpu_i2c.c's bit branch omits it. * * Stubbed (linkage only, never executed on the audited bit path): * - the ATOM BIOS hw i2c algo (only reachable when rec->hw_capable && * amdgpu_hw_i2c, which we do not take) * - amdgpu_atombios_i2c_init (driver-init helper) * - drm_dbg/drm_err (logging); stubs ALWAYS print so the fixed build's * "read failed" path is visible in the log (this also live-demonstrates * the uninitialized *val read at amdgpu_i2c.c:313-314 - DF-4962) * amdgpu_hw_i2c is defined to its production default value 1 * (amdgpu_drv.c:88) - !hw_capable bus records take the bit branch * regardless of this flag. */ #include <linux/module.h> #include <linux/printk.h> #include <linux/i2c.h> #include <drm/drmP.h> #include "amdgpu.h" #include "amdgpu_i2c.h" #include "amdgpu_mode.h" #include "amdgpu_atombios.h" #include "atombios_i2c.h" /* ------------------------------------------------------------------ */ /* Linkage stubs */ /* ------------------------------------------------------------------ */ int amdgpu_hw_i2c = 1; /* production default: amdgpu_drv.c:88 */ int amdgpu_atombios_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) { printk(KERN_INFO "agi2c: [stub] amdgpu_atombios_i2c_xfer - NOT REACHED\n"); return (-EIO); } u32 amdgpu_atombios_i2c_func(struct i2c_adapter *adap) { printk(KERN_INFO "agi2c: [stub] amdgpu_atombios_i2c_func - NOT REACHED\n"); return (I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL); } void amdgpu_atombios_i2c_init(struct amdgpu_device *adev) { printk(KERN_INFO "agi2c: [stub] amdgpu_atombios_i2c_init - NOT REACHED\n"); } /* * amdgpu_i2c.c's pre_xfer, post_xfer, get_data, get_clock, set_data and * set_clock expand RREG32/WREG32 to amdgpu_mm_rreg() and amdgpu_mm_wreg() * (amdgpu.h:1066-1069). Those callbacks are only ever dispatched by a * real bit-algo engine; DF's i2c_bit_algo master_xfer is the bit_xfer * stub (linux_i2c.c:106-111) and never calls them - the symbols only * need to RESOLVE. Never executed here. */ uint32_t amdgpu_mm_rreg(struct amdgpu_device *adev, uint32_t reg, uint32_t acc_flags) { printk(KERN_INFO "agi2c: [stub] amdgpu_mm_rreg(0x%x) - NOT REACHED\n", reg); return (0); } void amdgpu_mm_wreg(struct amdgpu_device *adev, uint32_t reg, uint32_t v, uint32_t acc_flags) { printk(KERN_INFO "agi2c: [stub] amdgpu_mm_wreg(0x%x, 0x%x) - NOT REACHED\n", reg, v); } static void agi2c_vprint(const char *tag, const char *func, const char *format, va_list ap) { char buf[160]; kvsnprintf(buf, sizeof(buf), format, ap); printk(KERN_INFO "agi2c: [%s %s] %s\n", tag, func, buf); } void drm_dbg(const char *func, unsigned int category, const char *format, ...) { va_list ap; va_start(ap, format); agi2c_vprint("drm_dbg stub", func, format, ap); va_end(ap); } void drm_err(const char *func, const char *format, ...) { va_list ap; va_start(ap, format); agi2c_vprint("drm_err stub", func, format, ap); va_end(ap); } /* ------------------------------------------------------------------ */ /* Faithful fake objects (BSS, like driver-allocated memory). */ /* amdgpu_i2c_create() only computes &dev->pdev->dev (an ADDRESS); */ /* nothing on the audited bit path dereferences the fake drm_device. */ /* ------------------------------------------------------------------ */ static struct pci_dev fpdev; static struct drm_device fdrm; static struct amdgpu_i2c_bus_rec frec; static struct amdgpu_connector fconn; static struct amdgpu_i2c_chan *probe_chan; static int __init agi2c_init(void) { struct amdgpu_i2c_chan *chan; fdrm.pdev = &fpdev; /* * GPIO-only DDC bus record, as parsed from the VBIOS GPIO_I2C table * for legacy connectors (no I2C_HW_CAP): valid=1, hw_capable=0, * mm_i2c=0. amdgpu_i2c_create() line 184 checks * "rec->hw_capable && amdgpu_hw_i2c" -> FALSE -> bit branch * (amdgpu_i2c.c:193-212), regardless of the amdgpu_hw_i2c default. */ memset(&frec, 0, sizeof(frec)); frec.valid = true; frec.i2c_id = 0; frec.hw_capable = false; frec.mm_i2c = false; printk(KERN_INFO "agi2c: calling REAL amdgpu_i2c_create(dev, rec, \"DF4961-probe\")\n"); chan = amdgpu_i2c_create(&fdrm, &frec, "DF4961-probe"); if (chan == NULL) { printk(KERN_INFO "agi2c: amdgpu_i2c_create returned NULL - abort\n"); return (0); } probe_chan = chan; printk(KERN_INFO "agi2c: chan=%p adapter.name='%s'\n", chan, chan->adapter.name); printk(KERN_INFO "agi2c: adapter.algo=%p (== &i2c_bit_algo: %s)" " master_xfer=%p\n", chan->adapter.algo, (chan->adapter.algo == &i2c_bit_algo) ? "YES - bit bus" : "no", chan->adapter.algo->master_xfer); printk(KERN_INFO "agi2c: adapter.lock_ops=%p <- i2c_transfer() needs this non-NULL\n", chan->adapter.lock_ops); if (chan->adapter.lock_ops != NULL) { printk(KERN_INFO "agi2c: lock_ops set - fix applied, firing anyway (no panic expected)\n"); } else { printk(KERN_INFO "agi2c: lock_ops == NULL: i2c_transfer() will call" " (NULL)->lock_bus() -> NULL function pointer -> PANIC\n"); } /* * Real exported entry point of the audited file, reached in * production from amdgpu_display.c:463 / amdgpu_connectors.c:292 * (DDC router) with exactly this shape of connector. */ memset(&fconn, 0, sizeof(fconn)); fconn.router_bus = chan; fconn.router.ddc_valid = true; fconn.router.i2c_addr = 0x70 >> 1; /* typical i2c mux */ fconn.router.ddc_mux_control_pin = (1 << 4); fconn.router.ddc_mux_state = 0; printk(KERN_INFO "agi2c: FIRING - REAL amdgpu_i2c_router_select_ddc_port(&fconn)...\n"); amdgpu_i2c_router_select_ddc_port(&fconn); printk(KERN_INFO "agi2c: survived amdgpu_i2c_router_select_ddc_port" " (unfixed build panics inside i2c_transfer before this line)\n"); return (0); } static void __exit agi2c_exit(void) { if (probe_chan != NULL) amdgpu_i2c_destroy(probe_chan); printk(KERN_INFO "agi2c: unloaded\n"); } module_init(agi2c_init); module_exit(agi2c_exit); MODULE_DESCRIPTION("DF-4961: amdgpu bit-bang i2c adapters have NULL adapter->lock_ops -> i2c_transfer() NULL function-pointer deref -> panic"); |