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