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
vhba_attach(vhba_softc_t * vhba)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
vhba_detach(vhba_softc_t * vhba)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
vhba_poll(struct cam_sim * sim)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
vhba_action(struct cam_sim * sim,union ccb * ccb)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
vhba_fill_sense(struct ccb_scsiio * csio,uint8_t key,uint8_t asc,uint8_t ascq)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
vhba_rwparm(uint8_t * cdb,uint64_t * offset,uint32_t * tl,uint64_t nblks,uint32_t blk_shift)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
vhba_default_cmd(struct ccb_scsiio * csio,lun_id_t max_lun,uint8_t * sparse_lun_map)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
vhba_set_status(struct ccb_hdr * ccbh,cam_status status)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
vhba_modprobe(module_t mod,int cmd,void * arg)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