DF-4200 / trigger_df4200.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 | /* * trigger_df4200.c โ DF-4200: NULL encoder_funcs->detect call in * amdgpu_connector_dp_detect (sys/dev/drm/amd/amdgpu/amdgpu_connectors.c:1387). * * On amdgpu hardware: any operation that forces a connector reprobe of a * DP-bridge connector (Travis/Nutmeg) whose *first* possible encoder is the * external bridge encoder walks into * * encoder = amdgpu_connector_best_single_encoder(connector); (:1335) * ... * encoder_funcs = encoder->helper_private; (:1386) * ret = encoder_funcs->detect(encoder, connector); (:1387) * * dce_v{8,10,11}_0_ext_helper_funcs have NO .detect ("no detect for * TMDS/LVDS yet" โ dce_v10_0.c:3411-3418), so on a machine whose BIOS * object table enumerates the external bridge before the internal UNIPHY * encoder (amdgpu_encoders.c:45-56 attaches in encoder_list order), and * where the aux/ddc probe at :1383 fails while dac_load_detect is true * (:1385, default-true for DP-bridge VGA/DVI-I connectors โ * amdgpu_connectors.c:1599,1646), this is a call through a NULL function * pointer: instant kernel panic. The sibling path in the same file guards * exactly this: `if (encoder_funcs->detect)` (amdgpu_connectors.c:1083). * * This trigger forces the reprobe via DRM_IOCTL_MODE_GETCONNECTOR with * count_modes==0 (probe path) after a hotplug emulation cycle. * Self-contained struct defs โ no libdrm needed. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <stdint.h> typedef uint32_t __u32; typedef uint64_t __u64; #define DRM_IOCTL_BASE 'd' #define DRM_IOCTL_MODE_GETRESOURCES _IOWR(DRM_IOCTL_BASE, 0xA0, struct drm_mode_card_res) #define DRM_IOCTL_MODE_GETCONNECTOR _IOWR(DRM_IOCTL_BASE, 0xA7, struct drm_mode_get_connector) struct drm_mode_modeinfo { __u32 clock, hdisplay, hsync_start, hsync_end, htotal, hskew, vdisplay, vsync_start, vsync_end, vtotal, vscan, vrefresh, flags, type; char name[32]; }; struct drm_mode_card_res { __u64 fb_id_ptr, crtc_id_ptr, connector_id_ptr, encoder_id_ptr; __u32 count_fbs, count_crtcs, count_connectors, count_encoders; __u32 min_width, max_width, min_height, max_height; }; struct drm_mode_get_connector { __u64 encoders_ptr, modes_ptr, props_ptr, prop_values_ptr; __u32 count_modes, count_props, count_encoders; __u32 encoder_id, connector_id, connector_type, connector_type_id; __u32 connection, mm_width, mm_height, subpixel; }; int main(void) { struct drm_mode_card_res res; struct drm_mode_get_connector conn; int fd, i; fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC); if (fd < 0) { printf("DF-4200 trigger: no /dev/dri/card0 โ no DRM hardware on " "this guest; amdgpu_connector_dp_detect unreachable.\n"); return 1; } memset(&res, 0, sizeof(res)); if (ioctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res) < 0) { perror("GETRESOURCES"); return 1; } __u32 ids[32]; memset(ids, 0, sizeof(ids)); res.connector_id_ptr = (__u64)ids; if (res.count_connectors > 32) res.count_connectors = 32; res.count_connectors = 0; /* first: sizes */ ioctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res); if (ioctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res) < 0) { perror("GETRESOURCES 2"); return 1; } printf("DF-4200 trigger: %u connectors\n", res.count_connectors); for (i = 0; i < (int)res.count_connectors; i++) { memset(&conn, 0, sizeof(conn)); conn.connector_id = ids[i]; conn.count_modes = 0; /* force the probe path (fill_modes) */ if (ioctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn) < 0) { perror("GETCONNECTOR"); continue; } printf(" connector %u type %u connection %u โ probe returned\n", ids[i], conn.connector_type, conn.connection); } /* * On affected hardware (Travis/Nutmeg DP-bridge laptop, bridge * encoder first in BIOS object table, ddc probe failing, * dac_load_detect=1) the last ioctl above never returns: the kernel * calls NULL encoder_funcs->detect at amdgpu_connectors.c:1387. */ printf("DF-4200 trigger: completed (no panic on this machine)\n"); return 0; } |