]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/tools/vhba/vhba.c
contrib/tzdata: import tzdata 2024a
[FreeBSD/FreeBSD.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 /*
27  * Virtual HBA infrastructure, to be used for testing as well as other cute hacks.
28  */
29 #include "vhba.h"
30 static vhba_softc_t *vhba;
31
32 #ifndef VHBA_MOD
33 #define VHBA_MOD        "vhba"
34 #endif
35
36 static void vhba_action(struct cam_sim *, union ccb *);
37 static void vhba_poll(struct cam_sim *);
38
39 static int
40 vhba_attach(vhba_softc_t *vhba)
41 {
42         TAILQ_INIT(&vhba->actv);
43         TAILQ_INIT(&vhba->done);
44         vhba->devq = cam_simq_alloc(VHBA_MAXCMDS);
45         if (vhba->devq == NULL) {
46                 return (ENOMEM);
47         }
48         vhba->sim = cam_sim_alloc(vhba_action, vhba_poll, VHBA_MOD, vhba, 0, &vhba->lock, VHBA_MAXCMDS, VHBA_MAXCMDS, vhba->devq);
49         if (vhba->sim == NULL) {
50                 cam_simq_free(vhba->devq);
51                 return (ENOMEM);
52         }
53         vhba_init(vhba);
54         mtx_lock(&vhba->lock);
55         if (xpt_bus_register(vhba->sim, 0, 0) != CAM_SUCCESS) {
56                 cam_sim_free(vhba->sim, TRUE);
57                 mtx_unlock(&vhba->lock);
58                 return (EIO);
59         }
60         mtx_unlock(&vhba->lock);
61         return (0);
62 }
63
64 static void
65 vhba_detach(vhba_softc_t *vhba)
66 {
67         /*
68          * We can't be called with anything queued up.
69          */
70         vhba_fini(vhba);
71         xpt_bus_deregister(cam_sim_path(vhba->sim));
72         cam_sim_free(vhba->sim, TRUE);
73 }
74
75 static void
76 vhba_poll(struct cam_sim *sim)
77 {
78         vhba_softc_t *vhba = cam_sim_softc(sim);
79         vhba_kick(vhba);
80 }
81
82 static void
83 vhba_action(struct cam_sim *sim, union ccb *ccb)
84 {
85         struct ccb_trans_settings *cts;
86         vhba_softc_t *vhba;
87
88         vhba = cam_sim_softc(sim);
89         if (vhba->private == NULL) {
90                 ccb->ccb_h.status = CAM_REQ_CMP_ERR;
91                 xpt_done(ccb);
92                 return;
93         }
94         switch (ccb->ccb_h.func_code) {
95         case XPT_SCSI_IO:
96                 ccb->ccb_h.status &= ~CAM_STATUS_MASK;
97                 ccb->ccb_h.status |= CAM_REQ_INPROG;
98                 TAILQ_INSERT_TAIL(&vhba->actv, &ccb->ccb_h, sim_links.tqe);
99                 vhba_kick(vhba);
100                 return;
101
102         case XPT_RESET_DEV:
103                 ccb->ccb_h.status = CAM_REQ_CMP;
104                 break;
105
106         case XPT_GET_TRAN_SETTINGS:
107                 cts = &ccb->cts;
108                 cts->protocol_version = SCSI_REV_SPC3;
109                 cts->protocol = PROTO_SCSI;
110                 cts->transport_version = 0;
111                 cts->transport = XPORT_PPB;
112                 ccb->ccb_h.status = CAM_REQ_CMP;
113                 break;
114
115         case XPT_CALC_GEOMETRY:
116                 cam_calc_geometry(&ccb->ccg, 1);
117                 break;
118
119         case XPT_RESET_BUS:             /* Reset the specified bus */
120                 ccb->ccb_h.status = CAM_REQ_CMP;
121                 break;
122
123         case XPT_PATH_INQ:              /* Path routing inquiry */
124         {
125                 struct ccb_pathinq *cpi = &ccb->cpi;
126
127                 cpi->version_num = 1;
128                 cpi->max_target = VHBA_MAXTGT - 1;
129                 cpi->max_lun = 16383;
130                 cpi->hba_misc = PIM_NOBUSRESET;
131                 cpi->initiator_id = cpi->max_target + 1;
132                 cpi->transport = XPORT_PPB;
133                 cpi->base_transfer_speed = 1000000;
134                 cpi->protocol = PROTO_SCSI;
135                 cpi->protocol_version = SCSI_REV_SPC3;
136                 strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
137                 strlcpy(cpi->hba_vid, "FakeHBA", HBA_IDLEN);
138                 strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
139                 cpi->unit_number = cam_sim_unit(sim);
140                 cpi->ccb_h.status = CAM_REQ_CMP;
141                 break;
142         }
143         default:
144                 ccb->ccb_h.status = CAM_REQ_INVALID;
145                 break;
146         }
147         xpt_done(ccb);
148 }
149
150 /*
151  * Common support
152  */
153 void
154 vhba_fill_sense(struct ccb_scsiio *csio, uint8_t key, uint8_t asc, uint8_t ascq)
155 {
156         csio->ccb_h.status = CAM_SCSI_STATUS_ERROR|CAM_AUTOSNS_VALID;
157         csio->scsi_status = SCSI_STATUS_CHECK_COND;
158         csio->sense_data.error_code = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR;
159         csio->sense_data.flags = key;
160         csio->sense_data.extra_len = 10;
161         csio->sense_data.add_sense_code = asc;
162         csio->sense_data.add_sense_code_qual = ascq;
163         csio->sense_len = sizeof (csio->sense_data);
164 }
165
166 int
167 vhba_rwparm(uint8_t *cdb, uint64_t *offset, uint32_t *tl, uint64_t nblks, uint32_t blk_shift)
168 {
169         uint32_t cnt;
170         uint64_t lba;
171
172         switch (cdb[0]) {
173         case WRITE_16:
174         case READ_16:
175                 cnt =   (((uint32_t)cdb[10]) <<  24) |
176                         (((uint32_t)cdb[11]) <<  16) |
177                         (((uint32_t)cdb[12]) <<   8) |
178                         ((uint32_t)cdb[13]);
179
180                 lba =   (((uint64_t)cdb[2]) << 56) |
181                         (((uint64_t)cdb[3]) << 48) |
182                         (((uint64_t)cdb[4]) << 40) |
183                         (((uint64_t)cdb[5]) << 32) |
184                         (((uint64_t)cdb[6]) << 24) |
185                         (((uint64_t)cdb[7]) << 16) |
186                         (((uint64_t)cdb[8]) <<  8) |
187                         ((uint64_t)cdb[9]);
188                 break;
189         case WRITE_12:
190         case READ_12:
191                 cnt =   (((uint32_t)cdb[6]) <<  16) |
192                         (((uint32_t)cdb[7]) <<   8) |
193                         ((u_int32_t)cdb[8]);
194
195                 lba =   (((uint32_t)cdb[2]) << 24) |
196                         (((uint32_t)cdb[3]) << 16) |
197                         (((uint32_t)cdb[4]) <<  8) |
198                         ((uint32_t)cdb[5]);
199                 break;
200         case WRITE_10:
201         case READ_10:
202                 cnt =   (((uint32_t)cdb[7]) <<  8) |
203                         ((u_int32_t)cdb[8]);
204
205                 lba =   (((uint32_t)cdb[2]) << 24) |
206                         (((uint32_t)cdb[3]) << 16) |
207                         (((uint32_t)cdb[4]) <<  8) |
208                         ((uint32_t)cdb[5]);
209                 break;
210         case WRITE_6:
211         case READ_6:
212                 cnt = cdb[4];
213                 if (cnt == 0) {
214                         cnt = 256;
215                 }
216                 lba =   (((uint32_t)cdb[1] & 0x1f) << 16) |
217                         (((uint32_t)cdb[2]) << 8) |
218                         ((uint32_t)cdb[3]);
219                 break;
220         default:
221                 return (-1);
222         }
223
224         if (lba + cnt > nblks) {
225                 return (-1);
226         }
227         *tl = cnt << blk_shift;
228         *offset = lba << blk_shift;
229         return (0);
230 }
231
232 void
233 vhba_default_cmd(struct ccb_scsiio *csio, lun_id_t max_lun, uint8_t *sparse_lun_map)
234 {
235         char junk[128];
236         const uint8_t niliqd[SHORT_INQUIRY_LENGTH] = {
237                 0x7f, 0x0, SCSI_REV_SPC3, 0x2, 32, 0, 0, 0x32,
238                 'P', 'A', 'N', 'A', 'S', 'A', 'S', ' ',
239                 'N', 'U', 'L', 'L', ' ', 'D', 'E', 'V',
240                 'I', 'C', 'E', ' ', ' ', ' ', ' ', ' ',
241                 '0', '0', '0', '1'
242         };
243         const uint8_t iqd[SHORT_INQUIRY_LENGTH] = {
244                 0, 0x0, SCSI_REV_SPC3, 0x2, 32, 0, 0, 0x32,
245                 'P', 'A', 'N', 'A', 'S', 'A', 'S', ' ',
246                 'V', 'I', 'R', 'T', ' ', 'M', 'E', 'M',
247                 'O', 'R', 'Y', ' ', 'D', 'I', 'S', 'K',
248                 '0', '0', '0', '1'
249         };
250         const uint8_t vp0data[6] = { 0, 0, 0, 0x2, 0, 0x80 };
251         const uint8_t vp80data[36] = { 0, 0x80, 0, 0x20 };
252         int i, attached_lun;
253         uint8_t *cdb, *ptr, status;
254         uint32_t data_len, nlun;
255
256         data_len = 0;
257         status = SCSI_STATUS_OK;
258
259         memset(&csio->sense_data, 0, sizeof (csio->sense_data));
260         cdb = csio->cdb_io.cdb_bytes;
261
262         attached_lun = 1;
263         if (csio->ccb_h.target_lun >= max_lun) {
264                 attached_lun = 0;
265         } else if (sparse_lun_map) {
266                 i = csio->ccb_h.target_lun & 0x7;
267                 if ((sparse_lun_map[csio->ccb_h.target_lun >> 3] & (1 << i)) == 0) {
268                         attached_lun = 0;
269                 }
270         }
271         if (attached_lun == 0 && cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) {
272                 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x25, 0x0);
273                 return;
274         }
275
276         switch (cdb[0]) {
277         case REQUEST_SENSE:
278                 data_len = csio->dxfer_len;
279                 if (cdb[4] < csio->dxfer_len)
280                         data_len = cdb[4];
281                 if (data_len) {
282                         memset(junk, 0, sizeof (junk));
283                         junk[0] = SSD_ERRCODE_VALID|SSD_CURRENT_ERROR;
284                         junk[2] = SSD_KEY_NO_SENSE;
285                         junk[7] = 10;
286                         memcpy(csio->data_ptr, junk,
287                             (data_len > sizeof junk)? sizeof junk : data_len);
288                 }
289                 csio->resid = csio->dxfer_len - data_len;
290                 break;
291         case INQUIRY:
292                 i = 0;
293                 if ((cdb[1] & 0x1f) == SI_EVPD) {
294                         if ((cdb[2] != 0 && cdb[2] != 0x80) || cdb[3] || cdb[5]) {
295                                 i = 1;
296                         }
297                 } else if ((cdb[1] & 0x1f) || cdb[2] || cdb[3] || cdb[5]) {
298                         i = 1;
299                 }
300                 if (i) {
301                         vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0);
302                         break;
303                 }
304                 if (attached_lun == 0) {
305                         if (cdb[1] & 0x1f) {
306                                 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0);
307                                 break;
308                         }
309                         memcpy(junk, niliqd, sizeof (niliqd));
310                         data_len = sizeof (niliqd);
311                 } else if (cdb[1] & 0x1f) {
312                         if (cdb[2] == 0) {
313                                 memcpy(junk, vp0data, sizeof (vp0data));
314                                 data_len = sizeof (vp0data);
315                         } else {
316                                 memcpy(junk, vp80data, sizeof (vp80data));
317                                 snprintf(&junk[4], sizeof (vp80data) - 4, "TGT%dLUN%d", csio->ccb_h.target_id, csio->ccb_h.target_lun);
318                                 for (i = 0; i < sizeof (vp80data); i++) {
319                                         if (junk[i] == 0) {
320                                                 junk[i] = ' ';
321                                         }
322                                 }
323                         }
324                         data_len = sizeof (vp80data);
325                 } else {
326                         memcpy(junk, iqd, sizeof (iqd));
327                         data_len = sizeof (iqd);
328                 }
329                 if (data_len > cdb[4]) {
330                         data_len = cdb[4];
331                 }
332                 if (data_len) {
333                         memcpy(csio->data_ptr, junk, data_len);
334                 }
335                 csio->resid = csio->dxfer_len - data_len;
336                 break;
337         case TEST_UNIT_READY:
338         case SYNCHRONIZE_CACHE:
339         case START_STOP:
340         case RESERVE:
341         case RELEASE:
342                 break;
343
344         case REPORT_LUNS:
345                 if (csio->dxfer_len) {
346                         memset(csio->data_ptr, 0, csio->dxfer_len);
347                 }
348                 ptr = NULL;
349                 for (nlun = i = 0; i < max_lun; i++) {
350                         if (sparse_lun_map) {
351                                 if ((sparse_lun_map[i >> 3] & (1 << (i & 0x7))) == 0) {
352                                         continue;
353                                 }
354                         }
355                         ptr = &csio->data_ptr[8 + ((nlun++) << 3)];
356                         if ((ptr + 8) > &csio->data_ptr[csio->dxfer_len]) {
357                                 continue;
358                         }
359                         if (i >= 256) {
360                                 ptr[0] = 0x40 | ((i >> 8) & 0x3f);
361                         }
362                         ptr[1] = i;
363                 }
364                 junk[0] = (nlun << 3) >> 24;
365                 junk[1] = (nlun << 3) >> 16;
366                 junk[2] = (nlun << 3) >> 8;
367                 junk[3] = (nlun << 3);
368                 memset(junk+4, 0, 4);
369                 if (csio->dxfer_len) {
370                         u_int amt;
371
372                         amt = MIN(csio->dxfer_len, 8);
373                         memcpy(csio->data_ptr, junk, amt);
374                         amt = MIN((nlun << 3) + 8,  csio->dxfer_len);
375                         csio->resid = csio->dxfer_len - amt;
376                 }
377                 break;
378
379         default:
380                 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x20, 0x0);
381                 break;
382         }
383 }
384
385 void
386 vhba_set_status(struct ccb_hdr *ccbh, cam_status status)
387 {
388         ccbh->status &= ~CAM_STATUS_MASK;
389         ccbh->status |= status;
390         if (status != CAM_REQ_CMP) {
391                 if ((ccbh->status & CAM_DEV_QFRZN) == 0) {
392                         ccbh->status |= CAM_DEV_QFRZN;
393                         xpt_freeze_devq(ccbh->path, 1);
394                 }
395         }
396 }
397
398 int
399 vhba_modprobe(module_t mod, int cmd, void *arg)
400 {
401         int error = 0;
402
403         switch (cmd) {
404         case MOD_LOAD:
405                 vhba = malloc(sizeof (*vhba), M_DEVBUF, M_WAITOK|M_ZERO);
406                 mtx_init(&vhba->lock, "vhba", NULL, MTX_DEF);
407                 error = vhba_attach(vhba);
408                 if (error) {
409                         mtx_destroy(&vhba->lock);
410                         free(vhba, M_DEVBUF);
411                 }
412                 break;
413         case MOD_UNLOAD:
414                 mtx_lock(&vhba->lock);
415                 if (TAILQ_FIRST(&vhba->done) || TAILQ_FIRST(&vhba->actv)) {
416                         error = EBUSY;
417                         mtx_unlock(&vhba->lock);
418                         break;
419                 }
420                 vhba_detach(vhba);
421                 mtx_unlock(&vhba->lock);
422                 mtx_destroy(&vhba->lock);
423                 free(vhba, M_DEVBUF);
424                 break;
425         default:
426                 error = EOPNOTSUPP;
427                 break;
428         }
429         return (error);
430 }