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