]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - tools/tools/vhba/vhba.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / tools / tools / vhba / vhba.c
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"
31 static vhba_softc_t *vhba;
32
33 #ifndef VHBA_MOD
34 #define VHBA_MOD        "vhba"
35 #endif
36
37 static void vhba_action(struct cam_sim *, union ccb *);
38 static void vhba_poll(struct cam_sim *);
39
40 static int
41 vhba_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
65 static void
66 vhba_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
76 static void
77 vhba_poll(struct cam_sim *sim)
78 {
79         vhba_softc_t *vhba = cam_sim_softc(sim);
80         vhba_kick(vhba);
81 }
82
83 static void
84 vhba_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  */
154 void
155 vhba_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
167 int
168 vhba_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
233 void
234 vhba_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
386 void
387 vhba_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
399 int
400 vhba_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 }