xref: /qemu/hw/scsi/scsi-generic.c (revision 0b8fa32f)
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"
16856dfd8aSMarkus Armbruster #include "qemu/ctype.h"
1749ab747fSPaolo Bonzini #include "qemu/error-report.h"
18*0b8fa32fSMarkus Armbruster #include "qemu/module.h"
1949ab747fSPaolo Bonzini #include "hw/scsi/scsi.h"
203d4a8bf0SPaolo Bonzini #include "hw/scsi/emulation.h"
214be74634SMarkus Armbruster #include "sysemu/block-backend.h"
2256853498SLaurent Vivier #include "trace.h"
2349ab747fSPaolo Bonzini 
2449ab747fSPaolo Bonzini #ifdef __linux__
2549ab747fSPaolo Bonzini 
2649ab747fSPaolo Bonzini #include <scsi/sg.h>
2708e2c9f1SPaolo Bonzini #include "scsi/constants.h"
2849ab747fSPaolo Bonzini 
2949ab747fSPaolo Bonzini #ifndef MAX_UINT
3049ab747fSPaolo Bonzini #define MAX_UINT ((unsigned int)-1)
3149ab747fSPaolo Bonzini #endif
3249ab747fSPaolo Bonzini 
3349ab747fSPaolo Bonzini typedef struct SCSIGenericReq {
3449ab747fSPaolo Bonzini     SCSIRequest req;
3549ab747fSPaolo Bonzini     uint8_t *buf;
3649ab747fSPaolo Bonzini     int buflen;
3749ab747fSPaolo Bonzini     int len;
3849ab747fSPaolo Bonzini     sg_io_hdr_t io_header;
3949ab747fSPaolo Bonzini } SCSIGenericReq;
4049ab747fSPaolo Bonzini 
4149ab747fSPaolo Bonzini static void scsi_generic_save_request(QEMUFile *f, SCSIRequest *req)
4249ab747fSPaolo Bonzini {
4349ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
4449ab747fSPaolo Bonzini 
4549ab747fSPaolo Bonzini     qemu_put_sbe32s(f, &r->buflen);
4649ab747fSPaolo Bonzini     if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
4749ab747fSPaolo Bonzini         assert(!r->req.sg);
4849ab747fSPaolo Bonzini         qemu_put_buffer(f, r->buf, r->req.cmd.xfer);
4949ab747fSPaolo Bonzini     }
5049ab747fSPaolo Bonzini }
5149ab747fSPaolo Bonzini 
5249ab747fSPaolo Bonzini static void scsi_generic_load_request(QEMUFile *f, SCSIRequest *req)
5349ab747fSPaolo Bonzini {
5449ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
5549ab747fSPaolo Bonzini 
5649ab747fSPaolo Bonzini     qemu_get_sbe32s(f, &r->buflen);
5749ab747fSPaolo Bonzini     if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
5849ab747fSPaolo Bonzini         assert(!r->req.sg);
5949ab747fSPaolo Bonzini         qemu_get_buffer(f, r->buf, r->req.cmd.xfer);
6049ab747fSPaolo Bonzini     }
6149ab747fSPaolo Bonzini }
6249ab747fSPaolo Bonzini 
6349ab747fSPaolo Bonzini static void scsi_free_request(SCSIRequest *req)
6449ab747fSPaolo Bonzini {
6549ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
6649ab747fSPaolo Bonzini 
6749ab747fSPaolo Bonzini     g_free(r->buf);
6849ab747fSPaolo Bonzini }
6949ab747fSPaolo Bonzini 
7049ab747fSPaolo Bonzini /* Helper function for command completion.  */
71fa0d653bSPaolo Bonzini static void scsi_command_complete_noio(SCSIGenericReq *r, int ret)
7249ab747fSPaolo Bonzini {
7349ab747fSPaolo Bonzini     int status;
741ead6b4eSPaolo Bonzini     SCSISense sense;
7549ab747fSPaolo Bonzini 
76fa0d653bSPaolo Bonzini     assert(r->req.aiocb == NULL);
77fa0d653bSPaolo Bonzini 
786c25fa6cSFam Zheng     if (r->req.io_canceled) {
79d5776465SFam Zheng         scsi_req_cancel_complete(&r->req);
806c25fa6cSFam Zheng         goto done;
816c25fa6cSFam Zheng     }
821ead6b4eSPaolo Bonzini     status = sg_io_sense_from_errno(-ret, &r->io_header, &sense);
831ead6b4eSPaolo Bonzini     if (status == CHECK_CONDITION) {
8449ab747fSPaolo Bonzini         if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
8549ab747fSPaolo Bonzini             r->req.sense_len = r->io_header.sb_len_wr;
861ead6b4eSPaolo Bonzini         } else {
871ead6b4eSPaolo Bonzini             scsi_req_build_sense(&r->req, sense);
881ead6b4eSPaolo Bonzini         }
8949ab747fSPaolo Bonzini     }
9049ab747fSPaolo Bonzini 
9156853498SLaurent Vivier     trace_scsi_generic_command_complete_noio(r, r->req.tag, status);
9249ab747fSPaolo Bonzini 
9349ab747fSPaolo Bonzini     scsi_req_complete(&r->req, status);
946c25fa6cSFam Zheng done:
9549ab747fSPaolo Bonzini     scsi_req_unref(&r->req);
9649ab747fSPaolo Bonzini }
9749ab747fSPaolo Bonzini 
98fa0d653bSPaolo Bonzini static void scsi_command_complete(void *opaque, int ret)
99fa0d653bSPaolo Bonzini {
100fa0d653bSPaolo Bonzini     SCSIGenericReq *r = (SCSIGenericReq *)opaque;
101b9e413ddSPaolo Bonzini     SCSIDevice *s = r->req.dev;
102fa0d653bSPaolo Bonzini 
103fa0d653bSPaolo Bonzini     assert(r->req.aiocb != NULL);
104fa0d653bSPaolo Bonzini     r->req.aiocb = NULL;
105b9e413ddSPaolo Bonzini 
106b9e413ddSPaolo Bonzini     aio_context_acquire(blk_get_aio_context(s->conf.blk));
107fa0d653bSPaolo Bonzini     scsi_command_complete_noio(r, ret);
108b9e413ddSPaolo Bonzini     aio_context_release(blk_get_aio_context(s->conf.blk));
109fa0d653bSPaolo Bonzini }
110fa0d653bSPaolo Bonzini 
1114be74634SMarkus Armbruster static int execute_command(BlockBackend *blk,
11249ab747fSPaolo Bonzini                            SCSIGenericReq *r, int direction,
113097310b5SMarkus Armbruster                            BlockCompletionFunc *complete)
11449ab747fSPaolo Bonzini {
11549ab747fSPaolo Bonzini     r->io_header.interface_id = 'S';
11649ab747fSPaolo Bonzini     r->io_header.dxfer_direction = direction;
11749ab747fSPaolo Bonzini     r->io_header.dxferp = r->buf;
11849ab747fSPaolo Bonzini     r->io_header.dxfer_len = r->buflen;
11949ab747fSPaolo Bonzini     r->io_header.cmdp = r->req.cmd.buf;
12049ab747fSPaolo Bonzini     r->io_header.cmd_len = r->req.cmd.len;
12149ab747fSPaolo Bonzini     r->io_header.mx_sb_len = sizeof(r->req.sense);
12249ab747fSPaolo Bonzini     r->io_header.sbp = r->req.sense;
12349ab747fSPaolo Bonzini     r->io_header.timeout = MAX_UINT;
12449ab747fSPaolo Bonzini     r->io_header.usr_ptr = r;
12549ab747fSPaolo Bonzini     r->io_header.flags |= SG_FLAG_DIRECT_IO;
12649ab747fSPaolo Bonzini 
1274be74634SMarkus Armbruster     r->req.aiocb = blk_aio_ioctl(blk, SG_IO, &r->io_header, complete, r);
128d836f8d3SPavel Hrdina     if (r->req.aiocb == NULL) {
129d836f8d3SPavel Hrdina         return -EIO;
130d836f8d3SPavel Hrdina     }
13149ab747fSPaolo Bonzini 
13249ab747fSPaolo Bonzini     return 0;
13349ab747fSPaolo Bonzini }
13449ab747fSPaolo Bonzini 
1350a96ca24SDaniel Henrique Barboza static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s)
1360a96ca24SDaniel Henrique Barboza {
1376c219fc8SPaolo Bonzini     uint8_t page, page_idx;
138a71c775bSDaniel Henrique Barboza 
1390a96ca24SDaniel Henrique Barboza     /*
1400a96ca24SDaniel Henrique Barboza      *  EVPD set to zero returns the standard INQUIRY data.
1410a96ca24SDaniel Henrique Barboza      *
1420a96ca24SDaniel Henrique Barboza      *  Check if scsi_version is unset (-1) to avoid re-defining it
1430a96ca24SDaniel Henrique Barboza      *  each time an INQUIRY with standard data is received.
1440a96ca24SDaniel Henrique Barboza      *  scsi_version is initialized with -1 in scsi_generic_reset
1450a96ca24SDaniel Henrique Barboza      *  and scsi_disk_reset, making sure that we'll set the
1460a96ca24SDaniel Henrique Barboza      *  scsi_version after a reset. If the version field of the
1470a96ca24SDaniel Henrique Barboza      *  INQUIRY response somehow changes after a guest reboot,
1480a96ca24SDaniel Henrique Barboza      *  we'll be able to keep track of it.
1490a96ca24SDaniel Henrique Barboza      *
1500a96ca24SDaniel Henrique Barboza      *  On SCSI-2 and older, first 3 bits of byte 2 is the
1510a96ca24SDaniel Henrique Barboza      *  ANSI-approved version, while on later versions the
1520a96ca24SDaniel Henrique Barboza      *  whole byte 2 contains the version. Check if we're dealing
1530a96ca24SDaniel Henrique Barboza      *  with a newer version and, in that case, assign the
1540a96ca24SDaniel Henrique Barboza      *  whole byte.
1550a96ca24SDaniel Henrique Barboza      */
1560a96ca24SDaniel Henrique Barboza     if (s->scsi_version == -1 && !(r->req.cmd.buf[1] & 0x01)) {
1570a96ca24SDaniel Henrique Barboza         s->scsi_version = r->buf[2] & 0x07;
1580a96ca24SDaniel Henrique Barboza         if (s->scsi_version > 2) {
1590a96ca24SDaniel Henrique Barboza             s->scsi_version = r->buf[2];
1600a96ca24SDaniel Henrique Barboza         }
1610a96ca24SDaniel Henrique Barboza     }
162a71c775bSDaniel Henrique Barboza 
163a71c775bSDaniel Henrique Barboza     if (s->type == TYPE_DISK && (r->req.cmd.buf[1] & 0x01)) {
164a71c775bSDaniel Henrique Barboza         page = r->req.cmd.buf[2];
165a71c775bSDaniel Henrique Barboza         if (page == 0xb0) {
1660a96ca24SDaniel Henrique Barboza             uint32_t max_transfer =
1670a96ca24SDaniel Henrique Barboza                 blk_get_max_transfer(s->conf.blk) / s->blocksize;
1680a96ca24SDaniel Henrique Barboza 
1690a96ca24SDaniel Henrique Barboza             assert(max_transfer);
1700a96ca24SDaniel Henrique Barboza             stl_be_p(&r->buf[8], max_transfer);
1710a96ca24SDaniel Henrique Barboza             /* Also take care of the opt xfer len. */
1720a96ca24SDaniel Henrique Barboza             stl_be_p(&r->buf[12],
1730a96ca24SDaniel Henrique Barboza                     MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12])));
174e909ff93SPaolo Bonzini         } else if (s->needs_vpd_bl_emulation && page == 0x00 && r->buflen >= 4) {
175a71c775bSDaniel Henrique Barboza             /*
176a71c775bSDaniel Henrique Barboza              * Now we're capable of supplying the VPD Block Limits
177a71c775bSDaniel Henrique Barboza              * response if the hardware can't. Add it in the INQUIRY
178a71c775bSDaniel Henrique Barboza              * Supported VPD pages response in case we are using the
179a71c775bSDaniel Henrique Barboza              * emulation for this device.
180a71c775bSDaniel Henrique Barboza              *
181a71c775bSDaniel Henrique Barboza              * This way, the guest kernel will be aware of the support
182a71c775bSDaniel Henrique Barboza              * and will use it to proper setup the SCSI device.
1836c219fc8SPaolo Bonzini              *
1846c219fc8SPaolo Bonzini              * VPD page numbers must be sorted, so insert 0xb0 at the
185e909ff93SPaolo Bonzini              * right place with an in-place insert.  When the while loop
186e909ff93SPaolo Bonzini              * begins the device response is at r[0] to r[page_idx - 1].
187a71c775bSDaniel Henrique Barboza              */
188e909ff93SPaolo Bonzini             page_idx = lduw_be_p(r->buf + 2) + 4;
189e909ff93SPaolo Bonzini             page_idx = MIN(page_idx, r->buflen);
190e909ff93SPaolo Bonzini             while (page_idx > 4 && r->buf[page_idx - 1] >= 0xb0) {
1916c219fc8SPaolo Bonzini                 if (page_idx < r->buflen) {
1926c219fc8SPaolo Bonzini                     r->buf[page_idx] = r->buf[page_idx - 1];
1936c219fc8SPaolo Bonzini                 }
194e909ff93SPaolo Bonzini                 page_idx--;
1956c219fc8SPaolo Bonzini             }
196e909ff93SPaolo Bonzini             if (page_idx < r->buflen) {
1976c219fc8SPaolo Bonzini                 r->buf[page_idx] = 0xb0;
198e909ff93SPaolo Bonzini             }
1996c219fc8SPaolo Bonzini             stw_be_p(r->buf + 2, lduw_be_p(r->buf + 2) + 1);
2000a96ca24SDaniel Henrique Barboza         }
2010a96ca24SDaniel Henrique Barboza     }
202a71c775bSDaniel Henrique Barboza }
203a71c775bSDaniel Henrique Barboza 
2043d4a8bf0SPaolo Bonzini static int scsi_generic_emulate_block_limits(SCSIGenericReq *r, SCSIDevice *s)
205a71c775bSDaniel Henrique Barboza {
2063d4a8bf0SPaolo Bonzini     int len;
2073d4a8bf0SPaolo Bonzini     uint8_t buf[64];
2083d4a8bf0SPaolo Bonzini 
2093d4a8bf0SPaolo Bonzini     SCSIBlockLimits bl = {
2103d4a8bf0SPaolo Bonzini         .max_io_sectors = blk_get_max_transfer(s->conf.blk) / s->blocksize
2113d4a8bf0SPaolo Bonzini     };
2123d4a8bf0SPaolo Bonzini 
2133d4a8bf0SPaolo Bonzini     memset(r->buf, 0, r->buflen);
2143d4a8bf0SPaolo Bonzini     stb_p(buf, s->type);
2153d4a8bf0SPaolo Bonzini     stb_p(buf + 1, 0xb0);
2163d4a8bf0SPaolo Bonzini     len = scsi_emulate_block_limits(buf + 4, &bl);
2173d4a8bf0SPaolo Bonzini     assert(len <= sizeof(buf) - 4);
2183d4a8bf0SPaolo Bonzini     stw_be_p(buf + 2, len);
2193d4a8bf0SPaolo Bonzini 
2203d4a8bf0SPaolo Bonzini     memcpy(r->buf, buf, MIN(r->buflen, len + 4));
2213d4a8bf0SPaolo Bonzini 
222a71c775bSDaniel Henrique Barboza     r->io_header.sb_len_wr = 0;
223a71c775bSDaniel Henrique Barboza 
224a71c775bSDaniel Henrique Barboza     /*
225a71c775bSDaniel Henrique Barboza     * We have valid contents in the reply buffer but the
226a71c775bSDaniel Henrique Barboza     * io_header can report a sense error coming from
227a71c775bSDaniel Henrique Barboza     * the hardware in scsi_command_complete_noio. Clean
228a71c775bSDaniel Henrique Barboza     * up the io_header to avoid reporting it.
229a71c775bSDaniel Henrique Barboza     */
230a71c775bSDaniel Henrique Barboza     r->io_header.driver_status = 0;
231a71c775bSDaniel Henrique Barboza     r->io_header.status = 0;
232a71c775bSDaniel Henrique Barboza 
233a71c775bSDaniel Henrique Barboza     return r->buflen;
234a71c775bSDaniel Henrique Barboza }
2350a96ca24SDaniel Henrique Barboza 
23649ab747fSPaolo Bonzini static void scsi_read_complete(void * opaque, int ret)
23749ab747fSPaolo Bonzini {
23849ab747fSPaolo Bonzini     SCSIGenericReq *r = (SCSIGenericReq *)opaque;
23949ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
24049ab747fSPaolo Bonzini     int len;
24149ab747fSPaolo Bonzini 
242fa0d653bSPaolo Bonzini     assert(r->req.aiocb != NULL);
24349ab747fSPaolo Bonzini     r->req.aiocb = NULL;
244fa0d653bSPaolo Bonzini 
245b9e413ddSPaolo Bonzini     aio_context_acquire(blk_get_aio_context(s->conf.blk));
246b9e413ddSPaolo Bonzini 
2476c25fa6cSFam Zheng     if (ret || r->req.io_canceled) {
248fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, ret);
249b9e413ddSPaolo Bonzini         goto done;
25049ab747fSPaolo Bonzini     }
251fa0d653bSPaolo Bonzini 
25249ab747fSPaolo Bonzini     len = r->io_header.dxfer_len - r->io_header.resid;
25356853498SLaurent Vivier     trace_scsi_generic_read_complete(r->req.tag, len);
25449ab747fSPaolo Bonzini 
25549ab747fSPaolo Bonzini     r->len = -1;
256a71c775bSDaniel Henrique Barboza 
257a71c775bSDaniel Henrique Barboza     /*
258a71c775bSDaniel Henrique Barboza      * Check if this is a VPD Block Limits request that
259a71c775bSDaniel Henrique Barboza      * resulted in sense error but would need emulation.
260a71c775bSDaniel Henrique Barboza      * In this case, emulate a valid VPD response.
261a71c775bSDaniel Henrique Barboza      */
262763c5687SPaolo Bonzini     if (s->needs_vpd_bl_emulation && ret == 0 &&
263763c5687SPaolo Bonzini         (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) &&
2643d4a8bf0SPaolo Bonzini         r->req.cmd.buf[0] == INQUIRY &&
2653d4a8bf0SPaolo Bonzini         (r->req.cmd.buf[1] & 0x01) &&
2663d4a8bf0SPaolo Bonzini         r->req.cmd.buf[2] == 0xb0) {
267763c5687SPaolo Bonzini         SCSISense sense =
268763c5687SPaolo Bonzini             scsi_parse_sense_buf(r->req.sense, r->io_header.sb_len_wr);
269763c5687SPaolo Bonzini         if (sense.key == ILLEGAL_REQUEST) {
2703d4a8bf0SPaolo Bonzini             len = scsi_generic_emulate_block_limits(r, s);
271a71c775bSDaniel Henrique Barboza             /*
272a71c775bSDaniel Henrique Barboza              * No need to let scsi_read_complete go on and handle an
273a71c775bSDaniel Henrique Barboza              * INQUIRY VPD BL request we created manually.
274a71c775bSDaniel Henrique Barboza              */
275a71c775bSDaniel Henrique Barboza             goto req_complete;
276a71c775bSDaniel Henrique Barboza         }
277a71c775bSDaniel Henrique Barboza     }
278a71c775bSDaniel Henrique Barboza 
27949ab747fSPaolo Bonzini     if (len == 0) {
280fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, 0);
281b9e413ddSPaolo Bonzini         goto done;
282fa0d653bSPaolo Bonzini     }
283fa0d653bSPaolo Bonzini 
28449ab747fSPaolo Bonzini     /* Snoop READ CAPACITY output to set the blocksize.  */
28553254e56SPaolo Bonzini     if (r->req.cmd.buf[0] == READ_CAPACITY_10 &&
28653254e56SPaolo Bonzini         (ldl_be_p(&r->buf[0]) != 0xffffffffU || s->max_lba == 0)) {
28749ab747fSPaolo Bonzini         s->blocksize = ldl_be_p(&r->buf[4]);
28853254e56SPaolo Bonzini         s->max_lba = ldl_be_p(&r->buf[0]) & 0xffffffffULL;
28949ab747fSPaolo Bonzini     } else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 &&
29049ab747fSPaolo Bonzini                (r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) {
29149ab747fSPaolo Bonzini         s->blocksize = ldl_be_p(&r->buf[8]);
29249ab747fSPaolo Bonzini         s->max_lba = ldq_be_p(&r->buf[0]);
29349ab747fSPaolo Bonzini     }
2944be74634SMarkus Armbruster     blk_set_guest_block_size(s->conf.blk, s->blocksize);
29549ab747fSPaolo Bonzini 
2960eb2baebSPaolo Bonzini     /* Patch MODE SENSE device specific parameters if the BDS is opened
2970eb2baebSPaolo Bonzini      * readonly.
2980eb2baebSPaolo Bonzini      */
2990eb2baebSPaolo Bonzini     if ((s->type == TYPE_DISK || s->type == TYPE_TAPE) &&
3000eb2baebSPaolo Bonzini         blk_is_read_only(s->conf.blk) &&
3010eb2baebSPaolo Bonzini         (r->req.cmd.buf[0] == MODE_SENSE ||
3020eb2baebSPaolo Bonzini          r->req.cmd.buf[0] == MODE_SENSE_10) &&
3030eb2baebSPaolo Bonzini         (r->req.cmd.buf[1] & 0x8) == 0) {
3040eb2baebSPaolo Bonzini         if (r->req.cmd.buf[0] == MODE_SENSE) {
3050eb2baebSPaolo Bonzini             r->buf[2] |= 0x80;
3060eb2baebSPaolo Bonzini         } else  {
3070eb2baebSPaolo Bonzini             r->buf[3] |= 0x80;
3080eb2baebSPaolo Bonzini         }
3090eb2baebSPaolo Bonzini     }
31029e560f0SDaniel Henrique Barboza     if (r->req.cmd.buf[0] == INQUIRY) {
3110a96ca24SDaniel Henrique Barboza         scsi_handle_inquiry_reply(r, s);
31229e560f0SDaniel Henrique Barboza     }
313a71c775bSDaniel Henrique Barboza 
314a71c775bSDaniel Henrique Barboza req_complete:
31549ab747fSPaolo Bonzini     scsi_req_data(&r->req, len);
31649ab747fSPaolo Bonzini     scsi_req_unref(&r->req);
317b9e413ddSPaolo Bonzini 
318b9e413ddSPaolo Bonzini done:
319b9e413ddSPaolo Bonzini     aio_context_release(blk_get_aio_context(s->conf.blk));
32049ab747fSPaolo Bonzini }
32149ab747fSPaolo Bonzini 
32249ab747fSPaolo Bonzini /* Read more data from scsi device into buffer.  */
32349ab747fSPaolo Bonzini static void scsi_read_data(SCSIRequest *req)
32449ab747fSPaolo Bonzini {
32549ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
32649ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
32749ab747fSPaolo Bonzini     int ret;
32849ab747fSPaolo Bonzini 
32956853498SLaurent Vivier     trace_scsi_generic_read_data(req->tag);
33049ab747fSPaolo Bonzini 
33149ab747fSPaolo Bonzini     /* The request is used as the AIO opaque value, so add a ref.  */
33249ab747fSPaolo Bonzini     scsi_req_ref(&r->req);
33349ab747fSPaolo Bonzini     if (r->len == -1) {
334fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, 0);
33549ab747fSPaolo Bonzini         return;
33649ab747fSPaolo Bonzini     }
33749ab747fSPaolo Bonzini 
3384be74634SMarkus Armbruster     ret = execute_command(s->conf.blk, r, SG_DXFER_FROM_DEV,
3394be74634SMarkus Armbruster                           scsi_read_complete);
34049ab747fSPaolo Bonzini     if (ret < 0) {
341fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, ret);
34249ab747fSPaolo Bonzini     }
34349ab747fSPaolo Bonzini }
34449ab747fSPaolo Bonzini 
34549ab747fSPaolo Bonzini static void scsi_write_complete(void * opaque, int ret)
34649ab747fSPaolo Bonzini {
34749ab747fSPaolo Bonzini     SCSIGenericReq *r = (SCSIGenericReq *)opaque;
34849ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
34949ab747fSPaolo Bonzini 
35056853498SLaurent Vivier     trace_scsi_generic_write_complete(ret);
351fa0d653bSPaolo Bonzini 
352fa0d653bSPaolo Bonzini     assert(r->req.aiocb != NULL);
35349ab747fSPaolo Bonzini     r->req.aiocb = NULL;
354fa0d653bSPaolo Bonzini 
355b9e413ddSPaolo Bonzini     aio_context_acquire(blk_get_aio_context(s->conf.blk));
356b9e413ddSPaolo Bonzini 
3576c25fa6cSFam Zheng     if (ret || r->req.io_canceled) {
358fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, ret);
359b9e413ddSPaolo Bonzini         goto done;
36049ab747fSPaolo Bonzini     }
36149ab747fSPaolo Bonzini 
36249ab747fSPaolo Bonzini     if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 &&
36349ab747fSPaolo Bonzini         s->type == TYPE_TAPE) {
36449ab747fSPaolo Bonzini         s->blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11];
36556853498SLaurent Vivier         trace_scsi_generic_write_complete_blocksize(s->blocksize);
36649ab747fSPaolo Bonzini     }
36749ab747fSPaolo Bonzini 
368fa0d653bSPaolo Bonzini     scsi_command_complete_noio(r, ret);
369b9e413ddSPaolo Bonzini 
370b9e413ddSPaolo Bonzini done:
371b9e413ddSPaolo Bonzini     aio_context_release(blk_get_aio_context(s->conf.blk));
37249ab747fSPaolo Bonzini }
37349ab747fSPaolo Bonzini 
37449ab747fSPaolo Bonzini /* Write data to a scsi device.  Returns nonzero on failure.
37549ab747fSPaolo Bonzini    The transfer may complete asynchronously.  */
37649ab747fSPaolo Bonzini static void scsi_write_data(SCSIRequest *req)
37749ab747fSPaolo Bonzini {
37849ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
37949ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
38049ab747fSPaolo Bonzini     int ret;
38149ab747fSPaolo Bonzini 
38256853498SLaurent Vivier     trace_scsi_generic_write_data(req->tag);
38349ab747fSPaolo Bonzini     if (r->len == 0) {
38449ab747fSPaolo Bonzini         r->len = r->buflen;
38549ab747fSPaolo Bonzini         scsi_req_data(&r->req, r->len);
38649ab747fSPaolo Bonzini         return;
38749ab747fSPaolo Bonzini     }
38849ab747fSPaolo Bonzini 
38949ab747fSPaolo Bonzini     /* The request is used as the AIO opaque value, so add a ref.  */
39049ab747fSPaolo Bonzini     scsi_req_ref(&r->req);
3914be74634SMarkus Armbruster     ret = execute_command(s->conf.blk, r, SG_DXFER_TO_DEV, scsi_write_complete);
39249ab747fSPaolo Bonzini     if (ret < 0) {
393fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, ret);
39449ab747fSPaolo Bonzini     }
39549ab747fSPaolo Bonzini }
39649ab747fSPaolo Bonzini 
39749ab747fSPaolo Bonzini /* Return a pointer to the data buffer.  */
39849ab747fSPaolo Bonzini static uint8_t *scsi_get_buf(SCSIRequest *req)
39949ab747fSPaolo Bonzini {
40049ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
40149ab747fSPaolo Bonzini 
40249ab747fSPaolo Bonzini     return r->buf;
40349ab747fSPaolo Bonzini }
40449ab747fSPaolo Bonzini 
40556853498SLaurent Vivier static void scsi_generic_command_dump(uint8_t *cmd, int len)
40656853498SLaurent Vivier {
40756853498SLaurent Vivier     int i;
40856853498SLaurent Vivier     char *line_buffer, *p;
40956853498SLaurent Vivier 
41056853498SLaurent Vivier     line_buffer = g_malloc(len * 5 + 1);
41156853498SLaurent Vivier 
41256853498SLaurent Vivier     for (i = 0, p = line_buffer; i < len; i++) {
41356853498SLaurent Vivier         p += sprintf(p, " 0x%02x", cmd[i]);
41456853498SLaurent Vivier     }
41556853498SLaurent Vivier     trace_scsi_generic_send_command(line_buffer);
41656853498SLaurent Vivier 
41756853498SLaurent Vivier     g_free(line_buffer);
41856853498SLaurent Vivier }
41956853498SLaurent Vivier 
42049ab747fSPaolo Bonzini /* Execute a scsi command.  Returns the length of the data expected by the
42149ab747fSPaolo Bonzini    command.  This will be Positive for data transfers from the device
42249ab747fSPaolo Bonzini    (eg. disk reads), negative for transfers to the device (eg. disk writes),
42349ab747fSPaolo Bonzini    and zero if the command does not transfer any data.  */
42449ab747fSPaolo Bonzini 
42549ab747fSPaolo Bonzini static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
42649ab747fSPaolo Bonzini {
42749ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
42849ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
42949ab747fSPaolo Bonzini     int ret;
43049ab747fSPaolo Bonzini 
43156853498SLaurent Vivier     if (trace_event_get_state_backends(TRACE_SCSI_GENERIC_SEND_COMMAND)) {
43256853498SLaurent Vivier         scsi_generic_command_dump(cmd, r->req.cmd.len);
43349ab747fSPaolo Bonzini     }
43449ab747fSPaolo Bonzini 
43549ab747fSPaolo Bonzini     if (r->req.cmd.xfer == 0) {
43649ab747fSPaolo Bonzini         g_free(r->buf);
43749ab747fSPaolo Bonzini         r->buflen = 0;
43849ab747fSPaolo Bonzini         r->buf = NULL;
43949ab747fSPaolo Bonzini         /* The request is used as the AIO opaque value, so add a ref.  */
44049ab747fSPaolo Bonzini         scsi_req_ref(&r->req);
4414be74634SMarkus Armbruster         ret = execute_command(s->conf.blk, r, SG_DXFER_NONE,
4424be74634SMarkus Armbruster                               scsi_command_complete);
44349ab747fSPaolo Bonzini         if (ret < 0) {
444fa0d653bSPaolo Bonzini             scsi_command_complete_noio(r, ret);
44549ab747fSPaolo Bonzini             return 0;
44649ab747fSPaolo Bonzini         }
44749ab747fSPaolo Bonzini         return 0;
44849ab747fSPaolo Bonzini     }
44949ab747fSPaolo Bonzini 
45049ab747fSPaolo Bonzini     if (r->buflen != r->req.cmd.xfer) {
45149ab747fSPaolo Bonzini         g_free(r->buf);
45249ab747fSPaolo Bonzini         r->buf = g_malloc(r->req.cmd.xfer);
45349ab747fSPaolo Bonzini         r->buflen = r->req.cmd.xfer;
45449ab747fSPaolo Bonzini     }
45549ab747fSPaolo Bonzini 
45649ab747fSPaolo Bonzini     memset(r->buf, 0, r->buflen);
45749ab747fSPaolo Bonzini     r->len = r->req.cmd.xfer;
45849ab747fSPaolo Bonzini     if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
45949ab747fSPaolo Bonzini         r->len = 0;
46049ab747fSPaolo Bonzini         return -r->req.cmd.xfer;
46149ab747fSPaolo Bonzini     } else {
46249ab747fSPaolo Bonzini         return r->req.cmd.xfer;
46349ab747fSPaolo Bonzini     }
46449ab747fSPaolo Bonzini }
46549ab747fSPaolo Bonzini 
4669fd7e859SPaolo Bonzini static int read_naa_id(const uint8_t *p, uint64_t *p_wwn)
4679fd7e859SPaolo Bonzini {
4689fd7e859SPaolo Bonzini     int i;
4699fd7e859SPaolo Bonzini 
4709fd7e859SPaolo Bonzini     if ((p[1] & 0xF) == 3) {
4719fd7e859SPaolo Bonzini         /* NAA designator type */
4729fd7e859SPaolo Bonzini         if (p[3] != 8) {
4739fd7e859SPaolo Bonzini             return -EINVAL;
4749fd7e859SPaolo Bonzini         }
4759fd7e859SPaolo Bonzini         *p_wwn = ldq_be_p(p + 4);
4769fd7e859SPaolo Bonzini         return 0;
4779fd7e859SPaolo Bonzini     }
4789fd7e859SPaolo Bonzini 
4799fd7e859SPaolo Bonzini     if ((p[1] & 0xF) == 8) {
4809fd7e859SPaolo Bonzini         /* SCSI name string designator type */
4819fd7e859SPaolo Bonzini         if (p[3] < 20 || memcmp(&p[4], "naa.", 4)) {
4829fd7e859SPaolo Bonzini             return -EINVAL;
4839fd7e859SPaolo Bonzini         }
4849fd7e859SPaolo Bonzini         if (p[3] > 20 && p[24] != ',') {
4859fd7e859SPaolo Bonzini             return -EINVAL;
4869fd7e859SPaolo Bonzini         }
4879fd7e859SPaolo Bonzini         *p_wwn = 0;
4889fd7e859SPaolo Bonzini         for (i = 8; i < 24; i++) {
48995a5befcSPeter Maydell             char c = qemu_toupper(p[i]);
4909fd7e859SPaolo Bonzini             c -= (c >= '0' && c <= '9' ? '0' : 'A' - 10);
4919fd7e859SPaolo Bonzini             *p_wwn = (*p_wwn << 4) | c;
4929fd7e859SPaolo Bonzini         }
4939fd7e859SPaolo Bonzini         return 0;
4949fd7e859SPaolo Bonzini     }
4959fd7e859SPaolo Bonzini 
4969fd7e859SPaolo Bonzini     return -EINVAL;
4979fd7e859SPaolo Bonzini }
4989fd7e859SPaolo Bonzini 
499a0c7e35bSDaniel Henrique Barboza int scsi_SG_IO_FROM_DEV(BlockBackend *blk, uint8_t *cmd, uint8_t cmd_size,
500a0c7e35bSDaniel Henrique Barboza                         uint8_t *buf, uint8_t buf_size)
501a0c7e35bSDaniel Henrique Barboza {
502a0c7e35bSDaniel Henrique Barboza     sg_io_hdr_t io_header;
503a0c7e35bSDaniel Henrique Barboza     uint8_t sensebuf[8];
504a0c7e35bSDaniel Henrique Barboza     int ret;
505a0c7e35bSDaniel Henrique Barboza 
506a0c7e35bSDaniel Henrique Barboza     memset(&io_header, 0, sizeof(io_header));
507a0c7e35bSDaniel Henrique Barboza     io_header.interface_id = 'S';
508a0c7e35bSDaniel Henrique Barboza     io_header.dxfer_direction = SG_DXFER_FROM_DEV;
509a0c7e35bSDaniel Henrique Barboza     io_header.dxfer_len = buf_size;
510a0c7e35bSDaniel Henrique Barboza     io_header.dxferp = buf;
511a0c7e35bSDaniel Henrique Barboza     io_header.cmdp = cmd;
512a0c7e35bSDaniel Henrique Barboza     io_header.cmd_len = cmd_size;
513a0c7e35bSDaniel Henrique Barboza     io_header.mx_sb_len = sizeof(sensebuf);
514a0c7e35bSDaniel Henrique Barboza     io_header.sbp = sensebuf;
515a0c7e35bSDaniel Henrique Barboza     io_header.timeout = 6000; /* XXX */
516a0c7e35bSDaniel Henrique Barboza 
517a0c7e35bSDaniel Henrique Barboza     ret = blk_ioctl(blk, SG_IO, &io_header);
518a0c7e35bSDaniel Henrique Barboza     if (ret < 0 || io_header.driver_status || io_header.host_status) {
519a0c7e35bSDaniel Henrique Barboza         return -1;
520a0c7e35bSDaniel Henrique Barboza     }
521a0c7e35bSDaniel Henrique Barboza     return 0;
522a0c7e35bSDaniel Henrique Barboza }
523a0c7e35bSDaniel Henrique Barboza 
524a71c775bSDaniel Henrique Barboza /*
525a71c775bSDaniel Henrique Barboza  * Executes an INQUIRY request with EVPD set to retrieve the
526a71c775bSDaniel Henrique Barboza  * available VPD pages of the device. If the device does
527a71c775bSDaniel Henrique Barboza  * not support the Block Limits page (page 0xb0), set
528a71c775bSDaniel Henrique Barboza  * the needs_vpd_bl_emulation flag for future use.
529a71c775bSDaniel Henrique Barboza  */
530a71c775bSDaniel Henrique Barboza static void scsi_generic_set_vpd_bl_emulation(SCSIDevice *s)
531a71c775bSDaniel Henrique Barboza {
532a71c775bSDaniel Henrique Barboza     uint8_t cmd[6];
533a71c775bSDaniel Henrique Barboza     uint8_t buf[250];
534a71c775bSDaniel Henrique Barboza     uint8_t page_len;
535a71c775bSDaniel Henrique Barboza     int ret, i;
536a71c775bSDaniel Henrique Barboza 
537a71c775bSDaniel Henrique Barboza     memset(cmd, 0, sizeof(cmd));
538a71c775bSDaniel Henrique Barboza     memset(buf, 0, sizeof(buf));
539a71c775bSDaniel Henrique Barboza     cmd[0] = INQUIRY;
540a71c775bSDaniel Henrique Barboza     cmd[1] = 1;
541a71c775bSDaniel Henrique Barboza     cmd[2] = 0x00;
542a71c775bSDaniel Henrique Barboza     cmd[4] = sizeof(buf);
543a71c775bSDaniel Henrique Barboza 
544a71c775bSDaniel Henrique Barboza     ret = scsi_SG_IO_FROM_DEV(s->conf.blk, cmd, sizeof(cmd),
545a71c775bSDaniel Henrique Barboza                               buf, sizeof(buf));
546a71c775bSDaniel Henrique Barboza     if (ret < 0) {
547a71c775bSDaniel Henrique Barboza         /*
548a71c775bSDaniel Henrique Barboza          * Do not assume anything if we can't retrieve the
549a71c775bSDaniel Henrique Barboza          * INQUIRY response to assert the VPD Block Limits
550a71c775bSDaniel Henrique Barboza          * support.
551a71c775bSDaniel Henrique Barboza          */
552a71c775bSDaniel Henrique Barboza         s->needs_vpd_bl_emulation = false;
553a71c775bSDaniel Henrique Barboza         return;
554a71c775bSDaniel Henrique Barboza     }
555a71c775bSDaniel Henrique Barboza 
556a71c775bSDaniel Henrique Barboza     page_len = buf[3];
55757dbb58dSPaolo Bonzini     for (i = 4; i < MIN(sizeof(buf), page_len + 4); i++) {
558a71c775bSDaniel Henrique Barboza         if (buf[i] == 0xb0) {
559a71c775bSDaniel Henrique Barboza             s->needs_vpd_bl_emulation = false;
560a71c775bSDaniel Henrique Barboza             return;
561a71c775bSDaniel Henrique Barboza         }
562a71c775bSDaniel Henrique Barboza     }
563a71c775bSDaniel Henrique Barboza     s->needs_vpd_bl_emulation = true;
564a71c775bSDaniel Henrique Barboza }
565a71c775bSDaniel Henrique Barboza 
566a71c775bSDaniel Henrique Barboza static void scsi_generic_read_device_identification(SCSIDevice *s)
5679fd7e859SPaolo Bonzini {
5689fd7e859SPaolo Bonzini     uint8_t cmd[6];
5699fd7e859SPaolo Bonzini     uint8_t buf[250];
5709fd7e859SPaolo Bonzini     int ret;
5719fd7e859SPaolo Bonzini     int i, len;
5729fd7e859SPaolo Bonzini 
5739fd7e859SPaolo Bonzini     memset(cmd, 0, sizeof(cmd));
5749fd7e859SPaolo Bonzini     memset(buf, 0, sizeof(buf));
5759fd7e859SPaolo Bonzini     cmd[0] = INQUIRY;
5769fd7e859SPaolo Bonzini     cmd[1] = 1;
5779fd7e859SPaolo Bonzini     cmd[2] = 0x83;
5789fd7e859SPaolo Bonzini     cmd[4] = sizeof(buf);
5799fd7e859SPaolo Bonzini 
580a0c7e35bSDaniel Henrique Barboza     ret = scsi_SG_IO_FROM_DEV(s->conf.blk, cmd, sizeof(cmd),
581a0c7e35bSDaniel Henrique Barboza                               buf, sizeof(buf));
582a0c7e35bSDaniel Henrique Barboza     if (ret < 0) {
5839fd7e859SPaolo Bonzini         return;
5849fd7e859SPaolo Bonzini     }
5859fd7e859SPaolo Bonzini 
5869fd7e859SPaolo Bonzini     len = MIN((buf[2] << 8) | buf[3], sizeof(buf) - 4);
5879fd7e859SPaolo Bonzini     for (i = 0; i + 3 <= len; ) {
5889fd7e859SPaolo Bonzini         const uint8_t *p = &buf[i + 4];
5899fd7e859SPaolo Bonzini         uint64_t wwn;
5909fd7e859SPaolo Bonzini 
5919fd7e859SPaolo Bonzini         if (i + (p[3] + 4) > len) {
5929fd7e859SPaolo Bonzini             break;
5939fd7e859SPaolo Bonzini         }
5949fd7e859SPaolo Bonzini 
5959fd7e859SPaolo Bonzini         if ((p[1] & 0x10) == 0) {
5969fd7e859SPaolo Bonzini             /* Associated with the logical unit */
5979fd7e859SPaolo Bonzini             if (read_naa_id(p, &wwn) == 0) {
5989fd7e859SPaolo Bonzini                 s->wwn = wwn;
5999fd7e859SPaolo Bonzini             }
6009fd7e859SPaolo Bonzini         } else if ((p[1] & 0x10) == 0x10) {
6019fd7e859SPaolo Bonzini             /* Associated with the target port */
6029fd7e859SPaolo Bonzini             if (read_naa_id(p, &wwn) == 0) {
6039fd7e859SPaolo Bonzini                 s->port_wwn = wwn;
6049fd7e859SPaolo Bonzini             }
6059fd7e859SPaolo Bonzini         }
6069fd7e859SPaolo Bonzini 
6079fd7e859SPaolo Bonzini         i += p[3] + 4;
6089fd7e859SPaolo Bonzini     }
6099fd7e859SPaolo Bonzini }
6109fd7e859SPaolo Bonzini 
611a71c775bSDaniel Henrique Barboza void scsi_generic_read_device_inquiry(SCSIDevice *s)
612a71c775bSDaniel Henrique Barboza {
613a71c775bSDaniel Henrique Barboza     scsi_generic_read_device_identification(s);
614a71c775bSDaniel Henrique Barboza     if (s->type == TYPE_DISK) {
615a71c775bSDaniel Henrique Barboza         scsi_generic_set_vpd_bl_emulation(s);
616a71c775bSDaniel Henrique Barboza     } else {
617a71c775bSDaniel Henrique Barboza         s->needs_vpd_bl_emulation = false;
618a71c775bSDaniel Henrique Barboza     }
619a71c775bSDaniel Henrique Barboza }
620a71c775bSDaniel Henrique Barboza 
6214be74634SMarkus Armbruster static int get_stream_blocksize(BlockBackend *blk)
62249ab747fSPaolo Bonzini {
62349ab747fSPaolo Bonzini     uint8_t cmd[6];
62449ab747fSPaolo Bonzini     uint8_t buf[12];
62549ab747fSPaolo Bonzini     int ret;
62649ab747fSPaolo Bonzini 
62749ab747fSPaolo Bonzini     memset(cmd, 0, sizeof(cmd));
62849ab747fSPaolo Bonzini     memset(buf, 0, sizeof(buf));
62949ab747fSPaolo Bonzini     cmd[0] = MODE_SENSE;
63049ab747fSPaolo Bonzini     cmd[4] = sizeof(buf);
63149ab747fSPaolo Bonzini 
632a0c7e35bSDaniel Henrique Barboza     ret = scsi_SG_IO_FROM_DEV(blk, cmd, sizeof(cmd), buf, sizeof(buf));
633a0c7e35bSDaniel Henrique Barboza     if (ret < 0) {
63449ab747fSPaolo Bonzini         return -1;
63549ab747fSPaolo Bonzini     }
636a0c7e35bSDaniel Henrique Barboza 
63749ab747fSPaolo Bonzini     return (buf[9] << 16) | (buf[10] << 8) | buf[11];
63849ab747fSPaolo Bonzini }
63949ab747fSPaolo Bonzini 
64049ab747fSPaolo Bonzini static void scsi_generic_reset(DeviceState *dev)
64149ab747fSPaolo Bonzini {
64249ab747fSPaolo Bonzini     SCSIDevice *s = SCSI_DEVICE(dev);
64349ab747fSPaolo Bonzini 
6442343be0dSPaolo Bonzini     s->scsi_version = s->default_scsi_version;
64549ab747fSPaolo Bonzini     scsi_device_purge_requests(s, SENSE_CODE(RESET));
64649ab747fSPaolo Bonzini }
64749ab747fSPaolo Bonzini 
648a818a4b6SFam Zheng static void scsi_generic_realize(SCSIDevice *s, Error **errp)
64949ab747fSPaolo Bonzini {
6506ee143a0SPaolo Bonzini     int rc;
65149ab747fSPaolo Bonzini     int sg_version;
65249ab747fSPaolo Bonzini     struct sg_scsi_id scsiid;
65349ab747fSPaolo Bonzini 
6544be74634SMarkus Armbruster     if (!s->conf.blk) {
655a818a4b6SFam Zheng         error_setg(errp, "drive property not set");
656a818a4b6SFam Zheng         return;
65749ab747fSPaolo Bonzini     }
65849ab747fSPaolo Bonzini 
6594be74634SMarkus Armbruster     if (blk_get_on_error(s->conf.blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC) {
660a818a4b6SFam Zheng         error_setg(errp, "Device doesn't support drive option werror");
661a818a4b6SFam Zheng         return;
66249ab747fSPaolo Bonzini     }
6634be74634SMarkus Armbruster     if (blk_get_on_error(s->conf.blk, 1) != BLOCKDEV_ON_ERROR_REPORT) {
664a818a4b6SFam Zheng         error_setg(errp, "Device doesn't support drive option rerror");
665a818a4b6SFam Zheng         return;
66649ab747fSPaolo Bonzini     }
66749ab747fSPaolo Bonzini 
66849ab747fSPaolo Bonzini     /* check we are using a driver managing SG_IO (version 3 and after */
6694be74634SMarkus Armbruster     rc = blk_ioctl(s->conf.blk, SG_GET_VERSION_NUM, &sg_version);
6706ee143a0SPaolo Bonzini     if (rc < 0) {
67109c2c6ffSPaolo Bonzini         error_setg_errno(errp, -rc, "cannot get SG_IO version number");
67209c2c6ffSPaolo Bonzini         if (rc != -EPERM) {
67309c2c6ffSPaolo Bonzini             error_append_hint(errp, "Is this a SCSI device?\n");
67409c2c6ffSPaolo Bonzini         }
675a818a4b6SFam Zheng         return;
67649ab747fSPaolo Bonzini     }
67749ab747fSPaolo Bonzini     if (sg_version < 30000) {
678a818a4b6SFam Zheng         error_setg(errp, "scsi generic interface too old");
679a818a4b6SFam Zheng         return;
68049ab747fSPaolo Bonzini     }
68149ab747fSPaolo Bonzini 
68249ab747fSPaolo Bonzini     /* get LUN of the /dev/sg? */
6834be74634SMarkus Armbruster     if (blk_ioctl(s->conf.blk, SG_GET_SCSI_ID, &scsiid)) {
684a818a4b6SFam Zheng         error_setg(errp, "SG_GET_SCSI_ID ioctl failed");
685a818a4b6SFam Zheng         return;
68649ab747fSPaolo Bonzini     }
687c6caae55SFam Zheng     if (!blkconf_apply_backend_options(&s->conf,
688d9bcd6f7SFam Zheng                                        blk_is_read_only(s->conf.blk),
689c6caae55SFam Zheng                                        true, errp)) {
690d9bcd6f7SFam Zheng         return;
691d9bcd6f7SFam Zheng     }
69249ab747fSPaolo Bonzini 
69349ab747fSPaolo Bonzini     /* define device state */
69449ab747fSPaolo Bonzini     s->type = scsiid.scsi_type;
69556853498SLaurent Vivier     trace_scsi_generic_realize_type(s->type);
69649ab747fSPaolo Bonzini 
69749ab747fSPaolo Bonzini     switch (s->type) {
69849ab747fSPaolo Bonzini     case TYPE_TAPE:
6994be74634SMarkus Armbruster         s->blocksize = get_stream_blocksize(s->conf.blk);
70049ab747fSPaolo Bonzini         if (s->blocksize == -1) {
70149ab747fSPaolo Bonzini             s->blocksize = 0;
70249ab747fSPaolo Bonzini         }
70349ab747fSPaolo Bonzini         break;
70449ab747fSPaolo Bonzini 
70549ab747fSPaolo Bonzini         /* Make a guess for block devices, we'll fix it when the guest sends.
70649ab747fSPaolo Bonzini          * READ CAPACITY.  If they don't, they likely would assume these sizes
70749ab747fSPaolo Bonzini          * anyway. (TODO: they could also send MODE SENSE).
70849ab747fSPaolo Bonzini          */
70949ab747fSPaolo Bonzini     case TYPE_ROM:
71049ab747fSPaolo Bonzini     case TYPE_WORM:
71149ab747fSPaolo Bonzini         s->blocksize = 2048;
71249ab747fSPaolo Bonzini         break;
71349ab747fSPaolo Bonzini     default:
71449ab747fSPaolo Bonzini         s->blocksize = 512;
71549ab747fSPaolo Bonzini         break;
71649ab747fSPaolo Bonzini     }
71749ab747fSPaolo Bonzini 
71856853498SLaurent Vivier     trace_scsi_generic_realize_blocksize(s->blocksize);
7199fd7e859SPaolo Bonzini 
72029e560f0SDaniel Henrique Barboza     /* Only used by scsi-block, but initialize it nevertheless to be clean.  */
72129e560f0SDaniel Henrique Barboza     s->default_scsi_version = -1;
722a71c775bSDaniel Henrique Barboza     scsi_generic_read_device_inquiry(s);
72349ab747fSPaolo Bonzini }
72449ab747fSPaolo Bonzini 
72549ab747fSPaolo Bonzini const SCSIReqOps scsi_generic_req_ops = {
72649ab747fSPaolo Bonzini     .size         = sizeof(SCSIGenericReq),
72749ab747fSPaolo Bonzini     .free_req     = scsi_free_request,
72849ab747fSPaolo Bonzini     .send_command = scsi_send_command,
72949ab747fSPaolo Bonzini     .read_data    = scsi_read_data,
73049ab747fSPaolo Bonzini     .write_data   = scsi_write_data,
73149ab747fSPaolo Bonzini     .get_buf      = scsi_get_buf,
73249ab747fSPaolo Bonzini     .load_request = scsi_generic_load_request,
73349ab747fSPaolo Bonzini     .save_request = scsi_generic_save_request,
73449ab747fSPaolo Bonzini };
73549ab747fSPaolo Bonzini 
73649ab747fSPaolo Bonzini static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
73749ab747fSPaolo Bonzini                                      uint8_t *buf, void *hba_private)
73849ab747fSPaolo Bonzini {
7399be38598SEduardo Habkost     return scsi_req_alloc(&scsi_generic_req_ops, d, tag, lun, hba_private);
74049ab747fSPaolo Bonzini }
74149ab747fSPaolo Bonzini 
74249ab747fSPaolo Bonzini static Property scsi_generic_properties[] = {
7434be74634SMarkus Armbruster     DEFINE_PROP_DRIVE("drive", SCSIDevice, conf.blk),
744d9bcd6f7SFam Zheng     DEFINE_PROP_BOOL("share-rw", SCSIDevice, conf.share_rw, false),
74549ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
74649ab747fSPaolo Bonzini };
74749ab747fSPaolo Bonzini 
7483e7e180aSPaolo Bonzini static int scsi_generic_parse_cdb(SCSIDevice *dev, SCSICommand *cmd,
7493e7e180aSPaolo Bonzini                                   uint8_t *buf, void *hba_private)
7503e7e180aSPaolo Bonzini {
7513e7e180aSPaolo Bonzini     return scsi_bus_parse_cdb(dev, cmd, buf, hba_private);
7523e7e180aSPaolo Bonzini }
7533e7e180aSPaolo Bonzini 
75449ab747fSPaolo Bonzini static void scsi_generic_class_initfn(ObjectClass *klass, void *data)
75549ab747fSPaolo Bonzini {
75649ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
75749ab747fSPaolo Bonzini     SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
75849ab747fSPaolo Bonzini 
759a818a4b6SFam Zheng     sc->realize      = scsi_generic_realize;
76049ab747fSPaolo Bonzini     sc->alloc_req    = scsi_new_request;
7613e7e180aSPaolo Bonzini     sc->parse_cdb    = scsi_generic_parse_cdb;
76249ab747fSPaolo Bonzini     dc->fw_name = "disk";
76349ab747fSPaolo Bonzini     dc->desc = "pass through generic scsi device (/dev/sg*)";
76449ab747fSPaolo Bonzini     dc->reset = scsi_generic_reset;
76549ab747fSPaolo Bonzini     dc->props = scsi_generic_properties;
76649ab747fSPaolo Bonzini     dc->vmsd  = &vmstate_scsi_device;
76749ab747fSPaolo Bonzini }
76849ab747fSPaolo Bonzini 
76949ab747fSPaolo Bonzini static const TypeInfo scsi_generic_info = {
77049ab747fSPaolo Bonzini     .name          = "scsi-generic",
77149ab747fSPaolo Bonzini     .parent        = TYPE_SCSI_DEVICE,
77249ab747fSPaolo Bonzini     .instance_size = sizeof(SCSIDevice),
77349ab747fSPaolo Bonzini     .class_init    = scsi_generic_class_initfn,
77449ab747fSPaolo Bonzini };
77549ab747fSPaolo Bonzini 
77649ab747fSPaolo Bonzini static void scsi_generic_register_types(void)
77749ab747fSPaolo Bonzini {
77849ab747fSPaolo Bonzini     type_register_static(&scsi_generic_info);
77949ab747fSPaolo Bonzini }
78049ab747fSPaolo Bonzini 
78149ab747fSPaolo Bonzini type_init(scsi_generic_register_types)
78249ab747fSPaolo Bonzini 
78349ab747fSPaolo Bonzini #endif /* __linux__ */
784