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);
27814042268SStefan 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);
31714042268SStefan 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);
39014042268SStefan 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*)";
800*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, 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