2 * Copyright (c) 2010 by Panasas, Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
28 * Virtual HBA infrastructure, to be used for testing as well as other cute hacks.
31 static vhba_softc_t *vhba;
34 #define VHBA_MOD "vhba"
37 static void vhba_action(struct cam_sim *, union ccb *);
38 static void vhba_poll(struct cam_sim *);
41 vhba_attach(vhba_softc_t *vhba)
43 TAILQ_INIT(&vhba->actv);
44 TAILQ_INIT(&vhba->done);
45 vhba->devq = cam_simq_alloc(VHBA_MAXCMDS);
46 if (vhba->devq == NULL) {
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);
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);
61 mtx_unlock(&vhba->lock);
66 vhba_detach(vhba_softc_t *vhba)
69 * We can't be called with anything queued up.
72 xpt_bus_deregister(cam_sim_path(vhba->sim));
73 cam_sim_free(vhba->sim, TRUE);
77 vhba_poll(struct cam_sim *sim)
79 vhba_softc_t *vhba = cam_sim_softc(sim);
84 vhba_action(struct cam_sim *sim, union ccb *ccb)
86 struct ccb_trans_settings *cts;
89 vhba = cam_sim_softc(sim);
90 if (vhba->private == NULL) {
91 ccb->ccb_h.status = CAM_REQ_CMP_ERR;
95 switch (ccb->ccb_h.func_code) {
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);
104 ccb->ccb_h.status = CAM_REQ_CMP;
107 case XPT_GET_TRAN_SETTINGS:
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;
116 case XPT_CALC_GEOMETRY:
117 cam_calc_geometry(&ccb->ccg, 1);
120 case XPT_RESET_BUS: /* Reset the specified bus */
121 ccb->ccb_h.status = CAM_REQ_CMP;
124 case XPT_PATH_INQ: /* Path routing inquiry */
126 struct ccb_pathinq *cpi = &ccb->cpi;
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;
145 ccb->ccb_h.status = CAM_REQ_INVALID;
155 vhba_fill_sense(struct ccb_scsiio *csio, uint8_t key, uint8_t asc, uint8_t ascq)
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);
168 vhba_rwparm(uint8_t *cdb, uint64_t *offset, uint32_t *tl, uint64_t nblks, uint32_t blk_shift)
176 cnt = (((uint32_t)cdb[10]) << 24) |
177 (((uint32_t)cdb[11]) << 16) |
178 (((uint32_t)cdb[12]) << 8) |
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) |
192 cnt = (((uint32_t)cdb[6]) << 16) |
193 (((uint32_t)cdb[7]) << 8) |
196 lba = (((uint32_t)cdb[2]) << 24) |
197 (((uint32_t)cdb[3]) << 16) |
198 (((uint32_t)cdb[4]) << 8) |
203 cnt = (((uint32_t)cdb[7]) << 8) |
206 lba = (((uint32_t)cdb[2]) << 24) |
207 (((uint32_t)cdb[3]) << 16) |
208 (((uint32_t)cdb[4]) << 8) |
217 lba = (((uint32_t)cdb[1] & 0x1f) << 16) |
218 (((uint32_t)cdb[2]) << 8) |
225 if (lba + cnt > nblks) {
228 *tl = cnt << blk_shift;
229 *offset = lba << blk_shift;
234 vhba_default_cmd(struct ccb_scsiio *csio, lun_id_t max_lun, uint8_t *sparse_lun_map)
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', ' ', ' ', ' ', ' ', ' ',
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',
251 const uint8_t vp0data[6] = { 0, 0, 0, 0x2, 0, 0x80 };
252 const uint8_t vp80data[36] = { 0, 0x80, 0, 0x20 };
254 uint8_t *cdb, *ptr, status;
255 uint32_t data_len, nlun;
258 status = SCSI_STATUS_OK;
260 memset(&csio->sense_data, 0, sizeof (csio->sense_data));
261 cdb = csio->cdb_io.cdb_bytes;
264 if (csio->ccb_h.target_lun >= max_lun) {
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) {
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);
279 data_len = csio->dxfer_len;
280 if (cdb[4] < csio->dxfer_len)
283 memset(junk, 0, sizeof (junk));
284 junk[0] = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR;
285 junk[2] = SSD_KEY_NO_SENSE;
287 memcpy(csio->data_ptr, junk,
288 (data_len > sizeof junk)? sizeof junk : data_len);
290 csio->resid = csio->dxfer_len - data_len;
294 if ((cdb[1] & 0x1f) == SI_EVPD) {
295 if ((cdb[2] != 0 && cdb[2] != 0x80) || cdb[3] || cdb[5]) {
298 } else if ((cdb[1] & 0x1f) || cdb[2] || cdb[3] || cdb[5]) {
302 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0);
305 if (attached_lun == 0) {
307 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0);
310 memcpy(junk, niliqd, sizeof (niliqd));
311 data_len = sizeof (niliqd);
312 } else if (cdb[1] & 0x1f) {
314 memcpy(junk, vp0data, sizeof (vp0data));
315 data_len = sizeof (vp0data);
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++) {
325 data_len = sizeof (vp80data);
327 memcpy(junk, iqd, sizeof (iqd));
328 data_len = sizeof (iqd);
330 if (data_len > cdb[4]) {
334 memcpy(csio->data_ptr, junk, data_len);
336 csio->resid = csio->dxfer_len - data_len;
338 case TEST_UNIT_READY:
339 case SYNCHRONIZE_CACHE:
346 if (csio->dxfer_len) {
347 memset(csio->data_ptr, 0, csio->dxfer_len);
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) {
356 ptr = &csio->data_ptr[8 + ((nlun++) << 3)];
357 if ((ptr + 8) > &csio->data_ptr[csio->dxfer_len]) {
361 ptr[0] = 0x40 | ((i >> 8) & 0x3f);
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) {
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;
381 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x20, 0x0);
387 vhba_set_status(struct ccb_hdr *ccbh, cam_status status)
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);
400 vhba_modprobe(module_t mod, int cmd, void *arg)
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);
410 mtx_destroy(&vhba->lock);
411 free(vhba, M_DEVBUF);
415 mtx_lock(&vhba->lock);
416 if (TAILQ_FIRST(&vhba->done) || TAILQ_FIRST(&vhba->actv)) {
418 mtx_unlock(&vhba->lock);
422 mtx_unlock(&vhba->lock);
423 mtx_destroy(&vhba->lock);
424 free(vhba, M_DEVBUF);