xref: /openbmc/qemu/hw/scsi/scsi-generic.c (revision 9be385980d37e8f4fd33f605f5fb1c3d144170a8)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * Generic SCSI Device support
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (c) 2007 Bull S.A.S.
549ab747fSPaolo Bonzini  * Based on code by Paul Brook
649ab747fSPaolo Bonzini  * Based on code by Fabrice Bellard
749ab747fSPaolo Bonzini  *
849ab747fSPaolo Bonzini  * Written by Laurent Vivier <Laurent.Vivier@bull.net>
949ab747fSPaolo Bonzini  *
1049ab747fSPaolo Bonzini  * This code is licensed under the LGPL.
1149ab747fSPaolo Bonzini  *
1249ab747fSPaolo Bonzini  */
1349ab747fSPaolo Bonzini 
14a4ab4792SPeter Maydell #include "qemu/osdep.h"
15da34e65cSMarkus Armbruster #include "qapi/error.h"
1649ab747fSPaolo Bonzini #include "qemu-common.h"
1749ab747fSPaolo Bonzini #include "qemu/error-report.h"
1849ab747fSPaolo Bonzini #include "hw/scsi/scsi.h"
194be74634SMarkus Armbruster #include "sysemu/block-backend.h"
2049ab747fSPaolo Bonzini #include "sysemu/blockdev.h"
2149ab747fSPaolo Bonzini 
2249ab747fSPaolo Bonzini #ifdef __linux__
2349ab747fSPaolo Bonzini 
2449ab747fSPaolo Bonzini //#define DEBUG_SCSI
2549ab747fSPaolo Bonzini 
2649ab747fSPaolo Bonzini #ifdef DEBUG_SCSI
2749ab747fSPaolo Bonzini #define DPRINTF(fmt, ...) \
2849ab747fSPaolo Bonzini do { printf("scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
2949ab747fSPaolo Bonzini #else
3049ab747fSPaolo Bonzini #define DPRINTF(fmt, ...) do {} while(0)
3149ab747fSPaolo Bonzini #endif
3249ab747fSPaolo Bonzini 
3349ab747fSPaolo Bonzini #define BADF(fmt, ...) \
3449ab747fSPaolo Bonzini do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
3549ab747fSPaolo Bonzini 
3649ab747fSPaolo Bonzini #include <scsi/sg.h>
3749ab747fSPaolo Bonzini #include "block/scsi.h"
3849ab747fSPaolo Bonzini 
3949ab747fSPaolo Bonzini #define SG_ERR_DRIVER_TIMEOUT  0x06
4049ab747fSPaolo Bonzini #define SG_ERR_DRIVER_SENSE    0x08
4149ab747fSPaolo Bonzini 
4249ab747fSPaolo Bonzini #define SG_ERR_DID_OK          0x00
4349ab747fSPaolo Bonzini #define SG_ERR_DID_NO_CONNECT  0x01
4449ab747fSPaolo Bonzini #define SG_ERR_DID_BUS_BUSY    0x02
4549ab747fSPaolo Bonzini #define SG_ERR_DID_TIME_OUT    0x03
4649ab747fSPaolo Bonzini 
4749ab747fSPaolo Bonzini #ifndef MAX_UINT
4849ab747fSPaolo Bonzini #define MAX_UINT ((unsigned int)-1)
4949ab747fSPaolo Bonzini #endif
5049ab747fSPaolo Bonzini 
5149ab747fSPaolo Bonzini typedef struct SCSIGenericReq {
5249ab747fSPaolo Bonzini     SCSIRequest req;
5349ab747fSPaolo Bonzini     uint8_t *buf;
5449ab747fSPaolo Bonzini     int buflen;
5549ab747fSPaolo Bonzini     int len;
5649ab747fSPaolo Bonzini     sg_io_hdr_t io_header;
5749ab747fSPaolo Bonzini } SCSIGenericReq;
5849ab747fSPaolo Bonzini 
5949ab747fSPaolo Bonzini static void scsi_generic_save_request(QEMUFile *f, SCSIRequest *req)
6049ab747fSPaolo Bonzini {
6149ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
6249ab747fSPaolo Bonzini 
6349ab747fSPaolo Bonzini     qemu_put_sbe32s(f, &r->buflen);
6449ab747fSPaolo Bonzini     if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
6549ab747fSPaolo Bonzini         assert(!r->req.sg);
6649ab747fSPaolo Bonzini         qemu_put_buffer(f, r->buf, r->req.cmd.xfer);
6749ab747fSPaolo Bonzini     }
6849ab747fSPaolo Bonzini }
6949ab747fSPaolo Bonzini 
7049ab747fSPaolo Bonzini static void scsi_generic_load_request(QEMUFile *f, SCSIRequest *req)
7149ab747fSPaolo Bonzini {
7249ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
7349ab747fSPaolo Bonzini 
7449ab747fSPaolo Bonzini     qemu_get_sbe32s(f, &r->buflen);
7549ab747fSPaolo Bonzini     if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
7649ab747fSPaolo Bonzini         assert(!r->req.sg);
7749ab747fSPaolo Bonzini         qemu_get_buffer(f, r->buf, r->req.cmd.xfer);
7849ab747fSPaolo Bonzini     }
7949ab747fSPaolo Bonzini }
8049ab747fSPaolo Bonzini 
8149ab747fSPaolo Bonzini static void scsi_free_request(SCSIRequest *req)
8249ab747fSPaolo Bonzini {
8349ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
8449ab747fSPaolo Bonzini 
8549ab747fSPaolo Bonzini     g_free(r->buf);
8649ab747fSPaolo Bonzini }
8749ab747fSPaolo Bonzini 
8849ab747fSPaolo Bonzini /* Helper function for command completion.  */
89fa0d653bSPaolo Bonzini static void scsi_command_complete_noio(SCSIGenericReq *r, int ret)
9049ab747fSPaolo Bonzini {
9149ab747fSPaolo Bonzini     int status;
9249ab747fSPaolo Bonzini 
93fa0d653bSPaolo Bonzini     assert(r->req.aiocb == NULL);
94fa0d653bSPaolo Bonzini 
956c25fa6cSFam Zheng     if (r->req.io_canceled) {
96d5776465SFam Zheng         scsi_req_cancel_complete(&r->req);
976c25fa6cSFam Zheng         goto done;
986c25fa6cSFam Zheng     }
9949ab747fSPaolo Bonzini     if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
10049ab747fSPaolo Bonzini         r->req.sense_len = r->io_header.sb_len_wr;
10149ab747fSPaolo Bonzini     }
10249ab747fSPaolo Bonzini 
10349ab747fSPaolo Bonzini     if (ret != 0) {
10449ab747fSPaolo Bonzini         switch (ret) {
10549ab747fSPaolo Bonzini         case -EDOM:
10649ab747fSPaolo Bonzini             status = TASK_SET_FULL;
10749ab747fSPaolo Bonzini             break;
10849ab747fSPaolo Bonzini         case -ENOMEM:
10949ab747fSPaolo Bonzini             status = CHECK_CONDITION;
11049ab747fSPaolo Bonzini             scsi_req_build_sense(&r->req, SENSE_CODE(TARGET_FAILURE));
11149ab747fSPaolo Bonzini             break;
11249ab747fSPaolo Bonzini         default:
11349ab747fSPaolo Bonzini             status = CHECK_CONDITION;
11449ab747fSPaolo Bonzini             scsi_req_build_sense(&r->req, SENSE_CODE(IO_ERROR));
11549ab747fSPaolo Bonzini             break;
11649ab747fSPaolo Bonzini         }
11749ab747fSPaolo Bonzini     } else {
11849ab747fSPaolo Bonzini         if (r->io_header.host_status == SG_ERR_DID_NO_CONNECT ||
11949ab747fSPaolo Bonzini             r->io_header.host_status == SG_ERR_DID_BUS_BUSY ||
12049ab747fSPaolo Bonzini             r->io_header.host_status == SG_ERR_DID_TIME_OUT ||
12149ab747fSPaolo Bonzini             (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT)) {
12249ab747fSPaolo Bonzini             status = BUSY;
12349ab747fSPaolo Bonzini             BADF("Driver Timeout\n");
12449ab747fSPaolo Bonzini         } else if (r->io_header.host_status) {
12549ab747fSPaolo Bonzini             status = CHECK_CONDITION;
12649ab747fSPaolo Bonzini             scsi_req_build_sense(&r->req, SENSE_CODE(I_T_NEXUS_LOSS));
12749ab747fSPaolo Bonzini         } else if (r->io_header.status) {
12849ab747fSPaolo Bonzini             status = r->io_header.status;
12949ab747fSPaolo Bonzini         } else if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
13049ab747fSPaolo Bonzini             status = CHECK_CONDITION;
13149ab747fSPaolo Bonzini         } else {
13249ab747fSPaolo Bonzini             status = GOOD;
13349ab747fSPaolo Bonzini         }
13449ab747fSPaolo Bonzini     }
13549ab747fSPaolo Bonzini     DPRINTF("Command complete 0x%p tag=0x%x status=%d\n",
13649ab747fSPaolo Bonzini             r, r->req.tag, status);
13749ab747fSPaolo Bonzini 
13849ab747fSPaolo Bonzini     scsi_req_complete(&r->req, status);
1396c25fa6cSFam Zheng done:
14049ab747fSPaolo Bonzini     scsi_req_unref(&r->req);
14149ab747fSPaolo Bonzini }
14249ab747fSPaolo Bonzini 
143fa0d653bSPaolo Bonzini static void scsi_command_complete(void *opaque, int ret)
144fa0d653bSPaolo Bonzini {
145fa0d653bSPaolo Bonzini     SCSIGenericReq *r = (SCSIGenericReq *)opaque;
146fa0d653bSPaolo Bonzini 
147fa0d653bSPaolo Bonzini     assert(r->req.aiocb != NULL);
148fa0d653bSPaolo Bonzini     r->req.aiocb = NULL;
149fa0d653bSPaolo Bonzini     scsi_command_complete_noio(r, ret);
150fa0d653bSPaolo Bonzini }
151fa0d653bSPaolo Bonzini 
1524be74634SMarkus Armbruster static int execute_command(BlockBackend *blk,
15349ab747fSPaolo Bonzini                            SCSIGenericReq *r, int direction,
154097310b5SMarkus Armbruster                            BlockCompletionFunc *complete)
15549ab747fSPaolo Bonzini {
15649ab747fSPaolo Bonzini     r->io_header.interface_id = 'S';
15749ab747fSPaolo Bonzini     r->io_header.dxfer_direction = direction;
15849ab747fSPaolo Bonzini     r->io_header.dxferp = r->buf;
15949ab747fSPaolo Bonzini     r->io_header.dxfer_len = r->buflen;
16049ab747fSPaolo Bonzini     r->io_header.cmdp = r->req.cmd.buf;
16149ab747fSPaolo Bonzini     r->io_header.cmd_len = r->req.cmd.len;
16249ab747fSPaolo Bonzini     r->io_header.mx_sb_len = sizeof(r->req.sense);
16349ab747fSPaolo Bonzini     r->io_header.sbp = r->req.sense;
16449ab747fSPaolo Bonzini     r->io_header.timeout = MAX_UINT;
16549ab747fSPaolo Bonzini     r->io_header.usr_ptr = r;
16649ab747fSPaolo Bonzini     r->io_header.flags |= SG_FLAG_DIRECT_IO;
16749ab747fSPaolo Bonzini 
1684be74634SMarkus Armbruster     r->req.aiocb = blk_aio_ioctl(blk, SG_IO, &r->io_header, complete, r);
169d836f8d3SPavel Hrdina     if (r->req.aiocb == NULL) {
170d836f8d3SPavel Hrdina         return -EIO;
171d836f8d3SPavel Hrdina     }
17249ab747fSPaolo Bonzini 
17349ab747fSPaolo Bonzini     return 0;
17449ab747fSPaolo Bonzini }
17549ab747fSPaolo Bonzini 
17649ab747fSPaolo Bonzini static void scsi_read_complete(void * opaque, int ret)
17749ab747fSPaolo Bonzini {
17849ab747fSPaolo Bonzini     SCSIGenericReq *r = (SCSIGenericReq *)opaque;
17949ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
18049ab747fSPaolo Bonzini     int len;
18149ab747fSPaolo Bonzini 
182fa0d653bSPaolo Bonzini     assert(r->req.aiocb != NULL);
18349ab747fSPaolo Bonzini     r->req.aiocb = NULL;
184fa0d653bSPaolo Bonzini 
1856c25fa6cSFam Zheng     if (ret || r->req.io_canceled) {
186fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, ret);
18749ab747fSPaolo Bonzini         return;
18849ab747fSPaolo Bonzini     }
189fa0d653bSPaolo Bonzini 
19049ab747fSPaolo Bonzini     len = r->io_header.dxfer_len - r->io_header.resid;
19149ab747fSPaolo Bonzini     DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len);
19249ab747fSPaolo Bonzini 
19349ab747fSPaolo Bonzini     r->len = -1;
19449ab747fSPaolo Bonzini     if (len == 0) {
195fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, 0);
196fa0d653bSPaolo Bonzini         return;
197fa0d653bSPaolo Bonzini     }
198fa0d653bSPaolo Bonzini 
19949ab747fSPaolo Bonzini     /* Snoop READ CAPACITY output to set the blocksize.  */
20053254e56SPaolo Bonzini     if (r->req.cmd.buf[0] == READ_CAPACITY_10 &&
20153254e56SPaolo Bonzini         (ldl_be_p(&r->buf[0]) != 0xffffffffU || s->max_lba == 0)) {
20249ab747fSPaolo Bonzini         s->blocksize = ldl_be_p(&r->buf[4]);
20353254e56SPaolo Bonzini         s->max_lba = ldl_be_p(&r->buf[0]) & 0xffffffffULL;
20449ab747fSPaolo Bonzini     } else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 &&
20549ab747fSPaolo Bonzini                (r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) {
20649ab747fSPaolo Bonzini         s->blocksize = ldl_be_p(&r->buf[8]);
20749ab747fSPaolo Bonzini         s->max_lba = ldq_be_p(&r->buf[0]);
20849ab747fSPaolo Bonzini     }
2094be74634SMarkus Armbruster     blk_set_guest_block_size(s->conf.blk, s->blocksize);
21049ab747fSPaolo Bonzini 
2110eb2baebSPaolo Bonzini     /* Patch MODE SENSE device specific parameters if the BDS is opened
2120eb2baebSPaolo Bonzini      * readonly.
2130eb2baebSPaolo Bonzini      */
2140eb2baebSPaolo Bonzini     if ((s->type == TYPE_DISK || s->type == TYPE_TAPE) &&
2150eb2baebSPaolo Bonzini         blk_is_read_only(s->conf.blk) &&
2160eb2baebSPaolo Bonzini         (r->req.cmd.buf[0] == MODE_SENSE ||
2170eb2baebSPaolo Bonzini          r->req.cmd.buf[0] == MODE_SENSE_10) &&
2180eb2baebSPaolo Bonzini         (r->req.cmd.buf[1] & 0x8) == 0) {
2190eb2baebSPaolo Bonzini         if (r->req.cmd.buf[0] == MODE_SENSE) {
2200eb2baebSPaolo Bonzini             r->buf[2] |= 0x80;
2210eb2baebSPaolo Bonzini         } else  {
2220eb2baebSPaolo Bonzini             r->buf[3] |= 0x80;
2230eb2baebSPaolo Bonzini         }
2240eb2baebSPaolo Bonzini     }
225063143d5SFam Zheng     if (s->type == TYPE_DISK &&
226063143d5SFam Zheng         r->req.cmd.buf[0] == INQUIRY &&
227063143d5SFam Zheng         r->req.cmd.buf[2] == 0xb0) {
228063143d5SFam Zheng         uint32_t max_xfer_len = blk_get_max_transfer_length(s->conf.blk);
229063143d5SFam Zheng         if (max_xfer_len) {
230063143d5SFam Zheng             stl_be_p(&r->buf[8], max_xfer_len);
231063143d5SFam Zheng             /* Also take care of the opt xfer len. */
232063143d5SFam Zheng             if (ldl_be_p(&r->buf[12]) > max_xfer_len) {
233063143d5SFam Zheng                 stl_be_p(&r->buf[12], max_xfer_len);
234063143d5SFam Zheng             }
235063143d5SFam Zheng         }
236063143d5SFam Zheng     }
23749ab747fSPaolo Bonzini     scsi_req_data(&r->req, len);
23849ab747fSPaolo Bonzini     scsi_req_unref(&r->req);
23949ab747fSPaolo Bonzini }
24049ab747fSPaolo Bonzini 
24149ab747fSPaolo Bonzini /* Read more data from scsi device into buffer.  */
24249ab747fSPaolo Bonzini static void scsi_read_data(SCSIRequest *req)
24349ab747fSPaolo Bonzini {
24449ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
24549ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
24649ab747fSPaolo Bonzini     int ret;
24749ab747fSPaolo Bonzini 
24849ab747fSPaolo Bonzini     DPRINTF("scsi_read_data 0x%x\n", req->tag);
24949ab747fSPaolo Bonzini 
25049ab747fSPaolo Bonzini     /* The request is used as the AIO opaque value, so add a ref.  */
25149ab747fSPaolo Bonzini     scsi_req_ref(&r->req);
25249ab747fSPaolo Bonzini     if (r->len == -1) {
253fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, 0);
25449ab747fSPaolo Bonzini         return;
25549ab747fSPaolo Bonzini     }
25649ab747fSPaolo Bonzini 
2574be74634SMarkus Armbruster     ret = execute_command(s->conf.blk, r, SG_DXFER_FROM_DEV,
2584be74634SMarkus Armbruster                           scsi_read_complete);
25949ab747fSPaolo Bonzini     if (ret < 0) {
260fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, ret);
26149ab747fSPaolo Bonzini     }
26249ab747fSPaolo Bonzini }
26349ab747fSPaolo Bonzini 
26449ab747fSPaolo Bonzini static void scsi_write_complete(void * opaque, int ret)
26549ab747fSPaolo Bonzini {
26649ab747fSPaolo Bonzini     SCSIGenericReq *r = (SCSIGenericReq *)opaque;
26749ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
26849ab747fSPaolo Bonzini 
26949ab747fSPaolo Bonzini     DPRINTF("scsi_write_complete() ret = %d\n", ret);
270fa0d653bSPaolo Bonzini 
271fa0d653bSPaolo Bonzini     assert(r->req.aiocb != NULL);
27249ab747fSPaolo Bonzini     r->req.aiocb = NULL;
273fa0d653bSPaolo Bonzini 
2746c25fa6cSFam Zheng     if (ret || r->req.io_canceled) {
275fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, ret);
27649ab747fSPaolo Bonzini         return;
27749ab747fSPaolo Bonzini     }
27849ab747fSPaolo Bonzini 
27949ab747fSPaolo Bonzini     if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 &&
28049ab747fSPaolo Bonzini         s->type == TYPE_TAPE) {
28149ab747fSPaolo Bonzini         s->blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11];
28249ab747fSPaolo Bonzini         DPRINTF("block size %d\n", s->blocksize);
28349ab747fSPaolo Bonzini     }
28449ab747fSPaolo Bonzini 
285fa0d653bSPaolo Bonzini     scsi_command_complete_noio(r, ret);
28649ab747fSPaolo Bonzini }
28749ab747fSPaolo Bonzini 
28849ab747fSPaolo Bonzini /* Write data to a scsi device.  Returns nonzero on failure.
28949ab747fSPaolo Bonzini    The transfer may complete asynchronously.  */
29049ab747fSPaolo Bonzini static void scsi_write_data(SCSIRequest *req)
29149ab747fSPaolo Bonzini {
29249ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
29349ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
29449ab747fSPaolo Bonzini     int ret;
29549ab747fSPaolo Bonzini 
29649ab747fSPaolo Bonzini     DPRINTF("scsi_write_data 0x%x\n", req->tag);
29749ab747fSPaolo Bonzini     if (r->len == 0) {
29849ab747fSPaolo Bonzini         r->len = r->buflen;
29949ab747fSPaolo Bonzini         scsi_req_data(&r->req, r->len);
30049ab747fSPaolo Bonzini         return;
30149ab747fSPaolo Bonzini     }
30249ab747fSPaolo Bonzini 
30349ab747fSPaolo Bonzini     /* The request is used as the AIO opaque value, so add a ref.  */
30449ab747fSPaolo Bonzini     scsi_req_ref(&r->req);
3054be74634SMarkus Armbruster     ret = execute_command(s->conf.blk, r, SG_DXFER_TO_DEV, scsi_write_complete);
30649ab747fSPaolo Bonzini     if (ret < 0) {
307fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, ret);
30849ab747fSPaolo Bonzini     }
30949ab747fSPaolo Bonzini }
31049ab747fSPaolo Bonzini 
31149ab747fSPaolo Bonzini /* Return a pointer to the data buffer.  */
31249ab747fSPaolo Bonzini static uint8_t *scsi_get_buf(SCSIRequest *req)
31349ab747fSPaolo Bonzini {
31449ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
31549ab747fSPaolo Bonzini 
31649ab747fSPaolo Bonzini     return r->buf;
31749ab747fSPaolo Bonzini }
31849ab747fSPaolo Bonzini 
31949ab747fSPaolo Bonzini /* Execute a scsi command.  Returns the length of the data expected by the
32049ab747fSPaolo Bonzini    command.  This will be Positive for data transfers from the device
32149ab747fSPaolo Bonzini    (eg. disk reads), negative for transfers to the device (eg. disk writes),
32249ab747fSPaolo Bonzini    and zero if the command does not transfer any data.  */
32349ab747fSPaolo Bonzini 
32449ab747fSPaolo Bonzini static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
32549ab747fSPaolo Bonzini {
32649ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
32749ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
32849ab747fSPaolo Bonzini     int ret;
32949ab747fSPaolo Bonzini 
33049ab747fSPaolo Bonzini #ifdef DEBUG_SCSI
33149ab747fSPaolo Bonzini     {
33249ab747fSPaolo Bonzini         int i;
33349ab747fSPaolo Bonzini         for (i = 1; i < r->req.cmd.len; i++) {
33449ab747fSPaolo Bonzini             printf(" 0x%02x", cmd[i]);
33549ab747fSPaolo Bonzini         }
33649ab747fSPaolo Bonzini         printf("\n");
33749ab747fSPaolo Bonzini     }
33849ab747fSPaolo Bonzini #endif
33949ab747fSPaolo Bonzini 
34049ab747fSPaolo Bonzini     if (r->req.cmd.xfer == 0) {
34149ab747fSPaolo Bonzini         g_free(r->buf);
34249ab747fSPaolo Bonzini         r->buflen = 0;
34349ab747fSPaolo Bonzini         r->buf = NULL;
34449ab747fSPaolo Bonzini         /* The request is used as the AIO opaque value, so add a ref.  */
34549ab747fSPaolo Bonzini         scsi_req_ref(&r->req);
3464be74634SMarkus Armbruster         ret = execute_command(s->conf.blk, r, SG_DXFER_NONE,
3474be74634SMarkus Armbruster                               scsi_command_complete);
34849ab747fSPaolo Bonzini         if (ret < 0) {
349fa0d653bSPaolo Bonzini             scsi_command_complete_noio(r, ret);
35049ab747fSPaolo Bonzini             return 0;
35149ab747fSPaolo Bonzini         }
35249ab747fSPaolo Bonzini         return 0;
35349ab747fSPaolo Bonzini     }
35449ab747fSPaolo Bonzini 
35549ab747fSPaolo Bonzini     if (r->buflen != r->req.cmd.xfer) {
35649ab747fSPaolo Bonzini         g_free(r->buf);
35749ab747fSPaolo Bonzini         r->buf = g_malloc(r->req.cmd.xfer);
35849ab747fSPaolo Bonzini         r->buflen = r->req.cmd.xfer;
35949ab747fSPaolo Bonzini     }
36049ab747fSPaolo Bonzini 
36149ab747fSPaolo Bonzini     memset(r->buf, 0, r->buflen);
36249ab747fSPaolo Bonzini     r->len = r->req.cmd.xfer;
36349ab747fSPaolo Bonzini     if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
36449ab747fSPaolo Bonzini         r->len = 0;
36549ab747fSPaolo Bonzini         return -r->req.cmd.xfer;
36649ab747fSPaolo Bonzini     } else {
36749ab747fSPaolo Bonzini         return r->req.cmd.xfer;
36849ab747fSPaolo Bonzini     }
36949ab747fSPaolo Bonzini }
37049ab747fSPaolo Bonzini 
3719fd7e859SPaolo Bonzini static int read_naa_id(const uint8_t *p, uint64_t *p_wwn)
3729fd7e859SPaolo Bonzini {
3739fd7e859SPaolo Bonzini     int i;
3749fd7e859SPaolo Bonzini 
3759fd7e859SPaolo Bonzini     if ((p[1] & 0xF) == 3) {
3769fd7e859SPaolo Bonzini         /* NAA designator type */
3779fd7e859SPaolo Bonzini         if (p[3] != 8) {
3789fd7e859SPaolo Bonzini             return -EINVAL;
3799fd7e859SPaolo Bonzini         }
3809fd7e859SPaolo Bonzini         *p_wwn = ldq_be_p(p + 4);
3819fd7e859SPaolo Bonzini         return 0;
3829fd7e859SPaolo Bonzini     }
3839fd7e859SPaolo Bonzini 
3849fd7e859SPaolo Bonzini     if ((p[1] & 0xF) == 8) {
3859fd7e859SPaolo Bonzini         /* SCSI name string designator type */
3869fd7e859SPaolo Bonzini         if (p[3] < 20 || memcmp(&p[4], "naa.", 4)) {
3879fd7e859SPaolo Bonzini             return -EINVAL;
3889fd7e859SPaolo Bonzini         }
3899fd7e859SPaolo Bonzini         if (p[3] > 20 && p[24] != ',') {
3909fd7e859SPaolo Bonzini             return -EINVAL;
3919fd7e859SPaolo Bonzini         }
3929fd7e859SPaolo Bonzini         *p_wwn = 0;
3939fd7e859SPaolo Bonzini         for (i = 8; i < 24; i++) {
3949fd7e859SPaolo Bonzini             char c = toupper(p[i]);
3959fd7e859SPaolo Bonzini             c -= (c >= '0' && c <= '9' ? '0' : 'A' - 10);
3969fd7e859SPaolo Bonzini             *p_wwn = (*p_wwn << 4) | c;
3979fd7e859SPaolo Bonzini         }
3989fd7e859SPaolo Bonzini         return 0;
3999fd7e859SPaolo Bonzini     }
4009fd7e859SPaolo Bonzini 
4019fd7e859SPaolo Bonzini     return -EINVAL;
4029fd7e859SPaolo Bonzini }
4039fd7e859SPaolo Bonzini 
4049fd7e859SPaolo Bonzini void scsi_generic_read_device_identification(SCSIDevice *s)
4059fd7e859SPaolo Bonzini {
4069fd7e859SPaolo Bonzini     uint8_t cmd[6];
4079fd7e859SPaolo Bonzini     uint8_t buf[250];
4089fd7e859SPaolo Bonzini     uint8_t sensebuf[8];
4099fd7e859SPaolo Bonzini     sg_io_hdr_t io_header;
4109fd7e859SPaolo Bonzini     int ret;
4119fd7e859SPaolo Bonzini     int i, len;
4129fd7e859SPaolo Bonzini 
4139fd7e859SPaolo Bonzini     memset(cmd, 0, sizeof(cmd));
4149fd7e859SPaolo Bonzini     memset(buf, 0, sizeof(buf));
4159fd7e859SPaolo Bonzini     cmd[0] = INQUIRY;
4169fd7e859SPaolo Bonzini     cmd[1] = 1;
4179fd7e859SPaolo Bonzini     cmd[2] = 0x83;
4189fd7e859SPaolo Bonzini     cmd[4] = sizeof(buf);
4199fd7e859SPaolo Bonzini 
4209fd7e859SPaolo Bonzini     memset(&io_header, 0, sizeof(io_header));
4219fd7e859SPaolo Bonzini     io_header.interface_id = 'S';
4229fd7e859SPaolo Bonzini     io_header.dxfer_direction = SG_DXFER_FROM_DEV;
4239fd7e859SPaolo Bonzini     io_header.dxfer_len = sizeof(buf);
4249fd7e859SPaolo Bonzini     io_header.dxferp = buf;
4259fd7e859SPaolo Bonzini     io_header.cmdp = cmd;
4269fd7e859SPaolo Bonzini     io_header.cmd_len = sizeof(cmd);
4279fd7e859SPaolo Bonzini     io_header.mx_sb_len = sizeof(sensebuf);
4289fd7e859SPaolo Bonzini     io_header.sbp = sensebuf;
4299fd7e859SPaolo Bonzini     io_header.timeout = 6000; /* XXX */
4309fd7e859SPaolo Bonzini 
4319fd7e859SPaolo Bonzini     ret = blk_ioctl(s->conf.blk, SG_IO, &io_header);
4329fd7e859SPaolo Bonzini     if (ret < 0 || io_header.driver_status || io_header.host_status) {
4339fd7e859SPaolo Bonzini         return;
4349fd7e859SPaolo Bonzini     }
4359fd7e859SPaolo Bonzini 
4369fd7e859SPaolo Bonzini     len = MIN((buf[2] << 8) | buf[3], sizeof(buf) - 4);
4379fd7e859SPaolo Bonzini     for (i = 0; i + 3 <= len; ) {
4389fd7e859SPaolo Bonzini         const uint8_t *p = &buf[i + 4];
4399fd7e859SPaolo Bonzini         uint64_t wwn;
4409fd7e859SPaolo Bonzini 
4419fd7e859SPaolo Bonzini         if (i + (p[3] + 4) > len) {
4429fd7e859SPaolo Bonzini             break;
4439fd7e859SPaolo Bonzini         }
4449fd7e859SPaolo Bonzini 
4459fd7e859SPaolo Bonzini         if ((p[1] & 0x10) == 0) {
4469fd7e859SPaolo Bonzini             /* Associated with the logical unit */
4479fd7e859SPaolo Bonzini             if (read_naa_id(p, &wwn) == 0) {
4489fd7e859SPaolo Bonzini                 s->wwn = wwn;
4499fd7e859SPaolo Bonzini             }
4509fd7e859SPaolo Bonzini         } else if ((p[1] & 0x10) == 0x10) {
4519fd7e859SPaolo Bonzini             /* Associated with the target port */
4529fd7e859SPaolo Bonzini             if (read_naa_id(p, &wwn) == 0) {
4539fd7e859SPaolo Bonzini                 s->port_wwn = wwn;
4549fd7e859SPaolo Bonzini             }
4559fd7e859SPaolo Bonzini         }
4569fd7e859SPaolo Bonzini 
4579fd7e859SPaolo Bonzini         i += p[3] + 4;
4589fd7e859SPaolo Bonzini     }
4599fd7e859SPaolo Bonzini }
4609fd7e859SPaolo Bonzini 
4614be74634SMarkus Armbruster static int get_stream_blocksize(BlockBackend *blk)
46249ab747fSPaolo Bonzini {
46349ab747fSPaolo Bonzini     uint8_t cmd[6];
46449ab747fSPaolo Bonzini     uint8_t buf[12];
46549ab747fSPaolo Bonzini     uint8_t sensebuf[8];
46649ab747fSPaolo Bonzini     sg_io_hdr_t io_header;
46749ab747fSPaolo Bonzini     int ret;
46849ab747fSPaolo Bonzini 
46949ab747fSPaolo Bonzini     memset(cmd, 0, sizeof(cmd));
47049ab747fSPaolo Bonzini     memset(buf, 0, sizeof(buf));
47149ab747fSPaolo Bonzini     cmd[0] = MODE_SENSE;
47249ab747fSPaolo Bonzini     cmd[4] = sizeof(buf);
47349ab747fSPaolo Bonzini 
47449ab747fSPaolo Bonzini     memset(&io_header, 0, sizeof(io_header));
47549ab747fSPaolo Bonzini     io_header.interface_id = 'S';
47649ab747fSPaolo Bonzini     io_header.dxfer_direction = SG_DXFER_FROM_DEV;
47749ab747fSPaolo Bonzini     io_header.dxfer_len = sizeof(buf);
47849ab747fSPaolo Bonzini     io_header.dxferp = buf;
47949ab747fSPaolo Bonzini     io_header.cmdp = cmd;
48049ab747fSPaolo Bonzini     io_header.cmd_len = sizeof(cmd);
48149ab747fSPaolo Bonzini     io_header.mx_sb_len = sizeof(sensebuf);
48249ab747fSPaolo Bonzini     io_header.sbp = sensebuf;
48349ab747fSPaolo Bonzini     io_header.timeout = 6000; /* XXX */
48449ab747fSPaolo Bonzini 
4854be74634SMarkus Armbruster     ret = blk_ioctl(blk, SG_IO, &io_header);
48649ab747fSPaolo Bonzini     if (ret < 0 || io_header.driver_status || io_header.host_status) {
48749ab747fSPaolo Bonzini         return -1;
48849ab747fSPaolo Bonzini     }
48949ab747fSPaolo Bonzini     return (buf[9] << 16) | (buf[10] << 8) | buf[11];
49049ab747fSPaolo Bonzini }
49149ab747fSPaolo Bonzini 
49249ab747fSPaolo Bonzini static void scsi_generic_reset(DeviceState *dev)
49349ab747fSPaolo Bonzini {
49449ab747fSPaolo Bonzini     SCSIDevice *s = SCSI_DEVICE(dev);
49549ab747fSPaolo Bonzini 
49649ab747fSPaolo Bonzini     scsi_device_purge_requests(s, SENSE_CODE(RESET));
49749ab747fSPaolo Bonzini }
49849ab747fSPaolo Bonzini 
499a818a4b6SFam Zheng static void scsi_generic_realize(SCSIDevice *s, Error **errp)
50049ab747fSPaolo Bonzini {
5016ee143a0SPaolo Bonzini     int rc;
50249ab747fSPaolo Bonzini     int sg_version;
50349ab747fSPaolo Bonzini     struct sg_scsi_id scsiid;
50449ab747fSPaolo Bonzini 
5054be74634SMarkus Armbruster     if (!s->conf.blk) {
506a818a4b6SFam Zheng         error_setg(errp, "drive property not set");
507a818a4b6SFam Zheng         return;
50849ab747fSPaolo Bonzini     }
50949ab747fSPaolo Bonzini 
5104be74634SMarkus Armbruster     if (blk_get_on_error(s->conf.blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC) {
511a818a4b6SFam Zheng         error_setg(errp, "Device doesn't support drive option werror");
512a818a4b6SFam Zheng         return;
51349ab747fSPaolo Bonzini     }
5144be74634SMarkus Armbruster     if (blk_get_on_error(s->conf.blk, 1) != BLOCKDEV_ON_ERROR_REPORT) {
515a818a4b6SFam Zheng         error_setg(errp, "Device doesn't support drive option rerror");
516a818a4b6SFam Zheng         return;
51749ab747fSPaolo Bonzini     }
51849ab747fSPaolo Bonzini 
51949ab747fSPaolo Bonzini     /* check we are using a driver managing SG_IO (version 3 and after */
5204be74634SMarkus Armbruster     rc = blk_ioctl(s->conf.blk, SG_GET_VERSION_NUM, &sg_version);
5216ee143a0SPaolo Bonzini     if (rc < 0) {
522a818a4b6SFam Zheng         error_setg(errp, "cannot get SG_IO version number: %s.  "
5236ee143a0SPaolo Bonzini                          "Is this a SCSI device?",
5246ee143a0SPaolo Bonzini                          strerror(-rc));
525a818a4b6SFam Zheng         return;
52649ab747fSPaolo Bonzini     }
52749ab747fSPaolo Bonzini     if (sg_version < 30000) {
528a818a4b6SFam Zheng         error_setg(errp, "scsi generic interface too old");
529a818a4b6SFam Zheng         return;
53049ab747fSPaolo Bonzini     }
53149ab747fSPaolo Bonzini 
53249ab747fSPaolo Bonzini     /* get LUN of the /dev/sg? */
5334be74634SMarkus Armbruster     if (blk_ioctl(s->conf.blk, SG_GET_SCSI_ID, &scsiid)) {
534a818a4b6SFam Zheng         error_setg(errp, "SG_GET_SCSI_ID ioctl failed");
535a818a4b6SFam Zheng         return;
53649ab747fSPaolo Bonzini     }
53749ab747fSPaolo Bonzini 
53849ab747fSPaolo Bonzini     /* define device state */
53949ab747fSPaolo Bonzini     s->type = scsiid.scsi_type;
54049ab747fSPaolo Bonzini     DPRINTF("device type %d\n", s->type);
54149ab747fSPaolo Bonzini 
54249ab747fSPaolo Bonzini     switch (s->type) {
54349ab747fSPaolo Bonzini     case TYPE_TAPE:
5444be74634SMarkus Armbruster         s->blocksize = get_stream_blocksize(s->conf.blk);
54549ab747fSPaolo Bonzini         if (s->blocksize == -1) {
54649ab747fSPaolo Bonzini             s->blocksize = 0;
54749ab747fSPaolo Bonzini         }
54849ab747fSPaolo Bonzini         break;
54949ab747fSPaolo Bonzini 
55049ab747fSPaolo Bonzini         /* Make a guess for block devices, we'll fix it when the guest sends.
55149ab747fSPaolo Bonzini          * READ CAPACITY.  If they don't, they likely would assume these sizes
55249ab747fSPaolo Bonzini          * anyway. (TODO: they could also send MODE SENSE).
55349ab747fSPaolo Bonzini          */
55449ab747fSPaolo Bonzini     case TYPE_ROM:
55549ab747fSPaolo Bonzini     case TYPE_WORM:
55649ab747fSPaolo Bonzini         s->blocksize = 2048;
55749ab747fSPaolo Bonzini         break;
55849ab747fSPaolo Bonzini     default:
55949ab747fSPaolo Bonzini         s->blocksize = 512;
56049ab747fSPaolo Bonzini         break;
56149ab747fSPaolo Bonzini     }
56249ab747fSPaolo Bonzini 
56349ab747fSPaolo Bonzini     DPRINTF("block size %d\n", s->blocksize);
5649fd7e859SPaolo Bonzini 
5659fd7e859SPaolo Bonzini     scsi_generic_read_device_identification(s);
56649ab747fSPaolo Bonzini }
56749ab747fSPaolo Bonzini 
56849ab747fSPaolo Bonzini const SCSIReqOps scsi_generic_req_ops = {
56949ab747fSPaolo Bonzini     .size         = sizeof(SCSIGenericReq),
57049ab747fSPaolo Bonzini     .free_req     = scsi_free_request,
57149ab747fSPaolo Bonzini     .send_command = scsi_send_command,
57249ab747fSPaolo Bonzini     .read_data    = scsi_read_data,
57349ab747fSPaolo Bonzini     .write_data   = scsi_write_data,
57449ab747fSPaolo Bonzini     .get_buf      = scsi_get_buf,
57549ab747fSPaolo Bonzini     .load_request = scsi_generic_load_request,
57649ab747fSPaolo Bonzini     .save_request = scsi_generic_save_request,
57749ab747fSPaolo Bonzini };
57849ab747fSPaolo Bonzini 
57949ab747fSPaolo Bonzini static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
58049ab747fSPaolo Bonzini                                      uint8_t *buf, void *hba_private)
58149ab747fSPaolo Bonzini {
582*9be38598SEduardo Habkost     return scsi_req_alloc(&scsi_generic_req_ops, d, tag, lun, hba_private);
58349ab747fSPaolo Bonzini }
58449ab747fSPaolo Bonzini 
58549ab747fSPaolo Bonzini static Property scsi_generic_properties[] = {
5864be74634SMarkus Armbruster     DEFINE_PROP_DRIVE("drive", SCSIDevice, conf.blk),
58749ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
58849ab747fSPaolo Bonzini };
58949ab747fSPaolo Bonzini 
5903e7e180aSPaolo Bonzini static int scsi_generic_parse_cdb(SCSIDevice *dev, SCSICommand *cmd,
5913e7e180aSPaolo Bonzini                                   uint8_t *buf, void *hba_private)
5923e7e180aSPaolo Bonzini {
5933e7e180aSPaolo Bonzini     return scsi_bus_parse_cdb(dev, cmd, buf, hba_private);
5943e7e180aSPaolo Bonzini }
5953e7e180aSPaolo Bonzini 
59649ab747fSPaolo Bonzini static void scsi_generic_class_initfn(ObjectClass *klass, void *data)
59749ab747fSPaolo Bonzini {
59849ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
59949ab747fSPaolo Bonzini     SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
60049ab747fSPaolo Bonzini 
601a818a4b6SFam Zheng     sc->realize      = scsi_generic_realize;
60249ab747fSPaolo Bonzini     sc->alloc_req    = scsi_new_request;
6033e7e180aSPaolo Bonzini     sc->parse_cdb    = scsi_generic_parse_cdb;
60449ab747fSPaolo Bonzini     dc->fw_name = "disk";
60549ab747fSPaolo Bonzini     dc->desc = "pass through generic scsi device (/dev/sg*)";
60649ab747fSPaolo Bonzini     dc->reset = scsi_generic_reset;
60749ab747fSPaolo Bonzini     dc->props = scsi_generic_properties;
60849ab747fSPaolo Bonzini     dc->vmsd  = &vmstate_scsi_device;
60949ab747fSPaolo Bonzini }
61049ab747fSPaolo Bonzini 
61149ab747fSPaolo Bonzini static const TypeInfo scsi_generic_info = {
61249ab747fSPaolo Bonzini     .name          = "scsi-generic",
61349ab747fSPaolo Bonzini     .parent        = TYPE_SCSI_DEVICE,
61449ab747fSPaolo Bonzini     .instance_size = sizeof(SCSIDevice),
61549ab747fSPaolo Bonzini     .class_init    = scsi_generic_class_initfn,
61649ab747fSPaolo Bonzini };
61749ab747fSPaolo Bonzini 
61849ab747fSPaolo Bonzini static void scsi_generic_register_types(void)
61949ab747fSPaolo Bonzini {
62049ab747fSPaolo Bonzini     type_register_static(&scsi_generic_info);
62149ab747fSPaolo Bonzini }
62249ab747fSPaolo Bonzini 
62349ab747fSPaolo Bonzini type_init(scsi_generic_register_types)
62449ab747fSPaolo Bonzini 
62549ab747fSPaolo Bonzini #endif /* __linux__ */
626