xref: /qemu/hw/scsi/scsi-generic.c (revision a108557b)
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"
180b8fa32fSMarkus Armbruster #include "qemu/module.h"
1949ab747fSPaolo Bonzini #include "hw/scsi/scsi.h"
20ca77ee28SMarkus Armbruster #include "migration/qemu-file-types.h"
21a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
22ce35e229SEduardo Habkost #include "hw/qdev-properties-system.h"
233d4a8bf0SPaolo Bonzini #include "hw/scsi/emulation.h"
244be74634SMarkus Armbruster #include "sysemu/block-backend.h"
2556853498SLaurent Vivier #include "trace.h"
2649ab747fSPaolo Bonzini 
2749ab747fSPaolo Bonzini #ifdef __linux__
2849ab747fSPaolo Bonzini 
2949ab747fSPaolo Bonzini #include <scsi/sg.h>
3008e2c9f1SPaolo Bonzini #include "scsi/constants.h"
3149ab747fSPaolo Bonzini 
3249ab747fSPaolo Bonzini #ifndef MAX_UINT
3349ab747fSPaolo Bonzini #define MAX_UINT ((unsigned int)-1)
3449ab747fSPaolo Bonzini #endif
3549ab747fSPaolo Bonzini 
3649ab747fSPaolo Bonzini typedef struct SCSIGenericReq {
3749ab747fSPaolo Bonzini     SCSIRequest req;
3849ab747fSPaolo Bonzini     uint8_t *buf;
3949ab747fSPaolo Bonzini     int buflen;
4049ab747fSPaolo Bonzini     int len;
4149ab747fSPaolo Bonzini     sg_io_hdr_t io_header;
4249ab747fSPaolo Bonzini } SCSIGenericReq;
4349ab747fSPaolo Bonzini 
4449ab747fSPaolo Bonzini static void scsi_generic_save_request(QEMUFile *f, SCSIRequest *req)
4549ab747fSPaolo Bonzini {
4649ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
4749ab747fSPaolo Bonzini 
4849ab747fSPaolo Bonzini     qemu_put_sbe32s(f, &r->buflen);
4949ab747fSPaolo Bonzini     if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
5049ab747fSPaolo Bonzini         assert(!r->req.sg);
5149ab747fSPaolo Bonzini         qemu_put_buffer(f, r->buf, r->req.cmd.xfer);
5249ab747fSPaolo Bonzini     }
5349ab747fSPaolo Bonzini }
5449ab747fSPaolo Bonzini 
5549ab747fSPaolo Bonzini static void scsi_generic_load_request(QEMUFile *f, SCSIRequest *req)
5649ab747fSPaolo Bonzini {
5749ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
5849ab747fSPaolo Bonzini 
5949ab747fSPaolo Bonzini     qemu_get_sbe32s(f, &r->buflen);
6049ab747fSPaolo Bonzini     if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
6149ab747fSPaolo Bonzini         assert(!r->req.sg);
6249ab747fSPaolo Bonzini         qemu_get_buffer(f, r->buf, r->req.cmd.xfer);
6349ab747fSPaolo Bonzini     }
6449ab747fSPaolo Bonzini }
6549ab747fSPaolo Bonzini 
6649ab747fSPaolo Bonzini static void scsi_free_request(SCSIRequest *req)
6749ab747fSPaolo Bonzini {
6849ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
6949ab747fSPaolo Bonzini 
7049ab747fSPaolo Bonzini     g_free(r->buf);
7149ab747fSPaolo Bonzini }
7249ab747fSPaolo Bonzini 
7349ab747fSPaolo Bonzini /* Helper function for command completion.  */
74fa0d653bSPaolo Bonzini static void scsi_command_complete_noio(SCSIGenericReq *r, int ret)
7549ab747fSPaolo Bonzini {
7649ab747fSPaolo Bonzini     int status;
771ead6b4eSPaolo Bonzini     SCSISense sense;
78*a108557bSHannes Reinecke     sg_io_hdr_t *io_hdr = &r->io_header;
7949ab747fSPaolo Bonzini 
80fa0d653bSPaolo Bonzini     assert(r->req.aiocb == NULL);
81fa0d653bSPaolo Bonzini 
826c25fa6cSFam Zheng     if (r->req.io_canceled) {
83d5776465SFam Zheng         scsi_req_cancel_complete(&r->req);
846c25fa6cSFam Zheng         goto done;
856c25fa6cSFam Zheng     }
86*a108557bSHannes Reinecke     if (ret < 0) {
87*a108557bSHannes Reinecke         status = scsi_sense_from_errno(-ret, &sense);
881ead6b4eSPaolo Bonzini         if (status == CHECK_CONDITION) {
891ead6b4eSPaolo Bonzini             scsi_req_build_sense(&r->req, sense);
901ead6b4eSPaolo Bonzini         }
91*a108557bSHannes Reinecke     } else if (io_hdr->host_status != SCSI_HOST_OK) {
92*a108557bSHannes Reinecke         status = scsi_sense_from_host_status(io_hdr->host_status, &sense);
93*a108557bSHannes Reinecke         if (status == CHECK_CONDITION) {
94*a108557bSHannes Reinecke             scsi_req_build_sense(&r->req, sense);
9549ab747fSPaolo Bonzini         }
96*a108557bSHannes Reinecke     } else if (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT) {
97*a108557bSHannes Reinecke         status = BUSY;
98*a108557bSHannes Reinecke     } else {
99*a108557bSHannes Reinecke         status = io_hdr->status;
100*a108557bSHannes Reinecke         if (io_hdr->driver_status & SG_ERR_DRIVER_SENSE) {
101*a108557bSHannes Reinecke             r->req.sense_len = io_hdr->sb_len_wr;
102*a108557bSHannes Reinecke         }
103*a108557bSHannes Reinecke     }
10456853498SLaurent Vivier     trace_scsi_generic_command_complete_noio(r, r->req.tag, status);
10549ab747fSPaolo Bonzini 
10649ab747fSPaolo Bonzini     scsi_req_complete(&r->req, status);
1076c25fa6cSFam Zheng done:
10849ab747fSPaolo Bonzini     scsi_req_unref(&r->req);
10949ab747fSPaolo Bonzini }
11049ab747fSPaolo Bonzini 
111fa0d653bSPaolo Bonzini static void scsi_command_complete(void *opaque, int ret)
112fa0d653bSPaolo Bonzini {
113fa0d653bSPaolo Bonzini     SCSIGenericReq *r = (SCSIGenericReq *)opaque;
114b9e413ddSPaolo Bonzini     SCSIDevice *s = r->req.dev;
115fa0d653bSPaolo Bonzini 
116fa0d653bSPaolo Bonzini     assert(r->req.aiocb != NULL);
117fa0d653bSPaolo Bonzini     r->req.aiocb = NULL;
118b9e413ddSPaolo Bonzini 
119b9e413ddSPaolo Bonzini     aio_context_acquire(blk_get_aio_context(s->conf.blk));
120fa0d653bSPaolo Bonzini     scsi_command_complete_noio(r, ret);
121b9e413ddSPaolo Bonzini     aio_context_release(blk_get_aio_context(s->conf.blk));
122fa0d653bSPaolo Bonzini }
123fa0d653bSPaolo Bonzini 
1244be74634SMarkus Armbruster static int execute_command(BlockBackend *blk,
12549ab747fSPaolo Bonzini                            SCSIGenericReq *r, int direction,
126097310b5SMarkus Armbruster                            BlockCompletionFunc *complete)
12749ab747fSPaolo Bonzini {
128c9b6609bSHannes Reinecke     SCSIDevice *s = r->req.dev;
129c9b6609bSHannes Reinecke 
13049ab747fSPaolo Bonzini     r->io_header.interface_id = 'S';
13149ab747fSPaolo Bonzini     r->io_header.dxfer_direction = direction;
13249ab747fSPaolo Bonzini     r->io_header.dxferp = r->buf;
13349ab747fSPaolo Bonzini     r->io_header.dxfer_len = r->buflen;
13449ab747fSPaolo Bonzini     r->io_header.cmdp = r->req.cmd.buf;
13549ab747fSPaolo Bonzini     r->io_header.cmd_len = r->req.cmd.len;
13649ab747fSPaolo Bonzini     r->io_header.mx_sb_len = sizeof(r->req.sense);
13749ab747fSPaolo Bonzini     r->io_header.sbp = r->req.sense;
138c9b6609bSHannes Reinecke     r->io_header.timeout = s->io_timeout * 1000;
13949ab747fSPaolo Bonzini     r->io_header.usr_ptr = r;
14049ab747fSPaolo Bonzini     r->io_header.flags |= SG_FLAG_DIRECT_IO;
14149ab747fSPaolo Bonzini 
142b2d50a33SHannes Reinecke     trace_scsi_generic_aio_sgio_command(r->req.tag, r->req.cmd.buf[0],
143b2d50a33SHannes Reinecke                                         r->io_header.timeout);
1444be74634SMarkus Armbruster     r->req.aiocb = blk_aio_ioctl(blk, SG_IO, &r->io_header, complete, r);
145d836f8d3SPavel Hrdina     if (r->req.aiocb == NULL) {
146d836f8d3SPavel Hrdina         return -EIO;
147d836f8d3SPavel Hrdina     }
14849ab747fSPaolo Bonzini 
14949ab747fSPaolo Bonzini     return 0;
15049ab747fSPaolo Bonzini }
15149ab747fSPaolo Bonzini 
1520a96ca24SDaniel Henrique Barboza static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s)
1530a96ca24SDaniel Henrique Barboza {
1546c219fc8SPaolo Bonzini     uint8_t page, page_idx;
155a71c775bSDaniel Henrique Barboza 
1560a96ca24SDaniel Henrique Barboza     /*
1570a96ca24SDaniel Henrique Barboza      *  EVPD set to zero returns the standard INQUIRY data.
1580a96ca24SDaniel Henrique Barboza      *
1590a96ca24SDaniel Henrique Barboza      *  Check if scsi_version is unset (-1) to avoid re-defining it
1600a96ca24SDaniel Henrique Barboza      *  each time an INQUIRY with standard data is received.
1610a96ca24SDaniel Henrique Barboza      *  scsi_version is initialized with -1 in scsi_generic_reset
1620a96ca24SDaniel Henrique Barboza      *  and scsi_disk_reset, making sure that we'll set the
1630a96ca24SDaniel Henrique Barboza      *  scsi_version after a reset. If the version field of the
1640a96ca24SDaniel Henrique Barboza      *  INQUIRY response somehow changes after a guest reboot,
1650a96ca24SDaniel Henrique Barboza      *  we'll be able to keep track of it.
1660a96ca24SDaniel Henrique Barboza      *
1670a96ca24SDaniel Henrique Barboza      *  On SCSI-2 and older, first 3 bits of byte 2 is the
1680a96ca24SDaniel Henrique Barboza      *  ANSI-approved version, while on later versions the
1690a96ca24SDaniel Henrique Barboza      *  whole byte 2 contains the version. Check if we're dealing
1700a96ca24SDaniel Henrique Barboza      *  with a newer version and, in that case, assign the
1710a96ca24SDaniel Henrique Barboza      *  whole byte.
1720a96ca24SDaniel Henrique Barboza      */
1730a96ca24SDaniel Henrique Barboza     if (s->scsi_version == -1 && !(r->req.cmd.buf[1] & 0x01)) {
1740a96ca24SDaniel Henrique Barboza         s->scsi_version = r->buf[2] & 0x07;
1750a96ca24SDaniel Henrique Barboza         if (s->scsi_version > 2) {
1760a96ca24SDaniel Henrique Barboza             s->scsi_version = r->buf[2];
1770a96ca24SDaniel Henrique Barboza         }
1780a96ca24SDaniel Henrique Barboza     }
179a71c775bSDaniel Henrique Barboza 
180afff2db6SDmitry Fomichev     if ((s->type == TYPE_DISK || s->type == TYPE_ZBC) &&
181afff2db6SDmitry Fomichev         (r->req.cmd.buf[1] & 0x01)) {
182a71c775bSDaniel Henrique Barboza         page = r->req.cmd.buf[2];
183a71c775bSDaniel Henrique Barboza         if (page == 0xb0) {
1840a96ca24SDaniel Henrique Barboza             uint32_t max_transfer =
1850a96ca24SDaniel Henrique Barboza                 blk_get_max_transfer(s->conf.blk) / s->blocksize;
1860a96ca24SDaniel Henrique Barboza 
1870a96ca24SDaniel Henrique Barboza             assert(max_transfer);
1880a96ca24SDaniel Henrique Barboza             stl_be_p(&r->buf[8], max_transfer);
1890a96ca24SDaniel Henrique Barboza             /* Also take care of the opt xfer len. */
1900a96ca24SDaniel Henrique Barboza             stl_be_p(&r->buf[12],
1910a96ca24SDaniel Henrique Barboza                     MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12])));
192e909ff93SPaolo Bonzini         } else if (s->needs_vpd_bl_emulation && page == 0x00 && r->buflen >= 4) {
193a71c775bSDaniel Henrique Barboza             /*
194a71c775bSDaniel Henrique Barboza              * Now we're capable of supplying the VPD Block Limits
195a71c775bSDaniel Henrique Barboza              * response if the hardware can't. Add it in the INQUIRY
196a71c775bSDaniel Henrique Barboza              * Supported VPD pages response in case we are using the
197a71c775bSDaniel Henrique Barboza              * emulation for this device.
198a71c775bSDaniel Henrique Barboza              *
199a71c775bSDaniel Henrique Barboza              * This way, the guest kernel will be aware of the support
200a71c775bSDaniel Henrique Barboza              * and will use it to proper setup the SCSI device.
2016c219fc8SPaolo Bonzini              *
2026c219fc8SPaolo Bonzini              * VPD page numbers must be sorted, so insert 0xb0 at the
203e909ff93SPaolo Bonzini              * right place with an in-place insert.  When the while loop
204e909ff93SPaolo Bonzini              * begins the device response is at r[0] to r[page_idx - 1].
205a71c775bSDaniel Henrique Barboza              */
206e909ff93SPaolo Bonzini             page_idx = lduw_be_p(r->buf + 2) + 4;
207e909ff93SPaolo Bonzini             page_idx = MIN(page_idx, r->buflen);
208e909ff93SPaolo Bonzini             while (page_idx > 4 && r->buf[page_idx - 1] >= 0xb0) {
2096c219fc8SPaolo Bonzini                 if (page_idx < r->buflen) {
2106c219fc8SPaolo Bonzini                     r->buf[page_idx] = r->buf[page_idx - 1];
2116c219fc8SPaolo Bonzini                 }
212e909ff93SPaolo Bonzini                 page_idx--;
2136c219fc8SPaolo Bonzini             }
214e909ff93SPaolo Bonzini             if (page_idx < r->buflen) {
2156c219fc8SPaolo Bonzini                 r->buf[page_idx] = 0xb0;
216e909ff93SPaolo Bonzini             }
2176c219fc8SPaolo Bonzini             stw_be_p(r->buf + 2, lduw_be_p(r->buf + 2) + 1);
2180a96ca24SDaniel Henrique Barboza         }
2190a96ca24SDaniel Henrique Barboza     }
220a71c775bSDaniel Henrique Barboza }
221a71c775bSDaniel Henrique Barboza 
2223d4a8bf0SPaolo Bonzini static int scsi_generic_emulate_block_limits(SCSIGenericReq *r, SCSIDevice *s)
223a71c775bSDaniel Henrique Barboza {
2243d4a8bf0SPaolo Bonzini     int len;
2253d4a8bf0SPaolo Bonzini     uint8_t buf[64];
2263d4a8bf0SPaolo Bonzini 
2273d4a8bf0SPaolo Bonzini     SCSIBlockLimits bl = {
2283d4a8bf0SPaolo Bonzini         .max_io_sectors = blk_get_max_transfer(s->conf.blk) / s->blocksize
2293d4a8bf0SPaolo Bonzini     };
2303d4a8bf0SPaolo Bonzini 
2313d4a8bf0SPaolo Bonzini     memset(r->buf, 0, r->buflen);
2323d4a8bf0SPaolo Bonzini     stb_p(buf, s->type);
2333d4a8bf0SPaolo Bonzini     stb_p(buf + 1, 0xb0);
2343d4a8bf0SPaolo Bonzini     len = scsi_emulate_block_limits(buf + 4, &bl);
2353d4a8bf0SPaolo Bonzini     assert(len <= sizeof(buf) - 4);
2363d4a8bf0SPaolo Bonzini     stw_be_p(buf + 2, len);
2373d4a8bf0SPaolo Bonzini 
2383d4a8bf0SPaolo Bonzini     memcpy(r->buf, buf, MIN(r->buflen, len + 4));
2393d4a8bf0SPaolo Bonzini 
240a71c775bSDaniel Henrique Barboza     r->io_header.sb_len_wr = 0;
241a71c775bSDaniel Henrique Barboza 
242a71c775bSDaniel Henrique Barboza     /*
243a71c775bSDaniel Henrique Barboza     * We have valid contents in the reply buffer but the
244a71c775bSDaniel Henrique Barboza     * io_header can report a sense error coming from
245a71c775bSDaniel Henrique Barboza     * the hardware in scsi_command_complete_noio. Clean
246a71c775bSDaniel Henrique Barboza     * up the io_header to avoid reporting it.
247a71c775bSDaniel Henrique Barboza     */
248a71c775bSDaniel Henrique Barboza     r->io_header.driver_status = 0;
249a71c775bSDaniel Henrique Barboza     r->io_header.status = 0;
250a71c775bSDaniel Henrique Barboza 
251a71c775bSDaniel Henrique Barboza     return r->buflen;
252a71c775bSDaniel Henrique Barboza }
2530a96ca24SDaniel Henrique Barboza 
25449ab747fSPaolo Bonzini static void scsi_read_complete(void * opaque, int ret)
25549ab747fSPaolo Bonzini {
25649ab747fSPaolo Bonzini     SCSIGenericReq *r = (SCSIGenericReq *)opaque;
25749ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
25849ab747fSPaolo Bonzini     int len;
25949ab747fSPaolo Bonzini 
260fa0d653bSPaolo Bonzini     assert(r->req.aiocb != NULL);
26149ab747fSPaolo Bonzini     r->req.aiocb = NULL;
262fa0d653bSPaolo Bonzini 
263b9e413ddSPaolo Bonzini     aio_context_acquire(blk_get_aio_context(s->conf.blk));
264b9e413ddSPaolo Bonzini 
2656c25fa6cSFam Zheng     if (ret || r->req.io_canceled) {
266fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, ret);
267b9e413ddSPaolo Bonzini         goto done;
26849ab747fSPaolo Bonzini     }
269fa0d653bSPaolo Bonzini 
27049ab747fSPaolo Bonzini     len = r->io_header.dxfer_len - r->io_header.resid;
27156853498SLaurent Vivier     trace_scsi_generic_read_complete(r->req.tag, len);
27249ab747fSPaolo Bonzini 
27349ab747fSPaolo Bonzini     r->len = -1;
274a71c775bSDaniel Henrique Barboza 
2751849f297SShin'ichiro Kawasaki     if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
2761849f297SShin'ichiro Kawasaki         SCSISense sense =
2771849f297SShin'ichiro Kawasaki             scsi_parse_sense_buf(r->req.sense, r->io_header.sb_len_wr);
2781849f297SShin'ichiro Kawasaki 
279a71c775bSDaniel Henrique Barboza         /*
280a71c775bSDaniel Henrique Barboza          * Check if this is a VPD Block Limits request that
281a71c775bSDaniel Henrique Barboza          * resulted in sense error but would need emulation.
282a71c775bSDaniel Henrique Barboza          * In this case, emulate a valid VPD response.
283a71c775bSDaniel Henrique Barboza          */
2841849f297SShin'ichiro Kawasaki         if (sense.key == ILLEGAL_REQUEST &&
2851849f297SShin'ichiro Kawasaki             s->needs_vpd_bl_emulation &&
2863d4a8bf0SPaolo Bonzini             r->req.cmd.buf[0] == INQUIRY &&
2873d4a8bf0SPaolo Bonzini             (r->req.cmd.buf[1] & 0x01) &&
2883d4a8bf0SPaolo Bonzini             r->req.cmd.buf[2] == 0xb0) {
2893d4a8bf0SPaolo Bonzini             len = scsi_generic_emulate_block_limits(r, s);
290a71c775bSDaniel Henrique Barboza             /*
2911849f297SShin'ichiro Kawasaki              * It's okay to jup to req_complete: no need to
2921849f297SShin'ichiro Kawasaki              * let scsi_handle_inquiry_reply handle an
293a71c775bSDaniel Henrique Barboza              * INQUIRY VPD BL request we created manually.
294a71c775bSDaniel Henrique Barboza              */
2951849f297SShin'ichiro Kawasaki         }
2961849f297SShin'ichiro Kawasaki         if (sense.key) {
297a71c775bSDaniel Henrique Barboza             goto req_complete;
298a71c775bSDaniel Henrique Barboza         }
299a71c775bSDaniel Henrique Barboza     }
300a71c775bSDaniel Henrique Barboza 
3019738c657SPaolo Bonzini     if (r->io_header.host_status != SCSI_HOST_OK ||
3029738c657SPaolo Bonzini         (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT) ||
3039738c657SPaolo Bonzini         r->io_header.status != GOOD ||
3049738c657SPaolo Bonzini         len == 0) {
305fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, 0);
306b9e413ddSPaolo Bonzini         goto done;
307fa0d653bSPaolo Bonzini     }
308fa0d653bSPaolo Bonzini 
30949ab747fSPaolo Bonzini     /* Snoop READ CAPACITY output to set the blocksize.  */
31053254e56SPaolo Bonzini     if (r->req.cmd.buf[0] == READ_CAPACITY_10 &&
31153254e56SPaolo Bonzini         (ldl_be_p(&r->buf[0]) != 0xffffffffU || s->max_lba == 0)) {
31249ab747fSPaolo Bonzini         s->blocksize = ldl_be_p(&r->buf[4]);
31353254e56SPaolo Bonzini         s->max_lba = ldl_be_p(&r->buf[0]) & 0xffffffffULL;
31449ab747fSPaolo Bonzini     } else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 &&
31549ab747fSPaolo Bonzini                (r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) {
31649ab747fSPaolo Bonzini         s->blocksize = ldl_be_p(&r->buf[8]);
31749ab747fSPaolo Bonzini         s->max_lba = ldq_be_p(&r->buf[0]);
31849ab747fSPaolo Bonzini     }
3194be74634SMarkus Armbruster     blk_set_guest_block_size(s->conf.blk, s->blocksize);
32049ab747fSPaolo Bonzini 
321afff2db6SDmitry Fomichev     /*
322afff2db6SDmitry Fomichev      * Patch MODE SENSE device specific parameters if the BDS is opened
3230eb2baebSPaolo Bonzini      * readonly.
3240eb2baebSPaolo Bonzini      */
325afff2db6SDmitry Fomichev     if ((s->type == TYPE_DISK || s->type == TYPE_TAPE || s->type == TYPE_ZBC) &&
32686b1cf32SKevin Wolf         !blk_is_writable(s->conf.blk) &&
3270eb2baebSPaolo Bonzini         (r->req.cmd.buf[0] == MODE_SENSE ||
3280eb2baebSPaolo Bonzini          r->req.cmd.buf[0] == MODE_SENSE_10) &&
3290eb2baebSPaolo Bonzini         (r->req.cmd.buf[1] & 0x8) == 0) {
3300eb2baebSPaolo Bonzini         if (r->req.cmd.buf[0] == MODE_SENSE) {
3310eb2baebSPaolo Bonzini             r->buf[2] |= 0x80;
3320eb2baebSPaolo Bonzini         } else  {
3330eb2baebSPaolo Bonzini             r->buf[3] |= 0x80;
3340eb2baebSPaolo Bonzini         }
3350eb2baebSPaolo Bonzini     }
33629e560f0SDaniel Henrique Barboza     if (r->req.cmd.buf[0] == INQUIRY) {
3370a96ca24SDaniel Henrique Barboza         scsi_handle_inquiry_reply(r, s);
33829e560f0SDaniel Henrique Barboza     }
339a71c775bSDaniel Henrique Barboza 
340a71c775bSDaniel Henrique Barboza req_complete:
34149ab747fSPaolo Bonzini     scsi_req_data(&r->req, len);
34249ab747fSPaolo Bonzini     scsi_req_unref(&r->req);
343b9e413ddSPaolo Bonzini 
344b9e413ddSPaolo Bonzini done:
345b9e413ddSPaolo Bonzini     aio_context_release(blk_get_aio_context(s->conf.blk));
34649ab747fSPaolo Bonzini }
34749ab747fSPaolo Bonzini 
34849ab747fSPaolo Bonzini /* Read more data from scsi device into buffer.  */
34949ab747fSPaolo Bonzini static void scsi_read_data(SCSIRequest *req)
35049ab747fSPaolo Bonzini {
35149ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
35249ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
35349ab747fSPaolo Bonzini     int ret;
35449ab747fSPaolo Bonzini 
35556853498SLaurent Vivier     trace_scsi_generic_read_data(req->tag);
35649ab747fSPaolo Bonzini 
35749ab747fSPaolo Bonzini     /* The request is used as the AIO opaque value, so add a ref.  */
35849ab747fSPaolo Bonzini     scsi_req_ref(&r->req);
35949ab747fSPaolo Bonzini     if (r->len == -1) {
360fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, 0);
36149ab747fSPaolo Bonzini         return;
36249ab747fSPaolo Bonzini     }
36349ab747fSPaolo Bonzini 
3644be74634SMarkus Armbruster     ret = execute_command(s->conf.blk, r, SG_DXFER_FROM_DEV,
3654be74634SMarkus Armbruster                           scsi_read_complete);
36649ab747fSPaolo Bonzini     if (ret < 0) {
367fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, ret);
36849ab747fSPaolo Bonzini     }
36949ab747fSPaolo Bonzini }
37049ab747fSPaolo Bonzini 
37149ab747fSPaolo Bonzini static void scsi_write_complete(void * opaque, int ret)
37249ab747fSPaolo Bonzini {
37349ab747fSPaolo Bonzini     SCSIGenericReq *r = (SCSIGenericReq *)opaque;
37449ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
37549ab747fSPaolo Bonzini 
37656853498SLaurent Vivier     trace_scsi_generic_write_complete(ret);
377fa0d653bSPaolo Bonzini 
378fa0d653bSPaolo Bonzini     assert(r->req.aiocb != NULL);
37949ab747fSPaolo Bonzini     r->req.aiocb = NULL;
380fa0d653bSPaolo Bonzini 
381b9e413ddSPaolo Bonzini     aio_context_acquire(blk_get_aio_context(s->conf.blk));
382b9e413ddSPaolo Bonzini 
3836c25fa6cSFam Zheng     if (ret || r->req.io_canceled) {
384fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, ret);
385b9e413ddSPaolo Bonzini         goto done;
38649ab747fSPaolo Bonzini     }
38749ab747fSPaolo Bonzini 
38849ab747fSPaolo Bonzini     if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 &&
38949ab747fSPaolo Bonzini         s->type == TYPE_TAPE) {
39049ab747fSPaolo Bonzini         s->blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11];
39156853498SLaurent Vivier         trace_scsi_generic_write_complete_blocksize(s->blocksize);
39249ab747fSPaolo Bonzini     }
39349ab747fSPaolo Bonzini 
394fa0d653bSPaolo Bonzini     scsi_command_complete_noio(r, ret);
395b9e413ddSPaolo Bonzini 
396b9e413ddSPaolo Bonzini done:
397b9e413ddSPaolo Bonzini     aio_context_release(blk_get_aio_context(s->conf.blk));
39849ab747fSPaolo Bonzini }
39949ab747fSPaolo Bonzini 
40049ab747fSPaolo Bonzini /* Write data to a scsi device.  Returns nonzero on failure.
40149ab747fSPaolo Bonzini    The transfer may complete asynchronously.  */
40249ab747fSPaolo Bonzini static void scsi_write_data(SCSIRequest *req)
40349ab747fSPaolo Bonzini {
40449ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
40549ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
40649ab747fSPaolo Bonzini     int ret;
40749ab747fSPaolo Bonzini 
40856853498SLaurent Vivier     trace_scsi_generic_write_data(req->tag);
40949ab747fSPaolo Bonzini     if (r->len == 0) {
41049ab747fSPaolo Bonzini         r->len = r->buflen;
41149ab747fSPaolo Bonzini         scsi_req_data(&r->req, r->len);
41249ab747fSPaolo Bonzini         return;
41349ab747fSPaolo Bonzini     }
41449ab747fSPaolo Bonzini 
41549ab747fSPaolo Bonzini     /* The request is used as the AIO opaque value, so add a ref.  */
41649ab747fSPaolo Bonzini     scsi_req_ref(&r->req);
4174be74634SMarkus Armbruster     ret = execute_command(s->conf.blk, r, SG_DXFER_TO_DEV, scsi_write_complete);
41849ab747fSPaolo Bonzini     if (ret < 0) {
419fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, ret);
42049ab747fSPaolo Bonzini     }
42149ab747fSPaolo Bonzini }
42249ab747fSPaolo Bonzini 
42349ab747fSPaolo Bonzini /* Return a pointer to the data buffer.  */
42449ab747fSPaolo Bonzini static uint8_t *scsi_get_buf(SCSIRequest *req)
42549ab747fSPaolo Bonzini {
42649ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
42749ab747fSPaolo Bonzini 
42849ab747fSPaolo Bonzini     return r->buf;
42949ab747fSPaolo Bonzini }
43049ab747fSPaolo Bonzini 
43156853498SLaurent Vivier static void scsi_generic_command_dump(uint8_t *cmd, int len)
43256853498SLaurent Vivier {
43356853498SLaurent Vivier     int i;
43456853498SLaurent Vivier     char *line_buffer, *p;
43556853498SLaurent Vivier 
43656853498SLaurent Vivier     line_buffer = g_malloc(len * 5 + 1);
43756853498SLaurent Vivier 
43856853498SLaurent Vivier     for (i = 0, p = line_buffer; i < len; i++) {
43956853498SLaurent Vivier         p += sprintf(p, " 0x%02x", cmd[i]);
44056853498SLaurent Vivier     }
44156853498SLaurent Vivier     trace_scsi_generic_send_command(line_buffer);
44256853498SLaurent Vivier 
44356853498SLaurent Vivier     g_free(line_buffer);
44456853498SLaurent Vivier }
44556853498SLaurent Vivier 
44649ab747fSPaolo Bonzini /* Execute a scsi command.  Returns the length of the data expected by the
44749ab747fSPaolo Bonzini    command.  This will be Positive for data transfers from the device
44849ab747fSPaolo Bonzini    (eg. disk reads), negative for transfers to the device (eg. disk writes),
44949ab747fSPaolo Bonzini    and zero if the command does not transfer any data.  */
45049ab747fSPaolo Bonzini 
45149ab747fSPaolo Bonzini static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
45249ab747fSPaolo Bonzini {
45349ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
45449ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
45549ab747fSPaolo Bonzini     int ret;
45649ab747fSPaolo Bonzini 
45756853498SLaurent Vivier     if (trace_event_get_state_backends(TRACE_SCSI_GENERIC_SEND_COMMAND)) {
45856853498SLaurent Vivier         scsi_generic_command_dump(cmd, r->req.cmd.len);
45949ab747fSPaolo Bonzini     }
46049ab747fSPaolo Bonzini 
46149ab747fSPaolo Bonzini     if (r->req.cmd.xfer == 0) {
46249ab747fSPaolo Bonzini         g_free(r->buf);
46349ab747fSPaolo Bonzini         r->buflen = 0;
46449ab747fSPaolo Bonzini         r->buf = NULL;
46549ab747fSPaolo Bonzini         /* The request is used as the AIO opaque value, so add a ref.  */
46649ab747fSPaolo Bonzini         scsi_req_ref(&r->req);
4674be74634SMarkus Armbruster         ret = execute_command(s->conf.blk, r, SG_DXFER_NONE,
4684be74634SMarkus Armbruster                               scsi_command_complete);
46949ab747fSPaolo Bonzini         if (ret < 0) {
470fa0d653bSPaolo Bonzini             scsi_command_complete_noio(r, ret);
47149ab747fSPaolo Bonzini             return 0;
47249ab747fSPaolo Bonzini         }
47349ab747fSPaolo Bonzini         return 0;
47449ab747fSPaolo Bonzini     }
47549ab747fSPaolo Bonzini 
47649ab747fSPaolo Bonzini     if (r->buflen != r->req.cmd.xfer) {
47749ab747fSPaolo Bonzini         g_free(r->buf);
47849ab747fSPaolo Bonzini         r->buf = g_malloc(r->req.cmd.xfer);
47949ab747fSPaolo Bonzini         r->buflen = r->req.cmd.xfer;
48049ab747fSPaolo Bonzini     }
48149ab747fSPaolo Bonzini 
48249ab747fSPaolo Bonzini     memset(r->buf, 0, r->buflen);
48349ab747fSPaolo Bonzini     r->len = r->req.cmd.xfer;
48449ab747fSPaolo Bonzini     if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
48549ab747fSPaolo Bonzini         r->len = 0;
48649ab747fSPaolo Bonzini         return -r->req.cmd.xfer;
48749ab747fSPaolo Bonzini     } else {
48849ab747fSPaolo Bonzini         return r->req.cmd.xfer;
48949ab747fSPaolo Bonzini     }
49049ab747fSPaolo Bonzini }
49149ab747fSPaolo Bonzini 
4929fd7e859SPaolo Bonzini static int read_naa_id(const uint8_t *p, uint64_t *p_wwn)
4939fd7e859SPaolo Bonzini {
4949fd7e859SPaolo Bonzini     int i;
4959fd7e859SPaolo Bonzini 
4969fd7e859SPaolo Bonzini     if ((p[1] & 0xF) == 3) {
4979fd7e859SPaolo Bonzini         /* NAA designator type */
4989fd7e859SPaolo Bonzini         if (p[3] != 8) {
4999fd7e859SPaolo Bonzini             return -EINVAL;
5009fd7e859SPaolo Bonzini         }
5019fd7e859SPaolo Bonzini         *p_wwn = ldq_be_p(p + 4);
5029fd7e859SPaolo Bonzini         return 0;
5039fd7e859SPaolo Bonzini     }
5049fd7e859SPaolo Bonzini 
5059fd7e859SPaolo Bonzini     if ((p[1] & 0xF) == 8) {
5069fd7e859SPaolo Bonzini         /* SCSI name string designator type */
5079fd7e859SPaolo Bonzini         if (p[3] < 20 || memcmp(&p[4], "naa.", 4)) {
5089fd7e859SPaolo Bonzini             return -EINVAL;
5099fd7e859SPaolo Bonzini         }
5109fd7e859SPaolo Bonzini         if (p[3] > 20 && p[24] != ',') {
5119fd7e859SPaolo Bonzini             return -EINVAL;
5129fd7e859SPaolo Bonzini         }
5139fd7e859SPaolo Bonzini         *p_wwn = 0;
5149fd7e859SPaolo Bonzini         for (i = 8; i < 24; i++) {
51595a5befcSPeter Maydell             char c = qemu_toupper(p[i]);
5169fd7e859SPaolo Bonzini             c -= (c >= '0' && c <= '9' ? '0' : 'A' - 10);
5179fd7e859SPaolo Bonzini             *p_wwn = (*p_wwn << 4) | c;
5189fd7e859SPaolo Bonzini         }
5199fd7e859SPaolo Bonzini         return 0;
5209fd7e859SPaolo Bonzini     }
5219fd7e859SPaolo Bonzini 
5229fd7e859SPaolo Bonzini     return -EINVAL;
5239fd7e859SPaolo Bonzini }
5249fd7e859SPaolo Bonzini 
525a0c7e35bSDaniel Henrique Barboza int scsi_SG_IO_FROM_DEV(BlockBackend *blk, uint8_t *cmd, uint8_t cmd_size,
526c9b6609bSHannes Reinecke                         uint8_t *buf, uint8_t buf_size, uint32_t timeout)
527a0c7e35bSDaniel Henrique Barboza {
528a0c7e35bSDaniel Henrique Barboza     sg_io_hdr_t io_header;
529a0c7e35bSDaniel Henrique Barboza     uint8_t sensebuf[8];
530a0c7e35bSDaniel Henrique Barboza     int ret;
531a0c7e35bSDaniel Henrique Barboza 
532a0c7e35bSDaniel Henrique Barboza     memset(&io_header, 0, sizeof(io_header));
533a0c7e35bSDaniel Henrique Barboza     io_header.interface_id = 'S';
534a0c7e35bSDaniel Henrique Barboza     io_header.dxfer_direction = SG_DXFER_FROM_DEV;
535a0c7e35bSDaniel Henrique Barboza     io_header.dxfer_len = buf_size;
536a0c7e35bSDaniel Henrique Barboza     io_header.dxferp = buf;
537a0c7e35bSDaniel Henrique Barboza     io_header.cmdp = cmd;
538a0c7e35bSDaniel Henrique Barboza     io_header.cmd_len = cmd_size;
539a0c7e35bSDaniel Henrique Barboza     io_header.mx_sb_len = sizeof(sensebuf);
540a0c7e35bSDaniel Henrique Barboza     io_header.sbp = sensebuf;
541c9b6609bSHannes Reinecke     io_header.timeout = timeout * 1000;
542a0c7e35bSDaniel Henrique Barboza 
543b2d50a33SHannes Reinecke     trace_scsi_generic_ioctl_sgio_command(cmd[0], io_header.timeout);
544a0c7e35bSDaniel Henrique Barboza     ret = blk_ioctl(blk, SG_IO, &io_header);
545b2d50a33SHannes Reinecke     if (ret < 0 || io_header.status ||
546b2d50a33SHannes Reinecke         io_header.driver_status || io_header.host_status) {
547b2d50a33SHannes Reinecke         trace_scsi_generic_ioctl_sgio_done(cmd[0], ret, io_header.status,
548b2d50a33SHannes Reinecke                                            io_header.host_status);
549a0c7e35bSDaniel Henrique Barboza         return -1;
550a0c7e35bSDaniel Henrique Barboza     }
551a0c7e35bSDaniel Henrique Barboza     return 0;
552a0c7e35bSDaniel Henrique Barboza }
553a0c7e35bSDaniel Henrique Barboza 
554a71c775bSDaniel Henrique Barboza /*
555a71c775bSDaniel Henrique Barboza  * Executes an INQUIRY request with EVPD set to retrieve the
556a71c775bSDaniel Henrique Barboza  * available VPD pages of the device. If the device does
557a71c775bSDaniel Henrique Barboza  * not support the Block Limits page (page 0xb0), set
558a71c775bSDaniel Henrique Barboza  * the needs_vpd_bl_emulation flag for future use.
559a71c775bSDaniel Henrique Barboza  */
560a71c775bSDaniel Henrique Barboza static void scsi_generic_set_vpd_bl_emulation(SCSIDevice *s)
561a71c775bSDaniel Henrique Barboza {
562a71c775bSDaniel Henrique Barboza     uint8_t cmd[6];
563a71c775bSDaniel Henrique Barboza     uint8_t buf[250];
564a71c775bSDaniel Henrique Barboza     uint8_t page_len;
565a71c775bSDaniel Henrique Barboza     int ret, i;
566a71c775bSDaniel Henrique Barboza 
567a71c775bSDaniel Henrique Barboza     memset(cmd, 0, sizeof(cmd));
568a71c775bSDaniel Henrique Barboza     memset(buf, 0, sizeof(buf));
569a71c775bSDaniel Henrique Barboza     cmd[0] = INQUIRY;
570a71c775bSDaniel Henrique Barboza     cmd[1] = 1;
571a71c775bSDaniel Henrique Barboza     cmd[2] = 0x00;
572a71c775bSDaniel Henrique Barboza     cmd[4] = sizeof(buf);
573a71c775bSDaniel Henrique Barboza 
574a71c775bSDaniel Henrique Barboza     ret = scsi_SG_IO_FROM_DEV(s->conf.blk, cmd, sizeof(cmd),
575c9b6609bSHannes Reinecke                               buf, sizeof(buf), s->io_timeout);
576a71c775bSDaniel Henrique Barboza     if (ret < 0) {
577a71c775bSDaniel Henrique Barboza         /*
578a71c775bSDaniel Henrique Barboza          * Do not assume anything if we can't retrieve the
579a71c775bSDaniel Henrique Barboza          * INQUIRY response to assert the VPD Block Limits
580a71c775bSDaniel Henrique Barboza          * support.
581a71c775bSDaniel Henrique Barboza          */
582a71c775bSDaniel Henrique Barboza         s->needs_vpd_bl_emulation = false;
583a71c775bSDaniel Henrique Barboza         return;
584a71c775bSDaniel Henrique Barboza     }
585a71c775bSDaniel Henrique Barboza 
586a71c775bSDaniel Henrique Barboza     page_len = buf[3];
58757dbb58dSPaolo Bonzini     for (i = 4; i < MIN(sizeof(buf), page_len + 4); i++) {
588a71c775bSDaniel Henrique Barboza         if (buf[i] == 0xb0) {
589a71c775bSDaniel Henrique Barboza             s->needs_vpd_bl_emulation = false;
590a71c775bSDaniel Henrique Barboza             return;
591a71c775bSDaniel Henrique Barboza         }
592a71c775bSDaniel Henrique Barboza     }
593a71c775bSDaniel Henrique Barboza     s->needs_vpd_bl_emulation = true;
594a71c775bSDaniel Henrique Barboza }
595a71c775bSDaniel Henrique Barboza 
596a71c775bSDaniel Henrique Barboza static void scsi_generic_read_device_identification(SCSIDevice *s)
5979fd7e859SPaolo Bonzini {
5989fd7e859SPaolo Bonzini     uint8_t cmd[6];
5999fd7e859SPaolo Bonzini     uint8_t buf[250];
6009fd7e859SPaolo Bonzini     int ret;
6019fd7e859SPaolo Bonzini     int i, len;
6029fd7e859SPaolo Bonzini 
6039fd7e859SPaolo Bonzini     memset(cmd, 0, sizeof(cmd));
6049fd7e859SPaolo Bonzini     memset(buf, 0, sizeof(buf));
6059fd7e859SPaolo Bonzini     cmd[0] = INQUIRY;
6069fd7e859SPaolo Bonzini     cmd[1] = 1;
6079fd7e859SPaolo Bonzini     cmd[2] = 0x83;
6089fd7e859SPaolo Bonzini     cmd[4] = sizeof(buf);
6099fd7e859SPaolo Bonzini 
610a0c7e35bSDaniel Henrique Barboza     ret = scsi_SG_IO_FROM_DEV(s->conf.blk, cmd, sizeof(cmd),
611c9b6609bSHannes Reinecke                               buf, sizeof(buf), s->io_timeout);
612a0c7e35bSDaniel Henrique Barboza     if (ret < 0) {
6139fd7e859SPaolo Bonzini         return;
6149fd7e859SPaolo Bonzini     }
6159fd7e859SPaolo Bonzini 
6169fd7e859SPaolo Bonzini     len = MIN((buf[2] << 8) | buf[3], sizeof(buf) - 4);
6179fd7e859SPaolo Bonzini     for (i = 0; i + 3 <= len; ) {
6189fd7e859SPaolo Bonzini         const uint8_t *p = &buf[i + 4];
6199fd7e859SPaolo Bonzini         uint64_t wwn;
6209fd7e859SPaolo Bonzini 
6219fd7e859SPaolo Bonzini         if (i + (p[3] + 4) > len) {
6229fd7e859SPaolo Bonzini             break;
6239fd7e859SPaolo Bonzini         }
6249fd7e859SPaolo Bonzini 
6259fd7e859SPaolo Bonzini         if ((p[1] & 0x10) == 0) {
6269fd7e859SPaolo Bonzini             /* Associated with the logical unit */
6279fd7e859SPaolo Bonzini             if (read_naa_id(p, &wwn) == 0) {
6289fd7e859SPaolo Bonzini                 s->wwn = wwn;
6299fd7e859SPaolo Bonzini             }
6309fd7e859SPaolo Bonzini         } else if ((p[1] & 0x10) == 0x10) {
6319fd7e859SPaolo Bonzini             /* Associated with the target port */
6329fd7e859SPaolo Bonzini             if (read_naa_id(p, &wwn) == 0) {
6339fd7e859SPaolo Bonzini                 s->port_wwn = wwn;
6349fd7e859SPaolo Bonzini             }
6359fd7e859SPaolo Bonzini         }
6369fd7e859SPaolo Bonzini 
6379fd7e859SPaolo Bonzini         i += p[3] + 4;
6389fd7e859SPaolo Bonzini     }
6399fd7e859SPaolo Bonzini }
6409fd7e859SPaolo Bonzini 
641a71c775bSDaniel Henrique Barboza void scsi_generic_read_device_inquiry(SCSIDevice *s)
642a71c775bSDaniel Henrique Barboza {
643a71c775bSDaniel Henrique Barboza     scsi_generic_read_device_identification(s);
644afff2db6SDmitry Fomichev     if (s->type == TYPE_DISK || s->type == TYPE_ZBC) {
645a71c775bSDaniel Henrique Barboza         scsi_generic_set_vpd_bl_emulation(s);
646a71c775bSDaniel Henrique Barboza     } else {
647a71c775bSDaniel Henrique Barboza         s->needs_vpd_bl_emulation = false;
648a71c775bSDaniel Henrique Barboza     }
649a71c775bSDaniel Henrique Barboza }
650a71c775bSDaniel Henrique Barboza 
6514be74634SMarkus Armbruster static int get_stream_blocksize(BlockBackend *blk)
65249ab747fSPaolo Bonzini {
65349ab747fSPaolo Bonzini     uint8_t cmd[6];
65449ab747fSPaolo Bonzini     uint8_t buf[12];
65549ab747fSPaolo Bonzini     int ret;
65649ab747fSPaolo Bonzini 
65749ab747fSPaolo Bonzini     memset(cmd, 0, sizeof(cmd));
65849ab747fSPaolo Bonzini     memset(buf, 0, sizeof(buf));
65949ab747fSPaolo Bonzini     cmd[0] = MODE_SENSE;
66049ab747fSPaolo Bonzini     cmd[4] = sizeof(buf);
66149ab747fSPaolo Bonzini 
662c9b6609bSHannes Reinecke     ret = scsi_SG_IO_FROM_DEV(blk, cmd, sizeof(cmd), buf, sizeof(buf), 6);
663a0c7e35bSDaniel Henrique Barboza     if (ret < 0) {
66449ab747fSPaolo Bonzini         return -1;
66549ab747fSPaolo Bonzini     }
666a0c7e35bSDaniel Henrique Barboza 
66749ab747fSPaolo Bonzini     return (buf[9] << 16) | (buf[10] << 8) | buf[11];
66849ab747fSPaolo Bonzini }
66949ab747fSPaolo Bonzini 
67049ab747fSPaolo Bonzini static void scsi_generic_reset(DeviceState *dev)
67149ab747fSPaolo Bonzini {
67249ab747fSPaolo Bonzini     SCSIDevice *s = SCSI_DEVICE(dev);
67349ab747fSPaolo Bonzini 
6742343be0dSPaolo Bonzini     s->scsi_version = s->default_scsi_version;
67549ab747fSPaolo Bonzini     scsi_device_purge_requests(s, SENSE_CODE(RESET));
67649ab747fSPaolo Bonzini }
67749ab747fSPaolo Bonzini 
678a818a4b6SFam Zheng static void scsi_generic_realize(SCSIDevice *s, Error **errp)
67949ab747fSPaolo Bonzini {
6806ee143a0SPaolo Bonzini     int rc;
68149ab747fSPaolo Bonzini     int sg_version;
68249ab747fSPaolo Bonzini     struct sg_scsi_id scsiid;
68349ab747fSPaolo Bonzini 
6844be74634SMarkus Armbruster     if (!s->conf.blk) {
685a818a4b6SFam Zheng         error_setg(errp, "drive property not set");
686a818a4b6SFam Zheng         return;
68749ab747fSPaolo Bonzini     }
68849ab747fSPaolo Bonzini 
689166854f7SZihao Chang     if (blk_get_on_error(s->conf.blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC &&
690166854f7SZihao Chang         blk_get_on_error(s->conf.blk, 0) != BLOCKDEV_ON_ERROR_REPORT) {
691a818a4b6SFam Zheng         error_setg(errp, "Device doesn't support drive option werror");
692a818a4b6SFam Zheng         return;
69349ab747fSPaolo Bonzini     }
6944be74634SMarkus Armbruster     if (blk_get_on_error(s->conf.blk, 1) != BLOCKDEV_ON_ERROR_REPORT) {
695a818a4b6SFam Zheng         error_setg(errp, "Device doesn't support drive option rerror");
696a818a4b6SFam Zheng         return;
69749ab747fSPaolo Bonzini     }
69849ab747fSPaolo Bonzini 
69949ab747fSPaolo Bonzini     /* check we are using a driver managing SG_IO (version 3 and after */
7004be74634SMarkus Armbruster     rc = blk_ioctl(s->conf.blk, SG_GET_VERSION_NUM, &sg_version);
7016ee143a0SPaolo Bonzini     if (rc < 0) {
70209c2c6ffSPaolo Bonzini         error_setg_errno(errp, -rc, "cannot get SG_IO version number");
70309c2c6ffSPaolo Bonzini         if (rc != -EPERM) {
70409c2c6ffSPaolo Bonzini             error_append_hint(errp, "Is this a SCSI device?\n");
70509c2c6ffSPaolo Bonzini         }
706a818a4b6SFam Zheng         return;
70749ab747fSPaolo Bonzini     }
70849ab747fSPaolo Bonzini     if (sg_version < 30000) {
709a818a4b6SFam Zheng         error_setg(errp, "scsi generic interface too old");
710a818a4b6SFam Zheng         return;
71149ab747fSPaolo Bonzini     }
71249ab747fSPaolo Bonzini 
71349ab747fSPaolo Bonzini     /* get LUN of the /dev/sg? */
7144be74634SMarkus Armbruster     if (blk_ioctl(s->conf.blk, SG_GET_SCSI_ID, &scsiid)) {
715a818a4b6SFam Zheng         error_setg(errp, "SG_GET_SCSI_ID ioctl failed");
716a818a4b6SFam Zheng         return;
71749ab747fSPaolo Bonzini     }
718c6caae55SFam Zheng     if (!blkconf_apply_backend_options(&s->conf,
71986b1cf32SKevin Wolf                                        !blk_supports_write_perm(s->conf.blk),
720c6caae55SFam Zheng                                        true, errp)) {
721d9bcd6f7SFam Zheng         return;
722d9bcd6f7SFam Zheng     }
72349ab747fSPaolo Bonzini 
72449ab747fSPaolo Bonzini     /* define device state */
72549ab747fSPaolo Bonzini     s->type = scsiid.scsi_type;
72656853498SLaurent Vivier     trace_scsi_generic_realize_type(s->type);
72749ab747fSPaolo Bonzini 
72849ab747fSPaolo Bonzini     switch (s->type) {
72949ab747fSPaolo Bonzini     case TYPE_TAPE:
7304be74634SMarkus Armbruster         s->blocksize = get_stream_blocksize(s->conf.blk);
73149ab747fSPaolo Bonzini         if (s->blocksize == -1) {
73249ab747fSPaolo Bonzini             s->blocksize = 0;
73349ab747fSPaolo Bonzini         }
73449ab747fSPaolo Bonzini         break;
73549ab747fSPaolo Bonzini 
73649ab747fSPaolo Bonzini         /* Make a guess for block devices, we'll fix it when the guest sends.
73749ab747fSPaolo Bonzini          * READ CAPACITY.  If they don't, they likely would assume these sizes
73849ab747fSPaolo Bonzini          * anyway. (TODO: they could also send MODE SENSE).
73949ab747fSPaolo Bonzini          */
74049ab747fSPaolo Bonzini     case TYPE_ROM:
74149ab747fSPaolo Bonzini     case TYPE_WORM:
74249ab747fSPaolo Bonzini         s->blocksize = 2048;
74349ab747fSPaolo Bonzini         break;
74449ab747fSPaolo Bonzini     default:
74549ab747fSPaolo Bonzini         s->blocksize = 512;
74649ab747fSPaolo Bonzini         break;
74749ab747fSPaolo Bonzini     }
74849ab747fSPaolo Bonzini 
74956853498SLaurent Vivier     trace_scsi_generic_realize_blocksize(s->blocksize);
7509fd7e859SPaolo Bonzini 
75129e560f0SDaniel Henrique Barboza     /* Only used by scsi-block, but initialize it nevertheless to be clean.  */
75229e560f0SDaniel Henrique Barboza     s->default_scsi_version = -1;
753c9b6609bSHannes Reinecke     s->io_timeout = DEFAULT_IO_TIMEOUT;
754a71c775bSDaniel Henrique Barboza     scsi_generic_read_device_inquiry(s);
75549ab747fSPaolo Bonzini }
75649ab747fSPaolo Bonzini 
75749ab747fSPaolo Bonzini const SCSIReqOps scsi_generic_req_ops = {
75849ab747fSPaolo Bonzini     .size         = sizeof(SCSIGenericReq),
75949ab747fSPaolo Bonzini     .free_req     = scsi_free_request,
76049ab747fSPaolo Bonzini     .send_command = scsi_send_command,
76149ab747fSPaolo Bonzini     .read_data    = scsi_read_data,
76249ab747fSPaolo Bonzini     .write_data   = scsi_write_data,
76349ab747fSPaolo Bonzini     .get_buf      = scsi_get_buf,
76449ab747fSPaolo Bonzini     .load_request = scsi_generic_load_request,
76549ab747fSPaolo Bonzini     .save_request = scsi_generic_save_request,
76649ab747fSPaolo Bonzini };
76749ab747fSPaolo Bonzini 
76849ab747fSPaolo Bonzini static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
76949ab747fSPaolo Bonzini                                      uint8_t *buf, void *hba_private)
77049ab747fSPaolo Bonzini {
7719be38598SEduardo Habkost     return scsi_req_alloc(&scsi_generic_req_ops, d, tag, lun, hba_private);
77249ab747fSPaolo Bonzini }
77349ab747fSPaolo Bonzini 
77449ab747fSPaolo Bonzini static Property scsi_generic_properties[] = {
7754be74634SMarkus Armbruster     DEFINE_PROP_DRIVE("drive", SCSIDevice, conf.blk),
776d9bcd6f7SFam Zheng     DEFINE_PROP_BOOL("share-rw", SCSIDevice, conf.share_rw, false),
777c9b6609bSHannes Reinecke     DEFINE_PROP_UINT32("io_timeout", SCSIDevice, io_timeout,
778c9b6609bSHannes Reinecke                        DEFAULT_IO_TIMEOUT),
77949ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
78049ab747fSPaolo Bonzini };
78149ab747fSPaolo Bonzini 
7823e7e180aSPaolo Bonzini static int scsi_generic_parse_cdb(SCSIDevice *dev, SCSICommand *cmd,
7833e7e180aSPaolo Bonzini                                   uint8_t *buf, void *hba_private)
7843e7e180aSPaolo Bonzini {
7853e7e180aSPaolo Bonzini     return scsi_bus_parse_cdb(dev, cmd, buf, hba_private);
7863e7e180aSPaolo Bonzini }
7873e7e180aSPaolo Bonzini 
78849ab747fSPaolo Bonzini static void scsi_generic_class_initfn(ObjectClass *klass, void *data)
78949ab747fSPaolo Bonzini {
79049ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
79149ab747fSPaolo Bonzini     SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
79249ab747fSPaolo Bonzini 
793a818a4b6SFam Zheng     sc->realize      = scsi_generic_realize;
79449ab747fSPaolo Bonzini     sc->alloc_req    = scsi_new_request;
7953e7e180aSPaolo Bonzini     sc->parse_cdb    = scsi_generic_parse_cdb;
79649ab747fSPaolo Bonzini     dc->fw_name = "disk";
79749ab747fSPaolo Bonzini     dc->desc = "pass through generic scsi device (/dev/sg*)";
79849ab747fSPaolo Bonzini     dc->reset = scsi_generic_reset;
7994f67d30bSMarc-André Lureau     device_class_set_props(dc, scsi_generic_properties);
80049ab747fSPaolo Bonzini     dc->vmsd  = &vmstate_scsi_device;
80149ab747fSPaolo Bonzini }
80249ab747fSPaolo Bonzini 
80349ab747fSPaolo Bonzini static const TypeInfo scsi_generic_info = {
80449ab747fSPaolo Bonzini     .name          = "scsi-generic",
80549ab747fSPaolo Bonzini     .parent        = TYPE_SCSI_DEVICE,
80649ab747fSPaolo Bonzini     .instance_size = sizeof(SCSIDevice),
80749ab747fSPaolo Bonzini     .class_init    = scsi_generic_class_initfn,
80849ab747fSPaolo Bonzini };
80949ab747fSPaolo Bonzini 
81049ab747fSPaolo Bonzini static void scsi_generic_register_types(void)
81149ab747fSPaolo Bonzini {
81249ab747fSPaolo Bonzini     type_register_static(&scsi_generic_info);
81349ab747fSPaolo Bonzini }
81449ab747fSPaolo Bonzini 
81549ab747fSPaolo Bonzini type_init(scsi_generic_register_types)
81649ab747fSPaolo Bonzini 
81749ab747fSPaolo Bonzini #endif /* __linux__ */
818