aboutsummaryrefslogtreecommitdiff
path: root/hw/scsi
diff options
context:
space:
mode:
Diffstat (limited to 'hw/scsi')
-rw-r--r--hw/scsi/esp.c30
-rw-r--r--hw/scsi/scsi-disk.c427
-rw-r--r--hw/scsi/scsi-generic.c246
-rw-r--r--hw/scsi/virtio-scsi-dataplane.c4
4 files changed, 398 insertions, 309 deletions
diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index 9ed9727744..630d923623 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -619,36 +619,6 @@ static const MemoryRegionOps sysbus_esp_mem_ops = {
.valid.accepts = esp_mem_accepts,
};
-ESPState *esp_init(hwaddr espaddr, int it_shift,
- ESPDMAMemoryReadWriteFunc dma_memory_read,
- ESPDMAMemoryReadWriteFunc dma_memory_write,
- void *dma_opaque, qemu_irq irq, qemu_irq *reset,
- qemu_irq *dma_enable)
-{
- DeviceState *dev;
- SysBusDevice *s;
- SysBusESPState *sysbus;
- ESPState *esp;
-
- dev = qdev_create(NULL, TYPE_ESP);
- sysbus = ESP_STATE(dev);
- esp = &sysbus->esp;
- esp->dma_memory_read = dma_memory_read;
- esp->dma_memory_write = dma_memory_write;
- esp->dma_opaque = dma_opaque;
- sysbus->it_shift = it_shift;
- /* XXX for now until rc4030 has been changed to use DMA enable signal */
- esp->dma_enabled = 1;
- qdev_init_nofail(dev);
- s = SYS_BUS_DEVICE(dev);
- sysbus_connect_irq(s, 0, irq);
- sysbus_mmio_map(s, 0, espaddr);
- *reset = qdev_get_gpio_in(dev, 0);
- *dma_enable = qdev_get_gpio_in(dev, 1);
-
- return esp;
-}
-
static const struct SCSIBusInfo esp_scsi_info = {
.tcq = false,
.max_target = ESP_MAX_DEVS,
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index aeaf611854..55a34b3895 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -585,219 +585,228 @@ static uint8_t *scsi_get_buf(SCSIRequest *req)
return (uint8_t *)r->iov.iov_base;
}
-static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
+int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
- int buflen = 0;
- int start;
-
- if (req->cmd.buf[1] & 0x1) {
- /* Vital product data */
- uint8_t page_code = req->cmd.buf[2];
-
- outbuf[buflen++] = s->qdev.type & 0x1f;
- outbuf[buflen++] = page_code ; // this page
- outbuf[buflen++] = 0x00;
- outbuf[buflen++] = 0x00;
- start = buflen;
-
- switch (page_code) {
- case 0x00: /* Supported page codes, mandatory */
- {
- DPRINTF("Inquiry EVPD[Supported pages] "
- "buffer size %zd\n", req->cmd.xfer);
- outbuf[buflen++] = 0x00; // list of supported pages (this page)
- if (s->serial) {
- outbuf[buflen++] = 0x80; // unit serial number
- }
- outbuf[buflen++] = 0x83; // device identification
- if (s->qdev.type == TYPE_DISK) {
- outbuf[buflen++] = 0xb0; // block limits
- outbuf[buflen++] = 0xb1; /* block device characteristics */
- outbuf[buflen++] = 0xb2; // thin provisioning
- }
- break;
- }
- case 0x80: /* Device serial number, optional */
- {
- int l;
+ uint8_t page_code = req->cmd.buf[2];
+ int start, buflen = 0;
- if (!s->serial) {
- DPRINTF("Inquiry (EVPD[Serial number] not supported\n");
- return -1;
- }
+ outbuf[buflen++] = s->qdev.type & 0x1f;
+ outbuf[buflen++] = page_code;
+ outbuf[buflen++] = 0x00;
+ outbuf[buflen++] = 0x00;
+ start = buflen;
- l = strlen(s->serial);
- if (l > 36) {
- l = 36;
- }
+ switch (page_code) {
+ case 0x00: /* Supported page codes, mandatory */
+ {
+ DPRINTF("Inquiry EVPD[Supported pages] "
+ "buffer size %zd\n", req->cmd.xfer);
+ outbuf[buflen++] = 0x00; /* list of supported pages (this page) */
+ if (s->serial) {
+ outbuf[buflen++] = 0x80; /* unit serial number */
+ }
+ outbuf[buflen++] = 0x83; /* device identification */
+ if (s->qdev.type == TYPE_DISK) {
+ outbuf[buflen++] = 0xb0; /* block limits */
+ outbuf[buflen++] = 0xb1; /* block device characteristics */
+ outbuf[buflen++] = 0xb2; /* thin provisioning */
+ }
+ break;
+ }
+ case 0x80: /* Device serial number, optional */
+ {
+ int l;
- DPRINTF("Inquiry EVPD[Serial number] "
- "buffer size %zd\n", req->cmd.xfer);
- memcpy(outbuf+buflen, s->serial, l);
- buflen += l;
- break;
+ if (!s->serial) {
+ DPRINTF("Inquiry (EVPD[Serial number] not supported\n");
+ return -1;
}
- case 0x83: /* Device identification page, mandatory */
- {
- const char *str = s->serial ?: blk_name(s->qdev.conf.blk);
- int max_len = s->serial ? 20 : 255 - 8;
- int id_len = strlen(str);
+ l = strlen(s->serial);
+ if (l > 36) {
+ l = 36;
+ }
- if (id_len > max_len) {
- id_len = max_len;
- }
- DPRINTF("Inquiry EVPD[Device identification] "
- "buffer size %zd\n", req->cmd.xfer);
-
- outbuf[buflen++] = 0x2; // ASCII
- outbuf[buflen++] = 0; // not officially assigned
- outbuf[buflen++] = 0; // reserved
- outbuf[buflen++] = id_len; // length of data following
- memcpy(outbuf+buflen, str, id_len);
- buflen += id_len;
-
- if (s->qdev.wwn) {
- outbuf[buflen++] = 0x1; // Binary
- outbuf[buflen++] = 0x3; // NAA
- outbuf[buflen++] = 0; // reserved
- outbuf[buflen++] = 8;
- stq_be_p(&outbuf[buflen], s->qdev.wwn);
- buflen += 8;
- }
+ DPRINTF("Inquiry EVPD[Serial number] "
+ "buffer size %zd\n", req->cmd.xfer);
+ memcpy(outbuf + buflen, s->serial, l);
+ buflen += l;
+ break;
+ }
- if (s->qdev.port_wwn) {
- outbuf[buflen++] = 0x61; // SAS / Binary
- outbuf[buflen++] = 0x93; // PIV / Target port / NAA
- outbuf[buflen++] = 0; // reserved
- outbuf[buflen++] = 8;
- stq_be_p(&outbuf[buflen], s->qdev.port_wwn);
- buflen += 8;
- }
+ case 0x83: /* Device identification page, mandatory */
+ {
+ const char *str = s->serial ?: blk_name(s->qdev.conf.blk);
+ int max_len = s->serial ? 20 : 255 - 8;
+ int id_len = strlen(str);
- if (s->port_index) {
- outbuf[buflen++] = 0x61; // SAS / Binary
- outbuf[buflen++] = 0x94; // PIV / Target port / relative target port
- outbuf[buflen++] = 0; // reserved
- outbuf[buflen++] = 4;
- stw_be_p(&outbuf[buflen + 2], s->port_index);
- buflen += 4;
- }
- break;
+ if (id_len > max_len) {
+ id_len = max_len;
}
- case 0xb0: /* block limits */
- {
- unsigned int unmap_sectors =
- s->qdev.conf.discard_granularity / s->qdev.blocksize;
- unsigned int min_io_size =
- s->qdev.conf.min_io_size / s->qdev.blocksize;
- unsigned int opt_io_size =
- s->qdev.conf.opt_io_size / s->qdev.blocksize;
- unsigned int max_unmap_sectors =
- s->max_unmap_size / s->qdev.blocksize;
- unsigned int max_io_sectors =
- s->max_io_size / s->qdev.blocksize;
-
- if (s->qdev.type == TYPE_ROM) {
- DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n",
- page_code);
- return -1;
- }
- if (s->qdev.type == TYPE_DISK) {
- int max_transfer_blk = blk_get_max_transfer(s->qdev.conf.blk);
- int max_io_sectors_blk =
- max_transfer_blk / s->qdev.blocksize;
-
- max_io_sectors =
- MIN_NON_ZERO(max_io_sectors_blk, max_io_sectors);
-
- /* min_io_size and opt_io_size can't be greater than
- * max_io_sectors */
- if (min_io_size) {
- min_io_size = MIN(min_io_size, max_io_sectors);
- }
- if (opt_io_size) {
- opt_io_size = MIN(opt_io_size, max_io_sectors);
- }
- }
- /* required VPD size with unmap support */
- buflen = 0x40;
- memset(outbuf + 4, 0, buflen - 4);
-
- outbuf[4] = 0x1; /* wsnz */
-
- /* optimal transfer length granularity */
- outbuf[6] = (min_io_size >> 8) & 0xff;
- outbuf[7] = min_io_size & 0xff;
-
- /* maximum transfer length */
- outbuf[8] = (max_io_sectors >> 24) & 0xff;
- outbuf[9] = (max_io_sectors >> 16) & 0xff;
- outbuf[10] = (max_io_sectors >> 8) & 0xff;
- outbuf[11] = max_io_sectors & 0xff;
-
- /* optimal transfer length */
- outbuf[12] = (opt_io_size >> 24) & 0xff;
- outbuf[13] = (opt_io_size >> 16) & 0xff;
- outbuf[14] = (opt_io_size >> 8) & 0xff;
- outbuf[15] = opt_io_size & 0xff;
-
- /* max unmap LBA count, default is 1GB */
- outbuf[20] = (max_unmap_sectors >> 24) & 0xff;
- outbuf[21] = (max_unmap_sectors >> 16) & 0xff;
- outbuf[22] = (max_unmap_sectors >> 8) & 0xff;
- outbuf[23] = max_unmap_sectors & 0xff;
-
- /* max unmap descriptors, 255 fit in 4 kb with an 8-byte header. */
- outbuf[24] = 0;
- outbuf[25] = 0;
- outbuf[26] = 0;
- outbuf[27] = 255;
-
- /* optimal unmap granularity */
- outbuf[28] = (unmap_sectors >> 24) & 0xff;
- outbuf[29] = (unmap_sectors >> 16) & 0xff;
- outbuf[30] = (unmap_sectors >> 8) & 0xff;
- outbuf[31] = unmap_sectors & 0xff;
-
- /* max write same size */
- outbuf[36] = 0;
- outbuf[37] = 0;
- outbuf[38] = 0;
- outbuf[39] = 0;
-
- outbuf[40] = (max_io_sectors >> 24) & 0xff;
- outbuf[41] = (max_io_sectors >> 16) & 0xff;
- outbuf[42] = (max_io_sectors >> 8) & 0xff;
- outbuf[43] = max_io_sectors & 0xff;
- break;
+ DPRINTF("Inquiry EVPD[Device identification] "
+ "buffer size %zd\n", req->cmd.xfer);
+
+ outbuf[buflen++] = 0x2; /* ASCII */
+ outbuf[buflen++] = 0; /* not officially assigned */
+ outbuf[buflen++] = 0; /* reserved */
+ outbuf[buflen++] = id_len; /* length of data following */
+ memcpy(outbuf + buflen, str, id_len);
+ buflen += id_len;
+
+ if (s->qdev.wwn) {
+ outbuf[buflen++] = 0x1; /* Binary */
+ outbuf[buflen++] = 0x3; /* NAA */
+ outbuf[buflen++] = 0; /* reserved */
+ outbuf[buflen++] = 8;
+ stq_be_p(&outbuf[buflen], s->qdev.wwn);
+ buflen += 8;
}
- case 0xb1: /* block device characteristics */
- {
- buflen = 8;
- outbuf[4] = (s->rotation_rate >> 8) & 0xff;
- outbuf[5] = s->rotation_rate & 0xff;
- outbuf[6] = 0;
- outbuf[7] = 0;
- break;
+
+ if (s->qdev.port_wwn) {
+ outbuf[buflen++] = 0x61; /* SAS / Binary */
+ outbuf[buflen++] = 0x93; /* PIV / Target port / NAA */
+ outbuf[buflen++] = 0; /* reserved */
+ outbuf[buflen++] = 8;
+ stq_be_p(&outbuf[buflen], s->qdev.port_wwn);
+ buflen += 8;
}
- case 0xb2: /* thin provisioning */
- {
- buflen = 8;
- outbuf[4] = 0;
- outbuf[5] = 0xe0; /* unmap & write_same 10/16 all supported */
- outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1;
- outbuf[7] = 0;
- break;
+
+ if (s->port_index) {
+ outbuf[buflen++] = 0x61; /* SAS / Binary */
+
+ /* PIV/Target port/relative target port */
+ outbuf[buflen++] = 0x94;
+
+ outbuf[buflen++] = 0; /* reserved */
+ outbuf[buflen++] = 4;
+ stw_be_p(&outbuf[buflen + 2], s->port_index);
+ buflen += 4;
}
- default:
+ break;
+ }
+ case 0xb0: /* block limits */
+ {
+ unsigned int unmap_sectors =
+ s->qdev.conf.discard_granularity / s->qdev.blocksize;
+ unsigned int min_io_size =
+ s->qdev.conf.min_io_size / s->qdev.blocksize;
+ unsigned int opt_io_size =
+ s->qdev.conf.opt_io_size / s->qdev.blocksize;
+ unsigned int max_unmap_sectors =
+ s->max_unmap_size / s->qdev.blocksize;
+ unsigned int max_io_sectors =
+ s->max_io_size / s->qdev.blocksize;
+
+ if (s->qdev.type == TYPE_ROM) {
+ DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n",
+ page_code);
return -1;
}
- /* done with EVPD */
- assert(buflen - start <= 255);
- outbuf[start - 1] = buflen - start;
- return buflen;
+ if (s->qdev.type == TYPE_DISK) {
+ int max_transfer_blk = blk_get_max_transfer(s->qdev.conf.blk);
+ int max_io_sectors_blk =
+ max_transfer_blk / s->qdev.blocksize;
+
+ max_io_sectors =
+ MIN_NON_ZERO(max_io_sectors_blk, max_io_sectors);
+
+ /* min_io_size and opt_io_size can't be greater than
+ * max_io_sectors */
+ if (min_io_size) {
+ min_io_size = MIN(min_io_size, max_io_sectors);
+ }
+ if (opt_io_size) {
+ opt_io_size = MIN(opt_io_size, max_io_sectors);
+ }
+ }
+ /* required VPD size with unmap support */
+ buflen = 0x40;
+ memset(outbuf + 4, 0, buflen - 4);
+
+ outbuf[4] = 0x1; /* wsnz */
+
+ /* optimal transfer length granularity */
+ outbuf[6] = (min_io_size >> 8) & 0xff;
+ outbuf[7] = min_io_size & 0xff;
+
+ /* maximum transfer length */
+ outbuf[8] = (max_io_sectors >> 24) & 0xff;
+ outbuf[9] = (max_io_sectors >> 16) & 0xff;
+ outbuf[10] = (max_io_sectors >> 8) & 0xff;
+ outbuf[11] = max_io_sectors & 0xff;
+
+ /* optimal transfer length */
+ outbuf[12] = (opt_io_size >> 24) & 0xff;
+ outbuf[13] = (opt_io_size >> 16) & 0xff;
+ outbuf[14] = (opt_io_size >> 8) & 0xff;
+ outbuf[15] = opt_io_size & 0xff;
+
+ /* max unmap LBA count, default is 1GB */
+ outbuf[20] = (max_unmap_sectors >> 24) & 0xff;
+ outbuf[21] = (max_unmap_sectors >> 16) & 0xff;
+ outbuf[22] = (max_unmap_sectors >> 8) & 0xff;
+ outbuf[23] = max_unmap_sectors & 0xff;
+
+ /* max unmap descriptors, 255 fit in 4 kb with an 8-byte header */
+ outbuf[24] = 0;
+ outbuf[25] = 0;
+ outbuf[26] = 0;
+ outbuf[27] = 255;
+
+ /* optimal unmap granularity */
+ outbuf[28] = (unmap_sectors >> 24) & 0xff;
+ outbuf[29] = (unmap_sectors >> 16) & 0xff;
+ outbuf[30] = (unmap_sectors >> 8) & 0xff;
+ outbuf[31] = unmap_sectors & 0xff;
+
+ /* max write same size */
+ outbuf[36] = 0;
+ outbuf[37] = 0;
+ outbuf[38] = 0;
+ outbuf[39] = 0;
+
+ outbuf[40] = (max_io_sectors >> 24) & 0xff;
+ outbuf[41] = (max_io_sectors >> 16) & 0xff;
+ outbuf[42] = (max_io_sectors >> 8) & 0xff;
+ outbuf[43] = max_io_sectors & 0xff;
+ break;
+ }
+ case 0xb1: /* block device characteristics */
+ {
+ buflen = 8;
+ outbuf[4] = (s->rotation_rate >> 8) & 0xff;
+ outbuf[5] = s->rotation_rate & 0xff;
+ outbuf[6] = 0;
+ outbuf[7] = 0;
+ break;
+ }
+ case 0xb2: /* thin provisioning */
+ {
+ buflen = 8;
+ outbuf[4] = 0;
+ outbuf[5] = 0xe0; /* unmap & write_same 10/16 all supported */
+ outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1;
+ outbuf[7] = 0;
+ break;
+ }
+ default:
+ return -1;
+ }
+ /* done with EVPD */
+ assert(buflen - start <= 255);
+ outbuf[start - 1] = buflen - start;
+ return buflen;
+}
+
+static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
+{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
+ int buflen = 0;
+
+ if (req->cmd.buf[1] & 0x1) {
+ /* Vital product data */
+ return scsi_disk_emulate_vpd_page(req, outbuf);
}
/* Standard INQUIRY data */
@@ -2569,8 +2578,6 @@ static int get_device_type(SCSIDiskState *s)
{
uint8_t cmd[16];
uint8_t buf[36];
- uint8_t sensebuf[8];
- sg_io_hdr_t io_header;
int ret;
memset(cmd, 0, sizeof(cmd));
@@ -2578,19 +2585,9 @@ static int get_device_type(SCSIDiskState *s)
cmd[0] = INQUIRY;
cmd[4] = sizeof(buf);
- memset(&io_header, 0, sizeof(io_header));
- io_header.interface_id = 'S';
- io_header.dxfer_direction = SG_DXFER_FROM_DEV;
- io_header.dxfer_len = sizeof(buf);
- io_header.dxferp = buf;
- io_header.cmdp = cmd;
- io_header.cmd_len = sizeof(cmd);
- io_header.mx_sb_len = sizeof(sensebuf);
- io_header.sbp = sensebuf;
- io_header.timeout = 6000; /* XXX */
-
- ret = blk_ioctl(s->qdev.conf.blk, SG_IO, &io_header);
- if (ret < 0 || io_header.driver_status || io_header.host_status) {
+ ret = scsi_SG_IO_FROM_DEV(s->qdev.conf.blk, cmd, sizeof(cmd),
+ buf, sizeof(buf));
+ if (ret < 0) {
return -1;
}
s->qdev.type = buf[0];
@@ -2648,7 +2645,7 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp)
s->features |= (1 << SCSI_DISK_F_NO_REMOVABLE_DEVOPS);
scsi_realize(&s->qdev, errp);
- scsi_generic_read_device_identification(&s->qdev);
+ scsi_generic_read_device_inquiry(&s->qdev);
}
typedef struct SCSIBlockReq {
@@ -3039,6 +3036,10 @@ static Property scsi_block_properties[] = {
DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.conf.blk),
DEFINE_PROP_BOOL("share-rw", SCSIDiskState, qdev.conf.share_rw, false),
DEFINE_PROP_UINT16("rotation_rate", SCSIDiskState, rotation_rate, 0),
+ DEFINE_PROP_UINT64("max_unmap_size", SCSIDiskState, max_unmap_size,
+ DEFAULT_MAX_UNMAP_SIZE),
+ DEFINE_PROP_UINT64("max_io_size", SCSIDiskState, max_io_size,
+ DEFAULT_MAX_IO_SIZE),
DEFINE_PROP_INT32("scsi_version", SCSIDiskState, qdev.default_scsi_version,
-1),
DEFINE_PROP_END_OF_LIST(),
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
index 03bce8ff39..d60c4d0fcf 100644
--- a/hw/scsi/scsi-generic.c
+++ b/hw/scsi/scsi-generic.c
@@ -142,10 +142,84 @@ static int execute_command(BlockBackend *blk,
return 0;
}
+static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s)
+{
+ uint8_t page, page_len;
+
+ /*
+ * EVPD set to zero returns the standard INQUIRY data.
+ *
+ * Check if scsi_version is unset (-1) to avoid re-defining it
+ * each time an INQUIRY with standard data is received.
+ * scsi_version is initialized with -1 in scsi_generic_reset
+ * and scsi_disk_reset, making sure that we'll set the
+ * scsi_version after a reset. If the version field of the
+ * INQUIRY response somehow changes after a guest reboot,
+ * we'll be able to keep track of it.
+ *
+ * On SCSI-2 and older, first 3 bits of byte 2 is the
+ * ANSI-approved version, while on later versions the
+ * whole byte 2 contains the version. Check if we're dealing
+ * with a newer version and, in that case, assign the
+ * whole byte.
+ */
+ if (s->scsi_version == -1 && !(r->req.cmd.buf[1] & 0x01)) {
+ s->scsi_version = r->buf[2] & 0x07;
+ if (s->scsi_version > 2) {
+ s->scsi_version = r->buf[2];
+ }
+ }
+
+ if (s->type == TYPE_DISK && (r->req.cmd.buf[1] & 0x01)) {
+ page = r->req.cmd.buf[2];
+ if (page == 0xb0) {
+ uint32_t max_transfer =
+ blk_get_max_transfer(s->conf.blk) / s->blocksize;
+
+ assert(max_transfer);
+ stl_be_p(&r->buf[8], max_transfer);
+ /* Also take care of the opt xfer len. */
+ stl_be_p(&r->buf[12],
+ MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12])));
+ } else if (page == 0x00 && s->needs_vpd_bl_emulation) {
+ /*
+ * Now we're capable of supplying the VPD Block Limits
+ * response if the hardware can't. Add it in the INQUIRY
+ * Supported VPD pages response in case we are using the
+ * emulation for this device.
+ *
+ * This way, the guest kernel will be aware of the support
+ * and will use it to proper setup the SCSI device.
+ */
+ page_len = r->buf[3];
+ r->buf[page_len + 4] = 0xb0;
+ r->buf[3] = ++page_len;
+ }
+ }
+}
+
+static int scsi_emulate_block_limits(SCSIGenericReq *r)
+{
+ r->buflen = scsi_disk_emulate_vpd_page(&r->req, r->buf);
+ r->io_header.sb_len_wr = 0;
+
+ /*
+ * We have valid contents in the reply buffer but the
+ * io_header can report a sense error coming from
+ * the hardware in scsi_command_complete_noio. Clean
+ * up the io_header to avoid reporting it.
+ */
+ r->io_header.driver_status = 0;
+ r->io_header.status = 0;
+
+ return r->buflen;
+}
+
static void scsi_read_complete(void * opaque, int ret)
{
SCSIGenericReq *r = (SCSIGenericReq *)opaque;
SCSIDevice *s = r->req.dev;
+ SCSISense sense;
int len;
assert(r->req.aiocb != NULL);
@@ -162,6 +236,27 @@ static void scsi_read_complete(void * opaque, int ret)
DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len);
r->len = -1;
+
+ /*
+ * Check if this is a VPD Block Limits request that
+ * resulted in sense error but would need emulation.
+ * In this case, emulate a valid VPD response.
+ */
+ if (s->needs_vpd_bl_emulation) {
+ int is_vpd_bl = r->req.cmd.buf[0] == INQUIRY &&
+ r->req.cmd.buf[1] & 0x01 &&
+ r->req.cmd.buf[2] == 0xb0;
+
+ if (is_vpd_bl && sg_io_sense_from_errno(-ret, &r->io_header, &sense)) {
+ len = scsi_emulate_block_limits(r);
+ /*
+ * No need to let scsi_read_complete go on and handle an
+ * INQUIRY VPD BL request we created manually.
+ */
+ goto req_complete;
+ }
+ }
+
if (len == 0) {
scsi_command_complete_noio(r, 0);
goto done;
@@ -194,40 +289,10 @@ static void scsi_read_complete(void * opaque, int ret)
}
}
if (r->req.cmd.buf[0] == INQUIRY) {
- /*
- * EVPD set to zero returns the standard INQUIRY data.
- *
- * Check if scsi_version is unset (-1) to avoid re-defining it
- * each time an INQUIRY with standard data is received.
- * scsi_version is initialized with -1 in scsi_generic_reset
- * and scsi_disk_reset, making sure that we'll set the
- * scsi_version after a reset. If the version field of the
- * INQUIRY response somehow changes after a guest reboot,
- * we'll be able to keep track of it.
- *
- * On SCSI-2 and older, first 3 bits of byte 2 is the
- * ANSI-approved version, while on later versions the
- * whole byte 2 contains the version. Check if we're dealing
- * with a newer version and, in that case, assign the
- * whole byte.
- */
- if (s->scsi_version == -1 && !(r->req.cmd.buf[1] & 0x01)) {
- s->scsi_version = r->buf[2] & 0x07;
- if (s->scsi_version > 2) {
- s->scsi_version = r->buf[2];
- }
- }
- if (s->type == TYPE_DISK && r->req.cmd.buf[2] == 0xb0) {
- uint32_t max_transfer =
- blk_get_max_transfer(s->conf.blk) / s->blocksize;
-
- assert(max_transfer);
- stl_be_p(&r->buf[8], max_transfer);
- /* Also take care of the opt xfer len. */
- stl_be_p(&r->buf[12],
- MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12])));
- }
+ scsi_handle_inquiry_reply(r, s);
}
+
+req_complete:
scsi_req_data(&r->req, len);
scsi_req_unref(&r->req);
@@ -404,35 +469,90 @@ static int read_naa_id(const uint8_t *p, uint64_t *p_wwn)
return -EINVAL;
}
-void scsi_generic_read_device_identification(SCSIDevice *s)
+int scsi_SG_IO_FROM_DEV(BlockBackend *blk, uint8_t *cmd, uint8_t cmd_size,
+ uint8_t *buf, uint8_t buf_size)
{
- uint8_t cmd[6];
- uint8_t buf[250];
- uint8_t sensebuf[8];
sg_io_hdr_t io_header;
+ uint8_t sensebuf[8];
int ret;
- int i, len;
-
- memset(cmd, 0, sizeof(cmd));
- memset(buf, 0, sizeof(buf));
- cmd[0] = INQUIRY;
- cmd[1] = 1;
- cmd[2] = 0x83;
- cmd[4] = sizeof(buf);
memset(&io_header, 0, sizeof(io_header));
io_header.interface_id = 'S';
io_header.dxfer_direction = SG_DXFER_FROM_DEV;
- io_header.dxfer_len = sizeof(buf);
+ io_header.dxfer_len = buf_size;
io_header.dxferp = buf;
io_header.cmdp = cmd;
- io_header.cmd_len = sizeof(cmd);
+ io_header.cmd_len = cmd_size;
io_header.mx_sb_len = sizeof(sensebuf);
io_header.sbp = sensebuf;
io_header.timeout = 6000; /* XXX */
- ret = blk_ioctl(s->conf.blk, SG_IO, &io_header);
+ ret = blk_ioctl(blk, SG_IO, &io_header);
if (ret < 0 || io_header.driver_status || io_header.host_status) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Executes an INQUIRY request with EVPD set to retrieve the
+ * available VPD pages of the device. If the device does
+ * not support the Block Limits page (page 0xb0), set
+ * the needs_vpd_bl_emulation flag for future use.
+ */
+static void scsi_generic_set_vpd_bl_emulation(SCSIDevice *s)
+{
+ uint8_t cmd[6];
+ uint8_t buf[250];
+ uint8_t page_len;
+ int ret, i;
+
+ memset(cmd, 0, sizeof(cmd));
+ memset(buf, 0, sizeof(buf));
+ cmd[0] = INQUIRY;
+ cmd[1] = 1;
+ cmd[2] = 0x00;
+ cmd[4] = sizeof(buf);
+
+ ret = scsi_SG_IO_FROM_DEV(s->conf.blk, cmd, sizeof(cmd),
+ buf, sizeof(buf));
+ if (ret < 0) {
+ /*
+ * Do not assume anything if we can't retrieve the
+ * INQUIRY response to assert the VPD Block Limits
+ * support.
+ */
+ s->needs_vpd_bl_emulation = false;
+ return;
+ }
+
+ page_len = buf[3];
+ for (i = 4; i < page_len + 4; i++) {
+ if (buf[i] == 0xb0) {
+ s->needs_vpd_bl_emulation = false;
+ return;
+ }
+ }
+ s->needs_vpd_bl_emulation = true;
+}
+
+static void scsi_generic_read_device_identification(SCSIDevice *s)
+{
+ uint8_t cmd[6];
+ uint8_t buf[250];
+ int ret;
+ int i, len;
+
+ memset(cmd, 0, sizeof(cmd));
+ memset(buf, 0, sizeof(buf));
+ cmd[0] = INQUIRY;
+ cmd[1] = 1;
+ cmd[2] = 0x83;
+ cmd[4] = sizeof(buf);
+
+ ret = scsi_SG_IO_FROM_DEV(s->conf.blk, cmd, sizeof(cmd),
+ buf, sizeof(buf));
+ if (ret < 0) {
return;
}
@@ -461,12 +581,20 @@ void scsi_generic_read_device_identification(SCSIDevice *s)
}
}
+void scsi_generic_read_device_inquiry(SCSIDevice *s)
+{
+ scsi_generic_read_device_identification(s);
+ if (s->type == TYPE_DISK) {
+ scsi_generic_set_vpd_bl_emulation(s);
+ } else {
+ s->needs_vpd_bl_emulation = false;
+ }
+}
+
static int get_stream_blocksize(BlockBackend *blk)
{
uint8_t cmd[6];
uint8_t buf[12];
- uint8_t sensebuf[8];
- sg_io_hdr_t io_header;
int ret;
memset(cmd, 0, sizeof(cmd));
@@ -474,21 +602,11 @@ static int get_stream_blocksize(BlockBackend *blk)
cmd[0] = MODE_SENSE;
cmd[4] = sizeof(buf);
- memset(&io_header, 0, sizeof(io_header));
- io_header.interface_id = 'S';
- io_header.dxfer_direction = SG_DXFER_FROM_DEV;
- io_header.dxfer_len = sizeof(buf);
- io_header.dxferp = buf;
- io_header.cmdp = cmd;
- io_header.cmd_len = sizeof(cmd);
- io_header.mx_sb_len = sizeof(sensebuf);
- io_header.sbp = sensebuf;
- io_header.timeout = 6000; /* XXX */
-
- ret = blk_ioctl(blk, SG_IO, &io_header);
- if (ret < 0 || io_header.driver_status || io_header.host_status) {
+ ret = scsi_SG_IO_FROM_DEV(blk, cmd, sizeof(cmd), buf, sizeof(buf));
+ if (ret < 0) {
return -1;
}
+
return (buf[9] << 16) | (buf[10] << 8) | buf[11];
}
@@ -574,7 +692,7 @@ static void scsi_generic_realize(SCSIDevice *s, Error **errp)
/* Only used by scsi-block, but initialize it nevertheless to be clean. */
s->default_scsi_version = -1;
- scsi_generic_read_device_identification(s);
+ scsi_generic_read_device_inquiry(s);
}
const SCSIReqOps scsi_generic_req_ops = {
diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c
index 912e5005d8..b995bab3a2 100644
--- a/hw/scsi/virtio-scsi-dataplane.c
+++ b/hw/scsi/virtio-scsi-dataplane.c
@@ -142,8 +142,8 @@ int virtio_scsi_dataplane_start(VirtIODevice *vdev)
/* Set up guest notifier (irq) */
rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true);
if (rc != 0) {
- fprintf(stderr, "virtio-scsi: Failed to set guest notifiers (%d), "
- "ensure -enable-kvm is set\n", rc);
+ error_report("virtio-scsi: Failed to set guest notifiers (%d), "
+ "ensure -accel kvm is set.", rc);
goto fail_guest_notifiers;
}