/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Code to inject out of range LBA's to a device * * Logged as an Illegal Request in sd or ssd error kstat kstat -p -m ssderr -s * Ill* 1 * * or spat out in messages as * Feb 6 20:31:35 v4u-v890d-gmp03 scsi: [ID 107833 kern.notice] Requested Block: 0 Error Block: 0 Feb 6 20:31:35 v4u-v890d-gmp03 scsi: [ID 107833 kern.notice] Vendor: HITACHI Serial Number: 50 028AE0060 Feb 6 20:31:35 v4u-v890d-gmp03 scsi: [ID 107833 kern.notice] Sense Key: Illegal Request Feb 6 20:31:35 v4u-v890d-gmp03 scsi: [ID 107833 kern.notice] ASC: 0x21 (logical block address out of range), ASCQ: 0x0, FRU: 0x0 Feb 6 20:31:35 v4u-v890d-gmp03 scsi: [ID 107833 kern.warning] WARNING: /pci@9,600000/SUNW,qlc@1/fp@0,0/ssd@w50060e800428ae20,0 (ssd4): Feb 6 20:31:35 v4u-v890d-gmp03 Error for Command: read(10) Error Level: Informational * * Only been tested on Sparc so far * * If you want to change read to write, look out. Use at your own risk * and make sure you know what you are doing * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int slice_to_int(char); static void usage(void); union lun_n_tag { uchar_t data; struct { #if defined(_BIT_FIELDS_LTOH) uchar_t reladdr:1; uchar_t bytchk:1; uchar_t res:1; uchar_t fua:1; uchar_t dpo:1; uchar_t lun:3; #elif defined(_BIT_FIELDS_HTOL) uchar_t lun:3; uchar_t dpo:1; uchar_t fua:1; uchar_t res:1; uchar_t bytchk:1; uchar_t reladdr:1; #else #error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined #endif } un; }; #define BLOCK 512 /* only do 1 block commands */ int main(int argc, char **argv) { int fd; int slice; off_t overflow = 15360; /* derived by experiment */ off_t cdb_offset; char bufaddr[BLOCK]; struct stat st; struct uscsi_cmd scsi_cmd; union scsi_cdb cdb; struct vtoc vtoc; static union lun_n_tag rlun_n_tag; struct timespec ts; int c, iterations; extern char *optarg; extern int optind; int errflg = 0; unsigned long long v; char device[MAXPATHLEN]; int first = 0; ts.tv_sec = 0; ts.tv_nsec = MICROSEC * 100; /* every 100 microseconds */ iterations = 1; while ((c = getopt(argc, argv, "d:o:t:i:")) != EOF) { switch (c) { case 'd': strcpy(device, optarg); break; case 'o': overflow = atoi(optarg); break; case 'i': iterations = atoi(optarg); break; case 't': printf("optarg = %s\n", optarg); v = atoll(optarg); printf("v = %llu \n", v); if (v > NANOSEC) { ts.tv_sec = v / NANOSEC; ts.tv_nsec = v % NANOSEC; printf("TS %u %u\n", ts.tv_sec, ts.tv_nsec); } else { ts.tv_nsec = (long) v; } break; case '?': errflg++; } if (errflg) { usage(); } } /* check it is a block device */ if (stat(device, &st) == -1) { perror("stat failed"); exit(1); } if (!(st.st_mode & S_IFBLK)) { fprintf(stderr, "Not a block device"); exit(2); } if ((fd = open(device, O_RDWR)) == -1) { perror("open on device failed : "); exit(1); } /* Read the VTOC here */ if (ioctl(fd, DKIOCGVTOC, &vtoc) == -1) { perror("ioctl to read VTOC failed"); exit(1); } slice = slice_to_int(device[strlen(device) - 1]); /* p_size is blocks */ cdb_offset = vtoc.v_part[slice].p_start + vtoc.v_part[slice].p_size; cdb_offset += overflow; printf("dev = %s slice = %u start = %u size = %u cdb_offset = %lu\n", device, slice, vtoc.v_part[slice].p_start, vtoc.v_part[slice].p_size, cdb_offset); /* set up scsi_cmd structure */ (void) memset((char *) &scsi_cmd, 0, sizeof(scsi_cmd)); (void) memset((char *) &cdb, 0, sizeof(cdb)); /* check that cdb_offset is less than the addr range of group1 */ if (cdb_offset > 0xffffffff) { fprintf(stderr, "ETOOBIGLUMP lba(%u) > GROUP1 size\n", cdb_offset); exit(1); } FORMG1ADDR(&cdb, cdb_offset); FORMG1COUNT(&cdb, 1); rlun_n_tag.un.dpo = 1; cdb.scc_cmd = SCMD_READ_G1; cdb.scc_lun = rlun_n_tag.data; scsi_cmd.uscsi_flags = USCSI_READ; scsi_cmd.uscsi_timeout = 600; scsi_cmd.uscsi_cdb = (caddr_t) & cdb; scsi_cmd.uscsi_cdblen = CDB_GROUP1; scsi_cmd.uscsi_bufaddr = bufaddr; scsi_cmd.uscsi_buflen = BLOCK; for (int x = 0; x < iterations; x++) { if (ioctl(fd, USCSICMD, &scsi_cmd) == -1) { if (!first) { first++; perror("uscsi ioctl failed"); fprintf(stderr, "errno = %u\n", errno); } } if (!first) { if (scsi_cmd.uscsi_status != 0) { fprintf(stderr, "USCSI io at %x length %x status %d\n", cdb_offset, scsi_cmd.uscsi_buflen, scsi_cmd.uscsi_status); } if (scsi_cmd.uscsi_resid) { fprintf(stderr, "ucmd.uscsi_resid %d\n", scsi_cmd.uscsi_resid); } } nanosleep(&ts, NULL); } close(fd); return (0); } static int slice_to_int(char arg) { int slice; char buf[2]; sprintf(buf, "%c", arg); slice = atoi(buf); if (slice > 7) { fprintf(stderr, "really slice %u?\n", slice); exit(1); } return (slice); } static void usage(void) { printf("uscsi_lba -d >raw device< -o offset -t timeout -i iterations\n"); exit(1); }