xref: /freebsd/tools/tools/vhba/vhba.c (revision 61e21613)
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