1208926Smjacob/*- 2208926Smjacob * Copyright (c) 2010 by Panasas, Inc. 3208926Smjacob * All rights reserved. 4208926Smjacob * 5208926Smjacob * Redistribution and use in source and binary forms, with or without 6208926Smjacob * modification, are permitted provided that the following conditions 7208926Smjacob * are met: 8208926Smjacob * 1. Redistributions of source code must retain the above copyright 9208926Smjacob * notice immediately at the beginning of the file, without modification, 10208926Smjacob * this list of conditions, and the following disclaimer. 11208926Smjacob * 2. The name of the author may not be used to endorse or promote products 12208926Smjacob * derived from this software without specific prior written permission. 13208926Smjacob * 14208926Smjacob * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15208926Smjacob * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16208926Smjacob * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17208926Smjacob * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 18208926Smjacob * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19208926Smjacob * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20208926Smjacob * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21208926Smjacob * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22208926Smjacob * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23208926Smjacob * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24208926Smjacob * SUCH DAMAGE. 25208926Smjacob */ 26208926Smjacob/* $FreeBSD$ */ 27208926Smjacob/* 28208926Smjacob * Virtual HBA infrastructure, to be used for testing as well as other cute hacks. 29208926Smjacob */ 30208926Smjacob#include "vhba.h" 31208926Smjacobstatic vhba_softc_t *vhba; 32208926Smjacob 33208926Smjacob#ifndef VHBA_MOD 34208926Smjacob#define VHBA_MOD "vhba" 35208926Smjacob#endif 36208926Smjacob 37208926Smjacobstatic void vhba_action(struct cam_sim *, union ccb *); 38208926Smjacobstatic void vhba_poll(struct cam_sim *); 39208926Smjacob 40208926Smjacobstatic int 41208926Smjacobvhba_attach(vhba_softc_t *vhba) 42208926Smjacob{ 43208926Smjacob TAILQ_INIT(&vhba->actv); 44208926Smjacob TAILQ_INIT(&vhba->done); 45208926Smjacob vhba->devq = cam_simq_alloc(VHBA_MAXCMDS); 46208926Smjacob if (vhba->devq == NULL) { 47208926Smjacob return (ENOMEM); 48208926Smjacob } 49208926Smjacob vhba->sim = cam_sim_alloc(vhba_action, vhba_poll, VHBA_MOD, vhba, 0, &vhba->lock, VHBA_MAXCMDS, VHBA_MAXCMDS, vhba->devq); 50208926Smjacob if (vhba->sim == NULL) { 51208926Smjacob cam_simq_free(vhba->devq); 52208926Smjacob return (ENOMEM); 53208926Smjacob } 54208926Smjacob vhba_init(vhba); 55208926Smjacob mtx_lock(&vhba->lock); 56208926Smjacob if (xpt_bus_register(vhba->sim, 0, 0) != CAM_SUCCESS) { 57208926Smjacob cam_sim_free(vhba->sim, TRUE); 58208926Smjacob mtx_unlock(&vhba->lock); 59208926Smjacob return (EIO); 60208926Smjacob } 61208926Smjacob mtx_unlock(&vhba->lock); 62208926Smjacob return (0); 63208926Smjacob} 64208926Smjacob 65208926Smjacobstatic void 66208926Smjacobvhba_detach(vhba_softc_t *vhba) 67208926Smjacob{ 68208926Smjacob /* 69208926Smjacob * We can't be called with anything queued up. 70208926Smjacob */ 71208926Smjacob vhba_fini(vhba); 72208926Smjacob xpt_bus_deregister(cam_sim_path(vhba->sim)); 73208926Smjacob cam_sim_free(vhba->sim, TRUE); 74208926Smjacob} 75208926Smjacob 76208926Smjacobstatic void 77208926Smjacobvhba_poll(struct cam_sim *sim) 78208926Smjacob{ 79208926Smjacob vhba_softc_t *vhba = cam_sim_softc(sim); 80208926Smjacob vhba_kick(vhba); 81208926Smjacob} 82208926Smjacob 83208926Smjacobstatic void 84208926Smjacobvhba_action(struct cam_sim *sim, union ccb *ccb) 85208926Smjacob{ 86208926Smjacob struct ccb_trans_settings *cts; 87208926Smjacob vhba_softc_t *vhba; 88208926Smjacob 89208926Smjacob vhba = cam_sim_softc(sim); 90208926Smjacob if (vhba->private == NULL) { 91208926Smjacob ccb->ccb_h.status = CAM_REQ_CMP_ERR; 92208926Smjacob xpt_done(ccb); 93208926Smjacob return; 94208926Smjacob } 95208926Smjacob switch (ccb->ccb_h.func_code) { 96208926Smjacob case XPT_SCSI_IO: 97208926Smjacob ccb->ccb_h.status &= ~CAM_STATUS_MASK; 98208926Smjacob ccb->ccb_h.status |= CAM_REQ_INPROG; 99208926Smjacob TAILQ_INSERT_TAIL(&vhba->actv, &ccb->ccb_h, sim_links.tqe); 100208926Smjacob vhba_kick(vhba); 101208926Smjacob return; 102208926Smjacob 103208926Smjacob case XPT_RESET_DEV: 104208926Smjacob ccb->ccb_h.status = CAM_REQ_CMP; 105208926Smjacob break; 106208926Smjacob 107208926Smjacob case XPT_GET_TRAN_SETTINGS: 108208926Smjacob cts = &ccb->cts; 109211183Smjacob cts->protocol_version = SCSI_REV_SPC3; 110208926Smjacob cts->protocol = PROTO_SCSI; 111208926Smjacob cts->transport_version = 0; 112208926Smjacob cts->transport = XPORT_PPB; 113208926Smjacob ccb->ccb_h.status = CAM_REQ_CMP; 114208926Smjacob break; 115208926Smjacob 116208926Smjacob case XPT_CALC_GEOMETRY: 117208926Smjacob cam_calc_geometry(&ccb->ccg, 1); 118208926Smjacob break; 119208926Smjacob 120208926Smjacob case XPT_RESET_BUS: /* Reset the specified bus */ 121208926Smjacob ccb->ccb_h.status = CAM_REQ_CMP; 122208926Smjacob break; 123208926Smjacob 124208926Smjacob case XPT_PATH_INQ: /* Path routing inquiry */ 125208926Smjacob { 126208926Smjacob struct ccb_pathinq *cpi = &ccb->cpi; 127208926Smjacob 128208926Smjacob cpi->version_num = 1; 129208926Smjacob cpi->max_target = VHBA_MAXTGT - 1; 130208926Smjacob cpi->max_lun = 16383; 131208926Smjacob cpi->hba_misc = PIM_NOBUSRESET; 132208926Smjacob cpi->initiator_id = cpi->max_target + 1; 133208926Smjacob cpi->transport = XPORT_PPB; 134208926Smjacob cpi->base_transfer_speed = 1000000; 135208926Smjacob cpi->protocol = PROTO_SCSI; 136211183Smjacob cpi->protocol_version = SCSI_REV_SPC3; 137208926Smjacob strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); 138208926Smjacob strlcpy(cpi->hba_vid, "FakeHBA", HBA_IDLEN); 139208926Smjacob strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); 140208926Smjacob cpi->unit_number = cam_sim_unit(sim); 141208926Smjacob cpi->ccb_h.status = CAM_REQ_CMP; 142208926Smjacob break; 143208926Smjacob } 144208926Smjacob default: 145208926Smjacob ccb->ccb_h.status = CAM_REQ_INVALID; 146208926Smjacob break; 147208926Smjacob } 148208926Smjacob xpt_done(ccb); 149208926Smjacob} 150208926Smjacob 151208926Smjacob/* 152208926Smjacob * Common support 153208926Smjacob */ 154208926Smjacobvoid 155208926Smjacobvhba_fill_sense(struct ccb_scsiio *csio, uint8_t key, uint8_t asc, uint8_t ascq) 156208926Smjacob{ 157208926Smjacob csio->ccb_h.status = CAM_SCSI_STATUS_ERROR|CAM_AUTOSNS_VALID; 158208926Smjacob csio->scsi_status = SCSI_STATUS_CHECK_COND; 159208926Smjacob csio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR; 160208926Smjacob csio->sense_data.flags = key; 161208926Smjacob csio->sense_data.extra_len = 10; 162208926Smjacob csio->sense_data.add_sense_code = asc; 163208926Smjacob csio->sense_data.add_sense_code_qual = ascq; 164208926Smjacob csio->sense_len = sizeof (csio->sense_data); 165208926Smjacob} 166208926Smjacob 167208926Smjacobint 168208926Smjacobvhba_rwparm(uint8_t *cdb, uint64_t *offset, uint32_t *tl, uint64_t nblks, uint32_t blk_shift) 169208926Smjacob{ 170208926Smjacob uint32_t cnt; 171208926Smjacob uint64_t lba; 172208926Smjacob 173208926Smjacob switch (cdb[0]) { 174208926Smjacob case WRITE_16: 175208926Smjacob case READ_16: 176208926Smjacob cnt = (((uint32_t)cdb[10]) << 24) | 177208926Smjacob (((uint32_t)cdb[11]) << 16) | 178208926Smjacob (((uint32_t)cdb[12]) << 8) | 179208926Smjacob ((uint32_t)cdb[13]); 180208926Smjacob 181208926Smjacob lba = (((uint64_t)cdb[2]) << 56) | 182208926Smjacob (((uint64_t)cdb[3]) << 48) | 183208926Smjacob (((uint64_t)cdb[4]) << 40) | 184208926Smjacob (((uint64_t)cdb[5]) << 32) | 185208926Smjacob (((uint64_t)cdb[6]) << 24) | 186208926Smjacob (((uint64_t)cdb[7]) << 16) | 187208926Smjacob (((uint64_t)cdb[8]) << 8) | 188208926Smjacob ((uint64_t)cdb[9]); 189208926Smjacob break; 190208926Smjacob case WRITE_12: 191208926Smjacob case READ_12: 192208926Smjacob cnt = (((uint32_t)cdb[6]) << 16) | 193208926Smjacob (((uint32_t)cdb[7]) << 8) | 194208926Smjacob ((u_int32_t)cdb[8]); 195208926Smjacob 196208926Smjacob lba = (((uint32_t)cdb[2]) << 24) | 197208926Smjacob (((uint32_t)cdb[3]) << 16) | 198208926Smjacob (((uint32_t)cdb[4]) << 8) | 199208926Smjacob ((uint32_t)cdb[5]); 200208926Smjacob break; 201208926Smjacob case WRITE_10: 202208926Smjacob case READ_10: 203208926Smjacob cnt = (((uint32_t)cdb[7]) << 8) | 204208926Smjacob ((u_int32_t)cdb[8]); 205208926Smjacob 206208926Smjacob lba = (((uint32_t)cdb[2]) << 24) | 207208926Smjacob (((uint32_t)cdb[3]) << 16) | 208208926Smjacob (((uint32_t)cdb[4]) << 8) | 209208926Smjacob ((uint32_t)cdb[5]); 210208926Smjacob break; 211208926Smjacob case WRITE_6: 212208926Smjacob case READ_6: 213208926Smjacob cnt = cdb[4]; 214208926Smjacob if (cnt == 0) { 215208926Smjacob cnt = 256; 216208926Smjacob } 217208926Smjacob lba = (((uint32_t)cdb[1] & 0x1f) << 16) | 218208926Smjacob (((uint32_t)cdb[2]) << 8) | 219208926Smjacob ((uint32_t)cdb[3]); 220208926Smjacob break; 221208926Smjacob default: 222208926Smjacob return (-1); 223208926Smjacob } 224208926Smjacob 225208926Smjacob if (lba + cnt > nblks) { 226208926Smjacob return (-1); 227208926Smjacob } 228208926Smjacob *tl = cnt << blk_shift; 229208926Smjacob *offset = lba << blk_shift; 230208926Smjacob return (0); 231208926Smjacob} 232208926Smjacob 233208926Smjacobvoid 234208926Smjacobvhba_default_cmd(struct ccb_scsiio *csio, lun_id_t max_lun, uint8_t *sparse_lun_map) 235208926Smjacob{ 236208926Smjacob char junk[128]; 237208926Smjacob const uint8_t niliqd[SHORT_INQUIRY_LENGTH] = { 238211183Smjacob 0x7f, 0x0, SCSI_REV_SPC3, 0x2, 32, 0, 0, 0x32, 239208926Smjacob 'P', 'A', 'N', 'A', 'S', 'A', 'S', ' ', 240208926Smjacob 'N', 'U', 'L', 'L', ' ', 'D', 'E', 'V', 241208926Smjacob 'I', 'C', 'E', ' ', ' ', ' ', ' ', ' ', 242208926Smjacob '0', '0', '0', '1' 243208926Smjacob }; 244208926Smjacob const uint8_t iqd[SHORT_INQUIRY_LENGTH] = { 245211183Smjacob 0, 0x0, SCSI_REV_SPC3, 0x2, 32, 0, 0, 0x32, 246208926Smjacob 'P', 'A', 'N', 'A', 'S', 'A', 'S', ' ', 247208926Smjacob 'V', 'I', 'R', 'T', ' ', 'M', 'E', 'M', 248208926Smjacob 'O', 'R', 'Y', ' ', 'D', 'I', 'S', 'K', 249208926Smjacob '0', '0', '0', '1' 250208926Smjacob }; 251208926Smjacob const uint8_t vp0data[6] = { 0, 0, 0, 0x2, 0, 0x80 }; 252208926Smjacob const uint8_t vp80data[36] = { 0, 0x80, 0, 0x20 }; 253208926Smjacob int i, attached_lun; 254208926Smjacob uint8_t *cdb, *ptr, status; 255208926Smjacob uint32_t data_len, nlun; 256208926Smjacob 257208926Smjacob data_len = 0; 258208926Smjacob status = SCSI_STATUS_OK; 259208926Smjacob 260208926Smjacob memset(&csio->sense_data, 0, sizeof (csio->sense_data)); 261208926Smjacob cdb = csio->cdb_io.cdb_bytes; 262208926Smjacob 263208926Smjacob attached_lun = 1; 264208926Smjacob if (csio->ccb_h.target_lun >= max_lun) { 265208926Smjacob attached_lun = 0; 266208926Smjacob } else if (sparse_lun_map) { 267208926Smjacob i = csio->ccb_h.target_lun & 0x7; 268208926Smjacob if ((sparse_lun_map[csio->ccb_h.target_lun >> 3] & (1 << i)) == 0) { 269208926Smjacob attached_lun = 0; 270208926Smjacob } 271208926Smjacob } 272208926Smjacob if (attached_lun == 0 && cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) { 273208926Smjacob vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x25, 0x0); 274208926Smjacob return; 275208926Smjacob } 276208926Smjacob 277208926Smjacob switch (cdb[0]) { 278208926Smjacob case REQUEST_SENSE: 279208926Smjacob data_len = csio->dxfer_len; 280208926Smjacob if (cdb[4] < csio->dxfer_len) 281208926Smjacob data_len = cdb[4]; 282208926Smjacob if (data_len) { 283208926Smjacob memset(junk, 0, sizeof (junk)); 284208926Smjacob junk[0] = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR; 285208926Smjacob junk[2] = SSD_KEY_NO_SENSE; 286208926Smjacob junk[7] = 10; 287208926Smjacob memcpy(csio->data_ptr, junk, 288208926Smjacob (data_len > sizeof junk)? sizeof junk : data_len); 289208926Smjacob } 290208926Smjacob csio->resid = csio->dxfer_len - data_len; 291208926Smjacob break; 292208926Smjacob case INQUIRY: 293208926Smjacob i = 0; 294208926Smjacob if ((cdb[1] & 0x1f) == SI_EVPD) { 295208926Smjacob if ((cdb[2] != 0 && cdb[2] != 0x80) || cdb[3] || cdb[5]) { 296208926Smjacob i = 1; 297208926Smjacob } 298208926Smjacob } else if ((cdb[1] & 0x1f) || cdb[2] || cdb[3] || cdb[5]) { 299208926Smjacob i = 1; 300208926Smjacob } 301208926Smjacob if (i) { 302208926Smjacob vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); 303208926Smjacob break; 304208926Smjacob } 305208926Smjacob if (attached_lun == 0) { 306208926Smjacob if (cdb[1] & 0x1f) { 307208926Smjacob vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); 308208926Smjacob break; 309208926Smjacob } 310208926Smjacob memcpy(junk, niliqd, sizeof (niliqd)); 311208926Smjacob data_len = sizeof (niliqd); 312208926Smjacob } else if (cdb[1] & 0x1f) { 313208926Smjacob if (cdb[2] == 0) { 314208926Smjacob memcpy(junk, vp0data, sizeof (vp0data)); 315208926Smjacob data_len = sizeof (vp0data); 316208926Smjacob } else { 317208926Smjacob memcpy(junk, vp80data, sizeof (vp80data)); 318208926Smjacob snprintf(&junk[4], sizeof (vp80data) - 4, "TGT%dLUN%d", csio->ccb_h.target_id, csio->ccb_h.target_lun); 319208926Smjacob for (i = 0; i < sizeof (vp80data); i++) { 320208926Smjacob if (junk[i] == 0) { 321208926Smjacob junk[i] = ' '; 322208926Smjacob } 323208926Smjacob } 324208926Smjacob } 325208926Smjacob data_len = sizeof (vp80data); 326208926Smjacob } else { 327208926Smjacob memcpy(junk, iqd, sizeof (iqd)); 328208926Smjacob data_len = sizeof (iqd); 329208926Smjacob } 330208926Smjacob if (data_len > cdb[4]) { 331208926Smjacob data_len = cdb[4]; 332208926Smjacob } 333208926Smjacob if (data_len) { 334208926Smjacob memcpy(csio->data_ptr, junk, data_len); 335208926Smjacob } 336208926Smjacob csio->resid = csio->dxfer_len - data_len; 337208926Smjacob break; 338208926Smjacob case TEST_UNIT_READY: 339208926Smjacob case SYNCHRONIZE_CACHE: 340208926Smjacob case START_STOP: 341208926Smjacob case RESERVE: 342208926Smjacob case RELEASE: 343208926Smjacob break; 344208926Smjacob 345208926Smjacob case REPORT_LUNS: 346208926Smjacob if (csio->dxfer_len) { 347208926Smjacob memset(csio->data_ptr, 0, csio->dxfer_len); 348208926Smjacob } 349208926Smjacob ptr = NULL; 350208926Smjacob for (nlun = i = 0; i < max_lun; i++) { 351208926Smjacob if (sparse_lun_map) { 352208926Smjacob if ((sparse_lun_map[i >> 3] & (1 << (i & 0x7))) == 0) { 353208926Smjacob continue; 354208926Smjacob } 355208926Smjacob } 356208926Smjacob ptr = &csio->data_ptr[8 + ((nlun++) << 3)]; 357208926Smjacob if ((ptr + 8) > &csio->data_ptr[csio->dxfer_len]) { 358208926Smjacob continue; 359208926Smjacob } 360208926Smjacob if (i >= 256) { 361208926Smjacob ptr[0] = 0x40 | ((i >> 8) & 0x3f); 362208926Smjacob } 363208926Smjacob ptr[1] = i; 364208926Smjacob } 365208926Smjacob junk[0] = (nlun << 3) >> 24; 366208926Smjacob junk[1] = (nlun << 3) >> 16; 367208926Smjacob junk[2] = (nlun << 3) >> 8; 368208926Smjacob junk[3] = (nlun << 3); 369208926Smjacob memset(junk+4, 0, 4); 370208926Smjacob if (csio->dxfer_len) { 371208926Smjacob u_int amt; 372208926Smjacob 373208926Smjacob amt = MIN(csio->dxfer_len, 8); 374208926Smjacob memcpy(csio->data_ptr, junk, amt); 375208926Smjacob amt = MIN((nlun << 3) + 8, csio->dxfer_len); 376208926Smjacob csio->resid = csio->dxfer_len - amt; 377208926Smjacob } 378208926Smjacob break; 379208926Smjacob 380208926Smjacob default: 381208926Smjacob vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x20, 0x0); 382208926Smjacob break; 383208926Smjacob } 384208926Smjacob} 385208926Smjacob 386208926Smjacobvoid 387208926Smjacobvhba_set_status(struct ccb_hdr *ccbh, cam_status status) 388208926Smjacob{ 389208926Smjacob ccbh->status &= ~CAM_STATUS_MASK; 390208926Smjacob ccbh->status |= status; 391208926Smjacob if (status != CAM_REQ_CMP) { 392208926Smjacob if ((ccbh->status & CAM_DEV_QFRZN) == 0) { 393208926Smjacob ccbh->status |= CAM_DEV_QFRZN; 394208926Smjacob xpt_freeze_devq(ccbh->path, 1); 395208926Smjacob } 396208926Smjacob } 397208926Smjacob} 398208926Smjacob 399208926Smjacobint 400208926Smjacobvhba_modprobe(module_t mod, int cmd, void *arg) 401208926Smjacob{ 402208926Smjacob int error = 0; 403208926Smjacob 404208926Smjacob switch (cmd) { 405208926Smjacob case MOD_LOAD: 406208926Smjacob vhba = malloc(sizeof (*vhba), M_DEVBUF, M_WAITOK|M_ZERO); 407208926Smjacob mtx_init(&vhba->lock, "vhba", NULL, MTX_DEF); 408208926Smjacob error = vhba_attach(vhba); 409208926Smjacob if (error) { 410208926Smjacob mtx_destroy(&vhba->lock); 411208926Smjacob free(vhba, M_DEVBUF); 412208926Smjacob } 413208926Smjacob break; 414208926Smjacob case MOD_UNLOAD: 415208926Smjacob mtx_lock(&vhba->lock); 416208926Smjacob if (TAILQ_FIRST(&vhba->done) || TAILQ_FIRST(&vhba->actv)) { 417208926Smjacob error = EBUSY; 418208926Smjacob mtx_unlock(&vhba->lock); 419208926Smjacob break; 420208926Smjacob } 421208926Smjacob vhba_detach(vhba); 422208926Smjacob mtx_unlock(&vhba->lock); 423208926Smjacob mtx_destroy(&vhba->lock); 424208926Smjacob free(vhba, M_DEVBUF); 425208926Smjacob break; 426208926Smjacob default: 427208926Smjacob error = EOPNOTSUPP; 428208926Smjacob break; 429208926Smjacob } 430208926Smjacob return (error); 431208926Smjacob} 432