xref: /qemu/hw/scsi/scsi-generic.c (revision 6ee143a0)
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"
1749ab747fSPaolo Bonzini #include "sysemu/blockdev.h"
1849ab747fSPaolo Bonzini 
1949ab747fSPaolo Bonzini #ifdef __linux__
2049ab747fSPaolo Bonzini 
2149ab747fSPaolo Bonzini //#define DEBUG_SCSI
2249ab747fSPaolo Bonzini 
2349ab747fSPaolo Bonzini #ifdef DEBUG_SCSI
2449ab747fSPaolo Bonzini #define DPRINTF(fmt, ...) \
2549ab747fSPaolo Bonzini do { printf("scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
2649ab747fSPaolo Bonzini #else
2749ab747fSPaolo Bonzini #define DPRINTF(fmt, ...) do {} while(0)
2849ab747fSPaolo Bonzini #endif
2949ab747fSPaolo Bonzini 
3049ab747fSPaolo Bonzini #define BADF(fmt, ...) \
3149ab747fSPaolo Bonzini do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
3249ab747fSPaolo Bonzini 
3349ab747fSPaolo Bonzini #include <stdio.h>
3449ab747fSPaolo Bonzini #include <sys/types.h>
3549ab747fSPaolo Bonzini #include <sys/stat.h>
3649ab747fSPaolo Bonzini #include <unistd.h>
3749ab747fSPaolo Bonzini #include <scsi/sg.h>
3849ab747fSPaolo Bonzini #include "block/scsi.h"
3949ab747fSPaolo Bonzini 
4049ab747fSPaolo Bonzini #define SG_ERR_DRIVER_TIMEOUT  0x06
4149ab747fSPaolo Bonzini #define SG_ERR_DRIVER_SENSE    0x08
4249ab747fSPaolo Bonzini 
4349ab747fSPaolo Bonzini #define SG_ERR_DID_OK          0x00
4449ab747fSPaolo Bonzini #define SG_ERR_DID_NO_CONNECT  0x01
4549ab747fSPaolo Bonzini #define SG_ERR_DID_BUS_BUSY    0x02
4649ab747fSPaolo Bonzini #define SG_ERR_DID_TIME_OUT    0x03
4749ab747fSPaolo Bonzini 
4849ab747fSPaolo Bonzini #ifndef MAX_UINT
4949ab747fSPaolo Bonzini #define MAX_UINT ((unsigned int)-1)
5049ab747fSPaolo Bonzini #endif
5149ab747fSPaolo Bonzini 
5249ab747fSPaolo Bonzini typedef struct SCSIGenericReq {
5349ab747fSPaolo Bonzini     SCSIRequest req;
5449ab747fSPaolo Bonzini     uint8_t *buf;
5549ab747fSPaolo Bonzini     int buflen;
5649ab747fSPaolo Bonzini     int len;
5749ab747fSPaolo Bonzini     sg_io_hdr_t io_header;
5849ab747fSPaolo Bonzini } SCSIGenericReq;
5949ab747fSPaolo Bonzini 
6049ab747fSPaolo Bonzini static void scsi_generic_save_request(QEMUFile *f, SCSIRequest *req)
6149ab747fSPaolo Bonzini {
6249ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
6349ab747fSPaolo Bonzini 
6449ab747fSPaolo Bonzini     qemu_put_sbe32s(f, &r->buflen);
6549ab747fSPaolo Bonzini     if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
6649ab747fSPaolo Bonzini         assert(!r->req.sg);
6749ab747fSPaolo Bonzini         qemu_put_buffer(f, r->buf, r->req.cmd.xfer);
6849ab747fSPaolo Bonzini     }
6949ab747fSPaolo Bonzini }
7049ab747fSPaolo Bonzini 
7149ab747fSPaolo Bonzini static void scsi_generic_load_request(QEMUFile *f, SCSIRequest *req)
7249ab747fSPaolo Bonzini {
7349ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
7449ab747fSPaolo Bonzini 
7549ab747fSPaolo Bonzini     qemu_get_sbe32s(f, &r->buflen);
7649ab747fSPaolo Bonzini     if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
7749ab747fSPaolo Bonzini         assert(!r->req.sg);
7849ab747fSPaolo Bonzini         qemu_get_buffer(f, r->buf, r->req.cmd.xfer);
7949ab747fSPaolo Bonzini     }
8049ab747fSPaolo Bonzini }
8149ab747fSPaolo Bonzini 
8249ab747fSPaolo Bonzini static void scsi_free_request(SCSIRequest *req)
8349ab747fSPaolo Bonzini {
8449ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
8549ab747fSPaolo Bonzini 
8649ab747fSPaolo Bonzini     g_free(r->buf);
8749ab747fSPaolo Bonzini }
8849ab747fSPaolo Bonzini 
8949ab747fSPaolo Bonzini /* Helper function for command completion.  */
9049ab747fSPaolo Bonzini static void scsi_command_complete(void *opaque, int ret)
9149ab747fSPaolo Bonzini {
9249ab747fSPaolo Bonzini     int status;
9349ab747fSPaolo Bonzini     SCSIGenericReq *r = (SCSIGenericReq *)opaque;
9449ab747fSPaolo Bonzini 
9549ab747fSPaolo Bonzini     r->req.aiocb = NULL;
9649ab747fSPaolo Bonzini     if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
9749ab747fSPaolo Bonzini         r->req.sense_len = r->io_header.sb_len_wr;
9849ab747fSPaolo Bonzini     }
9949ab747fSPaolo Bonzini 
10049ab747fSPaolo Bonzini     if (ret != 0) {
10149ab747fSPaolo Bonzini         switch (ret) {
10249ab747fSPaolo Bonzini         case -EDOM:
10349ab747fSPaolo Bonzini             status = TASK_SET_FULL;
10449ab747fSPaolo Bonzini             break;
10549ab747fSPaolo Bonzini         case -ENOMEM:
10649ab747fSPaolo Bonzini             status = CHECK_CONDITION;
10749ab747fSPaolo Bonzini             scsi_req_build_sense(&r->req, SENSE_CODE(TARGET_FAILURE));
10849ab747fSPaolo Bonzini             break;
10949ab747fSPaolo Bonzini         default:
11049ab747fSPaolo Bonzini             status = CHECK_CONDITION;
11149ab747fSPaolo Bonzini             scsi_req_build_sense(&r->req, SENSE_CODE(IO_ERROR));
11249ab747fSPaolo Bonzini             break;
11349ab747fSPaolo Bonzini         }
11449ab747fSPaolo Bonzini     } else {
11549ab747fSPaolo Bonzini         if (r->io_header.host_status == SG_ERR_DID_NO_CONNECT ||
11649ab747fSPaolo Bonzini             r->io_header.host_status == SG_ERR_DID_BUS_BUSY ||
11749ab747fSPaolo Bonzini             r->io_header.host_status == SG_ERR_DID_TIME_OUT ||
11849ab747fSPaolo Bonzini             (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT)) {
11949ab747fSPaolo Bonzini             status = BUSY;
12049ab747fSPaolo Bonzini             BADF("Driver Timeout\n");
12149ab747fSPaolo Bonzini         } else if (r->io_header.host_status) {
12249ab747fSPaolo Bonzini             status = CHECK_CONDITION;
12349ab747fSPaolo Bonzini             scsi_req_build_sense(&r->req, SENSE_CODE(I_T_NEXUS_LOSS));
12449ab747fSPaolo Bonzini         } else if (r->io_header.status) {
12549ab747fSPaolo Bonzini             status = r->io_header.status;
12649ab747fSPaolo Bonzini         } else if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
12749ab747fSPaolo Bonzini             status = CHECK_CONDITION;
12849ab747fSPaolo Bonzini         } else {
12949ab747fSPaolo Bonzini             status = GOOD;
13049ab747fSPaolo Bonzini         }
13149ab747fSPaolo Bonzini     }
13249ab747fSPaolo Bonzini     DPRINTF("Command complete 0x%p tag=0x%x status=%d\n",
13349ab747fSPaolo Bonzini             r, r->req.tag, status);
13449ab747fSPaolo Bonzini 
13549ab747fSPaolo Bonzini     scsi_req_complete(&r->req, status);
13649ab747fSPaolo Bonzini     if (!r->req.io_canceled) {
13749ab747fSPaolo Bonzini         scsi_req_unref(&r->req);
13849ab747fSPaolo Bonzini     }
13949ab747fSPaolo Bonzini }
14049ab747fSPaolo Bonzini 
14149ab747fSPaolo Bonzini /* Cancel a pending data transfer.  */
14249ab747fSPaolo Bonzini static void scsi_cancel_io(SCSIRequest *req)
14349ab747fSPaolo Bonzini {
14449ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
14549ab747fSPaolo Bonzini 
14649ab747fSPaolo Bonzini     DPRINTF("Cancel tag=0x%x\n", req->tag);
14749ab747fSPaolo Bonzini     if (r->req.aiocb) {
14849ab747fSPaolo Bonzini         bdrv_aio_cancel(r->req.aiocb);
14949ab747fSPaolo Bonzini 
15049ab747fSPaolo Bonzini         /* This reference was left in by scsi_*_data.  We take ownership of
15149ab747fSPaolo Bonzini          * it independent of whether bdrv_aio_cancel completes the request
15249ab747fSPaolo Bonzini          * or not.  */
15349ab747fSPaolo Bonzini         scsi_req_unref(&r->req);
15449ab747fSPaolo Bonzini     }
15549ab747fSPaolo Bonzini     r->req.aiocb = NULL;
15649ab747fSPaolo Bonzini }
15749ab747fSPaolo Bonzini 
15849ab747fSPaolo Bonzini static int execute_command(BlockDriverState *bdrv,
15949ab747fSPaolo Bonzini                            SCSIGenericReq *r, int direction,
16049ab747fSPaolo Bonzini 			   BlockDriverCompletionFunc *complete)
16149ab747fSPaolo Bonzini {
16249ab747fSPaolo Bonzini     r->io_header.interface_id = 'S';
16349ab747fSPaolo Bonzini     r->io_header.dxfer_direction = direction;
16449ab747fSPaolo Bonzini     r->io_header.dxferp = r->buf;
16549ab747fSPaolo Bonzini     r->io_header.dxfer_len = r->buflen;
16649ab747fSPaolo Bonzini     r->io_header.cmdp = r->req.cmd.buf;
16749ab747fSPaolo Bonzini     r->io_header.cmd_len = r->req.cmd.len;
16849ab747fSPaolo Bonzini     r->io_header.mx_sb_len = sizeof(r->req.sense);
16949ab747fSPaolo Bonzini     r->io_header.sbp = r->req.sense;
17049ab747fSPaolo Bonzini     r->io_header.timeout = MAX_UINT;
17149ab747fSPaolo Bonzini     r->io_header.usr_ptr = r;
17249ab747fSPaolo Bonzini     r->io_header.flags |= SG_FLAG_DIRECT_IO;
17349ab747fSPaolo Bonzini 
17449ab747fSPaolo Bonzini     r->req.aiocb = bdrv_aio_ioctl(bdrv, SG_IO, &r->io_header, complete, r);
175d836f8d3SPavel Hrdina     if (r->req.aiocb == NULL) {
176d836f8d3SPavel Hrdina         return -EIO;
177d836f8d3SPavel Hrdina     }
17849ab747fSPaolo Bonzini 
17949ab747fSPaolo Bonzini     return 0;
18049ab747fSPaolo Bonzini }
18149ab747fSPaolo Bonzini 
18249ab747fSPaolo Bonzini static void scsi_read_complete(void * opaque, int ret)
18349ab747fSPaolo Bonzini {
18449ab747fSPaolo Bonzini     SCSIGenericReq *r = (SCSIGenericReq *)opaque;
18549ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
18649ab747fSPaolo Bonzini     int len;
18749ab747fSPaolo Bonzini 
18849ab747fSPaolo Bonzini     r->req.aiocb = NULL;
18949ab747fSPaolo Bonzini     if (ret) {
19049ab747fSPaolo Bonzini         DPRINTF("IO error ret %d\n", ret);
19149ab747fSPaolo Bonzini         scsi_command_complete(r, ret);
19249ab747fSPaolo Bonzini         return;
19349ab747fSPaolo Bonzini     }
19449ab747fSPaolo Bonzini     len = r->io_header.dxfer_len - r->io_header.resid;
19549ab747fSPaolo Bonzini     DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len);
19649ab747fSPaolo Bonzini 
19749ab747fSPaolo Bonzini     r->len = -1;
19849ab747fSPaolo Bonzini     if (len == 0) {
19949ab747fSPaolo Bonzini         scsi_command_complete(r, 0);
20049ab747fSPaolo Bonzini     } else {
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         }
2111b7fd729SPaolo Bonzini         bdrv_set_guest_block_size(s->conf.bs, s->blocksize);
21249ab747fSPaolo Bonzini 
21349ab747fSPaolo Bonzini         scsi_req_data(&r->req, len);
21449ab747fSPaolo Bonzini         if (!r->req.io_canceled) {
21549ab747fSPaolo Bonzini             scsi_req_unref(&r->req);
21649ab747fSPaolo Bonzini         }
21749ab747fSPaolo Bonzini     }
21849ab747fSPaolo Bonzini }
21949ab747fSPaolo Bonzini 
22049ab747fSPaolo Bonzini /* Read more data from scsi device into buffer.  */
22149ab747fSPaolo Bonzini static void scsi_read_data(SCSIRequest *req)
22249ab747fSPaolo Bonzini {
22349ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
22449ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
22549ab747fSPaolo Bonzini     int ret;
22649ab747fSPaolo Bonzini 
22749ab747fSPaolo Bonzini     DPRINTF("scsi_read_data 0x%x\n", req->tag);
22849ab747fSPaolo Bonzini 
22949ab747fSPaolo Bonzini     /* The request is used as the AIO opaque value, so add a ref.  */
23049ab747fSPaolo Bonzini     scsi_req_ref(&r->req);
23149ab747fSPaolo Bonzini     if (r->len == -1) {
23249ab747fSPaolo Bonzini         scsi_command_complete(r, 0);
23349ab747fSPaolo Bonzini         return;
23449ab747fSPaolo Bonzini     }
23549ab747fSPaolo Bonzini 
23649ab747fSPaolo Bonzini     ret = execute_command(s->conf.bs, r, SG_DXFER_FROM_DEV, scsi_read_complete);
23749ab747fSPaolo Bonzini     if (ret < 0) {
23849ab747fSPaolo Bonzini         scsi_command_complete(r, ret);
23949ab747fSPaolo Bonzini     }
24049ab747fSPaolo Bonzini }
24149ab747fSPaolo Bonzini 
24249ab747fSPaolo Bonzini static void scsi_write_complete(void * opaque, int ret)
24349ab747fSPaolo Bonzini {
24449ab747fSPaolo Bonzini     SCSIGenericReq *r = (SCSIGenericReq *)opaque;
24549ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
24649ab747fSPaolo Bonzini 
24749ab747fSPaolo Bonzini     DPRINTF("scsi_write_complete() ret = %d\n", ret);
24849ab747fSPaolo Bonzini     r->req.aiocb = NULL;
24949ab747fSPaolo Bonzini     if (ret) {
25049ab747fSPaolo Bonzini         DPRINTF("IO error\n");
25149ab747fSPaolo Bonzini         scsi_command_complete(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 
26149ab747fSPaolo Bonzini     scsi_command_complete(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);
28149ab747fSPaolo Bonzini     ret = execute_command(s->conf.bs, r, SG_DXFER_TO_DEV, scsi_write_complete);
28249ab747fSPaolo Bonzini     if (ret < 0) {
28349ab747fSPaolo Bonzini         scsi_command_complete(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     DPRINTF("Command: lun=%d tag=0x%x len %zd data=0x%02x", lun, tag,
30749ab747fSPaolo Bonzini             r->req.cmd.xfer, cmd[0]);
30849ab747fSPaolo Bonzini 
30949ab747fSPaolo Bonzini #ifdef DEBUG_SCSI
31049ab747fSPaolo Bonzini     {
31149ab747fSPaolo Bonzini         int i;
31249ab747fSPaolo Bonzini         for (i = 1; i < r->req.cmd.len; i++) {
31349ab747fSPaolo Bonzini             printf(" 0x%02x", cmd[i]);
31449ab747fSPaolo Bonzini         }
31549ab747fSPaolo Bonzini         printf("\n");
31649ab747fSPaolo Bonzini     }
31749ab747fSPaolo Bonzini #endif
31849ab747fSPaolo Bonzini 
31949ab747fSPaolo Bonzini     if (r->req.cmd.xfer == 0) {
32049ab747fSPaolo Bonzini         if (r->buf != NULL)
32149ab747fSPaolo Bonzini             g_free(r->buf);
32249ab747fSPaolo Bonzini         r->buflen = 0;
32349ab747fSPaolo Bonzini         r->buf = NULL;
32449ab747fSPaolo Bonzini         /* The request is used as the AIO opaque value, so add a ref.  */
32549ab747fSPaolo Bonzini         scsi_req_ref(&r->req);
32649ab747fSPaolo Bonzini         ret = execute_command(s->conf.bs, r, SG_DXFER_NONE, scsi_command_complete);
32749ab747fSPaolo Bonzini         if (ret < 0) {
32849ab747fSPaolo Bonzini             scsi_command_complete(r, ret);
32949ab747fSPaolo Bonzini             return 0;
33049ab747fSPaolo Bonzini         }
33149ab747fSPaolo Bonzini         return 0;
33249ab747fSPaolo Bonzini     }
33349ab747fSPaolo Bonzini 
33449ab747fSPaolo Bonzini     if (r->buflen != r->req.cmd.xfer) {
33549ab747fSPaolo Bonzini         if (r->buf != NULL)
33649ab747fSPaolo Bonzini             g_free(r->buf);
33749ab747fSPaolo Bonzini         r->buf = g_malloc(r->req.cmd.xfer);
33849ab747fSPaolo Bonzini         r->buflen = r->req.cmd.xfer;
33949ab747fSPaolo Bonzini     }
34049ab747fSPaolo Bonzini 
34149ab747fSPaolo Bonzini     memset(r->buf, 0, r->buflen);
34249ab747fSPaolo Bonzini     r->len = r->req.cmd.xfer;
34349ab747fSPaolo Bonzini     if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
34449ab747fSPaolo Bonzini         r->len = 0;
34549ab747fSPaolo Bonzini         return -r->req.cmd.xfer;
34649ab747fSPaolo Bonzini     } else {
34749ab747fSPaolo Bonzini         return r->req.cmd.xfer;
34849ab747fSPaolo Bonzini     }
34949ab747fSPaolo Bonzini }
35049ab747fSPaolo Bonzini 
35149ab747fSPaolo Bonzini static int get_stream_blocksize(BlockDriverState *bdrv)
35249ab747fSPaolo Bonzini {
35349ab747fSPaolo Bonzini     uint8_t cmd[6];
35449ab747fSPaolo Bonzini     uint8_t buf[12];
35549ab747fSPaolo Bonzini     uint8_t sensebuf[8];
35649ab747fSPaolo Bonzini     sg_io_hdr_t io_header;
35749ab747fSPaolo Bonzini     int ret;
35849ab747fSPaolo Bonzini 
35949ab747fSPaolo Bonzini     memset(cmd, 0, sizeof(cmd));
36049ab747fSPaolo Bonzini     memset(buf, 0, sizeof(buf));
36149ab747fSPaolo Bonzini     cmd[0] = MODE_SENSE;
36249ab747fSPaolo Bonzini     cmd[4] = sizeof(buf);
36349ab747fSPaolo Bonzini 
36449ab747fSPaolo Bonzini     memset(&io_header, 0, sizeof(io_header));
36549ab747fSPaolo Bonzini     io_header.interface_id = 'S';
36649ab747fSPaolo Bonzini     io_header.dxfer_direction = SG_DXFER_FROM_DEV;
36749ab747fSPaolo Bonzini     io_header.dxfer_len = sizeof(buf);
36849ab747fSPaolo Bonzini     io_header.dxferp = buf;
36949ab747fSPaolo Bonzini     io_header.cmdp = cmd;
37049ab747fSPaolo Bonzini     io_header.cmd_len = sizeof(cmd);
37149ab747fSPaolo Bonzini     io_header.mx_sb_len = sizeof(sensebuf);
37249ab747fSPaolo Bonzini     io_header.sbp = sensebuf;
37349ab747fSPaolo Bonzini     io_header.timeout = 6000; /* XXX */
37449ab747fSPaolo Bonzini 
37549ab747fSPaolo Bonzini     ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
37649ab747fSPaolo Bonzini     if (ret < 0 || io_header.driver_status || io_header.host_status) {
37749ab747fSPaolo Bonzini         return -1;
37849ab747fSPaolo Bonzini     }
37949ab747fSPaolo Bonzini     return (buf[9] << 16) | (buf[10] << 8) | buf[11];
38049ab747fSPaolo Bonzini }
38149ab747fSPaolo Bonzini 
38249ab747fSPaolo Bonzini static void scsi_generic_reset(DeviceState *dev)
38349ab747fSPaolo Bonzini {
38449ab747fSPaolo Bonzini     SCSIDevice *s = SCSI_DEVICE(dev);
38549ab747fSPaolo Bonzini 
38649ab747fSPaolo Bonzini     scsi_device_purge_requests(s, SENSE_CODE(RESET));
38749ab747fSPaolo Bonzini }
38849ab747fSPaolo Bonzini 
38949ab747fSPaolo Bonzini static void scsi_destroy(SCSIDevice *s)
39049ab747fSPaolo Bonzini {
39149ab747fSPaolo Bonzini     scsi_device_purge_requests(s, SENSE_CODE(NO_SENSE));
39249ab747fSPaolo Bonzini     blockdev_mark_auto_del(s->conf.bs);
39349ab747fSPaolo Bonzini }
39449ab747fSPaolo Bonzini 
39549ab747fSPaolo Bonzini static int scsi_generic_initfn(SCSIDevice *s)
39649ab747fSPaolo Bonzini {
397*6ee143a0SPaolo Bonzini     int rc;
39849ab747fSPaolo Bonzini     int sg_version;
39949ab747fSPaolo Bonzini     struct sg_scsi_id scsiid;
40049ab747fSPaolo Bonzini 
40149ab747fSPaolo Bonzini     if (!s->conf.bs) {
40249ab747fSPaolo Bonzini         error_report("drive property not set");
40349ab747fSPaolo Bonzini         return -1;
40449ab747fSPaolo Bonzini     }
40549ab747fSPaolo Bonzini 
40649ab747fSPaolo Bonzini     if (bdrv_get_on_error(s->conf.bs, 0) != BLOCKDEV_ON_ERROR_ENOSPC) {
40749ab747fSPaolo Bonzini         error_report("Device doesn't support drive option werror");
40849ab747fSPaolo Bonzini         return -1;
40949ab747fSPaolo Bonzini     }
41049ab747fSPaolo Bonzini     if (bdrv_get_on_error(s->conf.bs, 1) != BLOCKDEV_ON_ERROR_REPORT) {
41149ab747fSPaolo Bonzini         error_report("Device doesn't support drive option rerror");
41249ab747fSPaolo Bonzini         return -1;
41349ab747fSPaolo Bonzini     }
41449ab747fSPaolo Bonzini 
41549ab747fSPaolo Bonzini     /* check we are using a driver managing SG_IO (version 3 and after */
416*6ee143a0SPaolo Bonzini     rc = bdrv_ioctl(s->conf.bs, SG_GET_VERSION_NUM, &sg_version);
417*6ee143a0SPaolo Bonzini     if (rc < 0) {
418*6ee143a0SPaolo Bonzini         error_report("cannot get SG_IO version number: %s.  "
419*6ee143a0SPaolo Bonzini                      "Is this a SCSI device?",
420*6ee143a0SPaolo Bonzini                      strerror(-rc));
42149ab747fSPaolo Bonzini         return -1;
42249ab747fSPaolo Bonzini     }
42349ab747fSPaolo Bonzini     if (sg_version < 30000) {
42449ab747fSPaolo Bonzini         error_report("scsi generic interface too old");
42549ab747fSPaolo Bonzini         return -1;
42649ab747fSPaolo Bonzini     }
42749ab747fSPaolo Bonzini 
42849ab747fSPaolo Bonzini     /* get LUN of the /dev/sg? */
42949ab747fSPaolo Bonzini     if (bdrv_ioctl(s->conf.bs, SG_GET_SCSI_ID, &scsiid)) {
43049ab747fSPaolo Bonzini         error_report("SG_GET_SCSI_ID ioctl failed");
43149ab747fSPaolo Bonzini         return -1;
43249ab747fSPaolo Bonzini     }
43349ab747fSPaolo Bonzini 
43449ab747fSPaolo Bonzini     /* define device state */
43549ab747fSPaolo Bonzini     s->type = scsiid.scsi_type;
43649ab747fSPaolo Bonzini     DPRINTF("device type %d\n", s->type);
43749ab747fSPaolo Bonzini     if (s->type == TYPE_DISK || s->type == TYPE_ROM) {
43849ab747fSPaolo Bonzini         add_boot_device_path(s->conf.bootindex, &s->qdev, NULL);
43949ab747fSPaolo Bonzini     }
44049ab747fSPaolo Bonzini 
44149ab747fSPaolo Bonzini     switch (s->type) {
44249ab747fSPaolo Bonzini     case TYPE_TAPE:
44349ab747fSPaolo Bonzini         s->blocksize = get_stream_blocksize(s->conf.bs);
44449ab747fSPaolo Bonzini         if (s->blocksize == -1) {
44549ab747fSPaolo Bonzini             s->blocksize = 0;
44649ab747fSPaolo Bonzini         }
44749ab747fSPaolo Bonzini         break;
44849ab747fSPaolo Bonzini 
44949ab747fSPaolo Bonzini         /* Make a guess for block devices, we'll fix it when the guest sends.
45049ab747fSPaolo Bonzini          * READ CAPACITY.  If they don't, they likely would assume these sizes
45149ab747fSPaolo Bonzini          * anyway. (TODO: they could also send MODE SENSE).
45249ab747fSPaolo Bonzini          */
45349ab747fSPaolo Bonzini     case TYPE_ROM:
45449ab747fSPaolo Bonzini     case TYPE_WORM:
45549ab747fSPaolo Bonzini         s->blocksize = 2048;
45649ab747fSPaolo Bonzini         break;
45749ab747fSPaolo Bonzini     default:
45849ab747fSPaolo Bonzini         s->blocksize = 512;
45949ab747fSPaolo Bonzini         break;
46049ab747fSPaolo Bonzini     }
46149ab747fSPaolo Bonzini 
46249ab747fSPaolo Bonzini     DPRINTF("block size %d\n", s->blocksize);
46349ab747fSPaolo Bonzini     return 0;
46449ab747fSPaolo Bonzini }
46549ab747fSPaolo Bonzini 
46649ab747fSPaolo Bonzini const SCSIReqOps scsi_generic_req_ops = {
46749ab747fSPaolo Bonzini     .size         = sizeof(SCSIGenericReq),
46849ab747fSPaolo Bonzini     .free_req     = scsi_free_request,
46949ab747fSPaolo Bonzini     .send_command = scsi_send_command,
47049ab747fSPaolo Bonzini     .read_data    = scsi_read_data,
47149ab747fSPaolo Bonzini     .write_data   = scsi_write_data,
47249ab747fSPaolo Bonzini     .cancel_io    = scsi_cancel_io,
47349ab747fSPaolo Bonzini     .get_buf      = scsi_get_buf,
47449ab747fSPaolo Bonzini     .load_request = scsi_generic_load_request,
47549ab747fSPaolo Bonzini     .save_request = scsi_generic_save_request,
47649ab747fSPaolo Bonzini };
47749ab747fSPaolo Bonzini 
47849ab747fSPaolo Bonzini static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
47949ab747fSPaolo Bonzini                                      uint8_t *buf, void *hba_private)
48049ab747fSPaolo Bonzini {
48149ab747fSPaolo Bonzini     SCSIRequest *req;
48249ab747fSPaolo Bonzini 
48349ab747fSPaolo Bonzini     req = scsi_req_alloc(&scsi_generic_req_ops, d, tag, lun, hba_private);
48449ab747fSPaolo Bonzini     return req;
48549ab747fSPaolo Bonzini }
48649ab747fSPaolo Bonzini 
48749ab747fSPaolo Bonzini static Property scsi_generic_properties[] = {
48849ab747fSPaolo Bonzini     DEFINE_PROP_DRIVE("drive", SCSIDevice, conf.bs),
48949ab747fSPaolo Bonzini     DEFINE_PROP_INT32("bootindex", SCSIDevice, conf.bootindex, -1),
49049ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
49149ab747fSPaolo Bonzini };
49249ab747fSPaolo Bonzini 
49349ab747fSPaolo Bonzini static void scsi_generic_class_initfn(ObjectClass *klass, void *data)
49449ab747fSPaolo Bonzini {
49549ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
49649ab747fSPaolo Bonzini     SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
49749ab747fSPaolo Bonzini 
49849ab747fSPaolo Bonzini     sc->init         = scsi_generic_initfn;
49949ab747fSPaolo Bonzini     sc->destroy      = scsi_destroy;
50049ab747fSPaolo Bonzini     sc->alloc_req    = scsi_new_request;
50149ab747fSPaolo Bonzini     dc->fw_name = "disk";
50249ab747fSPaolo Bonzini     dc->desc = "pass through generic scsi device (/dev/sg*)";
50349ab747fSPaolo Bonzini     dc->reset = scsi_generic_reset;
50449ab747fSPaolo Bonzini     dc->props = scsi_generic_properties;
50549ab747fSPaolo Bonzini     dc->vmsd  = &vmstate_scsi_device;
50649ab747fSPaolo Bonzini }
50749ab747fSPaolo Bonzini 
50849ab747fSPaolo Bonzini static const TypeInfo scsi_generic_info = {
50949ab747fSPaolo Bonzini     .name          = "scsi-generic",
51049ab747fSPaolo Bonzini     .parent        = TYPE_SCSI_DEVICE,
51149ab747fSPaolo Bonzini     .instance_size = sizeof(SCSIDevice),
51249ab747fSPaolo Bonzini     .class_init    = scsi_generic_class_initfn,
51349ab747fSPaolo Bonzini };
51449ab747fSPaolo Bonzini 
51549ab747fSPaolo Bonzini static void scsi_generic_register_types(void)
51649ab747fSPaolo Bonzini {
51749ab747fSPaolo Bonzini     type_register_static(&scsi_generic_info);
51849ab747fSPaolo Bonzini }
51949ab747fSPaolo Bonzini 
52049ab747fSPaolo Bonzini type_init(scsi_generic_register_types)
52149ab747fSPaolo Bonzini 
52249ab747fSPaolo Bonzini #endif /* __linux__ */
523