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 
42 typedef 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 
50 static void vhba_task(void *, int);
51 static void vhbarptluns_act(vhbarptluns_t *, struct ccb_scsiio *);
52 
53 void
54 vhba_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 
85 void
86 vhba_fini(vhba_softc_t *vhba)
87 {
88 	vhbarptluns_t *vhbas = vhba->private;
89 	vhba->private = NULL;
90 	free(vhbas->disk, M_DEVBUF);
91 }
92 
93 void
94 vhba_kick(vhba_softc_t *vhba)
95 {
96 	vhbarptluns_t *vhbas = vhba->private;
97 	taskqueue_enqueue(taskqueue_swi, &vhbas->qt);
98 }
99 
100 static void
101 vhba_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 
118 static void
119 vhbarptluns_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 }
366 DEV_MODULE(vhba_rtpluns, vhba_modprobe, NULL);
367