xref: /qemu/hw/scsi/scsi-generic.c (revision fa0d653b)
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 
1449ab747fSPaolo Bonzini #include "qemu-common.h"
1549ab747fSPaolo Bonzini #include "qemu/error-report.h"
1649ab747fSPaolo Bonzini #include "hw/scsi/scsi.h"
174be74634SMarkus Armbruster #include "sysemu/block-backend.h"
1849ab747fSPaolo Bonzini #include "sysemu/blockdev.h"
1949ab747fSPaolo Bonzini 
2049ab747fSPaolo Bonzini #ifdef __linux__
2149ab747fSPaolo Bonzini 
2249ab747fSPaolo Bonzini //#define DEBUG_SCSI
2349ab747fSPaolo Bonzini 
2449ab747fSPaolo Bonzini #ifdef DEBUG_SCSI
2549ab747fSPaolo Bonzini #define DPRINTF(fmt, ...) \
2649ab747fSPaolo Bonzini do { printf("scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
2749ab747fSPaolo Bonzini #else
2849ab747fSPaolo Bonzini #define DPRINTF(fmt, ...) do {} while(0)
2949ab747fSPaolo Bonzini #endif
3049ab747fSPaolo Bonzini 
3149ab747fSPaolo Bonzini #define BADF(fmt, ...) \
3249ab747fSPaolo Bonzini do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
3349ab747fSPaolo Bonzini 
3449ab747fSPaolo Bonzini #include <stdio.h>
3549ab747fSPaolo Bonzini #include <sys/types.h>
3649ab747fSPaolo Bonzini #include <sys/stat.h>
3749ab747fSPaolo Bonzini #include <unistd.h>
3849ab747fSPaolo Bonzini #include <scsi/sg.h>
3949ab747fSPaolo Bonzini #include "block/scsi.h"
4049ab747fSPaolo Bonzini 
4149ab747fSPaolo Bonzini #define SG_ERR_DRIVER_TIMEOUT  0x06
4249ab747fSPaolo Bonzini #define SG_ERR_DRIVER_SENSE    0x08
4349ab747fSPaolo Bonzini 
4449ab747fSPaolo Bonzini #define SG_ERR_DID_OK          0x00
4549ab747fSPaolo Bonzini #define SG_ERR_DID_NO_CONNECT  0x01
4649ab747fSPaolo Bonzini #define SG_ERR_DID_BUS_BUSY    0x02
4749ab747fSPaolo Bonzini #define SG_ERR_DID_TIME_OUT    0x03
4849ab747fSPaolo Bonzini 
4949ab747fSPaolo Bonzini #ifndef MAX_UINT
5049ab747fSPaolo Bonzini #define MAX_UINT ((unsigned int)-1)
5149ab747fSPaolo Bonzini #endif
5249ab747fSPaolo Bonzini 
5349ab747fSPaolo Bonzini typedef struct SCSIGenericReq {
5449ab747fSPaolo Bonzini     SCSIRequest req;
5549ab747fSPaolo Bonzini     uint8_t *buf;
5649ab747fSPaolo Bonzini     int buflen;
5749ab747fSPaolo Bonzini     int len;
5849ab747fSPaolo Bonzini     sg_io_hdr_t io_header;
5949ab747fSPaolo Bonzini } SCSIGenericReq;
6049ab747fSPaolo Bonzini 
6149ab747fSPaolo Bonzini static void scsi_generic_save_request(QEMUFile *f, SCSIRequest *req)
6249ab747fSPaolo Bonzini {
6349ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
6449ab747fSPaolo Bonzini 
6549ab747fSPaolo Bonzini     qemu_put_sbe32s(f, &r->buflen);
6649ab747fSPaolo Bonzini     if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
6749ab747fSPaolo Bonzini         assert(!r->req.sg);
6849ab747fSPaolo Bonzini         qemu_put_buffer(f, r->buf, r->req.cmd.xfer);
6949ab747fSPaolo Bonzini     }
7049ab747fSPaolo Bonzini }
7149ab747fSPaolo Bonzini 
7249ab747fSPaolo Bonzini static void scsi_generic_load_request(QEMUFile *f, SCSIRequest *req)
7349ab747fSPaolo Bonzini {
7449ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
7549ab747fSPaolo Bonzini 
7649ab747fSPaolo Bonzini     qemu_get_sbe32s(f, &r->buflen);
7749ab747fSPaolo Bonzini     if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
7849ab747fSPaolo Bonzini         assert(!r->req.sg);
7949ab747fSPaolo Bonzini         qemu_get_buffer(f, r->buf, r->req.cmd.xfer);
8049ab747fSPaolo Bonzini     }
8149ab747fSPaolo Bonzini }
8249ab747fSPaolo Bonzini 
8349ab747fSPaolo Bonzini static void scsi_free_request(SCSIRequest *req)
8449ab747fSPaolo Bonzini {
8549ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
8649ab747fSPaolo Bonzini 
8749ab747fSPaolo Bonzini     g_free(r->buf);
8849ab747fSPaolo Bonzini }
8949ab747fSPaolo Bonzini 
9049ab747fSPaolo Bonzini /* Helper function for command completion.  */
91*fa0d653bSPaolo Bonzini static void scsi_command_complete_noio(SCSIGenericReq *r, int ret)
9249ab747fSPaolo Bonzini {
9349ab747fSPaolo Bonzini     int status;
9449ab747fSPaolo Bonzini 
95*fa0d653bSPaolo Bonzini     assert(r->req.aiocb == NULL);
96*fa0d653bSPaolo Bonzini 
976c25fa6cSFam Zheng     if (r->req.io_canceled) {
98d5776465SFam Zheng         scsi_req_cancel_complete(&r->req);
996c25fa6cSFam Zheng         goto done;
1006c25fa6cSFam Zheng     }
10149ab747fSPaolo Bonzini     if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
10249ab747fSPaolo Bonzini         r->req.sense_len = r->io_header.sb_len_wr;
10349ab747fSPaolo Bonzini     }
10449ab747fSPaolo Bonzini 
10549ab747fSPaolo Bonzini     if (ret != 0) {
10649ab747fSPaolo Bonzini         switch (ret) {
10749ab747fSPaolo Bonzini         case -EDOM:
10849ab747fSPaolo Bonzini             status = TASK_SET_FULL;
10949ab747fSPaolo Bonzini             break;
11049ab747fSPaolo Bonzini         case -ENOMEM:
11149ab747fSPaolo Bonzini             status = CHECK_CONDITION;
11249ab747fSPaolo Bonzini             scsi_req_build_sense(&r->req, SENSE_CODE(TARGET_FAILURE));
11349ab747fSPaolo Bonzini             break;
11449ab747fSPaolo Bonzini         default:
11549ab747fSPaolo Bonzini             status = CHECK_CONDITION;
11649ab747fSPaolo Bonzini             scsi_req_build_sense(&r->req, SENSE_CODE(IO_ERROR));
11749ab747fSPaolo Bonzini             break;
11849ab747fSPaolo Bonzini         }
11949ab747fSPaolo Bonzini     } else {
12049ab747fSPaolo Bonzini         if (r->io_header.host_status == SG_ERR_DID_NO_CONNECT ||
12149ab747fSPaolo Bonzini             r->io_header.host_status == SG_ERR_DID_BUS_BUSY ||
12249ab747fSPaolo Bonzini             r->io_header.host_status == SG_ERR_DID_TIME_OUT ||
12349ab747fSPaolo Bonzini             (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT)) {
12449ab747fSPaolo Bonzini             status = BUSY;
12549ab747fSPaolo Bonzini             BADF("Driver Timeout\n");
12649ab747fSPaolo Bonzini         } else if (r->io_header.host_status) {
12749ab747fSPaolo Bonzini             status = CHECK_CONDITION;
12849ab747fSPaolo Bonzini             scsi_req_build_sense(&r->req, SENSE_CODE(I_T_NEXUS_LOSS));
12949ab747fSPaolo Bonzini         } else if (r->io_header.status) {
13049ab747fSPaolo Bonzini             status = r->io_header.status;
13149ab747fSPaolo Bonzini         } else if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
13249ab747fSPaolo Bonzini             status = CHECK_CONDITION;
13349ab747fSPaolo Bonzini         } else {
13449ab747fSPaolo Bonzini             status = GOOD;
13549ab747fSPaolo Bonzini         }
13649ab747fSPaolo Bonzini     }
13749ab747fSPaolo Bonzini     DPRINTF("Command complete 0x%p tag=0x%x status=%d\n",
13849ab747fSPaolo Bonzini             r, r->req.tag, status);
13949ab747fSPaolo Bonzini 
14049ab747fSPaolo Bonzini     scsi_req_complete(&r->req, status);
1416c25fa6cSFam Zheng done:
14249ab747fSPaolo Bonzini     scsi_req_unref(&r->req);
14349ab747fSPaolo Bonzini }
14449ab747fSPaolo Bonzini 
145*fa0d653bSPaolo Bonzini static void scsi_command_complete(void *opaque, int ret)
146*fa0d653bSPaolo Bonzini {
147*fa0d653bSPaolo Bonzini     SCSIGenericReq *r = (SCSIGenericReq *)opaque;
148*fa0d653bSPaolo Bonzini 
149*fa0d653bSPaolo Bonzini     assert(r->req.aiocb != NULL);
150*fa0d653bSPaolo Bonzini     r->req.aiocb = NULL;
151*fa0d653bSPaolo Bonzini     scsi_command_complete_noio(r, ret);
152*fa0d653bSPaolo Bonzini }
153*fa0d653bSPaolo Bonzini 
1544be74634SMarkus Armbruster static int execute_command(BlockBackend *blk,
15549ab747fSPaolo Bonzini                            SCSIGenericReq *r, int direction,
156097310b5SMarkus Armbruster                            BlockCompletionFunc *complete)
15749ab747fSPaolo Bonzini {
15849ab747fSPaolo Bonzini     r->io_header.interface_id = 'S';
15949ab747fSPaolo Bonzini     r->io_header.dxfer_direction = direction;
16049ab747fSPaolo Bonzini     r->io_header.dxferp = r->buf;
16149ab747fSPaolo Bonzini     r->io_header.dxfer_len = r->buflen;
16249ab747fSPaolo Bonzini     r->io_header.cmdp = r->req.cmd.buf;
16349ab747fSPaolo Bonzini     r->io_header.cmd_len = r->req.cmd.len;
16449ab747fSPaolo Bonzini     r->io_header.mx_sb_len = sizeof(r->req.sense);
16549ab747fSPaolo Bonzini     r->io_header.sbp = r->req.sense;
16649ab747fSPaolo Bonzini     r->io_header.timeout = MAX_UINT;
16749ab747fSPaolo Bonzini     r->io_header.usr_ptr = r;
16849ab747fSPaolo Bonzini     r->io_header.flags |= SG_FLAG_DIRECT_IO;
16949ab747fSPaolo Bonzini 
1704be74634SMarkus Armbruster     r->req.aiocb = blk_aio_ioctl(blk, SG_IO, &r->io_header, complete, r);
171d836f8d3SPavel Hrdina     if (r->req.aiocb == NULL) {
172d836f8d3SPavel Hrdina         return -EIO;
173d836f8d3SPavel Hrdina     }
17449ab747fSPaolo Bonzini 
17549ab747fSPaolo Bonzini     return 0;
17649ab747fSPaolo Bonzini }
17749ab747fSPaolo Bonzini 
17849ab747fSPaolo Bonzini static void scsi_read_complete(void * opaque, int ret)
17949ab747fSPaolo Bonzini {
18049ab747fSPaolo Bonzini     SCSIGenericReq *r = (SCSIGenericReq *)opaque;
18149ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
18249ab747fSPaolo Bonzini     int len;
18349ab747fSPaolo Bonzini 
184*fa0d653bSPaolo Bonzini     assert(r->req.aiocb != NULL);
18549ab747fSPaolo Bonzini     r->req.aiocb = NULL;
186*fa0d653bSPaolo Bonzini 
1876c25fa6cSFam Zheng     if (ret || r->req.io_canceled) {
188*fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, ret);
18949ab747fSPaolo Bonzini         return;
19049ab747fSPaolo Bonzini     }
191*fa0d653bSPaolo Bonzini 
19249ab747fSPaolo Bonzini     len = r->io_header.dxfer_len - r->io_header.resid;
19349ab747fSPaolo Bonzini     DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len);
19449ab747fSPaolo Bonzini 
19549ab747fSPaolo Bonzini     r->len = -1;
19649ab747fSPaolo Bonzini     if (len == 0) {
197*fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, 0);
198*fa0d653bSPaolo Bonzini         return;
199*fa0d653bSPaolo Bonzini     }
200*fa0d653bSPaolo Bonzini 
20149ab747fSPaolo Bonzini     /* Snoop READ CAPACITY output to set the blocksize.  */
20253254e56SPaolo Bonzini     if (r->req.cmd.buf[0] == READ_CAPACITY_10 &&
20353254e56SPaolo Bonzini         (ldl_be_p(&r->buf[0]) != 0xffffffffU || s->max_lba == 0)) {
20449ab747fSPaolo Bonzini         s->blocksize = ldl_be_p(&r->buf[4]);
20553254e56SPaolo Bonzini         s->max_lba = ldl_be_p(&r->buf[0]) & 0xffffffffULL;
20649ab747fSPaolo Bonzini     } else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 &&
20749ab747fSPaolo Bonzini                (r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) {
20849ab747fSPaolo Bonzini         s->blocksize = ldl_be_p(&r->buf[8]);
20949ab747fSPaolo Bonzini         s->max_lba = ldq_be_p(&r->buf[0]);
21049ab747fSPaolo Bonzini     }
2114be74634SMarkus Armbruster     blk_set_guest_block_size(s->conf.blk, s->blocksize);
21249ab747fSPaolo Bonzini 
21349ab747fSPaolo Bonzini     scsi_req_data(&r->req, len);
21449ab747fSPaolo Bonzini     scsi_req_unref(&r->req);
21549ab747fSPaolo Bonzini }
21649ab747fSPaolo Bonzini 
21749ab747fSPaolo Bonzini /* Read more data from scsi device into buffer.  */
21849ab747fSPaolo Bonzini static void scsi_read_data(SCSIRequest *req)
21949ab747fSPaolo Bonzini {
22049ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
22149ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
22249ab747fSPaolo Bonzini     int ret;
22349ab747fSPaolo Bonzini 
22449ab747fSPaolo Bonzini     DPRINTF("scsi_read_data 0x%x\n", req->tag);
22549ab747fSPaolo Bonzini 
22649ab747fSPaolo Bonzini     /* The request is used as the AIO opaque value, so add a ref.  */
22749ab747fSPaolo Bonzini     scsi_req_ref(&r->req);
22849ab747fSPaolo Bonzini     if (r->len == -1) {
229*fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, 0);
23049ab747fSPaolo Bonzini         return;
23149ab747fSPaolo Bonzini     }
23249ab747fSPaolo Bonzini 
2334be74634SMarkus Armbruster     ret = execute_command(s->conf.blk, r, SG_DXFER_FROM_DEV,
2344be74634SMarkus Armbruster                           scsi_read_complete);
23549ab747fSPaolo Bonzini     if (ret < 0) {
236*fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, ret);
23749ab747fSPaolo Bonzini     }
23849ab747fSPaolo Bonzini }
23949ab747fSPaolo Bonzini 
24049ab747fSPaolo Bonzini static void scsi_write_complete(void * opaque, int ret)
24149ab747fSPaolo Bonzini {
24249ab747fSPaolo Bonzini     SCSIGenericReq *r = (SCSIGenericReq *)opaque;
24349ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
24449ab747fSPaolo Bonzini 
24549ab747fSPaolo Bonzini     DPRINTF("scsi_write_complete() ret = %d\n", ret);
246*fa0d653bSPaolo Bonzini 
247*fa0d653bSPaolo Bonzini     assert(r->req.aiocb != NULL);
24849ab747fSPaolo Bonzini     r->req.aiocb = NULL;
249*fa0d653bSPaolo Bonzini 
2506c25fa6cSFam Zheng     if (ret || r->req.io_canceled) {
251*fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, ret);
25249ab747fSPaolo Bonzini         return;
25349ab747fSPaolo Bonzini     }
25449ab747fSPaolo Bonzini 
25549ab747fSPaolo Bonzini     if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 &&
25649ab747fSPaolo Bonzini         s->type == TYPE_TAPE) {
25749ab747fSPaolo Bonzini         s->blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11];
25849ab747fSPaolo Bonzini         DPRINTF("block size %d\n", s->blocksize);
25949ab747fSPaolo Bonzini     }
26049ab747fSPaolo Bonzini 
261*fa0d653bSPaolo Bonzini     scsi_command_complete_noio(r, ret);
26249ab747fSPaolo Bonzini }
26349ab747fSPaolo Bonzini 
26449ab747fSPaolo Bonzini /* Write data to a scsi device.  Returns nonzero on failure.
26549ab747fSPaolo Bonzini    The transfer may complete asynchronously.  */
26649ab747fSPaolo Bonzini static void scsi_write_data(SCSIRequest *req)
26749ab747fSPaolo Bonzini {
26849ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
26949ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
27049ab747fSPaolo Bonzini     int ret;
27149ab747fSPaolo Bonzini 
27249ab747fSPaolo Bonzini     DPRINTF("scsi_write_data 0x%x\n", req->tag);
27349ab747fSPaolo Bonzini     if (r->len == 0) {
27449ab747fSPaolo Bonzini         r->len = r->buflen;
27549ab747fSPaolo Bonzini         scsi_req_data(&r->req, r->len);
27649ab747fSPaolo Bonzini         return;
27749ab747fSPaolo Bonzini     }
27849ab747fSPaolo Bonzini 
27949ab747fSPaolo Bonzini     /* The request is used as the AIO opaque value, so add a ref.  */
28049ab747fSPaolo Bonzini     scsi_req_ref(&r->req);
2814be74634SMarkus Armbruster     ret = execute_command(s->conf.blk, r, SG_DXFER_TO_DEV, scsi_write_complete);
28249ab747fSPaolo Bonzini     if (ret < 0) {
283*fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, ret);
28449ab747fSPaolo Bonzini     }
28549ab747fSPaolo Bonzini }
28649ab747fSPaolo Bonzini 
28749ab747fSPaolo Bonzini /* Return a pointer to the data buffer.  */
28849ab747fSPaolo Bonzini static uint8_t *scsi_get_buf(SCSIRequest *req)
28949ab747fSPaolo Bonzini {
29049ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
29149ab747fSPaolo Bonzini 
29249ab747fSPaolo Bonzini     return r->buf;
29349ab747fSPaolo Bonzini }
29449ab747fSPaolo Bonzini 
29549ab747fSPaolo Bonzini /* Execute a scsi command.  Returns the length of the data expected by the
29649ab747fSPaolo Bonzini    command.  This will be Positive for data transfers from the device
29749ab747fSPaolo Bonzini    (eg. disk reads), negative for transfers to the device (eg. disk writes),
29849ab747fSPaolo Bonzini    and zero if the command does not transfer any data.  */
29949ab747fSPaolo Bonzini 
30049ab747fSPaolo Bonzini static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
30149ab747fSPaolo Bonzini {
30249ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
30349ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
30449ab747fSPaolo Bonzini     int ret;
30549ab747fSPaolo Bonzini 
30649ab747fSPaolo Bonzini #ifdef DEBUG_SCSI
30749ab747fSPaolo Bonzini     {
30849ab747fSPaolo Bonzini         int i;
30949ab747fSPaolo Bonzini         for (i = 1; i < r->req.cmd.len; i++) {
31049ab747fSPaolo Bonzini             printf(" 0x%02x", cmd[i]);
31149ab747fSPaolo Bonzini         }
31249ab747fSPaolo Bonzini         printf("\n");
31349ab747fSPaolo Bonzini     }
31449ab747fSPaolo Bonzini #endif
31549ab747fSPaolo Bonzini 
31649ab747fSPaolo Bonzini     if (r->req.cmd.xfer == 0) {
31749ab747fSPaolo Bonzini         g_free(r->buf);
31849ab747fSPaolo Bonzini         r->buflen = 0;
31949ab747fSPaolo Bonzini         r->buf = NULL;
32049ab747fSPaolo Bonzini         /* The request is used as the AIO opaque value, so add a ref.  */
32149ab747fSPaolo Bonzini         scsi_req_ref(&r->req);
3224be74634SMarkus Armbruster         ret = execute_command(s->conf.blk, r, SG_DXFER_NONE,
3234be74634SMarkus Armbruster                               scsi_command_complete);
32449ab747fSPaolo Bonzini         if (ret < 0) {
325*fa0d653bSPaolo Bonzini             scsi_command_complete_noio(r, ret);
32649ab747fSPaolo Bonzini             return 0;
32749ab747fSPaolo Bonzini         }
32849ab747fSPaolo Bonzini         return 0;
32949ab747fSPaolo Bonzini     }
33049ab747fSPaolo Bonzini 
33149ab747fSPaolo Bonzini     if (r->buflen != r->req.cmd.xfer) {
33249ab747fSPaolo Bonzini         g_free(r->buf);
33349ab747fSPaolo Bonzini         r->buf = g_malloc(r->req.cmd.xfer);
33449ab747fSPaolo Bonzini         r->buflen = r->req.cmd.xfer;
33549ab747fSPaolo Bonzini     }
33649ab747fSPaolo Bonzini 
33749ab747fSPaolo Bonzini     memset(r->buf, 0, r->buflen);
33849ab747fSPaolo Bonzini     r->len = r->req.cmd.xfer;
33949ab747fSPaolo Bonzini     if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
34049ab747fSPaolo Bonzini         r->len = 0;
34149ab747fSPaolo Bonzini         return -r->req.cmd.xfer;
34249ab747fSPaolo Bonzini     } else {
34349ab747fSPaolo Bonzini         return r->req.cmd.xfer;
34449ab747fSPaolo Bonzini     }
34549ab747fSPaolo Bonzini }
34649ab747fSPaolo Bonzini 
3474be74634SMarkus Armbruster static int get_stream_blocksize(BlockBackend *blk)
34849ab747fSPaolo Bonzini {
34949ab747fSPaolo Bonzini     uint8_t cmd[6];
35049ab747fSPaolo Bonzini     uint8_t buf[12];
35149ab747fSPaolo Bonzini     uint8_t sensebuf[8];
35249ab747fSPaolo Bonzini     sg_io_hdr_t io_header;
35349ab747fSPaolo Bonzini     int ret;
35449ab747fSPaolo Bonzini 
35549ab747fSPaolo Bonzini     memset(cmd, 0, sizeof(cmd));
35649ab747fSPaolo Bonzini     memset(buf, 0, sizeof(buf));
35749ab747fSPaolo Bonzini     cmd[0] = MODE_SENSE;
35849ab747fSPaolo Bonzini     cmd[4] = sizeof(buf);
35949ab747fSPaolo Bonzini 
36049ab747fSPaolo Bonzini     memset(&io_header, 0, sizeof(io_header));
36149ab747fSPaolo Bonzini     io_header.interface_id = 'S';
36249ab747fSPaolo Bonzini     io_header.dxfer_direction = SG_DXFER_FROM_DEV;
36349ab747fSPaolo Bonzini     io_header.dxfer_len = sizeof(buf);
36449ab747fSPaolo Bonzini     io_header.dxferp = buf;
36549ab747fSPaolo Bonzini     io_header.cmdp = cmd;
36649ab747fSPaolo Bonzini     io_header.cmd_len = sizeof(cmd);
36749ab747fSPaolo Bonzini     io_header.mx_sb_len = sizeof(sensebuf);
36849ab747fSPaolo Bonzini     io_header.sbp = sensebuf;
36949ab747fSPaolo Bonzini     io_header.timeout = 6000; /* XXX */
37049ab747fSPaolo Bonzini 
3714be74634SMarkus Armbruster     ret = blk_ioctl(blk, SG_IO, &io_header);
37249ab747fSPaolo Bonzini     if (ret < 0 || io_header.driver_status || io_header.host_status) {
37349ab747fSPaolo Bonzini         return -1;
37449ab747fSPaolo Bonzini     }
37549ab747fSPaolo Bonzini     return (buf[9] << 16) | (buf[10] << 8) | buf[11];
37649ab747fSPaolo Bonzini }
37749ab747fSPaolo Bonzini 
37849ab747fSPaolo Bonzini static void scsi_generic_reset(DeviceState *dev)
37949ab747fSPaolo Bonzini {
38049ab747fSPaolo Bonzini     SCSIDevice *s = SCSI_DEVICE(dev);
38149ab747fSPaolo Bonzini 
38249ab747fSPaolo Bonzini     scsi_device_purge_requests(s, SENSE_CODE(RESET));
38349ab747fSPaolo Bonzini }
38449ab747fSPaolo Bonzini 
385a818a4b6SFam Zheng static void scsi_generic_realize(SCSIDevice *s, Error **errp)
38649ab747fSPaolo Bonzini {
3876ee143a0SPaolo Bonzini     int rc;
38849ab747fSPaolo Bonzini     int sg_version;
38949ab747fSPaolo Bonzini     struct sg_scsi_id scsiid;
39049ab747fSPaolo Bonzini 
3914be74634SMarkus Armbruster     if (!s->conf.blk) {
392a818a4b6SFam Zheng         error_setg(errp, "drive property not set");
393a818a4b6SFam Zheng         return;
39449ab747fSPaolo Bonzini     }
39549ab747fSPaolo Bonzini 
3964be74634SMarkus Armbruster     if (blk_get_on_error(s->conf.blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC) {
397a818a4b6SFam Zheng         error_setg(errp, "Device doesn't support drive option werror");
398a818a4b6SFam Zheng         return;
39949ab747fSPaolo Bonzini     }
4004be74634SMarkus Armbruster     if (blk_get_on_error(s->conf.blk, 1) != BLOCKDEV_ON_ERROR_REPORT) {
401a818a4b6SFam Zheng         error_setg(errp, "Device doesn't support drive option rerror");
402a818a4b6SFam Zheng         return;
40349ab747fSPaolo Bonzini     }
40449ab747fSPaolo Bonzini 
40549ab747fSPaolo Bonzini     /* check we are using a driver managing SG_IO (version 3 and after */
4064be74634SMarkus Armbruster     rc = blk_ioctl(s->conf.blk, SG_GET_VERSION_NUM, &sg_version);
4076ee143a0SPaolo Bonzini     if (rc < 0) {
408a818a4b6SFam Zheng         error_setg(errp, "cannot get SG_IO version number: %s.  "
4096ee143a0SPaolo Bonzini                          "Is this a SCSI device?",
4106ee143a0SPaolo Bonzini                          strerror(-rc));
411a818a4b6SFam Zheng         return;
41249ab747fSPaolo Bonzini     }
41349ab747fSPaolo Bonzini     if (sg_version < 30000) {
414a818a4b6SFam Zheng         error_setg(errp, "scsi generic interface too old");
415a818a4b6SFam Zheng         return;
41649ab747fSPaolo Bonzini     }
41749ab747fSPaolo Bonzini 
41849ab747fSPaolo Bonzini     /* get LUN of the /dev/sg? */
4194be74634SMarkus Armbruster     if (blk_ioctl(s->conf.blk, SG_GET_SCSI_ID, &scsiid)) {
420a818a4b6SFam Zheng         error_setg(errp, "SG_GET_SCSI_ID ioctl failed");
421a818a4b6SFam Zheng         return;
42249ab747fSPaolo Bonzini     }
42349ab747fSPaolo Bonzini 
42449ab747fSPaolo Bonzini     /* define device state */
42549ab747fSPaolo Bonzini     s->type = scsiid.scsi_type;
42649ab747fSPaolo Bonzini     DPRINTF("device type %d\n", s->type);
42749ab747fSPaolo Bonzini 
42849ab747fSPaolo Bonzini     switch (s->type) {
42949ab747fSPaolo Bonzini     case TYPE_TAPE:
4304be74634SMarkus Armbruster         s->blocksize = get_stream_blocksize(s->conf.blk);
43149ab747fSPaolo Bonzini         if (s->blocksize == -1) {
43249ab747fSPaolo Bonzini             s->blocksize = 0;
43349ab747fSPaolo Bonzini         }
43449ab747fSPaolo Bonzini         break;
43549ab747fSPaolo Bonzini 
43649ab747fSPaolo Bonzini         /* Make a guess for block devices, we'll fix it when the guest sends.
43749ab747fSPaolo Bonzini          * READ CAPACITY.  If they don't, they likely would assume these sizes
43849ab747fSPaolo Bonzini          * anyway. (TODO: they could also send MODE SENSE).
43949ab747fSPaolo Bonzini          */
44049ab747fSPaolo Bonzini     case TYPE_ROM:
44149ab747fSPaolo Bonzini     case TYPE_WORM:
44249ab747fSPaolo Bonzini         s->blocksize = 2048;
44349ab747fSPaolo Bonzini         break;
44449ab747fSPaolo Bonzini     default:
44549ab747fSPaolo Bonzini         s->blocksize = 512;
44649ab747fSPaolo Bonzini         break;
44749ab747fSPaolo Bonzini     }
44849ab747fSPaolo Bonzini 
44949ab747fSPaolo Bonzini     DPRINTF("block size %d\n", s->blocksize);
45049ab747fSPaolo Bonzini }
45149ab747fSPaolo Bonzini 
45249ab747fSPaolo Bonzini const SCSIReqOps scsi_generic_req_ops = {
45349ab747fSPaolo Bonzini     .size         = sizeof(SCSIGenericReq),
45449ab747fSPaolo Bonzini     .free_req     = scsi_free_request,
45549ab747fSPaolo Bonzini     .send_command = scsi_send_command,
45649ab747fSPaolo Bonzini     .read_data    = scsi_read_data,
45749ab747fSPaolo Bonzini     .write_data   = scsi_write_data,
45849ab747fSPaolo Bonzini     .get_buf      = scsi_get_buf,
45949ab747fSPaolo Bonzini     .load_request = scsi_generic_load_request,
46049ab747fSPaolo Bonzini     .save_request = scsi_generic_save_request,
46149ab747fSPaolo Bonzini };
46249ab747fSPaolo Bonzini 
46349ab747fSPaolo Bonzini static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
46449ab747fSPaolo Bonzini                                      uint8_t *buf, void *hba_private)
46549ab747fSPaolo Bonzini {
46649ab747fSPaolo Bonzini     SCSIRequest *req;
46749ab747fSPaolo Bonzini 
46849ab747fSPaolo Bonzini     req = scsi_req_alloc(&scsi_generic_req_ops, d, tag, lun, hba_private);
46949ab747fSPaolo Bonzini     return req;
47049ab747fSPaolo Bonzini }
47149ab747fSPaolo Bonzini 
47249ab747fSPaolo Bonzini static Property scsi_generic_properties[] = {
4734be74634SMarkus Armbruster     DEFINE_PROP_DRIVE("drive", SCSIDevice, conf.blk),
47449ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
47549ab747fSPaolo Bonzini };
47649ab747fSPaolo Bonzini 
4773e7e180aSPaolo Bonzini static int scsi_generic_parse_cdb(SCSIDevice *dev, SCSICommand *cmd,
4783e7e180aSPaolo Bonzini                                   uint8_t *buf, void *hba_private)
4793e7e180aSPaolo Bonzini {
4803e7e180aSPaolo Bonzini     return scsi_bus_parse_cdb(dev, cmd, buf, hba_private);
4813e7e180aSPaolo Bonzini }
4823e7e180aSPaolo Bonzini 
48349ab747fSPaolo Bonzini static void scsi_generic_class_initfn(ObjectClass *klass, void *data)
48449ab747fSPaolo Bonzini {
48549ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
48649ab747fSPaolo Bonzini     SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
48749ab747fSPaolo Bonzini 
488a818a4b6SFam Zheng     sc->realize      = scsi_generic_realize;
48949ab747fSPaolo Bonzini     sc->alloc_req    = scsi_new_request;
4903e7e180aSPaolo Bonzini     sc->parse_cdb    = scsi_generic_parse_cdb;
49149ab747fSPaolo Bonzini     dc->fw_name = "disk";
49249ab747fSPaolo Bonzini     dc->desc = "pass through generic scsi device (/dev/sg*)";
49349ab747fSPaolo Bonzini     dc->reset = scsi_generic_reset;
49449ab747fSPaolo Bonzini     dc->props = scsi_generic_properties;
49549ab747fSPaolo Bonzini     dc->vmsd  = &vmstate_scsi_device;
49649ab747fSPaolo Bonzini }
49749ab747fSPaolo Bonzini 
49849ab747fSPaolo Bonzini static const TypeInfo scsi_generic_info = {
49949ab747fSPaolo Bonzini     .name          = "scsi-generic",
50049ab747fSPaolo Bonzini     .parent        = TYPE_SCSI_DEVICE,
50149ab747fSPaolo Bonzini     .instance_size = sizeof(SCSIDevice),
50249ab747fSPaolo Bonzini     .class_init    = scsi_generic_class_initfn,
50349ab747fSPaolo Bonzini };
50449ab747fSPaolo Bonzini 
50549ab747fSPaolo Bonzini static void scsi_generic_register_types(void)
50649ab747fSPaolo Bonzini {
50749ab747fSPaolo Bonzini     type_register_static(&scsi_generic_info);
50849ab747fSPaolo Bonzini }
50949ab747fSPaolo Bonzini 
51049ab747fSPaolo Bonzini type_init(scsi_generic_register_types)
51149ab747fSPaolo Bonzini 
51249ab747fSPaolo Bonzini #endif /* __linux__ */
513