1 /*- 2 * Copyright (c) 2010 by Panasas, Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice immediately at the beginning of the file, without modification, 10 * this list of conditions, and the following disclaimer. 11 * 2. The name of the author may not be used to endorse or promote products 12 * derived from this software without specific prior written permission. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 18 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 /* 27 * Virtual HBA infrastructure, to be used for testing as well as other cute hacks. 28 */ 29 #include "vhba.h" 30 static vhba_softc_t *vhba; 31 32 #ifndef VHBA_MOD 33 #define VHBA_MOD "vhba" 34 #endif 35 36 static void vhba_action(struct cam_sim *, union ccb *); 37 static void vhba_poll(struct cam_sim *); 38 39 static int 40 vhba_attach(vhba_softc_t *vhba) 41 { 42 TAILQ_INIT(&vhba->actv); 43 TAILQ_INIT(&vhba->done); 44 vhba->devq = cam_simq_alloc(VHBA_MAXCMDS); 45 if (vhba->devq == NULL) { 46 return (ENOMEM); 47 } 48 vhba->sim = cam_sim_alloc(vhba_action, vhba_poll, VHBA_MOD, vhba, 0, &vhba->lock, VHBA_MAXCMDS, VHBA_MAXCMDS, vhba->devq); 49 if (vhba->sim == NULL) { 50 cam_simq_free(vhba->devq); 51 return (ENOMEM); 52 } 53 vhba_init(vhba); 54 mtx_lock(&vhba->lock); 55 if (xpt_bus_register(vhba->sim, 0, 0) != CAM_SUCCESS) { 56 cam_sim_free(vhba->sim, TRUE); 57 mtx_unlock(&vhba->lock); 58 return (EIO); 59 } 60 mtx_unlock(&vhba->lock); 61 return (0); 62 } 63 64 static void 65 vhba_detach(vhba_softc_t *vhba) 66 { 67 /* 68 * We can't be called with anything queued up. 69 */ 70 vhba_fini(vhba); 71 xpt_bus_deregister(cam_sim_path(vhba->sim)); 72 cam_sim_free(vhba->sim, TRUE); 73 } 74 75 static void 76 vhba_poll(struct cam_sim *sim) 77 { 78 vhba_softc_t *vhba = cam_sim_softc(sim); 79 vhba_kick(vhba); 80 } 81 82 static void 83 vhba_action(struct cam_sim *sim, union ccb *ccb) 84 { 85 struct ccb_trans_settings *cts; 86 vhba_softc_t *vhba; 87 88 vhba = cam_sim_softc(sim); 89 if (vhba->private == NULL) { 90 ccb->ccb_h.status = CAM_REQ_CMP_ERR; 91 xpt_done(ccb); 92 return; 93 } 94 switch (ccb->ccb_h.func_code) { 95 case XPT_SCSI_IO: 96 ccb->ccb_h.status &= ~CAM_STATUS_MASK; 97 ccb->ccb_h.status |= CAM_REQ_INPROG; 98 TAILQ_INSERT_TAIL(&vhba->actv, &ccb->ccb_h, sim_links.tqe); 99 vhba_kick(vhba); 100 return; 101 102 case XPT_RESET_DEV: 103 ccb->ccb_h.status = CAM_REQ_CMP; 104 break; 105 106 case XPT_GET_TRAN_SETTINGS: 107 cts = &ccb->cts; 108 cts->protocol_version = SCSI_REV_SPC3; 109 cts->protocol = PROTO_SCSI; 110 cts->transport_version = 0; 111 cts->transport = XPORT_PPB; 112 ccb->ccb_h.status = CAM_REQ_CMP; 113 break; 114 115 case XPT_CALC_GEOMETRY: 116 cam_calc_geometry(&ccb->ccg, 1); 117 break; 118 119 case XPT_RESET_BUS: /* Reset the specified bus */ 120 ccb->ccb_h.status = CAM_REQ_CMP; 121 break; 122 123 case XPT_PATH_INQ: /* Path routing inquiry */ 124 { 125 struct ccb_pathinq *cpi = &ccb->cpi; 126 127 cpi->version_num = 1; 128 cpi->max_target = VHBA_MAXTGT - 1; 129 cpi->max_lun = 16383; 130 cpi->hba_misc = PIM_NOBUSRESET; 131 cpi->initiator_id = cpi->max_target + 1; 132 cpi->transport = XPORT_PPB; 133 cpi->base_transfer_speed = 1000000; 134 cpi->protocol = PROTO_SCSI; 135 cpi->protocol_version = SCSI_REV_SPC3; 136 strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); 137 strlcpy(cpi->hba_vid, "FakeHBA", HBA_IDLEN); 138 strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); 139 cpi->unit_number = cam_sim_unit(sim); 140 cpi->ccb_h.status = CAM_REQ_CMP; 141 break; 142 } 143 default: 144 ccb->ccb_h.status = CAM_REQ_INVALID; 145 break; 146 } 147 xpt_done(ccb); 148 } 149 150 /* 151 * Common support 152 */ 153 void 154 vhba_fill_sense(struct ccb_scsiio *csio, uint8_t key, uint8_t asc, uint8_t ascq) 155 { 156 csio->ccb_h.status = CAM_SCSI_STATUS_ERROR|CAM_AUTOSNS_VALID; 157 csio->scsi_status = SCSI_STATUS_CHECK_COND; 158 csio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR; 159 csio->sense_data.flags = key; 160 csio->sense_data.extra_len = 10; 161 csio->sense_data.add_sense_code = asc; 162 csio->sense_data.add_sense_code_qual = ascq; 163 csio->sense_len = sizeof (csio->sense_data); 164 } 165 166 int 167 vhba_rwparm(uint8_t *cdb, uint64_t *offset, uint32_t *tl, uint64_t nblks, uint32_t blk_shift) 168 { 169 uint32_t cnt; 170 uint64_t lba; 171 172 switch (cdb[0]) { 173 case WRITE_16: 174 case READ_16: 175 cnt = (((uint32_t)cdb[10]) << 24) | 176 (((uint32_t)cdb[11]) << 16) | 177 (((uint32_t)cdb[12]) << 8) | 178 ((uint32_t)cdb[13]); 179 180 lba = (((uint64_t)cdb[2]) << 56) | 181 (((uint64_t)cdb[3]) << 48) | 182 (((uint64_t)cdb[4]) << 40) | 183 (((uint64_t)cdb[5]) << 32) | 184 (((uint64_t)cdb[6]) << 24) | 185 (((uint64_t)cdb[7]) << 16) | 186 (((uint64_t)cdb[8]) << 8) | 187 ((uint64_t)cdb[9]); 188 break; 189 case WRITE_12: 190 case READ_12: 191 cnt = (((uint32_t)cdb[6]) << 16) | 192 (((uint32_t)cdb[7]) << 8) | 193 ((u_int32_t)cdb[8]); 194 195 lba = (((uint32_t)cdb[2]) << 24) | 196 (((uint32_t)cdb[3]) << 16) | 197 (((uint32_t)cdb[4]) << 8) | 198 ((uint32_t)cdb[5]); 199 break; 200 case WRITE_10: 201 case READ_10: 202 cnt = (((uint32_t)cdb[7]) << 8) | 203 ((u_int32_t)cdb[8]); 204 205 lba = (((uint32_t)cdb[2]) << 24) | 206 (((uint32_t)cdb[3]) << 16) | 207 (((uint32_t)cdb[4]) << 8) | 208 ((uint32_t)cdb[5]); 209 break; 210 case WRITE_6: 211 case READ_6: 212 cnt = cdb[4]; 213 if (cnt == 0) { 214 cnt = 256; 215 } 216 lba = (((uint32_t)cdb[1] & 0x1f) << 16) | 217 (((uint32_t)cdb[2]) << 8) | 218 ((uint32_t)cdb[3]); 219 break; 220 default: 221 return (-1); 222 } 223 224 if (lba + cnt > nblks) { 225 return (-1); 226 } 227 *tl = cnt << blk_shift; 228 *offset = lba << blk_shift; 229 return (0); 230 } 231 232 void 233 vhba_default_cmd(struct ccb_scsiio *csio, lun_id_t max_lun, uint8_t *sparse_lun_map) 234 { 235 char junk[128]; 236 const uint8_t niliqd[SHORT_INQUIRY_LENGTH] = { 237 0x7f, 0x0, SCSI_REV_SPC3, 0x2, 32, 0, 0, 0x32, 238 'P', 'A', 'N', 'A', 'S', 'A', 'S', ' ', 239 'N', 'U', 'L', 'L', ' ', 'D', 'E', 'V', 240 'I', 'C', 'E', ' ', ' ', ' ', ' ', ' ', 241 '0', '0', '0', '1' 242 }; 243 const uint8_t iqd[SHORT_INQUIRY_LENGTH] = { 244 0, 0x0, SCSI_REV_SPC3, 0x2, 32, 0, 0, 0x32, 245 'P', 'A', 'N', 'A', 'S', 'A', 'S', ' ', 246 'V', 'I', 'R', 'T', ' ', 'M', 'E', 'M', 247 'O', 'R', 'Y', ' ', 'D', 'I', 'S', 'K', 248 '0', '0', '0', '1' 249 }; 250 const uint8_t vp0data[6] = { 0, 0, 0, 0x2, 0, 0x80 }; 251 const uint8_t vp80data[36] = { 0, 0x80, 0, 0x20 }; 252 int i, attached_lun; 253 uint8_t *cdb, *ptr, status; 254 uint32_t data_len, nlun; 255 256 data_len = 0; 257 status = SCSI_STATUS_OK; 258 259 memset(&csio->sense_data, 0, sizeof (csio->sense_data)); 260 cdb = csio->cdb_io.cdb_bytes; 261 262 attached_lun = 1; 263 if (csio->ccb_h.target_lun >= max_lun) { 264 attached_lun = 0; 265 } else if (sparse_lun_map) { 266 i = csio->ccb_h.target_lun & 0x7; 267 if ((sparse_lun_map[csio->ccb_h.target_lun >> 3] & (1 << i)) == 0) { 268 attached_lun = 0; 269 } 270 } 271 if (attached_lun == 0 && cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) { 272 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x25, 0x0); 273 return; 274 } 275 276 switch (cdb[0]) { 277 case REQUEST_SENSE: 278 data_len = csio->dxfer_len; 279 if (cdb[4] < csio->dxfer_len) 280 data_len = cdb[4]; 281 if (data_len) { 282 memset(junk, 0, sizeof (junk)); 283 junk[0] = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR; 284 junk[2] = SSD_KEY_NO_SENSE; 285 junk[7] = 10; 286 memcpy(csio->data_ptr, junk, 287 (data_len > sizeof junk)? sizeof junk : data_len); 288 } 289 csio->resid = csio->dxfer_len - data_len; 290 break; 291 case INQUIRY: 292 i = 0; 293 if ((cdb[1] & 0x1f) == SI_EVPD) { 294 if ((cdb[2] != 0 && cdb[2] != 0x80) || cdb[3] || cdb[5]) { 295 i = 1; 296 } 297 } else if ((cdb[1] & 0x1f) || cdb[2] || cdb[3] || cdb[5]) { 298 i = 1; 299 } 300 if (i) { 301 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); 302 break; 303 } 304 if (attached_lun == 0) { 305 if (cdb[1] & 0x1f) { 306 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); 307 break; 308 } 309 memcpy(junk, niliqd, sizeof (niliqd)); 310 data_len = sizeof (niliqd); 311 } else if (cdb[1] & 0x1f) { 312 if (cdb[2] == 0) { 313 memcpy(junk, vp0data, sizeof (vp0data)); 314 data_len = sizeof (vp0data); 315 } else { 316 memcpy(junk, vp80data, sizeof (vp80data)); 317 snprintf(&junk[4], sizeof (vp80data) - 4, "TGT%dLUN%d", csio->ccb_h.target_id, csio->ccb_h.target_lun); 318 for (i = 0; i < sizeof (vp80data); i++) { 319 if (junk[i] == 0) { 320 junk[i] = ' '; 321 } 322 } 323 } 324 data_len = sizeof (vp80data); 325 } else { 326 memcpy(junk, iqd, sizeof (iqd)); 327 data_len = sizeof (iqd); 328 } 329 if (data_len > cdb[4]) { 330 data_len = cdb[4]; 331 } 332 if (data_len) { 333 memcpy(csio->data_ptr, junk, data_len); 334 } 335 csio->resid = csio->dxfer_len - data_len; 336 break; 337 case TEST_UNIT_READY: 338 case SYNCHRONIZE_CACHE: 339 case START_STOP: 340 case RESERVE: 341 case RELEASE: 342 break; 343 344 case REPORT_LUNS: 345 if (csio->dxfer_len) { 346 memset(csio->data_ptr, 0, csio->dxfer_len); 347 } 348 ptr = NULL; 349 for (nlun = i = 0; i < max_lun; i++) { 350 if (sparse_lun_map) { 351 if ((sparse_lun_map[i >> 3] & (1 << (i & 0x7))) == 0) { 352 continue; 353 } 354 } 355 ptr = &csio->data_ptr[8 + ((nlun++) << 3)]; 356 if ((ptr + 8) > &csio->data_ptr[csio->dxfer_len]) { 357 continue; 358 } 359 if (i >= 256) { 360 ptr[0] = 0x40 | ((i >> 8) & 0x3f); 361 } 362 ptr[1] = i; 363 } 364 junk[0] = (nlun << 3) >> 24; 365 junk[1] = (nlun << 3) >> 16; 366 junk[2] = (nlun << 3) >> 8; 367 junk[3] = (nlun << 3); 368 memset(junk+4, 0, 4); 369 if (csio->dxfer_len) { 370 u_int amt; 371 372 amt = MIN(csio->dxfer_len, 8); 373 memcpy(csio->data_ptr, junk, amt); 374 amt = MIN((nlun << 3) + 8, csio->dxfer_len); 375 csio->resid = csio->dxfer_len - amt; 376 } 377 break; 378 379 default: 380 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x20, 0x0); 381 break; 382 } 383 } 384 385 void 386 vhba_set_status(struct ccb_hdr *ccbh, cam_status status) 387 { 388 ccbh->status &= ~CAM_STATUS_MASK; 389 ccbh->status |= status; 390 if (status != CAM_REQ_CMP) { 391 if ((ccbh->status & CAM_DEV_QFRZN) == 0) { 392 ccbh->status |= CAM_DEV_QFRZN; 393 xpt_freeze_devq(ccbh->path, 1); 394 } 395 } 396 } 397 398 int 399 vhba_modprobe(module_t mod, int cmd, void *arg) 400 { 401 int error = 0; 402 403 switch (cmd) { 404 case MOD_LOAD: 405 vhba = malloc(sizeof (*vhba), M_DEVBUF, M_WAITOK|M_ZERO); 406 mtx_init(&vhba->lock, "vhba", NULL, MTX_DEF); 407 error = vhba_attach(vhba); 408 if (error) { 409 mtx_destroy(&vhba->lock); 410 free(vhba, M_DEVBUF); 411 } 412 break; 413 case MOD_UNLOAD: 414 mtx_lock(&vhba->lock); 415 if (TAILQ_FIRST(&vhba->done) || TAILQ_FIRST(&vhba->actv)) { 416 error = EBUSY; 417 mtx_unlock(&vhba->lock); 418 break; 419 } 420 vhba_detach(vhba); 421 mtx_unlock(&vhba->lock); 422 mtx_destroy(&vhba->lock); 423 free(vhba, M_DEVBUF); 424 break; 425 default: 426 error = EOPNOTSUPP; 427 break; 428 } 429 return (error); 430 } 431