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 * A VHBA device to test REPORT LUN functionality.
29 */
30#include "vhba.h"
31
32#define	MAX_TGT		1
33#define	MAX_LUN		1024
34
35#define	DISK_SIZE	32
36#define	DISK_SHIFT	9
37#define	DISK_NBLKS	((DISK_SIZE << 20) >> DISK_SHIFT)
38#define	PSEUDO_SPT	64
39#define	PSEUDO_HDS	64
40#define	PSEUDO_SPC	(PSEUDO_SPT * PSEUDO_HDS)
41
42typedef struct {
43	vhba_softc_t *	vhba;
44	uint8_t *	disk;
45	size_t		disk_size;
46	struct task	qt;
47	uint8_t 	rpbitmap[MAX_LUN >> 3];
48} vhbarptluns_t;
49
50static void vhba_task(void *, int);
51static void vhbarptluns_act(vhbarptluns_t *, struct ccb_scsiio *);
52
53void
54vhba_init(vhba_softc_t *vhba)
55{
56	static vhbarptluns_t vhbas;
57	struct timeval now;
58	int i;
59
60	vhbas.vhba = vhba;
61	vhbas.disk_size = DISK_SIZE << 20;
62	vhbas.disk = malloc(vhbas.disk_size, M_DEVBUF, M_WAITOK|M_ZERO);
63	vhba->private = &vhbas;
64	printf("setting luns");
65	getmicrotime(&now);
66	if (now.tv_usec & 0x1) {
67		vhbas.rpbitmap[0] |= 1;
68	}
69	for (i = 1; i < 8; i++) {
70		if (arc4random() & 1) {
71			printf(" %d", i);
72			vhbas.rpbitmap[0] |= (1 << i);
73		}
74	}
75	for (i = 8; i < MAX_LUN; i++) {
76		if ((arc4random() % i) == 0) {
77			vhbas.rpbitmap[i >> 3] |= (1 << (i & 0x7));
78			printf(" %d", i);
79		}
80	}
81	printf("\n");
82	TASK_INIT(&vhbas.qt, 0, vhba_task, &vhbas);
83}
84
85void
86vhba_fini(vhba_softc_t *vhba)
87{
88	vhbarptluns_t *vhbas = vhba->private;
89	vhba->private = NULL;
90	free(vhbas->disk, M_DEVBUF);
91}
92
93void
94vhba_kick(vhba_softc_t *vhba)
95{
96	vhbarptluns_t *vhbas = vhba->private;
97	taskqueue_enqueue(taskqueue_swi, &vhbas->qt);
98}
99
100static void
101vhba_task(void *arg, int pending)
102{
103	vhbarptluns_t *vhbas = arg;
104	struct ccb_hdr *ccbh;
105
106	mtx_lock(&vhbas->vhba->lock);
107	while ((ccbh = TAILQ_FIRST(&vhbas->vhba->actv)) != NULL) {
108		TAILQ_REMOVE(&vhbas->vhba->actv, ccbh, sim_links.tqe);
109                vhbarptluns_act(vhbas, (struct ccb_scsiio *)ccbh);
110	}
111	while ((ccbh = TAILQ_FIRST(&vhbas->vhba->done)) != NULL) {
112		TAILQ_REMOVE(&vhbas->vhba->done, ccbh, sim_links.tqe);
113		xpt_done((union ccb *)ccbh);
114	}
115	mtx_unlock(&vhbas->vhba->lock);
116}
117
118static void
119vhbarptluns_act(vhbarptluns_t *vhbas, struct ccb_scsiio *csio)
120{
121	char junk[128];
122	uint8_t *cdb, *ptr, status;
123	uint32_t data_len;
124	uint64_t off;
125	int i, attached_lun = 0;
126
127	data_len = 0;
128	status = SCSI_STATUS_OK;
129
130	memset(&csio->sense_data, 0, sizeof (csio->sense_data));
131	cdb = csio->cdb_io.cdb_bytes;
132
133	if (csio->ccb_h.target_id >= MAX_TGT) {
134		csio->ccb_h.status = CAM_SEL_TIMEOUT;
135		TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe);
136		return;
137	}
138
139	if (csio->ccb_h.target_lun < MAX_LUN) {
140		i = csio->ccb_h.target_lun & 0x7;
141		if (vhbas->rpbitmap[csio->ccb_h.target_lun >> 3] & (1 << i)) {
142			attached_lun = 1;
143		}
144	}
145	if (attached_lun == 0 && cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) {
146		vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x25, 0x0);
147		TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe);
148		return;
149	}
150
151	switch (cdb[0]) {
152	case MODE_SENSE:
153	case MODE_SENSE_10:
154	{
155		unsigned int nbyte;
156		uint8_t page = cdb[2] & SMS_PAGE_CODE;
157		uint8_t pgctl = cdb[2] & SMS_PAGE_CTRL_MASK;
158
159		switch (page) {
160		case SMS_FORMAT_DEVICE_PAGE:
161		case SMS_GEOMETRY_PAGE:
162		case SMS_CACHE_PAGE:
163		case SMS_CONTROL_MODE_PAGE:
164		case SMS_ALL_PAGES_PAGE:
165			break;
166		default:
167			vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0);
168			TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe);
169			return;
170		}
171		memset(junk, 0, sizeof (junk));
172		if (cdb[1] & SMS_DBD) {
173			ptr = &junk[4];
174		} else {
175			ptr = junk;
176			ptr[3] = 8;
177			ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff;
178			ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff;
179			ptr[6] = ((1 << DISK_SHIFT) >>  8) & 0xff;
180			ptr[7] = ((1 << DISK_SHIFT)) & 0xff;
181
182			ptr[8] = (DISK_NBLKS >> 24) & 0xff;
183			ptr[9] = (DISK_NBLKS >> 16) & 0xff;
184			ptr[10] = (DISK_NBLKS >> 8) & 0xff;
185			ptr[11] = DISK_NBLKS & 0xff;
186			ptr += 12;
187		}
188
189		if (page == SMS_ALL_PAGES_PAGE || page == SMS_FORMAT_DEVICE_PAGE) {
190			ptr[0] = SMS_FORMAT_DEVICE_PAGE;
191			ptr[1] = 24;
192			if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) {
193				/* tracks per zone */
194				/* ptr[2] = 0; */
195				/* ptr[3] = 0; */
196				/* alternate sectors per zone */
197				/* ptr[4] = 0; */
198				/* ptr[5] = 0; */
199				/* alternate tracks per zone */
200				/* ptr[6] = 0; */
201				/* ptr[7] = 0; */
202				/* alternate tracks per logical unit */
203				/* ptr[8] = 0; */
204				/* ptr[9] = 0; */
205				/* sectors per track */
206				ptr[10] = (PSEUDO_SPT >> 8) & 0xff;
207				ptr[11] = PSEUDO_SPT & 0xff;
208				/* data bytes per physical sector */
209				ptr[12] = ((1 << DISK_SHIFT) >> 8) & 0xff;
210				ptr[13] = (1 << DISK_SHIFT) & 0xff;
211				/* interleave */
212				/* ptr[14] = 0; */
213				/* ptr[15] = 1; */
214				/* track skew factor */
215				/* ptr[16] = 0; */
216				/* ptr[17] = 0; */
217				/* cylinder skew factor */
218				/* ptr[18] = 0; */
219				/* ptr[19] = 0; */
220				/* SSRC, HSEC, RMB, SURF */
221			}
222			ptr += 26;
223		}
224
225		if (page == SMS_ALL_PAGES_PAGE || page == SMS_GEOMETRY_PAGE) {
226			ptr[0] = SMS_GEOMETRY_PAGE;
227			ptr[1] = 24;
228			if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) {
229				uint32_t cyl = (DISK_NBLKS + ((PSEUDO_SPC - 1))) / PSEUDO_SPC;
230				/* number of cylinders */
231				ptr[2] = (cyl >> 24) & 0xff;
232				ptr[3] = (cyl >> 16) & 0xff;
233				ptr[4] = cyl & 0xff;
234				/* number of heads */
235				ptr[5] = PSEUDO_HDS;
236				/* starting cylinder- write precompensation */
237				/* ptr[6] = 0; */
238				/* ptr[7] = 0; */
239				/* ptr[8] = 0; */
240				/* starting cylinder- reduced write current */
241				/* ptr[9] = 0; */
242				/* ptr[10] = 0; */
243				/* ptr[11] = 0; */
244				/* drive step rate */
245				/* ptr[12] = 0; */
246				/* ptr[13] = 0; */
247				/* landing zone cylinder */
248				/* ptr[14] = 0; */
249				/* ptr[15] = 0; */
250				/* ptr[16] = 0; */
251				/* RPL */
252				/* ptr[17] = 0; */
253				/* rotational offset */
254				/* ptr[18] = 0; */
255				/* medium rotation rate -  7200 RPM */
256				ptr[20] = 0x1c;
257				ptr[21] = 0x20;
258			}
259			ptr += 26;
260		}
261
262		if (page == SMS_ALL_PAGES_PAGE || page == SMS_CACHE_PAGE) {
263			ptr[0] = SMS_CACHE_PAGE;
264			ptr[1] = 18;
265			ptr[2] = 1 << 2;
266			ptr += 20;
267		}
268
269		if (page == SMS_ALL_PAGES_PAGE || page == SMS_CONTROL_MODE_PAGE) {
270			ptr[0] = SMS_CONTROL_MODE_PAGE;
271			ptr[1] = 10;
272			if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) {
273				ptr[3] = 1 << 4; /* unrestricted reordering allowed */
274				ptr[8] = 0x75;   /* 30000 ms */
275				ptr[9] = 0x30;
276			}
277			ptr += 12;
278		}
279		nbyte = (char *)ptr - &junk[0];
280		ptr[0] = nbyte - 4;
281
282		if (cdb[0] == MODE_SENSE) {
283			data_len = min(cdb[4], csio->dxfer_len);
284		} else {
285			uint16_t tw = (cdb[7] << 8) | cdb[8];
286			data_len = min(tw, csio->dxfer_len);
287		}
288		data_len = min(data_len, nbyte);
289		if (data_len) {
290			memcpy(csio->data_ptr, junk, data_len);
291		}
292		csio->resid = csio->dxfer_len - data_len;
293		break;
294	}
295	case READ_6:
296	case READ_10:
297	case READ_12:
298	case READ_16:
299	case WRITE_6:
300	case WRITE_10:
301	case WRITE_12:
302	case WRITE_16:
303		if (vhba_rwparm(cdb, &off, &data_len, DISK_NBLKS, DISK_SHIFT)) {
304			vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0);
305			break;
306		}
307		if (data_len) {
308			if ((cdb[0] & 0xf) == 8) {
309				memcpy(csio->data_ptr, &vhbas->disk[off], data_len);
310			} else {
311				memcpy(&vhbas->disk[off], csio->data_ptr, data_len);
312			}
313			csio->resid = csio->dxfer_len - data_len;
314		} else {
315			csio->resid = csio->dxfer_len;
316		}
317		break;
318		break;
319
320	case READ_CAPACITY:
321		if (cdb[2] || cdb[3] || cdb[4] || cdb[5]) {
322			vhba_fill_sense(csio, SSD_KEY_UNIT_ATTENTION, 0x24, 0x0);
323			break;
324		}
325		if (cdb[8] & 0x1) { /* PMI */
326			csio->data_ptr[0] = 0xff;
327			csio->data_ptr[1] = 0xff;
328			csio->data_ptr[2] = 0xff;
329			csio->data_ptr[3] = 0xff;
330		} else {
331			uint64_t last_blk = DISK_NBLKS - 1;
332			if (last_blk < 0xffffffffULL) {
333			    csio->data_ptr[0] = (last_blk >> 24) & 0xff;
334			    csio->data_ptr[1] = (last_blk >> 16) & 0xff;
335			    csio->data_ptr[2] = (last_blk >>  8) & 0xff;
336			    csio->data_ptr[3] = (last_blk) & 0xff;
337			} else {
338			    csio->data_ptr[0] = 0xff;
339			    csio->data_ptr[1] = 0xff;
340			    csio->data_ptr[2] = 0xff;
341			    csio->data_ptr[3] = 0xff;
342			}
343		}
344		csio->data_ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff;
345		csio->data_ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff;
346		csio->data_ptr[6] = ((1 << DISK_SHIFT) >>  8) & 0xff;
347		csio->data_ptr[7] = ((1 << DISK_SHIFT)) & 0xff;
348		break;
349
350	default:
351		vhba_default_cmd(csio, MAX_LUN, vhbas->rpbitmap);
352		break;
353	}
354	csio->ccb_h.status &= ~CAM_STATUS_MASK;
355	if (csio->scsi_status != SCSI_STATUS_OK) {
356		csio->ccb_h.status |= CAM_SCSI_STATUS_ERROR;
357		if (csio->scsi_status == SCSI_STATUS_CHECK_COND) {
358			csio->ccb_h.status |= CAM_AUTOSNS_VALID;
359		}
360	} else {
361		csio->scsi_status = SCSI_STATUS_OK;
362		csio->ccb_h.status |= CAM_REQ_CMP;
363	}
364	TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe);
365}
366DEV_MODULE(vhba_rtpluns, vhba_modprobe, NULL);
367