xref: /qemu/hw/scsi/scsi-generic.c (revision 7c7a9f57)
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 
scsi_generic_save_request(QEMUFile * f,SCSIRequest * req)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 
scsi_generic_load_request(QEMUFile * f,SCSIRequest * req)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 
scsi_free_request(SCSIRequest * req)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.  */
scsi_command_complete_noio(SCSIGenericReq * r,int ret)74fa0d653bSPaolo Bonzini static void scsi_command_complete_noio(SCSIGenericReq *r, int ret)
7549ab747fSPaolo Bonzini {
7649ab747fSPaolo Bonzini     int status;
771ead6b4eSPaolo Bonzini     SCSISense sense;
78a108557bSHannes 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     }
86a108557bSHannes Reinecke     if (ret < 0) {
87a108557bSHannes 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         }
91a108557bSHannes Reinecke     } else if (io_hdr->host_status != SCSI_HOST_OK) {
92f3126d65SHannes Reinecke         scsi_req_complete_failed(&r->req, io_hdr->host_status);
93f3126d65SHannes Reinecke         goto done;
94a108557bSHannes Reinecke     } else if (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT) {
95a108557bSHannes Reinecke         status = BUSY;
96a108557bSHannes Reinecke     } else {
97a108557bSHannes Reinecke         status = io_hdr->status;
98a108557bSHannes Reinecke         if (io_hdr->driver_status & SG_ERR_DRIVER_SENSE) {
99a108557bSHannes Reinecke             r->req.sense_len = io_hdr->sb_len_wr;
100a108557bSHannes Reinecke         }
101a108557bSHannes Reinecke     }
10256853498SLaurent Vivier     trace_scsi_generic_command_complete_noio(r, r->req.tag, status);
10349ab747fSPaolo Bonzini 
10449ab747fSPaolo Bonzini     scsi_req_complete(&r->req, status);
1056c25fa6cSFam Zheng done:
10649ab747fSPaolo Bonzini     scsi_req_unref(&r->req);
10749ab747fSPaolo Bonzini }
10849ab747fSPaolo Bonzini 
scsi_command_complete(void * opaque,int ret)109fa0d653bSPaolo Bonzini static void scsi_command_complete(void *opaque, int ret)
110fa0d653bSPaolo Bonzini {
111fa0d653bSPaolo Bonzini     SCSIGenericReq *r = (SCSIGenericReq *)opaque;
1127b7fc3d0SStefan Hajnoczi 
113fa0d653bSPaolo Bonzini     assert(r->req.aiocb != NULL);
114fa0d653bSPaolo Bonzini     r->req.aiocb = NULL;
115b9e413ddSPaolo Bonzini 
116fa0d653bSPaolo Bonzini     scsi_command_complete_noio(r, ret);
117fa0d653bSPaolo Bonzini }
118fa0d653bSPaolo Bonzini 
execute_command(BlockBackend * blk,SCSIGenericReq * r,int direction,BlockCompletionFunc * complete)1194be74634SMarkus Armbruster static int execute_command(BlockBackend *blk,
12049ab747fSPaolo Bonzini                            SCSIGenericReq *r, int direction,
121097310b5SMarkus Armbruster                            BlockCompletionFunc *complete)
12249ab747fSPaolo Bonzini {
123c9b6609bSHannes Reinecke     SCSIDevice *s = r->req.dev;
124c9b6609bSHannes Reinecke 
12549ab747fSPaolo Bonzini     r->io_header.interface_id = 'S';
12649ab747fSPaolo Bonzini     r->io_header.dxfer_direction = direction;
12749ab747fSPaolo Bonzini     r->io_header.dxferp = r->buf;
12849ab747fSPaolo Bonzini     r->io_header.dxfer_len = r->buflen;
12949ab747fSPaolo Bonzini     r->io_header.cmdp = r->req.cmd.buf;
13049ab747fSPaolo Bonzini     r->io_header.cmd_len = r->req.cmd.len;
13149ab747fSPaolo Bonzini     r->io_header.mx_sb_len = sizeof(r->req.sense);
13249ab747fSPaolo Bonzini     r->io_header.sbp = r->req.sense;
133c9b6609bSHannes Reinecke     r->io_header.timeout = s->io_timeout * 1000;
13449ab747fSPaolo Bonzini     r->io_header.usr_ptr = r;
13549ab747fSPaolo Bonzini     r->io_header.flags |= SG_FLAG_DIRECT_IO;
13649ab747fSPaolo Bonzini 
137b2d50a33SHannes Reinecke     trace_scsi_generic_aio_sgio_command(r->req.tag, r->req.cmd.buf[0],
138b2d50a33SHannes Reinecke                                         r->io_header.timeout);
1394be74634SMarkus Armbruster     r->req.aiocb = blk_aio_ioctl(blk, SG_IO, &r->io_header, complete, r);
140d836f8d3SPavel Hrdina     if (r->req.aiocb == NULL) {
141d836f8d3SPavel Hrdina         return -EIO;
142d836f8d3SPavel Hrdina     }
14349ab747fSPaolo Bonzini 
14449ab747fSPaolo Bonzini     return 0;
14549ab747fSPaolo Bonzini }
14649ab747fSPaolo Bonzini 
calculate_max_transfer(SCSIDevice * s)14751e15194SKevin Wolf static uint64_t calculate_max_transfer(SCSIDevice *s)
14851e15194SKevin Wolf {
14951e15194SKevin Wolf     uint64_t max_transfer = blk_get_max_hw_transfer(s->conf.blk);
15051e15194SKevin Wolf     uint32_t max_iov = blk_get_max_hw_iov(s->conf.blk);
15151e15194SKevin Wolf 
15251e15194SKevin Wolf     assert(max_transfer);
15351e15194SKevin Wolf     max_transfer = MIN_NON_ZERO(max_transfer,
15451e15194SKevin Wolf                                 max_iov * qemu_real_host_page_size());
15551e15194SKevin Wolf 
15651e15194SKevin Wolf     return max_transfer / s->blocksize;
15751e15194SKevin Wolf }
15851e15194SKevin Wolf 
scsi_handle_inquiry_reply(SCSIGenericReq * r,SCSIDevice * s,int len)15906b80795SMaxim Levitsky static int scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s, int len)
1600a96ca24SDaniel Henrique Barboza {
1616c219fc8SPaolo Bonzini     uint8_t page, page_idx;
162a71c775bSDaniel Henrique Barboza 
1630a96ca24SDaniel Henrique Barboza     /*
1640a96ca24SDaniel Henrique Barboza      *  EVPD set to zero returns the standard INQUIRY data.
1650a96ca24SDaniel Henrique Barboza      *
1660a96ca24SDaniel Henrique Barboza      *  Check if scsi_version is unset (-1) to avoid re-defining it
1670a96ca24SDaniel Henrique Barboza      *  each time an INQUIRY with standard data is received.
1680a96ca24SDaniel Henrique Barboza      *  scsi_version is initialized with -1 in scsi_generic_reset
1690a96ca24SDaniel Henrique Barboza      *  and scsi_disk_reset, making sure that we'll set the
1700a96ca24SDaniel Henrique Barboza      *  scsi_version after a reset. If the version field of the
1710a96ca24SDaniel Henrique Barboza      *  INQUIRY response somehow changes after a guest reboot,
1720a96ca24SDaniel Henrique Barboza      *  we'll be able to keep track of it.
1730a96ca24SDaniel Henrique Barboza      *
1740a96ca24SDaniel Henrique Barboza      *  On SCSI-2 and older, first 3 bits of byte 2 is the
1750a96ca24SDaniel Henrique Barboza      *  ANSI-approved version, while on later versions the
1760a96ca24SDaniel Henrique Barboza      *  whole byte 2 contains the version. Check if we're dealing
1770a96ca24SDaniel Henrique Barboza      *  with a newer version and, in that case, assign the
1780a96ca24SDaniel Henrique Barboza      *  whole byte.
1790a96ca24SDaniel Henrique Barboza      */
1800a96ca24SDaniel Henrique Barboza     if (s->scsi_version == -1 && !(r->req.cmd.buf[1] & 0x01)) {
1810a96ca24SDaniel Henrique Barboza         s->scsi_version = r->buf[2] & 0x07;
1820a96ca24SDaniel Henrique Barboza         if (s->scsi_version > 2) {
1830a96ca24SDaniel Henrique Barboza             s->scsi_version = r->buf[2];
1840a96ca24SDaniel Henrique Barboza         }
1850a96ca24SDaniel Henrique Barboza     }
186a71c775bSDaniel Henrique Barboza 
187afff2db6SDmitry Fomichev     if ((s->type == TYPE_DISK || s->type == TYPE_ZBC) &&
188afff2db6SDmitry Fomichev         (r->req.cmd.buf[1] & 0x01)) {
189a71c775bSDaniel Henrique Barboza         page = r->req.cmd.buf[2];
1909bd634b2SPaolo Bonzini         if (page == 0xb0 && r->buflen >= 8) {
1919bd634b2SPaolo Bonzini             uint8_t buf[16] = {};
1929bd634b2SPaolo Bonzini             uint8_t buf_used = MIN(r->buflen, 16);
19351e15194SKevin Wolf             uint64_t max_transfer = calculate_max_transfer(s);
1949bd634b2SPaolo Bonzini 
1959bd634b2SPaolo Bonzini             memcpy(buf, r->buf, buf_used);
1969bd634b2SPaolo Bonzini             stl_be_p(&buf[8], max_transfer);
1979bd634b2SPaolo Bonzini             stl_be_p(&buf[12], MIN_NON_ZERO(max_transfer, ldl_be_p(&buf[12])));
1989bd634b2SPaolo Bonzini             memcpy(r->buf + 8, buf + 8, buf_used - 8);
1999bd634b2SPaolo Bonzini 
200e909ff93SPaolo Bonzini         } else if (s->needs_vpd_bl_emulation && page == 0x00 && r->buflen >= 4) {
201a71c775bSDaniel Henrique Barboza             /*
202a71c775bSDaniel Henrique Barboza              * Now we're capable of supplying the VPD Block Limits
203a71c775bSDaniel Henrique Barboza              * response if the hardware can't. Add it in the INQUIRY
204a71c775bSDaniel Henrique Barboza              * Supported VPD pages response in case we are using the
205a71c775bSDaniel Henrique Barboza              * emulation for this device.
206a71c775bSDaniel Henrique Barboza              *
207a71c775bSDaniel Henrique Barboza              * This way, the guest kernel will be aware of the support
208a71c775bSDaniel Henrique Barboza              * and will use it to proper setup the SCSI device.
2096c219fc8SPaolo Bonzini              *
2106c219fc8SPaolo Bonzini              * VPD page numbers must be sorted, so insert 0xb0 at the
211e909ff93SPaolo Bonzini              * right place with an in-place insert.  When the while loop
212e909ff93SPaolo Bonzini              * begins the device response is at r[0] to r[page_idx - 1].
213a71c775bSDaniel Henrique Barboza              */
214e909ff93SPaolo Bonzini             page_idx = lduw_be_p(r->buf + 2) + 4;
215e909ff93SPaolo Bonzini             page_idx = MIN(page_idx, r->buflen);
216e909ff93SPaolo Bonzini             while (page_idx > 4 && r->buf[page_idx - 1] >= 0xb0) {
2176c219fc8SPaolo Bonzini                 if (page_idx < r->buflen) {
2186c219fc8SPaolo Bonzini                     r->buf[page_idx] = r->buf[page_idx - 1];
2196c219fc8SPaolo Bonzini                 }
220e909ff93SPaolo Bonzini                 page_idx--;
2216c219fc8SPaolo Bonzini             }
222e909ff93SPaolo Bonzini             if (page_idx < r->buflen) {
2236c219fc8SPaolo Bonzini                 r->buf[page_idx] = 0xb0;
224e909ff93SPaolo Bonzini             }
2256c219fc8SPaolo Bonzini             stw_be_p(r->buf + 2, lduw_be_p(r->buf + 2) + 1);
22606b80795SMaxim Levitsky 
22706b80795SMaxim Levitsky             if (len < r->buflen) {
22806b80795SMaxim Levitsky                 len++;
2290a96ca24SDaniel Henrique Barboza             }
2300a96ca24SDaniel Henrique Barboza         }
231a71c775bSDaniel Henrique Barboza     }
23206b80795SMaxim Levitsky     return len;
23306b80795SMaxim Levitsky }
234a71c775bSDaniel Henrique Barboza 
scsi_generic_emulate_block_limits(SCSIGenericReq * r,SCSIDevice * s)2353d4a8bf0SPaolo Bonzini static int scsi_generic_emulate_block_limits(SCSIGenericReq *r, SCSIDevice *s)
236a71c775bSDaniel Henrique Barboza {
2373d4a8bf0SPaolo Bonzini     int len;
2383d4a8bf0SPaolo Bonzini     uint8_t buf[64];
2393d4a8bf0SPaolo Bonzini 
2403d4a8bf0SPaolo Bonzini     SCSIBlockLimits bl = {
24151e15194SKevin Wolf         .max_io_sectors = calculate_max_transfer(s),
2423d4a8bf0SPaolo Bonzini     };
2433d4a8bf0SPaolo Bonzini 
2443d4a8bf0SPaolo Bonzini     memset(r->buf, 0, r->buflen);
2453d4a8bf0SPaolo Bonzini     stb_p(buf, s->type);
2463d4a8bf0SPaolo Bonzini     stb_p(buf + 1, 0xb0);
2473d4a8bf0SPaolo Bonzini     len = scsi_emulate_block_limits(buf + 4, &bl);
2483d4a8bf0SPaolo Bonzini     assert(len <= sizeof(buf) - 4);
2493d4a8bf0SPaolo Bonzini     stw_be_p(buf + 2, len);
2503d4a8bf0SPaolo Bonzini 
2513d4a8bf0SPaolo Bonzini     memcpy(r->buf, buf, MIN(r->buflen, len + 4));
2523d4a8bf0SPaolo Bonzini 
253a71c775bSDaniel Henrique Barboza     r->io_header.sb_len_wr = 0;
254a71c775bSDaniel Henrique Barboza 
255a71c775bSDaniel Henrique Barboza     /*
256a71c775bSDaniel Henrique Barboza     * We have valid contents in the reply buffer but the
257a71c775bSDaniel Henrique Barboza     * io_header can report a sense error coming from
258a71c775bSDaniel Henrique Barboza     * the hardware in scsi_command_complete_noio. Clean
259a71c775bSDaniel Henrique Barboza     * up the io_header to avoid reporting it.
260a71c775bSDaniel Henrique Barboza     */
261a71c775bSDaniel Henrique Barboza     r->io_header.driver_status = 0;
262a71c775bSDaniel Henrique Barboza     r->io_header.status = 0;
263a71c775bSDaniel Henrique Barboza 
264a71c775bSDaniel Henrique Barboza     return r->buflen;
265a71c775bSDaniel Henrique Barboza }
2660a96ca24SDaniel Henrique Barboza 
scsi_read_complete(void * opaque,int ret)26749ab747fSPaolo Bonzini static void scsi_read_complete(void * opaque, int ret)
26849ab747fSPaolo Bonzini {
26949ab747fSPaolo Bonzini     SCSIGenericReq *r = (SCSIGenericReq *)opaque;
27049ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
27149ab747fSPaolo Bonzini     int len;
27249ab747fSPaolo Bonzini 
273fa0d653bSPaolo Bonzini     assert(r->req.aiocb != NULL);
27449ab747fSPaolo Bonzini     r->req.aiocb = NULL;
275fa0d653bSPaolo Bonzini 
2766c25fa6cSFam Zheng     if (ret || r->req.io_canceled) {
277fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, ret);
278*14042268SStefan Hajnoczi         return;
27949ab747fSPaolo Bonzini     }
280fa0d653bSPaolo Bonzini 
28149ab747fSPaolo Bonzini     len = r->io_header.dxfer_len - r->io_header.resid;
28256853498SLaurent Vivier     trace_scsi_generic_read_complete(r->req.tag, len);
28349ab747fSPaolo Bonzini 
28449ab747fSPaolo Bonzini     r->len = -1;
285a71c775bSDaniel Henrique Barboza 
2861849f297SShin'ichiro Kawasaki     if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
2871849f297SShin'ichiro Kawasaki         SCSISense sense =
2881849f297SShin'ichiro Kawasaki             scsi_parse_sense_buf(r->req.sense, r->io_header.sb_len_wr);
2891849f297SShin'ichiro Kawasaki 
290a71c775bSDaniel Henrique Barboza         /*
291a71c775bSDaniel Henrique Barboza          * Check if this is a VPD Block Limits request that
292a71c775bSDaniel Henrique Barboza          * resulted in sense error but would need emulation.
293a71c775bSDaniel Henrique Barboza          * In this case, emulate a valid VPD response.
294a71c775bSDaniel Henrique Barboza          */
2951849f297SShin'ichiro Kawasaki         if (sense.key == ILLEGAL_REQUEST &&
2961849f297SShin'ichiro Kawasaki             s->needs_vpd_bl_emulation &&
2973d4a8bf0SPaolo Bonzini             r->req.cmd.buf[0] == INQUIRY &&
2983d4a8bf0SPaolo Bonzini             (r->req.cmd.buf[1] & 0x01) &&
2993d4a8bf0SPaolo Bonzini             r->req.cmd.buf[2] == 0xb0) {
3003d4a8bf0SPaolo Bonzini             len = scsi_generic_emulate_block_limits(r, s);
301a71c775bSDaniel Henrique Barboza             /*
3021849f297SShin'ichiro Kawasaki              * It's okay to jup to req_complete: no need to
3031849f297SShin'ichiro Kawasaki              * let scsi_handle_inquiry_reply handle an
304a71c775bSDaniel Henrique Barboza              * INQUIRY VPD BL request we created manually.
305a71c775bSDaniel Henrique Barboza              */
3061849f297SShin'ichiro Kawasaki         }
3071849f297SShin'ichiro Kawasaki         if (sense.key) {
308a71c775bSDaniel Henrique Barboza             goto req_complete;
309a71c775bSDaniel Henrique Barboza         }
310a71c775bSDaniel Henrique Barboza     }
311a71c775bSDaniel Henrique Barboza 
3129738c657SPaolo Bonzini     if (r->io_header.host_status != SCSI_HOST_OK ||
3139738c657SPaolo Bonzini         (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT) ||
3149738c657SPaolo Bonzini         r->io_header.status != GOOD ||
3159738c657SPaolo Bonzini         len == 0) {
316fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, 0);
317*14042268SStefan Hajnoczi         return;
318fa0d653bSPaolo Bonzini     }
319fa0d653bSPaolo Bonzini 
32049ab747fSPaolo Bonzini     /* Snoop READ CAPACITY output to set the blocksize.  */
32153254e56SPaolo Bonzini     if (r->req.cmd.buf[0] == READ_CAPACITY_10 &&
32253254e56SPaolo Bonzini         (ldl_be_p(&r->buf[0]) != 0xffffffffU || s->max_lba == 0)) {
32349ab747fSPaolo Bonzini         s->blocksize = ldl_be_p(&r->buf[4]);
32453254e56SPaolo Bonzini         s->max_lba = ldl_be_p(&r->buf[0]) & 0xffffffffULL;
32549ab747fSPaolo Bonzini     } else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 &&
32649ab747fSPaolo Bonzini                (r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) {
32749ab747fSPaolo Bonzini         s->blocksize = ldl_be_p(&r->buf[8]);
32849ab747fSPaolo Bonzini         s->max_lba = ldq_be_p(&r->buf[0]);
32949ab747fSPaolo Bonzini     }
33049ab747fSPaolo Bonzini 
331afff2db6SDmitry Fomichev     /*
332afff2db6SDmitry Fomichev      * Patch MODE SENSE device specific parameters if the BDS is opened
3330eb2baebSPaolo Bonzini      * readonly.
3340eb2baebSPaolo Bonzini      */
335afff2db6SDmitry Fomichev     if ((s->type == TYPE_DISK || s->type == TYPE_TAPE || s->type == TYPE_ZBC) &&
33686b1cf32SKevin Wolf         !blk_is_writable(s->conf.blk) &&
3370eb2baebSPaolo Bonzini         (r->req.cmd.buf[0] == MODE_SENSE ||
3380eb2baebSPaolo Bonzini          r->req.cmd.buf[0] == MODE_SENSE_10) &&
3390eb2baebSPaolo Bonzini         (r->req.cmd.buf[1] & 0x8) == 0) {
3400eb2baebSPaolo Bonzini         if (r->req.cmd.buf[0] == MODE_SENSE) {
3410eb2baebSPaolo Bonzini             r->buf[2] |= 0x80;
3420eb2baebSPaolo Bonzini         } else  {
3430eb2baebSPaolo Bonzini             r->buf[3] |= 0x80;
3440eb2baebSPaolo Bonzini         }
3450eb2baebSPaolo Bonzini     }
34629e560f0SDaniel Henrique Barboza     if (r->req.cmd.buf[0] == INQUIRY) {
34706b80795SMaxim Levitsky         len = scsi_handle_inquiry_reply(r, s, len);
34829e560f0SDaniel Henrique Barboza     }
349a71c775bSDaniel Henrique Barboza 
350a71c775bSDaniel Henrique Barboza req_complete:
35149ab747fSPaolo Bonzini     scsi_req_data(&r->req, len);
35249ab747fSPaolo Bonzini     scsi_req_unref(&r->req);
35349ab747fSPaolo Bonzini }
35449ab747fSPaolo Bonzini 
35549ab747fSPaolo Bonzini /* Read more data from scsi device into buffer.  */
scsi_read_data(SCSIRequest * req)35649ab747fSPaolo Bonzini static void scsi_read_data(SCSIRequest *req)
35749ab747fSPaolo Bonzini {
35849ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
35949ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
36049ab747fSPaolo Bonzini     int ret;
36149ab747fSPaolo Bonzini 
36256853498SLaurent Vivier     trace_scsi_generic_read_data(req->tag);
36349ab747fSPaolo Bonzini 
36449ab747fSPaolo Bonzini     /* The request is used as the AIO opaque value, so add a ref.  */
36549ab747fSPaolo Bonzini     scsi_req_ref(&r->req);
36649ab747fSPaolo Bonzini     if (r->len == -1) {
367fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, 0);
36849ab747fSPaolo Bonzini         return;
36949ab747fSPaolo Bonzini     }
37049ab747fSPaolo Bonzini 
3714be74634SMarkus Armbruster     ret = execute_command(s->conf.blk, r, SG_DXFER_FROM_DEV,
3724be74634SMarkus Armbruster                           scsi_read_complete);
37349ab747fSPaolo Bonzini     if (ret < 0) {
374fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, ret);
37549ab747fSPaolo Bonzini     }
37649ab747fSPaolo Bonzini }
37749ab747fSPaolo Bonzini 
scsi_write_complete(void * opaque,int ret)37849ab747fSPaolo Bonzini static void scsi_write_complete(void * opaque, int ret)
37949ab747fSPaolo Bonzini {
38049ab747fSPaolo Bonzini     SCSIGenericReq *r = (SCSIGenericReq *)opaque;
38149ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
38249ab747fSPaolo Bonzini 
38356853498SLaurent Vivier     trace_scsi_generic_write_complete(ret);
384fa0d653bSPaolo Bonzini 
385fa0d653bSPaolo Bonzini     assert(r->req.aiocb != NULL);
38649ab747fSPaolo Bonzini     r->req.aiocb = NULL;
387fa0d653bSPaolo Bonzini 
3886c25fa6cSFam Zheng     if (ret || r->req.io_canceled) {
389fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, ret);
390*14042268SStefan Hajnoczi         return;
39149ab747fSPaolo Bonzini     }
39249ab747fSPaolo Bonzini 
39349ab747fSPaolo Bonzini     if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 &&
39449ab747fSPaolo Bonzini         s->type == TYPE_TAPE) {
39549ab747fSPaolo Bonzini         s->blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11];
39656853498SLaurent Vivier         trace_scsi_generic_write_complete_blocksize(s->blocksize);
39749ab747fSPaolo Bonzini     }
39849ab747fSPaolo Bonzini 
399fa0d653bSPaolo Bonzini     scsi_command_complete_noio(r, ret);
40049ab747fSPaolo Bonzini }
40149ab747fSPaolo Bonzini 
40249ab747fSPaolo Bonzini /* Write data to a scsi device.  Returns nonzero on failure.
40349ab747fSPaolo Bonzini    The transfer may complete asynchronously.  */
scsi_write_data(SCSIRequest * req)40449ab747fSPaolo Bonzini static void scsi_write_data(SCSIRequest *req)
40549ab747fSPaolo Bonzini {
40649ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
40749ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
40849ab747fSPaolo Bonzini     int ret;
40949ab747fSPaolo Bonzini 
41056853498SLaurent Vivier     trace_scsi_generic_write_data(req->tag);
41149ab747fSPaolo Bonzini     if (r->len == 0) {
41249ab747fSPaolo Bonzini         r->len = r->buflen;
41349ab747fSPaolo Bonzini         scsi_req_data(&r->req, r->len);
41449ab747fSPaolo Bonzini         return;
41549ab747fSPaolo Bonzini     }
41649ab747fSPaolo Bonzini 
41749ab747fSPaolo Bonzini     /* The request is used as the AIO opaque value, so add a ref.  */
41849ab747fSPaolo Bonzini     scsi_req_ref(&r->req);
4194be74634SMarkus Armbruster     ret = execute_command(s->conf.blk, r, SG_DXFER_TO_DEV, scsi_write_complete);
42049ab747fSPaolo Bonzini     if (ret < 0) {
421fa0d653bSPaolo Bonzini         scsi_command_complete_noio(r, ret);
42249ab747fSPaolo Bonzini     }
42349ab747fSPaolo Bonzini }
42449ab747fSPaolo Bonzini 
42549ab747fSPaolo Bonzini /* Return a pointer to the data buffer.  */
scsi_get_buf(SCSIRequest * req)42649ab747fSPaolo Bonzini static uint8_t *scsi_get_buf(SCSIRequest *req)
42749ab747fSPaolo Bonzini {
42849ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
42949ab747fSPaolo Bonzini 
43049ab747fSPaolo Bonzini     return r->buf;
43149ab747fSPaolo Bonzini }
43249ab747fSPaolo Bonzini 
scsi_generic_command_dump(uint8_t * cmd,int len)43356853498SLaurent Vivier static void scsi_generic_command_dump(uint8_t *cmd, int len)
43456853498SLaurent Vivier {
43556853498SLaurent Vivier     int i;
43656853498SLaurent Vivier     char *line_buffer, *p;
43756853498SLaurent Vivier 
43856853498SLaurent Vivier     line_buffer = g_malloc(len * 5 + 1);
43956853498SLaurent Vivier 
44056853498SLaurent Vivier     for (i = 0, p = line_buffer; i < len; i++) {
44156853498SLaurent Vivier         p += sprintf(p, " 0x%02x", cmd[i]);
44256853498SLaurent Vivier     }
44356853498SLaurent Vivier     trace_scsi_generic_send_command(line_buffer);
44456853498SLaurent Vivier 
44556853498SLaurent Vivier     g_free(line_buffer);
44656853498SLaurent Vivier }
44756853498SLaurent Vivier 
44849ab747fSPaolo Bonzini /* Execute a scsi command.  Returns the length of the data expected by the
44949ab747fSPaolo Bonzini    command.  This will be Positive for data transfers from the device
45049ab747fSPaolo Bonzini    (eg. disk reads), negative for transfers to the device (eg. disk writes),
45149ab747fSPaolo Bonzini    and zero if the command does not transfer any data.  */
45249ab747fSPaolo Bonzini 
scsi_send_command(SCSIRequest * req,uint8_t * cmd)45349ab747fSPaolo Bonzini static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
45449ab747fSPaolo Bonzini {
45549ab747fSPaolo Bonzini     SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
45649ab747fSPaolo Bonzini     SCSIDevice *s = r->req.dev;
45749ab747fSPaolo Bonzini     int ret;
45849ab747fSPaolo Bonzini 
45956853498SLaurent Vivier     if (trace_event_get_state_backends(TRACE_SCSI_GENERIC_SEND_COMMAND)) {
46056853498SLaurent Vivier         scsi_generic_command_dump(cmd, r->req.cmd.len);
46149ab747fSPaolo Bonzini     }
46249ab747fSPaolo Bonzini 
46349ab747fSPaolo Bonzini     if (r->req.cmd.xfer == 0) {
46449ab747fSPaolo Bonzini         g_free(r->buf);
46549ab747fSPaolo Bonzini         r->buflen = 0;
46649ab747fSPaolo Bonzini         r->buf = NULL;
46749ab747fSPaolo Bonzini         /* The request is used as the AIO opaque value, so add a ref.  */
46849ab747fSPaolo Bonzini         scsi_req_ref(&r->req);
4694be74634SMarkus Armbruster         ret = execute_command(s->conf.blk, r, SG_DXFER_NONE,
4704be74634SMarkus Armbruster                               scsi_command_complete);
47149ab747fSPaolo Bonzini         if (ret < 0) {
472fa0d653bSPaolo Bonzini             scsi_command_complete_noio(r, ret);
47349ab747fSPaolo Bonzini             return 0;
47449ab747fSPaolo Bonzini         }
47549ab747fSPaolo Bonzini         return 0;
47649ab747fSPaolo Bonzini     }
47749ab747fSPaolo Bonzini 
47849ab747fSPaolo Bonzini     if (r->buflen != r->req.cmd.xfer) {
47949ab747fSPaolo Bonzini         g_free(r->buf);
48049ab747fSPaolo Bonzini         r->buf = g_malloc(r->req.cmd.xfer);
48149ab747fSPaolo Bonzini         r->buflen = r->req.cmd.xfer;
48249ab747fSPaolo Bonzini     }
48349ab747fSPaolo Bonzini 
48449ab747fSPaolo Bonzini     memset(r->buf, 0, r->buflen);
48549ab747fSPaolo Bonzini     r->len = r->req.cmd.xfer;
48649ab747fSPaolo Bonzini     if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
48749ab747fSPaolo Bonzini         r->len = 0;
48849ab747fSPaolo Bonzini         return -r->req.cmd.xfer;
48949ab747fSPaolo Bonzini     } else {
49049ab747fSPaolo Bonzini         return r->req.cmd.xfer;
49149ab747fSPaolo Bonzini     }
49249ab747fSPaolo Bonzini }
49349ab747fSPaolo Bonzini 
read_naa_id(const uint8_t * p,uint64_t * p_wwn)4949fd7e859SPaolo Bonzini static int read_naa_id(const uint8_t *p, uint64_t *p_wwn)
4959fd7e859SPaolo Bonzini {
4969fd7e859SPaolo Bonzini     int i;
4979fd7e859SPaolo Bonzini 
4989fd7e859SPaolo Bonzini     if ((p[1] & 0xF) == 3) {
4999fd7e859SPaolo Bonzini         /* NAA designator type */
5009fd7e859SPaolo Bonzini         if (p[3] != 8) {
5019fd7e859SPaolo Bonzini             return -EINVAL;
5029fd7e859SPaolo Bonzini         }
5039fd7e859SPaolo Bonzini         *p_wwn = ldq_be_p(p + 4);
5049fd7e859SPaolo Bonzini         return 0;
5059fd7e859SPaolo Bonzini     }
5069fd7e859SPaolo Bonzini 
5079fd7e859SPaolo Bonzini     if ((p[1] & 0xF) == 8) {
5089fd7e859SPaolo Bonzini         /* SCSI name string designator type */
5099fd7e859SPaolo Bonzini         if (p[3] < 20 || memcmp(&p[4], "naa.", 4)) {
5109fd7e859SPaolo Bonzini             return -EINVAL;
5119fd7e859SPaolo Bonzini         }
5129fd7e859SPaolo Bonzini         if (p[3] > 20 && p[24] != ',') {
5139fd7e859SPaolo Bonzini             return -EINVAL;
5149fd7e859SPaolo Bonzini         }
5159fd7e859SPaolo Bonzini         *p_wwn = 0;
5169fd7e859SPaolo Bonzini         for (i = 8; i < 24; i++) {
51795a5befcSPeter Maydell             char c = qemu_toupper(p[i]);
5189fd7e859SPaolo Bonzini             c -= (c >= '0' && c <= '9' ? '0' : 'A' - 10);
5199fd7e859SPaolo Bonzini             *p_wwn = (*p_wwn << 4) | c;
5209fd7e859SPaolo Bonzini         }
5219fd7e859SPaolo Bonzini         return 0;
5229fd7e859SPaolo Bonzini     }
5239fd7e859SPaolo Bonzini 
5249fd7e859SPaolo Bonzini     return -EINVAL;
5259fd7e859SPaolo Bonzini }
5269fd7e859SPaolo Bonzini 
scsi_SG_IO_FROM_DEV(BlockBackend * blk,uint8_t * cmd,uint8_t cmd_size,uint8_t * buf,uint8_t buf_size,uint32_t timeout)527a0c7e35bSDaniel Henrique Barboza int scsi_SG_IO_FROM_DEV(BlockBackend *blk, uint8_t *cmd, uint8_t cmd_size,
528c9b6609bSHannes Reinecke                         uint8_t *buf, uint8_t buf_size, uint32_t timeout)
529a0c7e35bSDaniel Henrique Barboza {
530a0c7e35bSDaniel Henrique Barboza     sg_io_hdr_t io_header;
531a0c7e35bSDaniel Henrique Barboza     uint8_t sensebuf[8];
532a0c7e35bSDaniel Henrique Barboza     int ret;
533a0c7e35bSDaniel Henrique Barboza 
534a0c7e35bSDaniel Henrique Barboza     memset(&io_header, 0, sizeof(io_header));
535a0c7e35bSDaniel Henrique Barboza     io_header.interface_id = 'S';
536a0c7e35bSDaniel Henrique Barboza     io_header.dxfer_direction = SG_DXFER_FROM_DEV;
537a0c7e35bSDaniel Henrique Barboza     io_header.dxfer_len = buf_size;
538a0c7e35bSDaniel Henrique Barboza     io_header.dxferp = buf;
539a0c7e35bSDaniel Henrique Barboza     io_header.cmdp = cmd;
540a0c7e35bSDaniel Henrique Barboza     io_header.cmd_len = cmd_size;
541a0c7e35bSDaniel Henrique Barboza     io_header.mx_sb_len = sizeof(sensebuf);
542a0c7e35bSDaniel Henrique Barboza     io_header.sbp = sensebuf;
543c9b6609bSHannes Reinecke     io_header.timeout = timeout * 1000;
544a0c7e35bSDaniel Henrique Barboza 
545b2d50a33SHannes Reinecke     trace_scsi_generic_ioctl_sgio_command(cmd[0], io_header.timeout);
546a0c7e35bSDaniel Henrique Barboza     ret = blk_ioctl(blk, SG_IO, &io_header);
547b2d50a33SHannes Reinecke     if (ret < 0 || io_header.status ||
548b2d50a33SHannes Reinecke         io_header.driver_status || io_header.host_status) {
549b2d50a33SHannes Reinecke         trace_scsi_generic_ioctl_sgio_done(cmd[0], ret, io_header.status,
550b2d50a33SHannes Reinecke                                            io_header.host_status);
551a0c7e35bSDaniel Henrique Barboza         return -1;
552a0c7e35bSDaniel Henrique Barboza     }
553a0c7e35bSDaniel Henrique Barboza     return 0;
554a0c7e35bSDaniel Henrique Barboza }
555a0c7e35bSDaniel Henrique Barboza 
556a71c775bSDaniel Henrique Barboza /*
557a71c775bSDaniel Henrique Barboza  * Executes an INQUIRY request with EVPD set to retrieve the
558a71c775bSDaniel Henrique Barboza  * available VPD pages of the device. If the device does
559a71c775bSDaniel Henrique Barboza  * not support the Block Limits page (page 0xb0), set
560a71c775bSDaniel Henrique Barboza  * the needs_vpd_bl_emulation flag for future use.
561a71c775bSDaniel Henrique Barboza  */
scsi_generic_set_vpd_bl_emulation(SCSIDevice * s)562a71c775bSDaniel Henrique Barboza static void scsi_generic_set_vpd_bl_emulation(SCSIDevice *s)
563a71c775bSDaniel Henrique Barboza {
564a71c775bSDaniel Henrique Barboza     uint8_t cmd[6];
565a71c775bSDaniel Henrique Barboza     uint8_t buf[250];
566a71c775bSDaniel Henrique Barboza     uint8_t page_len;
567a71c775bSDaniel Henrique Barboza     int ret, i;
568a71c775bSDaniel Henrique Barboza 
569a71c775bSDaniel Henrique Barboza     memset(cmd, 0, sizeof(cmd));
570a71c775bSDaniel Henrique Barboza     memset(buf, 0, sizeof(buf));
571a71c775bSDaniel Henrique Barboza     cmd[0] = INQUIRY;
572a71c775bSDaniel Henrique Barboza     cmd[1] = 1;
573a71c775bSDaniel Henrique Barboza     cmd[2] = 0x00;
574a71c775bSDaniel Henrique Barboza     cmd[4] = sizeof(buf);
575a71c775bSDaniel Henrique Barboza 
576a71c775bSDaniel Henrique Barboza     ret = scsi_SG_IO_FROM_DEV(s->conf.blk, cmd, sizeof(cmd),
577c9b6609bSHannes Reinecke                               buf, sizeof(buf), s->io_timeout);
578a71c775bSDaniel Henrique Barboza     if (ret < 0) {
579a71c775bSDaniel Henrique Barboza         /*
580a71c775bSDaniel Henrique Barboza          * Do not assume anything if we can't retrieve the
581a71c775bSDaniel Henrique Barboza          * INQUIRY response to assert the VPD Block Limits
582a71c775bSDaniel Henrique Barboza          * support.
583a71c775bSDaniel Henrique Barboza          */
584a71c775bSDaniel Henrique Barboza         s->needs_vpd_bl_emulation = false;
585a71c775bSDaniel Henrique Barboza         return;
586a71c775bSDaniel Henrique Barboza     }
587a71c775bSDaniel Henrique Barboza 
588a71c775bSDaniel Henrique Barboza     page_len = buf[3];
58957dbb58dSPaolo Bonzini     for (i = 4; i < MIN(sizeof(buf), page_len + 4); i++) {
590a71c775bSDaniel Henrique Barboza         if (buf[i] == 0xb0) {
591a71c775bSDaniel Henrique Barboza             s->needs_vpd_bl_emulation = false;
592a71c775bSDaniel Henrique Barboza             return;
593a71c775bSDaniel Henrique Barboza         }
594a71c775bSDaniel Henrique Barboza     }
595a71c775bSDaniel Henrique Barboza     s->needs_vpd_bl_emulation = true;
596a71c775bSDaniel Henrique Barboza }
597a71c775bSDaniel Henrique Barboza 
scsi_generic_read_device_identification(SCSIDevice * s)598a71c775bSDaniel Henrique Barboza static void scsi_generic_read_device_identification(SCSIDevice *s)
5999fd7e859SPaolo Bonzini {
6009fd7e859SPaolo Bonzini     uint8_t cmd[6];
6019fd7e859SPaolo Bonzini     uint8_t buf[250];
6029fd7e859SPaolo Bonzini     int ret;
6039fd7e859SPaolo Bonzini     int i, len;
6049fd7e859SPaolo Bonzini 
6059fd7e859SPaolo Bonzini     memset(cmd, 0, sizeof(cmd));
6069fd7e859SPaolo Bonzini     memset(buf, 0, sizeof(buf));
6079fd7e859SPaolo Bonzini     cmd[0] = INQUIRY;
6089fd7e859SPaolo Bonzini     cmd[1] = 1;
6099fd7e859SPaolo Bonzini     cmd[2] = 0x83;
6109fd7e859SPaolo Bonzini     cmd[4] = sizeof(buf);
6119fd7e859SPaolo Bonzini 
612a0c7e35bSDaniel Henrique Barboza     ret = scsi_SG_IO_FROM_DEV(s->conf.blk, cmd, sizeof(cmd),
613c9b6609bSHannes Reinecke                               buf, sizeof(buf), s->io_timeout);
614a0c7e35bSDaniel Henrique Barboza     if (ret < 0) {
6159fd7e859SPaolo Bonzini         return;
6169fd7e859SPaolo Bonzini     }
6179fd7e859SPaolo Bonzini 
6189fd7e859SPaolo Bonzini     len = MIN((buf[2] << 8) | buf[3], sizeof(buf) - 4);
6199fd7e859SPaolo Bonzini     for (i = 0; i + 3 <= len; ) {
6209fd7e859SPaolo Bonzini         const uint8_t *p = &buf[i + 4];
6219fd7e859SPaolo Bonzini         uint64_t wwn;
6229fd7e859SPaolo Bonzini 
6239fd7e859SPaolo Bonzini         if (i + (p[3] + 4) > len) {
6249fd7e859SPaolo Bonzini             break;
6259fd7e859SPaolo Bonzini         }
6269fd7e859SPaolo Bonzini 
6279fd7e859SPaolo Bonzini         if ((p[1] & 0x10) == 0) {
6289fd7e859SPaolo Bonzini             /* Associated with the logical unit */
6299fd7e859SPaolo Bonzini             if (read_naa_id(p, &wwn) == 0) {
6309fd7e859SPaolo Bonzini                 s->wwn = wwn;
6319fd7e859SPaolo Bonzini             }
6329fd7e859SPaolo Bonzini         } else if ((p[1] & 0x10) == 0x10) {
6339fd7e859SPaolo Bonzini             /* Associated with the target port */
6349fd7e859SPaolo Bonzini             if (read_naa_id(p, &wwn) == 0) {
6359fd7e859SPaolo Bonzini                 s->port_wwn = wwn;
6369fd7e859SPaolo Bonzini             }
6379fd7e859SPaolo Bonzini         }
6389fd7e859SPaolo Bonzini 
6399fd7e859SPaolo Bonzini         i += p[3] + 4;
6409fd7e859SPaolo Bonzini     }
6419fd7e859SPaolo Bonzini }
6429fd7e859SPaolo Bonzini 
scsi_generic_read_device_inquiry(SCSIDevice * s)643a71c775bSDaniel Henrique Barboza void scsi_generic_read_device_inquiry(SCSIDevice *s)
644a71c775bSDaniel Henrique Barboza {
645a71c775bSDaniel Henrique Barboza     scsi_generic_read_device_identification(s);
646afff2db6SDmitry Fomichev     if (s->type == TYPE_DISK || s->type == TYPE_ZBC) {
647a71c775bSDaniel Henrique Barboza         scsi_generic_set_vpd_bl_emulation(s);
648a71c775bSDaniel Henrique Barboza     } else {
649a71c775bSDaniel Henrique Barboza         s->needs_vpd_bl_emulation = false;
650a71c775bSDaniel Henrique Barboza     }
651a71c775bSDaniel Henrique Barboza }
652a71c775bSDaniel Henrique Barboza 
get_stream_blocksize(BlockBackend * blk)6534be74634SMarkus Armbruster static int get_stream_blocksize(BlockBackend *blk)
65449ab747fSPaolo Bonzini {
65549ab747fSPaolo Bonzini     uint8_t cmd[6];
65649ab747fSPaolo Bonzini     uint8_t buf[12];
65749ab747fSPaolo Bonzini     int ret;
65849ab747fSPaolo Bonzini 
65949ab747fSPaolo Bonzini     memset(cmd, 0, sizeof(cmd));
66049ab747fSPaolo Bonzini     memset(buf, 0, sizeof(buf));
66149ab747fSPaolo Bonzini     cmd[0] = MODE_SENSE;
66249ab747fSPaolo Bonzini     cmd[4] = sizeof(buf);
66349ab747fSPaolo Bonzini 
664c9b6609bSHannes Reinecke     ret = scsi_SG_IO_FROM_DEV(blk, cmd, sizeof(cmd), buf, sizeof(buf), 6);
665a0c7e35bSDaniel Henrique Barboza     if (ret < 0) {
66649ab747fSPaolo Bonzini         return -1;
66749ab747fSPaolo Bonzini     }
668a0c7e35bSDaniel Henrique Barboza 
66949ab747fSPaolo Bonzini     return (buf[9] << 16) | (buf[10] << 8) | buf[11];
67049ab747fSPaolo Bonzini }
67149ab747fSPaolo Bonzini 
scsi_generic_reset(DeviceState * dev)67249ab747fSPaolo Bonzini static void scsi_generic_reset(DeviceState *dev)
67349ab747fSPaolo Bonzini {
67449ab747fSPaolo Bonzini     SCSIDevice *s = SCSI_DEVICE(dev);
67549ab747fSPaolo Bonzini 
6762343be0dSPaolo Bonzini     s->scsi_version = s->default_scsi_version;
67749ab747fSPaolo Bonzini     scsi_device_purge_requests(s, SENSE_CODE(RESET));
67849ab747fSPaolo Bonzini }
67949ab747fSPaolo Bonzini 
scsi_generic_realize(SCSIDevice * s,Error ** errp)680a818a4b6SFam Zheng static void scsi_generic_realize(SCSIDevice *s, Error **errp)
68149ab747fSPaolo Bonzini {
6826ee143a0SPaolo Bonzini     int rc;
68349ab747fSPaolo Bonzini     int sg_version;
68449ab747fSPaolo Bonzini     struct sg_scsi_id scsiid;
68549ab747fSPaolo Bonzini 
6864be74634SMarkus Armbruster     if (!s->conf.blk) {
687a818a4b6SFam Zheng         error_setg(errp, "drive property not set");
688a818a4b6SFam Zheng         return;
68949ab747fSPaolo Bonzini     }
69049ab747fSPaolo Bonzini 
691166854f7SZihao Chang     if (blk_get_on_error(s->conf.blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC &&
692166854f7SZihao Chang         blk_get_on_error(s->conf.blk, 0) != BLOCKDEV_ON_ERROR_REPORT) {
693a818a4b6SFam Zheng         error_setg(errp, "Device doesn't support drive option werror");
694a818a4b6SFam Zheng         return;
69549ab747fSPaolo Bonzini     }
6964be74634SMarkus Armbruster     if (blk_get_on_error(s->conf.blk, 1) != BLOCKDEV_ON_ERROR_REPORT) {
697a818a4b6SFam Zheng         error_setg(errp, "Device doesn't support drive option rerror");
698a818a4b6SFam Zheng         return;
69949ab747fSPaolo Bonzini     }
70049ab747fSPaolo Bonzini 
70149ab747fSPaolo Bonzini     /* check we are using a driver managing SG_IO (version 3 and after */
7024be74634SMarkus Armbruster     rc = blk_ioctl(s->conf.blk, SG_GET_VERSION_NUM, &sg_version);
7036ee143a0SPaolo Bonzini     if (rc < 0) {
70409c2c6ffSPaolo Bonzini         error_setg_errno(errp, -rc, "cannot get SG_IO version number");
70509c2c6ffSPaolo Bonzini         if (rc != -EPERM) {
70609c2c6ffSPaolo Bonzini             error_append_hint(errp, "Is this a SCSI device?\n");
70709c2c6ffSPaolo Bonzini         }
708a818a4b6SFam Zheng         return;
70949ab747fSPaolo Bonzini     }
71049ab747fSPaolo Bonzini     if (sg_version < 30000) {
711a818a4b6SFam Zheng         error_setg(errp, "scsi generic interface too old");
712a818a4b6SFam Zheng         return;
71349ab747fSPaolo Bonzini     }
71449ab747fSPaolo Bonzini 
71549ab747fSPaolo Bonzini     /* get LUN of the /dev/sg? */
7164be74634SMarkus Armbruster     if (blk_ioctl(s->conf.blk, SG_GET_SCSI_ID, &scsiid)) {
717a818a4b6SFam Zheng         error_setg(errp, "SG_GET_SCSI_ID ioctl failed");
718a818a4b6SFam Zheng         return;
71949ab747fSPaolo Bonzini     }
720c6caae55SFam Zheng     if (!blkconf_apply_backend_options(&s->conf,
72186b1cf32SKevin Wolf                                        !blk_supports_write_perm(s->conf.blk),
722c6caae55SFam Zheng                                        true, errp)) {
723d9bcd6f7SFam Zheng         return;
724d9bcd6f7SFam Zheng     }
72549ab747fSPaolo Bonzini 
72649ab747fSPaolo Bonzini     /* define device state */
72749ab747fSPaolo Bonzini     s->type = scsiid.scsi_type;
72856853498SLaurent Vivier     trace_scsi_generic_realize_type(s->type);
72949ab747fSPaolo Bonzini 
73049ab747fSPaolo Bonzini     switch (s->type) {
73149ab747fSPaolo Bonzini     case TYPE_TAPE:
7324be74634SMarkus Armbruster         s->blocksize = get_stream_blocksize(s->conf.blk);
73349ab747fSPaolo Bonzini         if (s->blocksize == -1) {
73449ab747fSPaolo Bonzini             s->blocksize = 0;
73549ab747fSPaolo Bonzini         }
73649ab747fSPaolo Bonzini         break;
73749ab747fSPaolo Bonzini 
73849ab747fSPaolo Bonzini         /* Make a guess for block devices, we'll fix it when the guest sends.
73949ab747fSPaolo Bonzini          * READ CAPACITY.  If they don't, they likely would assume these sizes
74049ab747fSPaolo Bonzini          * anyway. (TODO: they could also send MODE SENSE).
74149ab747fSPaolo Bonzini          */
74249ab747fSPaolo Bonzini     case TYPE_ROM:
74349ab747fSPaolo Bonzini     case TYPE_WORM:
74449ab747fSPaolo Bonzini         s->blocksize = 2048;
74549ab747fSPaolo Bonzini         break;
74649ab747fSPaolo Bonzini     default:
74749ab747fSPaolo Bonzini         s->blocksize = 512;
74849ab747fSPaolo Bonzini         break;
74949ab747fSPaolo Bonzini     }
75049ab747fSPaolo Bonzini 
75156853498SLaurent Vivier     trace_scsi_generic_realize_blocksize(s->blocksize);
7529fd7e859SPaolo Bonzini 
75329e560f0SDaniel Henrique Barboza     /* Only used by scsi-block, but initialize it nevertheless to be clean.  */
75429e560f0SDaniel Henrique Barboza     s->default_scsi_version = -1;
755a71c775bSDaniel Henrique Barboza     scsi_generic_read_device_inquiry(s);
75649ab747fSPaolo Bonzini }
75749ab747fSPaolo Bonzini 
75849ab747fSPaolo Bonzini const SCSIReqOps scsi_generic_req_ops = {
75949ab747fSPaolo Bonzini     .size         = sizeof(SCSIGenericReq),
76049ab747fSPaolo Bonzini     .free_req     = scsi_free_request,
76149ab747fSPaolo Bonzini     .send_command = scsi_send_command,
76249ab747fSPaolo Bonzini     .read_data    = scsi_read_data,
76349ab747fSPaolo Bonzini     .write_data   = scsi_write_data,
76449ab747fSPaolo Bonzini     .get_buf      = scsi_get_buf,
76549ab747fSPaolo Bonzini     .load_request = scsi_generic_load_request,
76649ab747fSPaolo Bonzini     .save_request = scsi_generic_save_request,
76749ab747fSPaolo Bonzini };
76849ab747fSPaolo Bonzini 
scsi_new_request(SCSIDevice * d,uint32_t tag,uint32_t lun,uint8_t * buf,void * hba_private)76949ab747fSPaolo Bonzini static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
77049ab747fSPaolo Bonzini                                      uint8_t *buf, void *hba_private)
77149ab747fSPaolo Bonzini {
7729be38598SEduardo Habkost     return scsi_req_alloc(&scsi_generic_req_ops, d, tag, lun, hba_private);
77349ab747fSPaolo Bonzini }
77449ab747fSPaolo Bonzini 
77549ab747fSPaolo Bonzini static Property scsi_generic_properties[] = {
7764be74634SMarkus Armbruster     DEFINE_PROP_DRIVE("drive", SCSIDevice, conf.blk),
777d9bcd6f7SFam Zheng     DEFINE_PROP_BOOL("share-rw", SCSIDevice, conf.share_rw, false),
778c9b6609bSHannes Reinecke     DEFINE_PROP_UINT32("io_timeout", SCSIDevice, io_timeout,
779c9b6609bSHannes Reinecke                        DEFAULT_IO_TIMEOUT),
78049ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
78149ab747fSPaolo Bonzini };
78249ab747fSPaolo Bonzini 
scsi_generic_parse_cdb(SCSIDevice * dev,SCSICommand * cmd,uint8_t * buf,size_t buf_len,void * hba_private)7833e7e180aSPaolo Bonzini static int scsi_generic_parse_cdb(SCSIDevice *dev, SCSICommand *cmd,
784fe9d8927SJohn Millikin                                   uint8_t *buf, size_t buf_len,
785fe9d8927SJohn Millikin                                   void *hba_private)
7863e7e180aSPaolo Bonzini {
787fe9d8927SJohn Millikin     return scsi_bus_parse_cdb(dev, cmd, buf, buf_len, hba_private);
7883e7e180aSPaolo Bonzini }
7893e7e180aSPaolo Bonzini 
scsi_generic_class_initfn(ObjectClass * klass,void * data)79049ab747fSPaolo Bonzini static void scsi_generic_class_initfn(ObjectClass *klass, void *data)
79149ab747fSPaolo Bonzini {
79249ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
79349ab747fSPaolo Bonzini     SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
79449ab747fSPaolo Bonzini 
795a818a4b6SFam Zheng     sc->realize      = scsi_generic_realize;
79649ab747fSPaolo Bonzini     sc->alloc_req    = scsi_new_request;
7973e7e180aSPaolo Bonzini     sc->parse_cdb    = scsi_generic_parse_cdb;
79849ab747fSPaolo Bonzini     dc->fw_name = "disk";
79949ab747fSPaolo Bonzini     dc->desc = "pass through generic scsi device (/dev/sg*)";
80049ab747fSPaolo Bonzini     dc->reset = scsi_generic_reset;
8014f67d30bSMarc-André Lureau     device_class_set_props(dc, scsi_generic_properties);
80249ab747fSPaolo Bonzini     dc->vmsd  = &vmstate_scsi_device;
80349ab747fSPaolo Bonzini }
80449ab747fSPaolo Bonzini 
80549ab747fSPaolo Bonzini static const TypeInfo scsi_generic_info = {
80649ab747fSPaolo Bonzini     .name          = "scsi-generic",
80749ab747fSPaolo Bonzini     .parent        = TYPE_SCSI_DEVICE,
80849ab747fSPaolo Bonzini     .instance_size = sizeof(SCSIDevice),
80949ab747fSPaolo Bonzini     .class_init    = scsi_generic_class_initfn,
81049ab747fSPaolo Bonzini };
81149ab747fSPaolo Bonzini 
scsi_generic_register_types(void)81249ab747fSPaolo Bonzini static void scsi_generic_register_types(void)
81349ab747fSPaolo Bonzini {
81449ab747fSPaolo Bonzini     type_register_static(&scsi_generic_info);
81549ab747fSPaolo Bonzini }
81649ab747fSPaolo Bonzini 
81749ab747fSPaolo Bonzini type_init(scsi_generic_register_types)
81849ab747fSPaolo Bonzini 
81949ab747fSPaolo Bonzini #endif /* __linux__ */
820