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