1208926Smjacob/*-
2208926Smjacob * Copyright (c) 2010 by Panasas, Inc.
3208926Smjacob * All rights reserved.
4208926Smjacob *
5208926Smjacob * Redistribution and use in source and binary forms, with or without
6208926Smjacob * modification, are permitted provided that the following conditions
7208926Smjacob * are met:
8208926Smjacob * 1. Redistributions of source code must retain the above copyright
9208926Smjacob *    notice immediately at the beginning of the file, without modification,
10208926Smjacob *    this list of conditions, and the following disclaimer.
11208926Smjacob * 2. The name of the author may not be used to endorse or promote products
12208926Smjacob *    derived from this software without specific prior written permission.
13208926Smjacob *
14208926Smjacob * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15208926Smjacob * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16208926Smjacob * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17208926Smjacob * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18208926Smjacob * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19208926Smjacob * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20208926Smjacob * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21208926Smjacob * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22208926Smjacob * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23208926Smjacob * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24208926Smjacob * SUCH DAMAGE.
25208926Smjacob */
26208926Smjacob/* $FreeBSD$ */
27208926Smjacob/*
28208926Smjacob * A VHBA device to test REPORT LUN functionality.
29208926Smjacob */
30208926Smjacob#include "vhba.h"
31208926Smjacob
32208926Smjacob#define	MAX_TGT		1
33208926Smjacob#define	MAX_LUN		1024
34208926Smjacob
35208926Smjacob#define	DISK_SIZE	32
36208926Smjacob#define	DISK_SHIFT	9
37208926Smjacob#define	DISK_NBLKS	((DISK_SIZE << 20) >> DISK_SHIFT)
38208926Smjacob#define	PSEUDO_SPT	64
39208926Smjacob#define	PSEUDO_HDS	64
40208926Smjacob#define	PSEUDO_SPC	(PSEUDO_SPT * PSEUDO_HDS)
41208926Smjacob
42208926Smjacobtypedef struct {
43208926Smjacob	vhba_softc_t *	vhba;
44208926Smjacob	uint8_t *	disk;
45208926Smjacob	size_t		disk_size;
46208926Smjacob	struct task	qt;
47208926Smjacob	uint8_t 	rpbitmap[MAX_LUN >> 3];
48208926Smjacob} vhbarptluns_t;
49208926Smjacob
50208926Smjacobstatic void vhba_task(void *, int);
51208926Smjacobstatic void vhbarptluns_act(vhbarptluns_t *, struct ccb_scsiio *);
52208926Smjacob
53208926Smjacobvoid
54208926Smjacobvhba_init(vhba_softc_t *vhba)
55208926Smjacob{
56208926Smjacob	static vhbarptluns_t vhbas;
57208926Smjacob	struct timeval now;
58208926Smjacob	int i;
59208926Smjacob
60208926Smjacob	vhbas.vhba = vhba;
61208926Smjacob	vhbas.disk_size = DISK_SIZE << 20;
62208926Smjacob	vhbas.disk = malloc(vhbas.disk_size, M_DEVBUF, M_WAITOK|M_ZERO);
63208926Smjacob	vhba->private = &vhbas;
64208926Smjacob	printf("setting luns");
65208926Smjacob	getmicrotime(&now);
66208926Smjacob	if (now.tv_usec & 0x1) {
67208926Smjacob		vhbas.rpbitmap[0] |= 1;
68208926Smjacob	}
69208926Smjacob	for (i = 1; i < 8; i++) {
70208926Smjacob		if (arc4random() & 1) {
71208926Smjacob			printf(" %d", i);
72208926Smjacob			vhbas.rpbitmap[0] |= (1 << i);
73208926Smjacob		}
74208926Smjacob	}
75208926Smjacob	for (i = 8; i < MAX_LUN; i++) {
76208926Smjacob		if ((arc4random() % i) == 0) {
77208926Smjacob			vhbas.rpbitmap[i >> 3] |= (1 << (i & 0x7));
78208926Smjacob			printf(" %d", i);
79208926Smjacob		}
80208926Smjacob	}
81208926Smjacob	printf("\n");
82208926Smjacob	TASK_INIT(&vhbas.qt, 0, vhba_task, &vhbas);
83208926Smjacob}
84208926Smjacob
85208926Smjacobvoid
86208926Smjacobvhba_fini(vhba_softc_t *vhba)
87208926Smjacob{
88208926Smjacob	vhbarptluns_t *vhbas = vhba->private;
89208926Smjacob	vhba->private = NULL;
90208926Smjacob	free(vhbas->disk, M_DEVBUF);
91208926Smjacob}
92208926Smjacob
93208926Smjacobvoid
94208926Smjacobvhba_kick(vhba_softc_t *vhba)
95208926Smjacob{
96208926Smjacob	vhbarptluns_t *vhbas = vhba->private;
97208926Smjacob	taskqueue_enqueue(taskqueue_swi, &vhbas->qt);
98208926Smjacob}
99208926Smjacob
100208926Smjacobstatic void
101208926Smjacobvhba_task(void *arg, int pending)
102208926Smjacob{
103208926Smjacob	vhbarptluns_t *vhbas = arg;
104208926Smjacob	struct ccb_hdr *ccbh;
105208926Smjacob
106208926Smjacob	mtx_lock(&vhbas->vhba->lock);
107208926Smjacob	while ((ccbh = TAILQ_FIRST(&vhbas->vhba->actv)) != NULL) {
108208926Smjacob		TAILQ_REMOVE(&vhbas->vhba->actv, ccbh, sim_links.tqe);
109208926Smjacob                vhbarptluns_act(vhbas, (struct ccb_scsiio *)ccbh);
110208926Smjacob	}
111208926Smjacob	while ((ccbh = TAILQ_FIRST(&vhbas->vhba->done)) != NULL) {
112208926Smjacob		TAILQ_REMOVE(&vhbas->vhba->done, ccbh, sim_links.tqe);
113208926Smjacob		xpt_done((union ccb *)ccbh);
114208926Smjacob	}
115208926Smjacob	mtx_unlock(&vhbas->vhba->lock);
116208926Smjacob}
117208926Smjacob
118208926Smjacobstatic void
119208926Smjacobvhbarptluns_act(vhbarptluns_t *vhbas, struct ccb_scsiio *csio)
120208926Smjacob{
121208926Smjacob	char junk[128];
122208926Smjacob	uint8_t *cdb, *ptr, status;
123208926Smjacob	uint32_t data_len;
124208926Smjacob	uint64_t off;
125208926Smjacob	int i, attached_lun = 0;
126208926Smjacob
127208926Smjacob	data_len = 0;
128208926Smjacob	status = SCSI_STATUS_OK;
129208926Smjacob
130208926Smjacob	memset(&csio->sense_data, 0, sizeof (csio->sense_data));
131208926Smjacob	cdb = csio->cdb_io.cdb_bytes;
132208926Smjacob
133208926Smjacob	if (csio->ccb_h.target_id >= MAX_TGT) {
134208926Smjacob		csio->ccb_h.status = CAM_SEL_TIMEOUT;
135208926Smjacob		TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe);
136208926Smjacob		return;
137208926Smjacob	}
138208926Smjacob
139208926Smjacob	if (csio->ccb_h.target_lun < MAX_LUN) {
140208926Smjacob		i = csio->ccb_h.target_lun & 0x7;
141208926Smjacob		if (vhbas->rpbitmap[csio->ccb_h.target_lun >> 3] & (1 << i)) {
142208926Smjacob			attached_lun = 1;
143208926Smjacob		}
144208926Smjacob	}
145208926Smjacob	if (attached_lun == 0 && cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) {
146208926Smjacob		vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x25, 0x0);
147208926Smjacob		TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe);
148208926Smjacob		return;
149208926Smjacob	}
150208926Smjacob
151208926Smjacob	switch (cdb[0]) {
152208926Smjacob	case MODE_SENSE:
153208926Smjacob	case MODE_SENSE_10:
154208926Smjacob	{
155208926Smjacob		unsigned int nbyte;
156208926Smjacob		uint8_t page = cdb[2] & SMS_PAGE_CODE;
157208926Smjacob		uint8_t pgctl = cdb[2] & SMS_PAGE_CTRL_MASK;
158208926Smjacob
159208926Smjacob		switch (page) {
160208926Smjacob		case SMS_FORMAT_DEVICE_PAGE:
161208926Smjacob		case SMS_GEOMETRY_PAGE:
162208926Smjacob		case SMS_CACHE_PAGE:
163208926Smjacob		case SMS_CONTROL_MODE_PAGE:
164208926Smjacob		case SMS_ALL_PAGES_PAGE:
165208926Smjacob			break;
166208926Smjacob		default:
167208926Smjacob			vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0);
168208926Smjacob			TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe);
169208926Smjacob			return;
170208926Smjacob		}
171208926Smjacob		memset(junk, 0, sizeof (junk));
172208926Smjacob		if (cdb[1] & SMS_DBD) {
173208926Smjacob			ptr = &junk[4];
174208926Smjacob		} else {
175208926Smjacob			ptr = junk;
176208926Smjacob			ptr[3] = 8;
177208926Smjacob			ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff;
178208926Smjacob			ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff;
179208926Smjacob			ptr[6] = ((1 << DISK_SHIFT) >>  8) & 0xff;
180208926Smjacob			ptr[7] = ((1 << DISK_SHIFT)) & 0xff;
181208926Smjacob
182208926Smjacob			ptr[8] = (DISK_NBLKS >> 24) & 0xff;
183208926Smjacob			ptr[9] = (DISK_NBLKS >> 16) & 0xff;
184208926Smjacob			ptr[10] = (DISK_NBLKS >> 8) & 0xff;
185208926Smjacob			ptr[11] = DISK_NBLKS & 0xff;
186208926Smjacob			ptr += 12;
187208926Smjacob		}
188208926Smjacob
189208926Smjacob		if (page == SMS_ALL_PAGES_PAGE || page == SMS_FORMAT_DEVICE_PAGE) {
190208926Smjacob			ptr[0] = SMS_FORMAT_DEVICE_PAGE;
191208926Smjacob			ptr[1] = 24;
192208926Smjacob			if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) {
193208926Smjacob				/* tracks per zone */
194208926Smjacob				/* ptr[2] = 0; */
195208926Smjacob				/* ptr[3] = 0; */
196208926Smjacob				/* alternate sectors per zone */
197208926Smjacob				/* ptr[4] = 0; */
198208926Smjacob				/* ptr[5] = 0; */
199208926Smjacob				/* alternate tracks per zone */
200208926Smjacob				/* ptr[6] = 0; */
201208926Smjacob				/* ptr[7] = 0; */
202208926Smjacob				/* alternate tracks per logical unit */
203208926Smjacob				/* ptr[8] = 0; */
204208926Smjacob				/* ptr[9] = 0; */
205208926Smjacob				/* sectors per track */
206208926Smjacob				ptr[10] = (PSEUDO_SPT >> 8) & 0xff;
207208926Smjacob				ptr[11] = PSEUDO_SPT & 0xff;
208208926Smjacob				/* data bytes per physical sector */
209208926Smjacob				ptr[12] = ((1 << DISK_SHIFT) >> 8) & 0xff;
210208926Smjacob				ptr[13] = (1 << DISK_SHIFT) & 0xff;
211208926Smjacob				/* interleave */
212208926Smjacob				/* ptr[14] = 0; */
213208926Smjacob				/* ptr[15] = 1; */
214208926Smjacob				/* track skew factor */
215208926Smjacob				/* ptr[16] = 0; */
216208926Smjacob				/* ptr[17] = 0; */
217208926Smjacob				/* cylinder skew factor */
218208926Smjacob				/* ptr[18] = 0; */
219208926Smjacob				/* ptr[19] = 0; */
220208926Smjacob				/* SSRC, HSEC, RMB, SURF */
221208926Smjacob			}
222208926Smjacob			ptr += 26;
223208926Smjacob		}
224208926Smjacob
225208926Smjacob		if (page == SMS_ALL_PAGES_PAGE || page == SMS_GEOMETRY_PAGE) {
226208926Smjacob			ptr[0] = SMS_GEOMETRY_PAGE;
227208926Smjacob			ptr[1] = 24;
228208926Smjacob			if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) {
229208926Smjacob				uint32_t cyl = (DISK_NBLKS + ((PSEUDO_SPC - 1))) / PSEUDO_SPC;
230208926Smjacob				/* number of cylinders */
231208926Smjacob				ptr[2] = (cyl >> 24) & 0xff;
232208926Smjacob				ptr[3] = (cyl >> 16) & 0xff;
233208926Smjacob				ptr[4] = cyl & 0xff;
234208926Smjacob				/* number of heads */
235208926Smjacob				ptr[5] = PSEUDO_HDS;
236208926Smjacob				/* starting cylinder- write precompensation */
237208926Smjacob				/* ptr[6] = 0; */
238208926Smjacob				/* ptr[7] = 0; */
239208926Smjacob				/* ptr[8] = 0; */
240208926Smjacob				/* starting cylinder- reduced write current */
241208926Smjacob				/* ptr[9] = 0; */
242208926Smjacob				/* ptr[10] = 0; */
243208926Smjacob				/* ptr[11] = 0; */
244208926Smjacob				/* drive step rate */
245208926Smjacob				/* ptr[12] = 0; */
246208926Smjacob				/* ptr[13] = 0; */
247208926Smjacob				/* landing zone cylinder */
248208926Smjacob				/* ptr[14] = 0; */
249208926Smjacob				/* ptr[15] = 0; */
250208926Smjacob				/* ptr[16] = 0; */
251208926Smjacob				/* RPL */
252208926Smjacob				/* ptr[17] = 0; */
253208926Smjacob				/* rotational offset */
254208926Smjacob				/* ptr[18] = 0; */
255208926Smjacob				/* medium rotation rate -  7200 RPM */
256208926Smjacob				ptr[20] = 0x1c;
257208926Smjacob				ptr[21] = 0x20;
258208926Smjacob			}
259208926Smjacob			ptr += 26;
260208926Smjacob		}
261208926Smjacob
262208926Smjacob		if (page == SMS_ALL_PAGES_PAGE || page == SMS_CACHE_PAGE) {
263208926Smjacob			ptr[0] = SMS_CACHE_PAGE;
264208926Smjacob			ptr[1] = 18;
265208926Smjacob			ptr[2] = 1 << 2;
266208926Smjacob			ptr += 20;
267208926Smjacob		}
268208926Smjacob
269208926Smjacob		if (page == SMS_ALL_PAGES_PAGE || page == SMS_CONTROL_MODE_PAGE) {
270208926Smjacob			ptr[0] = SMS_CONTROL_MODE_PAGE;
271208926Smjacob			ptr[1] = 10;
272208926Smjacob			if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) {
273208926Smjacob				ptr[3] = 1 << 4; /* unrestricted reordering allowed */
274208926Smjacob				ptr[8] = 0x75;   /* 30000 ms */
275208926Smjacob				ptr[9] = 0x30;
276208926Smjacob			}
277208926Smjacob			ptr += 12;
278208926Smjacob		}
279208926Smjacob		nbyte = (char *)ptr - &junk[0];
280208926Smjacob		ptr[0] = nbyte - 4;
281208926Smjacob
282208926Smjacob		if (cdb[0] == MODE_SENSE) {
283208926Smjacob			data_len = min(cdb[4], csio->dxfer_len);
284208926Smjacob		} else {
285208926Smjacob			uint16_t tw = (cdb[7] << 8) | cdb[8];
286208926Smjacob			data_len = min(tw, csio->dxfer_len);
287208926Smjacob		}
288208926Smjacob		data_len = min(data_len, nbyte);
289208926Smjacob		if (data_len) {
290208926Smjacob			memcpy(csio->data_ptr, junk, data_len);
291208926Smjacob		}
292208926Smjacob		csio->resid = csio->dxfer_len - data_len;
293208926Smjacob		break;
294208926Smjacob	}
295208926Smjacob	case READ_6:
296208926Smjacob	case READ_10:
297208926Smjacob	case READ_12:
298208926Smjacob	case READ_16:
299208926Smjacob	case WRITE_6:
300208926Smjacob	case WRITE_10:
301208926Smjacob	case WRITE_12:
302208926Smjacob	case WRITE_16:
303208926Smjacob		if (vhba_rwparm(cdb, &off, &data_len, DISK_NBLKS, DISK_SHIFT)) {
304208926Smjacob			vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0);
305208926Smjacob			break;
306208926Smjacob		}
307208926Smjacob		if (data_len) {
308208926Smjacob			if ((cdb[0] & 0xf) == 8) {
309208926Smjacob				memcpy(csio->data_ptr, &vhbas->disk[off], data_len);
310208926Smjacob			} else {
311208926Smjacob				memcpy(&vhbas->disk[off], csio->data_ptr, data_len);
312208926Smjacob			}
313208926Smjacob			csio->resid = csio->dxfer_len - data_len;
314208926Smjacob		} else {
315208926Smjacob			csio->resid = csio->dxfer_len;
316208926Smjacob		}
317208926Smjacob		break;
318208926Smjacob		break;
319208926Smjacob
320208926Smjacob	case READ_CAPACITY:
321208926Smjacob		if (cdb[2] || cdb[3] || cdb[4] || cdb[5]) {
322208926Smjacob			vhba_fill_sense(csio, SSD_KEY_UNIT_ATTENTION, 0x24, 0x0);
323208926Smjacob			break;
324208926Smjacob		}
325208926Smjacob		if (cdb[8] & 0x1) { /* PMI */
326208926Smjacob			csio->data_ptr[0] = 0xff;
327208926Smjacob			csio->data_ptr[1] = 0xff;
328208926Smjacob			csio->data_ptr[2] = 0xff;
329208926Smjacob			csio->data_ptr[3] = 0xff;
330208926Smjacob		} else {
331208926Smjacob			uint64_t last_blk = DISK_NBLKS - 1;
332208926Smjacob			if (last_blk < 0xffffffffULL) {
333208926Smjacob			    csio->data_ptr[0] = (last_blk >> 24) & 0xff;
334208926Smjacob			    csio->data_ptr[1] = (last_blk >> 16) & 0xff;
335208926Smjacob			    csio->data_ptr[2] = (last_blk >>  8) & 0xff;
336208926Smjacob			    csio->data_ptr[3] = (last_blk) & 0xff;
337208926Smjacob			} else {
338208926Smjacob			    csio->data_ptr[0] = 0xff;
339208926Smjacob			    csio->data_ptr[1] = 0xff;
340208926Smjacob			    csio->data_ptr[2] = 0xff;
341208926Smjacob			    csio->data_ptr[3] = 0xff;
342208926Smjacob			}
343208926Smjacob		}
344208926Smjacob		csio->data_ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff;
345208926Smjacob		csio->data_ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff;
346208926Smjacob		csio->data_ptr[6] = ((1 << DISK_SHIFT) >>  8) & 0xff;
347208926Smjacob		csio->data_ptr[7] = ((1 << DISK_SHIFT)) & 0xff;
348208926Smjacob		break;
349208926Smjacob
350208926Smjacob	default:
351208926Smjacob		vhba_default_cmd(csio, MAX_LUN, vhbas->rpbitmap);
352208926Smjacob		break;
353208926Smjacob	}
354208926Smjacob	csio->ccb_h.status &= ~CAM_STATUS_MASK;
355208926Smjacob	if (csio->scsi_status != SCSI_STATUS_OK) {
356208926Smjacob		csio->ccb_h.status |= CAM_SCSI_STATUS_ERROR;
357208926Smjacob		if (csio->scsi_status == SCSI_STATUS_CHECK_COND) {
358208926Smjacob			csio->ccb_h.status |= CAM_AUTOSNS_VALID;
359208926Smjacob		}
360208926Smjacob	} else {
361208926Smjacob		csio->scsi_status = SCSI_STATUS_OK;
362208926Smjacob		csio->ccb_h.status |= CAM_REQ_CMP;
363208926Smjacob	}
364208926Smjacob	TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe);
365208926Smjacob}
366208926SmjacobDEV_MODULE(vhba_rtpluns, vhba_modprobe, NULL);
367