New upstream version 17.08
[deb_dpdk.git] / examples / vhost_scsi / scsi.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2010-2017 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 /**
35  * This work is largely based on the "vhost-user-scsi" implementation by
36  * SPDK(https://github.com/spdk/spdk).
37  */
38
39 #include <stdio.h>
40 #include <stdint.h>
41 #include <unistd.h>
42 #include <assert.h>
43 #include <ctype.h>
44 #include <string.h>
45 #include <stddef.h>
46
47 #include <rte_atomic.h>
48 #include <rte_cycles.h>
49 #include <rte_log.h>
50 #include <rte_malloc.h>
51 #include <rte_byteorder.h>
52
53 #include "vhost_scsi.h"
54 #include "scsi_spec.h"
55
56 #define INQ_OFFSET(field) (offsetof(struct scsi_cdb_inquiry_data, field) + \
57                           sizeof(((struct scsi_cdb_inquiry_data *)0x0)->field))
58
59 static void
60 vhost_strcpy_pad(void *dst, const char *src, size_t size, int pad)
61 {
62         size_t len;
63
64         len = strlen(src);
65         if (len < size) {
66                 memcpy(dst, src, len);
67                 memset((char *)dst + len, pad, size - len);
68         } else {
69                 memcpy(dst, src, size);
70         }
71 }
72
73 static int
74 vhost_hex2bin(char ch)
75 {
76         if ((ch >= '0') && (ch <= '9'))
77                 return ch - '0';
78         ch = tolower(ch);
79         if ((ch >= 'a') && (ch <= 'f'))
80                 return ch - 'a' + 10;
81         return (int)ch;
82 }
83
84 static void
85 vhost_bdev_scsi_set_naa_ieee_extended(const char *name, uint8_t *buf)
86 {
87         int i, value, count = 0;
88         uint64_t *temp64, local_value;
89
90         for (i = 0; (i < 16) && (name[i] != '\0'); i++) {
91                 value = vhost_hex2bin(name[i]);
92                 if (i % 2)
93                         buf[count++] |= value << 4;
94                 else
95                         buf[count] = value;
96         }
97
98         local_value = *(uint64_t *)buf;
99         /*
100          * see spc3r23 7.6.3.6.2,
101          *  NAA IEEE Extended identifer format
102          */
103         local_value &= 0x0fff000000ffffffull;
104         /* NAA 02, and 00 03 47 for IEEE Intel */
105         local_value |= 0x2000000347000000ull;
106
107         temp64 = (uint64_t *)buf;
108         *temp64 = rte_cpu_to_be_64(local_value);
109 }
110
111 static void
112 scsi_task_build_sense_data(struct vhost_scsi_task *task, int sk,
113                            int asc, int ascq)
114 {
115         uint8_t *cp;
116         int resp_code;
117
118         resp_code = 0x70; /* Current + Fixed format */
119
120         /* Sense Data */
121         cp = (uint8_t *)task->resp->sense;
122
123         /* VALID(7) RESPONSE CODE(6-0) */
124         cp[0] = 0x80 | resp_code;
125         /* Obsolete */
126         cp[1] = 0;
127         /* FILEMARK(7) EOM(6) ILI(5) SENSE KEY(3-0) */
128         cp[2] = sk & 0xf;
129         /* INFORMATION */
130         memset(&cp[3], 0, 4);
131
132         /* ADDITIONAL SENSE LENGTH */
133         cp[7] = 10;
134
135         /* COMMAND-SPECIFIC INFORMATION */
136         memset(&cp[8], 0, 4);
137         /* ADDITIONAL SENSE CODE */
138         cp[12] = asc;
139         /* ADDITIONAL SENSE CODE QUALIFIER */
140         cp[13] = ascq;
141         /* FIELD REPLACEABLE UNIT CODE */
142         cp[14] = 0;
143
144         /* SKSV(7) SENSE KEY SPECIFIC(6-0,7-0,7-0) */
145         cp[15] = 0;
146         cp[16] = 0;
147         cp[17] = 0;
148
149         /* SenseLength */
150         task->resp->sense_len = 18;
151 }
152
153 static void
154 scsi_task_set_status(struct vhost_scsi_task *task, int sc, int sk,
155                      int asc, int ascq)
156 {
157         if (sc == SCSI_STATUS_CHECK_CONDITION)
158                 scsi_task_build_sense_data(task, sk, asc, ascq);
159         task->resp->status = sc;
160 }
161
162 static int
163 vhost_bdev_scsi_inquiry_command(struct vhost_block_dev *bdev,
164                                 struct vhost_scsi_task *task)
165 {
166         int hlen = 0;
167         uint32_t alloc_len = 0;
168         uint16_t len = 0;
169         uint16_t *temp16;
170         int pc;
171         int pd;
172         int evpd;
173         int i;
174         uint8_t *buf;
175         struct scsi_cdb_inquiry *inq;
176
177         inq = (struct scsi_cdb_inquiry *)task->req->cdb;
178
179         assert(task->iovs_cnt == 1);
180
181         /* At least 36Bytes for inquiry command */
182         if (task->data_len < 0x24)
183                 goto inq_error;
184
185         pd = SPC_PERIPHERAL_DEVICE_TYPE_DISK;
186         pc = inq->page_code;
187         evpd = inq->evpd & 0x1;
188
189         if (!evpd && pc)
190                 goto inq_error;
191
192         if (evpd) {
193                 struct scsi_vpd_page *vpage = (struct scsi_vpd_page *)
194                                               task->iovs[0].iov_base;
195
196                 /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
197                 vpage->peripheral = pd;
198                 /* PAGE CODE */
199                 vpage->page_code = pc;
200
201                 switch (pc) {
202                 case SPC_VPD_SUPPORTED_VPD_PAGES:
203                         hlen = 4;
204                         vpage->params[0] = SPC_VPD_SUPPORTED_VPD_PAGES;
205                         vpage->params[1] = SPC_VPD_UNIT_SERIAL_NUMBER;
206                         vpage->params[2] = SPC_VPD_DEVICE_IDENTIFICATION;
207                         len = 3;
208                         /* PAGE LENGTH */
209                         vpage->alloc_len = rte_cpu_to_be_16(len);
210                         break;
211                 case SPC_VPD_UNIT_SERIAL_NUMBER:
212                         hlen = 4;
213                         strncpy((char *)vpage->params, bdev->name, 32);
214                         vpage->alloc_len = rte_cpu_to_be_16(32);
215                         break;
216                 case SPC_VPD_DEVICE_IDENTIFICATION:
217                         buf = vpage->params;
218                         struct scsi_desig_desc *desig;
219
220                         hlen = 4;
221                         /* NAA designator */
222                         desig = (struct scsi_desig_desc *)buf;
223                         desig->code_set = SPC_VPD_CODE_SET_BINARY;
224                         desig->protocol_id = SPC_PROTOCOL_IDENTIFIER_ISCSI;
225                         desig->type = SPC_VPD_IDENTIFIER_TYPE_NAA;
226                         desig->association = SPC_VPD_ASSOCIATION_LOGICAL_UNIT;
227                         desig->reserved0 = 0;
228                         desig->piv = 1;
229                         desig->reserved1 = 0;
230                         desig->len = 8;
231                         vhost_bdev_scsi_set_naa_ieee_extended(bdev->name,
232                                                               desig->desig);
233                         len = sizeof(struct scsi_desig_desc) + 8;
234
235                         buf += sizeof(struct scsi_desig_desc) + desig->len;
236
237                         /* T10 Vendor ID designator */
238                         desig = (struct scsi_desig_desc *)buf;
239                         desig->code_set = SPC_VPD_CODE_SET_ASCII;
240                         desig->protocol_id = SPC_PROTOCOL_IDENTIFIER_ISCSI;
241                         desig->type = SPC_VPD_IDENTIFIER_TYPE_T10_VENDOR_ID;
242                         desig->association = SPC_VPD_ASSOCIATION_LOGICAL_UNIT;
243                         desig->reserved0 = 0;
244                         desig->piv = 1;
245                         desig->reserved1 = 0;
246                         desig->len = 8 + 16 + 32;
247                         strncpy((char *)desig->desig, "INTEL", 8);
248                         vhost_strcpy_pad((char *)&desig->desig[8],
249                                          bdev->product_name, 16, ' ');
250                         strncpy((char *)&desig->desig[24], bdev->name, 32);
251                         len += sizeof(struct scsi_desig_desc) + 8 + 16 + 32;
252
253                         buf += sizeof(struct scsi_desig_desc) + desig->len;
254
255                         /* SCSI Device Name designator */
256                         desig = (struct scsi_desig_desc *)buf;
257                         desig->code_set = SPC_VPD_CODE_SET_UTF8;
258                         desig->protocol_id = SPC_PROTOCOL_IDENTIFIER_ISCSI;
259                         desig->type = SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME;
260                         desig->association = SPC_VPD_ASSOCIATION_TARGET_DEVICE;
261                         desig->reserved0 = 0;
262                         desig->piv = 1;
263                         desig->reserved1 = 0;
264                         desig->len = snprintf((char *)desig->desig,
265                                               255, "%s", bdev->name);
266                         len += sizeof(struct scsi_desig_desc) + desig->len;
267
268                         buf += sizeof(struct scsi_desig_desc) + desig->len;
269                         vpage->alloc_len = rte_cpu_to_be_16(len);
270                         break;
271                 default:
272                         goto inq_error;
273                 }
274
275         } else {
276                 struct scsi_cdb_inquiry_data *inqdata =
277                         (struct scsi_cdb_inquiry_data *)task->iovs[0].iov_base;
278                 /* Standard INQUIRY data */
279                 /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */
280                 inqdata->peripheral = pd;
281
282                 /* RMB(7) */
283                 inqdata->rmb = 0;
284
285                 /* VERSION */
286                 /* See SPC3/SBC2/MMC4/SAM2 for more details */
287                 inqdata->version = SPC_VERSION_SPC3;
288
289                 /* NORMACA(5) HISUP(4) RESPONSE DATA FORMAT(3-0) */
290                 /* format 2 */ /* hierarchical support */
291                 inqdata->response = 2 | 1 << 4;
292
293                 hlen = 5;
294
295                 /* SCCS(7) ACC(6) TPGS(5-4) 3PC(3) PROTECT(0) */
296                 /* Not support TPGS */
297                 inqdata->flags = 0;
298
299                 /* MULTIP */
300                 inqdata->flags2 = 0x10;
301
302                 /* WBUS16(5) SYNC(4) LINKED(3) CMDQUE(1) VS(0) */
303                 /* CMDQUE */
304                 inqdata->flags3 = 0x2;
305
306                 /* T10 VENDOR IDENTIFICATION */
307                 strncpy((char *)inqdata->t10_vendor_id, "INTEL", 8);
308
309                 /* PRODUCT IDENTIFICATION */
310                 strncpy((char *)inqdata->product_id, bdev->product_name, 16);
311
312                 /* PRODUCT REVISION LEVEL */
313                 strncpy((char *)inqdata->product_rev, "0001", 4);
314
315                 /* Standard inquiry data ends here. Only populate
316                  * remaining fields if alloc_len indicates enough
317                  * space to hold it.
318                  */
319                 len = INQ_OFFSET(product_rev) - 5;
320
321                 if (alloc_len >= INQ_OFFSET(vendor)) {
322                         /* Vendor specific */
323                         memset(inqdata->vendor, 0x20, 20);
324                         len += sizeof(inqdata->vendor);
325                 }
326
327                 if (alloc_len >= INQ_OFFSET(ius)) {
328                         /* CLOCKING(3-2) QAS(1) IUS(0) */
329                         inqdata->ius = 0;
330                         len += sizeof(inqdata->ius);
331                 }
332
333                 if (alloc_len >= INQ_OFFSET(reserved)) {
334                         /* Reserved */
335                         inqdata->reserved = 0;
336                         len += sizeof(inqdata->reserved);
337                 }
338
339                 /* VERSION DESCRIPTOR 1-8 */
340                 if (alloc_len >= INQ_OFFSET(reserved) + 2) {
341                         temp16 = (uint16_t *)&inqdata->desc[0];
342                         *temp16 = rte_cpu_to_be_16(0x0960);
343                         len += 2;
344                 }
345
346                 if (alloc_len >= INQ_OFFSET(reserved) + 4) {
347                         /* SPC-3 (no version claimed) */
348                         temp16 = (uint16_t *)&inqdata->desc[2];
349                         *temp16 = rte_cpu_to_be_16(0x0300);
350                         len += 2;
351                 }
352
353                 if (alloc_len >= INQ_OFFSET(reserved) + 6) {
354                         /* SBC-2 (no version claimed) */
355                         temp16 = (uint16_t *)&inqdata->desc[4];
356                         *temp16 = rte_cpu_to_be_16(0x0320);
357                         len += 2;
358                 }
359
360                 if (alloc_len >= INQ_OFFSET(reserved) + 8) {
361                         /* SAM-2 (no version claimed) */
362                         temp16 = (uint16_t *)&inqdata->desc[6];
363                         *temp16 = rte_cpu_to_be_16(0x0040);
364                         len += 2;
365                 }
366
367                 if (alloc_len > INQ_OFFSET(reserved) + 8) {
368                         i = alloc_len - (INQ_OFFSET(reserved) + 8);
369                         if (i > 30)
370                                 i = 30;
371                         memset(&inqdata->desc[8], 0, i);
372                         len += i;
373                 }
374
375                 /* ADDITIONAL LENGTH */
376                 inqdata->add_len = len;
377         }
378
379         /* STATUS GOOD */
380         scsi_task_set_status(task, SCSI_STATUS_GOOD, 0, 0, 0);
381         return hlen + len;
382
383 inq_error:
384         scsi_task_set_status(task, SCSI_STATUS_CHECK_CONDITION,
385                                      SCSI_SENSE_ILLEGAL_REQUEST,
386                                      SCSI_ASC_INVALID_FIELD_IN_CDB,
387                                      SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
388         return 0;
389 }
390
391 static int
392 vhost_bdev_scsi_readwrite(struct vhost_block_dev *bdev,
393                           struct vhost_scsi_task *task,
394                           uint64_t lba, __rte_unused uint32_t xfer_len)
395 {
396         uint32_t i;
397         uint64_t offset;
398         uint32_t nbytes = 0;
399
400         offset = lba * bdev->blocklen;
401
402         for (i = 0; i < task->iovs_cnt; i++) {
403                 if (task->dxfer_dir == SCSI_DIR_TO_DEV)
404                         memcpy(bdev->data + offset, task->iovs[i].iov_base,
405                                task->iovs[i].iov_len);
406                 else
407                         memcpy(task->iovs[i].iov_base, bdev->data + offset,
408                                task->iovs[i].iov_len);
409                 offset += task->iovs[i].iov_len;
410                 nbytes += task->iovs[i].iov_len;
411         }
412
413         return nbytes;
414 }
415
416 static int
417 vhost_bdev_scsi_process_block(struct vhost_block_dev *bdev,
418                               struct vhost_scsi_task *task)
419 {
420         uint64_t lba, *temp64;
421         uint32_t xfer_len, *temp32;
422         uint16_t *temp16;
423         uint8_t *cdb = (uint8_t *)task->req->cdb;
424
425         switch (cdb[0]) {
426         case SBC_READ_6:
427         case SBC_WRITE_6:
428                 lba = (uint64_t)cdb[1] << 16;
429                 lba |= (uint64_t)cdb[2] << 8;
430                 lba |= (uint64_t)cdb[3];
431                 xfer_len = cdb[4];
432                 if (xfer_len == 0)
433                         xfer_len = 256;
434                 return vhost_bdev_scsi_readwrite(bdev, task, lba, xfer_len);
435
436         case SBC_READ_10:
437         case SBC_WRITE_10:
438                 temp32 = (uint32_t *)&cdb[2];
439                 lba = rte_be_to_cpu_32(*temp32);
440                 temp16 = (uint16_t *)&cdb[7];
441                 xfer_len = rte_be_to_cpu_16(*temp16);
442                 return vhost_bdev_scsi_readwrite(bdev, task, lba, xfer_len);
443
444         case SBC_READ_12:
445         case SBC_WRITE_12:
446                 temp32 = (uint32_t *)&cdb[2];
447                 lba = rte_be_to_cpu_32(*temp32);
448                 temp32 = (uint32_t *)&cdb[6];
449                 xfer_len = rte_be_to_cpu_32(*temp32);
450                 return vhost_bdev_scsi_readwrite(bdev, task, lba, xfer_len);
451
452         case SBC_READ_16:
453         case SBC_WRITE_16:
454                 temp64 = (uint64_t *)&cdb[2];
455                 lba = rte_be_to_cpu_64(*temp64);
456                 temp32 = (uint32_t *)&cdb[10];
457                 xfer_len = rte_be_to_cpu_32(*temp32);
458                 return vhost_bdev_scsi_readwrite(bdev, task, lba, xfer_len);
459
460         case SBC_READ_CAPACITY_10: {
461                 uint8_t buffer[8];
462
463                 if (bdev->blockcnt - 1 > 0xffffffffULL)
464                         memset(buffer, 0xff, 4);
465                 else {
466                         temp32 = (uint32_t *)buffer;
467                         *temp32 = rte_cpu_to_be_32(bdev->blockcnt - 1);
468                 }
469                 temp32 = (uint32_t *)&buffer[4];
470                 *temp32 = rte_cpu_to_be_32(bdev->blocklen);
471                 memcpy(task->iovs[0].iov_base, buffer, sizeof(buffer));
472                 task->resp->status = SCSI_STATUS_GOOD;
473                 return sizeof(buffer);
474         }
475
476         case SBC_SYNCHRONIZE_CACHE_10:
477         case SBC_SYNCHRONIZE_CACHE_16:
478                 task->resp->status = SCSI_STATUS_GOOD;
479                 return 0;
480         }
481
482         scsi_task_set_status(task, SCSI_STATUS_CHECK_CONDITION,
483                              SCSI_SENSE_ILLEGAL_REQUEST,
484                              SCSI_ASC_INVALID_FIELD_IN_CDB,
485                              SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
486         return 0;
487 }
488
489 int
490 vhost_bdev_process_scsi_commands(struct vhost_block_dev *bdev,
491                                  struct vhost_scsi_task *task)
492 {
493         int len;
494         uint8_t *data;
495         uint64_t *temp64, fmt_lun = 0;
496         uint32_t *temp32;
497         const uint8_t *lun;
498         uint8_t *cdb = (uint8_t *)task->req->cdb;
499
500         lun = (const uint8_t *)task->req->lun;
501         /* only 1 LUN supported */
502         if (lun[0] != 1 || lun[1] >= 1)
503                 return -1;
504
505         switch (cdb[0]) {
506         case SPC_INQUIRY:
507                 len = vhost_bdev_scsi_inquiry_command(bdev, task);
508                 task->data_len = len;
509                 break;
510         case SPC_REPORT_LUNS:
511                 data = (uint8_t *)task->iovs[0].iov_base;
512                 fmt_lun |= (0x0ULL & 0x00ffULL) << 48;
513                 temp64 = (uint64_t *)&data[8];
514                 *temp64 = rte_cpu_to_be_64(fmt_lun);
515                 temp32 = (uint32_t *)data;
516                 *temp32 = rte_cpu_to_be_32(8);
517                 task->data_len = 16;
518                 scsi_task_set_status(task, SCSI_STATUS_GOOD, 0, 0, 0);
519                 break;
520         case SPC_MODE_SELECT_6:
521         case SPC_MODE_SELECT_10:
522                 /* don't support it now */
523                 scsi_task_set_status(task, SCSI_STATUS_GOOD, 0, 0, 0);
524                 break;
525         case SPC_MODE_SENSE_6:
526         case SPC_MODE_SENSE_10:
527                 /* don't support it now */
528                 scsi_task_set_status(task, SCSI_STATUS_GOOD, 0, 0, 0);
529                 break;
530         case SPC_TEST_UNIT_READY:
531                 scsi_task_set_status(task, SCSI_STATUS_GOOD, 0, 0, 0);
532                 break;
533         default:
534                 len = vhost_bdev_scsi_process_block(bdev, task);
535                 task->data_len = len;
536         }
537
538         return 0;
539 }