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