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"
31static vhba_softc_t *vhba;
32
33#ifndef	VHBA_MOD
34#define	VHBA_MOD	"vhba"
35#endif
36
37static void vhba_action(struct cam_sim *, union ccb *);
38static void vhba_poll(struct cam_sim *);
39
40static int
41vhba_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
65static void
66vhba_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
76static void
77vhba_poll(struct cam_sim *sim)
78{
79	vhba_softc_t *vhba = cam_sim_softc(sim);
80	vhba_kick(vhba);
81}
82
83static void
84vhba_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 */
154void
155vhba_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
167int
168vhba_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
233void
234vhba_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
386void
387vhba_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
399int
400vhba_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