|
|
ae23c9 |
From a9bfa7b102f1de3dbff308806099a7529b4e96b4 Mon Sep 17 00:00:00 2001
|
|
|
ae23c9 |
From: Paolo Bonzini <pbonzini@redhat.com>
|
|
|
ae23c9 |
Date: Thu, 20 Dec 2018 12:30:56 +0000
|
|
|
ae23c9 |
Subject: [PATCH 1/8] hw/scsi: cleanups before VPD BL emulation
|
|
|
ae23c9 |
|
|
|
ae23c9 |
RH-Author: Paolo Bonzini <pbonzini@redhat.com>
|
|
|
ae23c9 |
Message-id: <20181220123103.29579-2-pbonzini@redhat.com>
|
|
|
ae23c9 |
Patchwork-id: 83712
|
|
|
ae23c9 |
O-Subject: [PATCH 1/8] hw/scsi: cleanups before VPD BL emulation
|
|
|
ae23c9 |
Bugzilla: 1639957
|
|
|
ae23c9 |
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
|
|
ae23c9 |
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
|
|
|
ae23c9 |
RH-Acked-by: Laurent Vivier <lvivier@redhat.com>
|
|
|
ae23c9 |
|
|
|
ae23c9 |
From: Daniel Henrique Barboza <danielhb413@gmail.com>
|
|
|
ae23c9 |
|
|
|
ae23c9 |
To add support for the emulation of Block Limits VPD page
|
|
|
ae23c9 |
for passthrough devices, a few adjustments in the current code
|
|
|
ae23c9 |
base is required to avoid repetition and improve clarity.
|
|
|
ae23c9 |
|
|
|
ae23c9 |
In scsi-generic.c, detach the Inquiry handling from
|
|
|
ae23c9 |
scsi_read_complete and put it into a new function called
|
|
|
ae23c9 |
scsi_handle_inquiry_reply. This change aims to avoid
|
|
|
ae23c9 |
cluttering of scsi_read_complete when we more logic in the
|
|
|
ae23c9 |
Inquiry response handling is added in the next patches,
|
|
|
ae23c9 |
centralizing the changes in the new function.
|
|
|
ae23c9 |
|
|
|
ae23c9 |
In scsi-disk.c, take the build of all emulated VPD pages
|
|
|
ae23c9 |
from scsi_disk_emulate_inquiry and make it available to
|
|
|
ae23c9 |
other files into a non-static function called
|
|
|
ae23c9 |
scsi_disk_emulate_vpd_page. Making it public will allow
|
|
|
ae23c9 |
the future VPD BL emulation code for passthrough devices
|
|
|
ae23c9 |
to use it from scsi-generic.c, avoiding copy/pasting this
|
|
|
ae23c9 |
code solely for that purpose. It also has the advantage of
|
|
|
ae23c9 |
providing emulation of all VPD pages in case we need to
|
|
|
ae23c9 |
emulate other pages in other scenarios. As a bonus,
|
|
|
ae23c9 |
scsi_disk_emulate_inquiry got tidier.
|
|
|
ae23c9 |
|
|
|
ae23c9 |
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
|
|
|
ae23c9 |
Message-Id: <20180627172432.11120-2-danielhb413@gmail.com>
|
|
|
ae23c9 |
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
|
|
ae23c9 |
(cherry picked from commit 0a96ca2437646bad197b0108c5f4a93e7ead05a9)
|
|
|
ae23c9 |
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
|
|
|
ae23c9 |
---
|
|
|
ae23c9 |
hw/scsi/scsi-disk.c | 407 +++++++++++++++++++++++++------------------------
|
|
|
ae23c9 |
hw/scsi/scsi-generic.c | 71 +++++----
|
|
|
ae23c9 |
include/hw/scsi/scsi.h | 1 +
|
|
|
ae23c9 |
3 files changed, 249 insertions(+), 230 deletions(-)
|
|
|
ae23c9 |
|
|
|
ae23c9 |
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
|
|
|
ae23c9 |
index ded23d3..ae5b4c0 100644
|
|
|
ae23c9 |
--- a/hw/scsi/scsi-disk.c
|
|
|
ae23c9 |
+++ b/hw/scsi/scsi-disk.c
|
|
|
ae23c9 |
@@ -585,219 +585,228 @@ static uint8_t *scsi_get_buf(SCSIRequest *req)
|
|
|
ae23c9 |
return (uint8_t *)r->iov.iov_base;
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
|
|
|
ae23c9 |
-static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
|
|
|
ae23c9 |
+int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf)
|
|
|
ae23c9 |
{
|
|
|
ae23c9 |
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
|
|
|
ae23c9 |
- int buflen = 0;
|
|
|
ae23c9 |
- int start;
|
|
|
ae23c9 |
-
|
|
|
ae23c9 |
- if (req->cmd.buf[1] & 0x1) {
|
|
|
ae23c9 |
- /* Vital product data */
|
|
|
ae23c9 |
- uint8_t page_code = req->cmd.buf[2];
|
|
|
ae23c9 |
-
|
|
|
ae23c9 |
- outbuf[buflen++] = s->qdev.type & 0x1f;
|
|
|
ae23c9 |
- outbuf[buflen++] = page_code ; // this page
|
|
|
ae23c9 |
- outbuf[buflen++] = 0x00;
|
|
|
ae23c9 |
- outbuf[buflen++] = 0x00;
|
|
|
ae23c9 |
- start = buflen;
|
|
|
ae23c9 |
-
|
|
|
ae23c9 |
- switch (page_code) {
|
|
|
ae23c9 |
- case 0x00: /* Supported page codes, mandatory */
|
|
|
ae23c9 |
- {
|
|
|
ae23c9 |
- DPRINTF("Inquiry EVPD[Supported pages] "
|
|
|
ae23c9 |
- "buffer size %zd\n", req->cmd.xfer);
|
|
|
ae23c9 |
- outbuf[buflen++] = 0x00; // list of supported pages (this page)
|
|
|
ae23c9 |
- if (s->serial) {
|
|
|
ae23c9 |
- outbuf[buflen++] = 0x80; // unit serial number
|
|
|
ae23c9 |
- }
|
|
|
ae23c9 |
- outbuf[buflen++] = 0x83; // device identification
|
|
|
ae23c9 |
- if (s->qdev.type == TYPE_DISK) {
|
|
|
ae23c9 |
- outbuf[buflen++] = 0xb0; // block limits
|
|
|
ae23c9 |
- outbuf[buflen++] = 0xb1; /* block device characteristics */
|
|
|
ae23c9 |
- outbuf[buflen++] = 0xb2; // thin provisioning
|
|
|
ae23c9 |
- }
|
|
|
ae23c9 |
- break;
|
|
|
ae23c9 |
- }
|
|
|
ae23c9 |
- case 0x80: /* Device serial number, optional */
|
|
|
ae23c9 |
- {
|
|
|
ae23c9 |
- int l;
|
|
|
ae23c9 |
+ uint8_t page_code = req->cmd.buf[2];
|
|
|
ae23c9 |
+ int start, buflen = 0;
|
|
|
ae23c9 |
|
|
|
ae23c9 |
- if (!s->serial) {
|
|
|
ae23c9 |
- DPRINTF("Inquiry (EVPD[Serial number] not supported\n");
|
|
|
ae23c9 |
- return -1;
|
|
|
ae23c9 |
- }
|
|
|
ae23c9 |
+ outbuf[buflen++] = s->qdev.type & 0x1f;
|
|
|
ae23c9 |
+ outbuf[buflen++] = page_code;
|
|
|
ae23c9 |
+ outbuf[buflen++] = 0x00;
|
|
|
ae23c9 |
+ outbuf[buflen++] = 0x00;
|
|
|
ae23c9 |
+ start = buflen;
|
|
|
ae23c9 |
|
|
|
ae23c9 |
- l = strlen(s->serial);
|
|
|
ae23c9 |
- if (l > 36) {
|
|
|
ae23c9 |
- l = 36;
|
|
|
ae23c9 |
- }
|
|
|
ae23c9 |
+ switch (page_code) {
|
|
|
ae23c9 |
+ case 0x00: /* Supported page codes, mandatory */
|
|
|
ae23c9 |
+ {
|
|
|
ae23c9 |
+ DPRINTF("Inquiry EVPD[Supported pages] "
|
|
|
ae23c9 |
+ "buffer size %zd\n", req->cmd.xfer);
|
|
|
ae23c9 |
+ outbuf[buflen++] = 0x00; /* list of supported pages (this page) */
|
|
|
ae23c9 |
+ if (s->serial) {
|
|
|
ae23c9 |
+ outbuf[buflen++] = 0x80; /* unit serial number */
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ outbuf[buflen++] = 0x83; /* device identification */
|
|
|
ae23c9 |
+ if (s->qdev.type == TYPE_DISK) {
|
|
|
ae23c9 |
+ outbuf[buflen++] = 0xb0; /* block limits */
|
|
|
ae23c9 |
+ outbuf[buflen++] = 0xb1; /* block device characteristics */
|
|
|
ae23c9 |
+ outbuf[buflen++] = 0xb2; /* thin provisioning */
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ break;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ case 0x80: /* Device serial number, optional */
|
|
|
ae23c9 |
+ {
|
|
|
ae23c9 |
+ int l;
|
|
|
ae23c9 |
|
|
|
ae23c9 |
- DPRINTF("Inquiry EVPD[Serial number] "
|
|
|
ae23c9 |
- "buffer size %zd\n", req->cmd.xfer);
|
|
|
ae23c9 |
- memcpy(outbuf+buflen, s->serial, l);
|
|
|
ae23c9 |
- buflen += l;
|
|
|
ae23c9 |
- break;
|
|
|
ae23c9 |
+ if (!s->serial) {
|
|
|
ae23c9 |
+ DPRINTF("Inquiry (EVPD[Serial number] not supported\n");
|
|
|
ae23c9 |
+ return -1;
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
|
|
|
ae23c9 |
- case 0x83: /* Device identification page, mandatory */
|
|
|
ae23c9 |
- {
|
|
|
ae23c9 |
- const char *str = s->serial ?: blk_name(s->qdev.conf.blk);
|
|
|
ae23c9 |
- int max_len = s->serial ? 20 : 255 - 8;
|
|
|
ae23c9 |
- int id_len = strlen(str);
|
|
|
ae23c9 |
+ l = strlen(s->serial);
|
|
|
ae23c9 |
+ if (l > 36) {
|
|
|
ae23c9 |
+ l = 36;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
|
|
|
ae23c9 |
- if (id_len > max_len) {
|
|
|
ae23c9 |
- id_len = max_len;
|
|
|
ae23c9 |
- }
|
|
|
ae23c9 |
- DPRINTF("Inquiry EVPD[Device identification] "
|
|
|
ae23c9 |
- "buffer size %zd\n", req->cmd.xfer);
|
|
|
ae23c9 |
-
|
|
|
ae23c9 |
- outbuf[buflen++] = 0x2; // ASCII
|
|
|
ae23c9 |
- outbuf[buflen++] = 0; // not officially assigned
|
|
|
ae23c9 |
- outbuf[buflen++] = 0; // reserved
|
|
|
ae23c9 |
- outbuf[buflen++] = id_len; // length of data following
|
|
|
ae23c9 |
- memcpy(outbuf+buflen, str, id_len);
|
|
|
ae23c9 |
- buflen += id_len;
|
|
|
ae23c9 |
-
|
|
|
ae23c9 |
- if (s->qdev.wwn) {
|
|
|
ae23c9 |
- outbuf[buflen++] = 0x1; // Binary
|
|
|
ae23c9 |
- outbuf[buflen++] = 0x3; // NAA
|
|
|
ae23c9 |
- outbuf[buflen++] = 0; // reserved
|
|
|
ae23c9 |
- outbuf[buflen++] = 8;
|
|
|
ae23c9 |
- stq_be_p(&outbuf[buflen], s->qdev.wwn);
|
|
|
ae23c9 |
- buflen += 8;
|
|
|
ae23c9 |
- }
|
|
|
ae23c9 |
+ DPRINTF("Inquiry EVPD[Serial number] "
|
|
|
ae23c9 |
+ "buffer size %zd\n", req->cmd.xfer);
|
|
|
ae23c9 |
+ memcpy(outbuf + buflen, s->serial, l);
|
|
|
ae23c9 |
+ buflen += l;
|
|
|
ae23c9 |
+ break;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
|
|
|
ae23c9 |
- if (s->qdev.port_wwn) {
|
|
|
ae23c9 |
- outbuf[buflen++] = 0x61; // SAS / Binary
|
|
|
ae23c9 |
- outbuf[buflen++] = 0x93; // PIV / Target port / NAA
|
|
|
ae23c9 |
- outbuf[buflen++] = 0; // reserved
|
|
|
ae23c9 |
- outbuf[buflen++] = 8;
|
|
|
ae23c9 |
- stq_be_p(&outbuf[buflen], s->qdev.port_wwn);
|
|
|
ae23c9 |
- buflen += 8;
|
|
|
ae23c9 |
- }
|
|
|
ae23c9 |
+ case 0x83: /* Device identification page, mandatory */
|
|
|
ae23c9 |
+ {
|
|
|
ae23c9 |
+ const char *str = s->serial ?: blk_name(s->qdev.conf.blk);
|
|
|
ae23c9 |
+ int max_len = s->serial ? 20 : 255 - 8;
|
|
|
ae23c9 |
+ int id_len = strlen(str);
|
|
|
ae23c9 |
|
|
|
ae23c9 |
- if (s->port_index) {
|
|
|
ae23c9 |
- outbuf[buflen++] = 0x61; // SAS / Binary
|
|
|
ae23c9 |
- outbuf[buflen++] = 0x94; // PIV / Target port / relative target port
|
|
|
ae23c9 |
- outbuf[buflen++] = 0; // reserved
|
|
|
ae23c9 |
- outbuf[buflen++] = 4;
|
|
|
ae23c9 |
- stw_be_p(&outbuf[buflen + 2], s->port_index);
|
|
|
ae23c9 |
- buflen += 4;
|
|
|
ae23c9 |
- }
|
|
|
ae23c9 |
- break;
|
|
|
ae23c9 |
+ if (id_len > max_len) {
|
|
|
ae23c9 |
+ id_len = max_len;
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
- case 0xb0: /* block limits */
|
|
|
ae23c9 |
- {
|
|
|
ae23c9 |
- unsigned int unmap_sectors =
|
|
|
ae23c9 |
- s->qdev.conf.discard_granularity / s->qdev.blocksize;
|
|
|
ae23c9 |
- unsigned int min_io_size =
|
|
|
ae23c9 |
- s->qdev.conf.min_io_size / s->qdev.blocksize;
|
|
|
ae23c9 |
- unsigned int opt_io_size =
|
|
|
ae23c9 |
- s->qdev.conf.opt_io_size / s->qdev.blocksize;
|
|
|
ae23c9 |
- unsigned int max_unmap_sectors =
|
|
|
ae23c9 |
- s->max_unmap_size / s->qdev.blocksize;
|
|
|
ae23c9 |
- unsigned int max_io_sectors =
|
|
|
ae23c9 |
- s->max_io_size / s->qdev.blocksize;
|
|
|
ae23c9 |
-
|
|
|
ae23c9 |
- if (s->qdev.type == TYPE_ROM) {
|
|
|
ae23c9 |
- DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n",
|
|
|
ae23c9 |
- page_code);
|
|
|
ae23c9 |
- return -1;
|
|
|
ae23c9 |
- }
|
|
|
ae23c9 |
- if (s->qdev.type == TYPE_DISK) {
|
|
|
ae23c9 |
- int max_transfer_blk = blk_get_max_transfer(s->qdev.conf.blk);
|
|
|
ae23c9 |
- int max_io_sectors_blk =
|
|
|
ae23c9 |
- max_transfer_blk / s->qdev.blocksize;
|
|
|
ae23c9 |
-
|
|
|
ae23c9 |
- max_io_sectors =
|
|
|
ae23c9 |
- MIN_NON_ZERO(max_io_sectors_blk, max_io_sectors);
|
|
|
ae23c9 |
-
|
|
|
ae23c9 |
- /* min_io_size and opt_io_size can't be greater than
|
|
|
ae23c9 |
- * max_io_sectors */
|
|
|
ae23c9 |
- if (min_io_size) {
|
|
|
ae23c9 |
- min_io_size = MIN(min_io_size, max_io_sectors);
|
|
|
ae23c9 |
- }
|
|
|
ae23c9 |
- if (opt_io_size) {
|
|
|
ae23c9 |
- opt_io_size = MIN(opt_io_size, max_io_sectors);
|
|
|
ae23c9 |
- }
|
|
|
ae23c9 |
- }
|
|
|
ae23c9 |
- /* required VPD size with unmap support */
|
|
|
ae23c9 |
- buflen = 0x40;
|
|
|
ae23c9 |
- memset(outbuf + 4, 0, buflen - 4);
|
|
|
ae23c9 |
-
|
|
|
ae23c9 |
- outbuf[4] = 0x1; /* wsnz */
|
|
|
ae23c9 |
-
|
|
|
ae23c9 |
- /* optimal transfer length granularity */
|
|
|
ae23c9 |
- outbuf[6] = (min_io_size >> 8) & 0xff;
|
|
|
ae23c9 |
- outbuf[7] = min_io_size & 0xff;
|
|
|
ae23c9 |
-
|
|
|
ae23c9 |
- /* maximum transfer length */
|
|
|
ae23c9 |
- outbuf[8] = (max_io_sectors >> 24) & 0xff;
|
|
|
ae23c9 |
- outbuf[9] = (max_io_sectors >> 16) & 0xff;
|
|
|
ae23c9 |
- outbuf[10] = (max_io_sectors >> 8) & 0xff;
|
|
|
ae23c9 |
- outbuf[11] = max_io_sectors & 0xff;
|
|
|
ae23c9 |
-
|
|
|
ae23c9 |
- /* optimal transfer length */
|
|
|
ae23c9 |
- outbuf[12] = (opt_io_size >> 24) & 0xff;
|
|
|
ae23c9 |
- outbuf[13] = (opt_io_size >> 16) & 0xff;
|
|
|
ae23c9 |
- outbuf[14] = (opt_io_size >> 8) & 0xff;
|
|
|
ae23c9 |
- outbuf[15] = opt_io_size & 0xff;
|
|
|
ae23c9 |
-
|
|
|
ae23c9 |
- /* max unmap LBA count, default is 1GB */
|
|
|
ae23c9 |
- outbuf[20] = (max_unmap_sectors >> 24) & 0xff;
|
|
|
ae23c9 |
- outbuf[21] = (max_unmap_sectors >> 16) & 0xff;
|
|
|
ae23c9 |
- outbuf[22] = (max_unmap_sectors >> 8) & 0xff;
|
|
|
ae23c9 |
- outbuf[23] = max_unmap_sectors & 0xff;
|
|
|
ae23c9 |
-
|
|
|
ae23c9 |
- /* max unmap descriptors, 255 fit in 4 kb with an 8-byte header. */
|
|
|
ae23c9 |
- outbuf[24] = 0;
|
|
|
ae23c9 |
- outbuf[25] = 0;
|
|
|
ae23c9 |
- outbuf[26] = 0;
|
|
|
ae23c9 |
- outbuf[27] = 255;
|
|
|
ae23c9 |
-
|
|
|
ae23c9 |
- /* optimal unmap granularity */
|
|
|
ae23c9 |
- outbuf[28] = (unmap_sectors >> 24) & 0xff;
|
|
|
ae23c9 |
- outbuf[29] = (unmap_sectors >> 16) & 0xff;
|
|
|
ae23c9 |
- outbuf[30] = (unmap_sectors >> 8) & 0xff;
|
|
|
ae23c9 |
- outbuf[31] = unmap_sectors & 0xff;
|
|
|
ae23c9 |
-
|
|
|
ae23c9 |
- /* max write same size */
|
|
|
ae23c9 |
- outbuf[36] = 0;
|
|
|
ae23c9 |
- outbuf[37] = 0;
|
|
|
ae23c9 |
- outbuf[38] = 0;
|
|
|
ae23c9 |
- outbuf[39] = 0;
|
|
|
ae23c9 |
-
|
|
|
ae23c9 |
- outbuf[40] = (max_io_sectors >> 24) & 0xff;
|
|
|
ae23c9 |
- outbuf[41] = (max_io_sectors >> 16) & 0xff;
|
|
|
ae23c9 |
- outbuf[42] = (max_io_sectors >> 8) & 0xff;
|
|
|
ae23c9 |
- outbuf[43] = max_io_sectors & 0xff;
|
|
|
ae23c9 |
- break;
|
|
|
ae23c9 |
+ DPRINTF("Inquiry EVPD[Device identification] "
|
|
|
ae23c9 |
+ "buffer size %zd\n", req->cmd.xfer);
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ outbuf[buflen++] = 0x2; /* ASCII */
|
|
|
ae23c9 |
+ outbuf[buflen++] = 0; /* not officially assigned */
|
|
|
ae23c9 |
+ outbuf[buflen++] = 0; /* reserved */
|
|
|
ae23c9 |
+ outbuf[buflen++] = id_len; /* length of data following */
|
|
|
ae23c9 |
+ memcpy(outbuf + buflen, str, id_len);
|
|
|
ae23c9 |
+ buflen += id_len;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ if (s->qdev.wwn) {
|
|
|
ae23c9 |
+ outbuf[buflen++] = 0x1; /* Binary */
|
|
|
ae23c9 |
+ outbuf[buflen++] = 0x3; /* NAA */
|
|
|
ae23c9 |
+ outbuf[buflen++] = 0; /* reserved */
|
|
|
ae23c9 |
+ outbuf[buflen++] = 8;
|
|
|
ae23c9 |
+ stq_be_p(&outbuf[buflen], s->qdev.wwn);
|
|
|
ae23c9 |
+ buflen += 8;
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
- case 0xb1: /* block device characteristics */
|
|
|
ae23c9 |
- {
|
|
|
ae23c9 |
- buflen = 8;
|
|
|
ae23c9 |
- outbuf[4] = (s->rotation_rate >> 8) & 0xff;
|
|
|
ae23c9 |
- outbuf[5] = s->rotation_rate & 0xff;
|
|
|
ae23c9 |
- outbuf[6] = 0;
|
|
|
ae23c9 |
- outbuf[7] = 0;
|
|
|
ae23c9 |
- break;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ if (s->qdev.port_wwn) {
|
|
|
ae23c9 |
+ outbuf[buflen++] = 0x61; /* SAS / Binary */
|
|
|
ae23c9 |
+ outbuf[buflen++] = 0x93; /* PIV / Target port / NAA */
|
|
|
ae23c9 |
+ outbuf[buflen++] = 0; /* reserved */
|
|
|
ae23c9 |
+ outbuf[buflen++] = 8;
|
|
|
ae23c9 |
+ stq_be_p(&outbuf[buflen], s->qdev.port_wwn);
|
|
|
ae23c9 |
+ buflen += 8;
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
- case 0xb2: /* thin provisioning */
|
|
|
ae23c9 |
- {
|
|
|
ae23c9 |
- buflen = 8;
|
|
|
ae23c9 |
- outbuf[4] = 0;
|
|
|
ae23c9 |
- outbuf[5] = 0xe0; /* unmap & write_same 10/16 all supported */
|
|
|
ae23c9 |
- outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1;
|
|
|
ae23c9 |
- outbuf[7] = 0;
|
|
|
ae23c9 |
- break;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ if (s->port_index) {
|
|
|
ae23c9 |
+ outbuf[buflen++] = 0x61; /* SAS / Binary */
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ /* PIV/Target port/relative target port */
|
|
|
ae23c9 |
+ outbuf[buflen++] = 0x94;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ outbuf[buflen++] = 0; /* reserved */
|
|
|
ae23c9 |
+ outbuf[buflen++] = 4;
|
|
|
ae23c9 |
+ stw_be_p(&outbuf[buflen + 2], s->port_index);
|
|
|
ae23c9 |
+ buflen += 4;
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
- default:
|
|
|
ae23c9 |
+ break;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ case 0xb0: /* block limits */
|
|
|
ae23c9 |
+ {
|
|
|
ae23c9 |
+ unsigned int unmap_sectors =
|
|
|
ae23c9 |
+ s->qdev.conf.discard_granularity / s->qdev.blocksize;
|
|
|
ae23c9 |
+ unsigned int min_io_size =
|
|
|
ae23c9 |
+ s->qdev.conf.min_io_size / s->qdev.blocksize;
|
|
|
ae23c9 |
+ unsigned int opt_io_size =
|
|
|
ae23c9 |
+ s->qdev.conf.opt_io_size / s->qdev.blocksize;
|
|
|
ae23c9 |
+ unsigned int max_unmap_sectors =
|
|
|
ae23c9 |
+ s->max_unmap_size / s->qdev.blocksize;
|
|
|
ae23c9 |
+ unsigned int max_io_sectors =
|
|
|
ae23c9 |
+ s->max_io_size / s->qdev.blocksize;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ if (s->qdev.type == TYPE_ROM) {
|
|
|
ae23c9 |
+ DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n",
|
|
|
ae23c9 |
+ page_code);
|
|
|
ae23c9 |
return -1;
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
- /* done with EVPD */
|
|
|
ae23c9 |
- assert(buflen - start <= 255);
|
|
|
ae23c9 |
- outbuf[start - 1] = buflen - start;
|
|
|
ae23c9 |
- return buflen;
|
|
|
ae23c9 |
+ if (s->qdev.type == TYPE_DISK) {
|
|
|
ae23c9 |
+ int max_transfer_blk = blk_get_max_transfer(s->qdev.conf.blk);
|
|
|
ae23c9 |
+ int max_io_sectors_blk =
|
|
|
ae23c9 |
+ max_transfer_blk / s->qdev.blocksize;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ max_io_sectors =
|
|
|
ae23c9 |
+ MIN_NON_ZERO(max_io_sectors_blk, max_io_sectors);
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ /* min_io_size and opt_io_size can't be greater than
|
|
|
ae23c9 |
+ * max_io_sectors */
|
|
|
ae23c9 |
+ if (min_io_size) {
|
|
|
ae23c9 |
+ min_io_size = MIN(min_io_size, max_io_sectors);
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ if (opt_io_size) {
|
|
|
ae23c9 |
+ opt_io_size = MIN(opt_io_size, max_io_sectors);
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ /* required VPD size with unmap support */
|
|
|
ae23c9 |
+ buflen = 0x40;
|
|
|
ae23c9 |
+ memset(outbuf + 4, 0, buflen - 4);
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ outbuf[4] = 0x1; /* wsnz */
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ /* optimal transfer length granularity */
|
|
|
ae23c9 |
+ outbuf[6] = (min_io_size >> 8) & 0xff;
|
|
|
ae23c9 |
+ outbuf[7] = min_io_size & 0xff;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ /* maximum transfer length */
|
|
|
ae23c9 |
+ outbuf[8] = (max_io_sectors >> 24) & 0xff;
|
|
|
ae23c9 |
+ outbuf[9] = (max_io_sectors >> 16) & 0xff;
|
|
|
ae23c9 |
+ outbuf[10] = (max_io_sectors >> 8) & 0xff;
|
|
|
ae23c9 |
+ outbuf[11] = max_io_sectors & 0xff;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ /* optimal transfer length */
|
|
|
ae23c9 |
+ outbuf[12] = (opt_io_size >> 24) & 0xff;
|
|
|
ae23c9 |
+ outbuf[13] = (opt_io_size >> 16) & 0xff;
|
|
|
ae23c9 |
+ outbuf[14] = (opt_io_size >> 8) & 0xff;
|
|
|
ae23c9 |
+ outbuf[15] = opt_io_size & 0xff;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ /* max unmap LBA count, default is 1GB */
|
|
|
ae23c9 |
+ outbuf[20] = (max_unmap_sectors >> 24) & 0xff;
|
|
|
ae23c9 |
+ outbuf[21] = (max_unmap_sectors >> 16) & 0xff;
|
|
|
ae23c9 |
+ outbuf[22] = (max_unmap_sectors >> 8) & 0xff;
|
|
|
ae23c9 |
+ outbuf[23] = max_unmap_sectors & 0xff;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ /* max unmap descriptors, 255 fit in 4 kb with an 8-byte header */
|
|
|
ae23c9 |
+ outbuf[24] = 0;
|
|
|
ae23c9 |
+ outbuf[25] = 0;
|
|
|
ae23c9 |
+ outbuf[26] = 0;
|
|
|
ae23c9 |
+ outbuf[27] = 255;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ /* optimal unmap granularity */
|
|
|
ae23c9 |
+ outbuf[28] = (unmap_sectors >> 24) & 0xff;
|
|
|
ae23c9 |
+ outbuf[29] = (unmap_sectors >> 16) & 0xff;
|
|
|
ae23c9 |
+ outbuf[30] = (unmap_sectors >> 8) & 0xff;
|
|
|
ae23c9 |
+ outbuf[31] = unmap_sectors & 0xff;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ /* max write same size */
|
|
|
ae23c9 |
+ outbuf[36] = 0;
|
|
|
ae23c9 |
+ outbuf[37] = 0;
|
|
|
ae23c9 |
+ outbuf[38] = 0;
|
|
|
ae23c9 |
+ outbuf[39] = 0;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ outbuf[40] = (max_io_sectors >> 24) & 0xff;
|
|
|
ae23c9 |
+ outbuf[41] = (max_io_sectors >> 16) & 0xff;
|
|
|
ae23c9 |
+ outbuf[42] = (max_io_sectors >> 8) & 0xff;
|
|
|
ae23c9 |
+ outbuf[43] = max_io_sectors & 0xff;
|
|
|
ae23c9 |
+ break;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ case 0xb1: /* block device characteristics */
|
|
|
ae23c9 |
+ {
|
|
|
ae23c9 |
+ buflen = 8;
|
|
|
ae23c9 |
+ outbuf[4] = (s->rotation_rate >> 8) & 0xff;
|
|
|
ae23c9 |
+ outbuf[5] = s->rotation_rate & 0xff;
|
|
|
ae23c9 |
+ outbuf[6] = 0;
|
|
|
ae23c9 |
+ outbuf[7] = 0;
|
|
|
ae23c9 |
+ break;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ case 0xb2: /* thin provisioning */
|
|
|
ae23c9 |
+ {
|
|
|
ae23c9 |
+ buflen = 8;
|
|
|
ae23c9 |
+ outbuf[4] = 0;
|
|
|
ae23c9 |
+ outbuf[5] = 0xe0; /* unmap & write_same 10/16 all supported */
|
|
|
ae23c9 |
+ outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1;
|
|
|
ae23c9 |
+ outbuf[7] = 0;
|
|
|
ae23c9 |
+ break;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ default:
|
|
|
ae23c9 |
+ return -1;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ /* done with EVPD */
|
|
|
ae23c9 |
+ assert(buflen - start <= 255);
|
|
|
ae23c9 |
+ outbuf[start - 1] = buflen - start;
|
|
|
ae23c9 |
+ return buflen;
|
|
|
ae23c9 |
+}
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
|
|
|
ae23c9 |
+{
|
|
|
ae23c9 |
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
|
|
|
ae23c9 |
+ int buflen = 0;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ if (req->cmd.buf[1] & 0x1) {
|
|
|
ae23c9 |
+ /* Vital product data */
|
|
|
ae23c9 |
+ return scsi_disk_emulate_vpd_page(req, outbuf);
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
|
|
|
ae23c9 |
/* Standard INQUIRY data */
|
|
|
ae23c9 |
@@ -3040,6 +3049,10 @@ static Property scsi_block_properties[] = {
|
|
|
ae23c9 |
DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.conf.blk),
|
|
|
ae23c9 |
DEFINE_PROP_BOOL("share-rw", SCSIDiskState, qdev.conf.share_rw, false),
|
|
|
ae23c9 |
DEFINE_PROP_UINT16("rotation_rate", SCSIDiskState, rotation_rate, 0),
|
|
|
ae23c9 |
+ DEFINE_PROP_UINT64("max_unmap_size", SCSIDiskState, max_unmap_size,
|
|
|
ae23c9 |
+ DEFAULT_MAX_UNMAP_SIZE),
|
|
|
ae23c9 |
+ DEFINE_PROP_UINT64("max_io_size", SCSIDiskState, max_io_size,
|
|
|
ae23c9 |
+ DEFAULT_MAX_IO_SIZE),
|
|
|
ae23c9 |
DEFINE_PROP_INT32("scsi_version", SCSIDiskState, qdev.default_scsi_version,
|
|
|
ae23c9 |
-1),
|
|
|
ae23c9 |
DEFINE_PROP_END_OF_LIST(),
|
|
|
ae23c9 |
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
|
|
|
ae23c9 |
index 381f04e..796162c 100644
|
|
|
ae23c9 |
--- a/hw/scsi/scsi-generic.c
|
|
|
ae23c9 |
+++ b/hw/scsi/scsi-generic.c
|
|
|
ae23c9 |
@@ -143,6 +143,43 @@ static int execute_command(BlockBackend *blk,
|
|
|
ae23c9 |
return 0;
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
|
|
|
ae23c9 |
+static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s)
|
|
|
ae23c9 |
+{
|
|
|
ae23c9 |
+ /*
|
|
|
ae23c9 |
+ * EVPD set to zero returns the standard INQUIRY data.
|
|
|
ae23c9 |
+ *
|
|
|
ae23c9 |
+ * Check if scsi_version is unset (-1) to avoid re-defining it
|
|
|
ae23c9 |
+ * each time an INQUIRY with standard data is received.
|
|
|
ae23c9 |
+ * scsi_version is initialized with -1 in scsi_generic_reset
|
|
|
ae23c9 |
+ * and scsi_disk_reset, making sure that we'll set the
|
|
|
ae23c9 |
+ * scsi_version after a reset. If the version field of the
|
|
|
ae23c9 |
+ * INQUIRY response somehow changes after a guest reboot,
|
|
|
ae23c9 |
+ * we'll be able to keep track of it.
|
|
|
ae23c9 |
+ *
|
|
|
ae23c9 |
+ * On SCSI-2 and older, first 3 bits of byte 2 is the
|
|
|
ae23c9 |
+ * ANSI-approved version, while on later versions the
|
|
|
ae23c9 |
+ * whole byte 2 contains the version. Check if we're dealing
|
|
|
ae23c9 |
+ * with a newer version and, in that case, assign the
|
|
|
ae23c9 |
+ * whole byte.
|
|
|
ae23c9 |
+ */
|
|
|
ae23c9 |
+ if (s->scsi_version == -1 && !(r->req.cmd.buf[1] & 0x01)) {
|
|
|
ae23c9 |
+ s->scsi_version = r->buf[2] & 0x07;
|
|
|
ae23c9 |
+ if (s->scsi_version > 2) {
|
|
|
ae23c9 |
+ s->scsi_version = r->buf[2];
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ if (s->type == TYPE_DISK && r->req.cmd.buf[2] == 0xb0) {
|
|
|
ae23c9 |
+ uint32_t max_transfer =
|
|
|
ae23c9 |
+ blk_get_max_transfer(s->conf.blk) / s->blocksize;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ assert(max_transfer);
|
|
|
ae23c9 |
+ stl_be_p(&r->buf[8], max_transfer);
|
|
|
ae23c9 |
+ /* Also take care of the opt xfer len. */
|
|
|
ae23c9 |
+ stl_be_p(&r->buf[12],
|
|
|
ae23c9 |
+ MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12])));
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+}
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
static void scsi_read_complete(void * opaque, int ret)
|
|
|
ae23c9 |
{
|
|
|
ae23c9 |
SCSIGenericReq *r = (SCSIGenericReq *)opaque;
|
|
|
ae23c9 |
@@ -195,39 +232,7 @@ static void scsi_read_complete(void * opaque, int ret)
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
if (r->req.cmd.buf[0] == INQUIRY) {
|
|
|
ae23c9 |
- /*
|
|
|
ae23c9 |
- * EVPD set to zero returns the standard INQUIRY data.
|
|
|
ae23c9 |
- *
|
|
|
ae23c9 |
- * Check if scsi_version is unset (-1) to avoid re-defining it
|
|
|
ae23c9 |
- * each time an INQUIRY with standard data is received.
|
|
|
ae23c9 |
- * scsi_version is initialized with -1 in scsi_generic_reset
|
|
|
ae23c9 |
- * and scsi_disk_reset, making sure that we'll set the
|
|
|
ae23c9 |
- * scsi_version after a reset. If the version field of the
|
|
|
ae23c9 |
- * INQUIRY response somehow changes after a guest reboot,
|
|
|
ae23c9 |
- * we'll be able to keep track of it.
|
|
|
ae23c9 |
- *
|
|
|
ae23c9 |
- * On SCSI-2 and older, first 3 bits of byte 2 is the
|
|
|
ae23c9 |
- * ANSI-approved version, while on later versions the
|
|
|
ae23c9 |
- * whole byte 2 contains the version. Check if we're dealing
|
|
|
ae23c9 |
- * with a newer version and, in that case, assign the
|
|
|
ae23c9 |
- * whole byte.
|
|
|
ae23c9 |
- */
|
|
|
ae23c9 |
- if (s->scsi_version == -1 && !(r->req.cmd.buf[1] & 0x01)) {
|
|
|
ae23c9 |
- s->scsi_version = r->buf[2] & 0x07;
|
|
|
ae23c9 |
- if (s->scsi_version > 2) {
|
|
|
ae23c9 |
- s->scsi_version = r->buf[2];
|
|
|
ae23c9 |
- }
|
|
|
ae23c9 |
- }
|
|
|
ae23c9 |
- if (s->type == TYPE_DISK && r->req.cmd.buf[2] == 0xb0) {
|
|
|
ae23c9 |
- uint32_t max_transfer =
|
|
|
ae23c9 |
- blk_get_max_transfer(s->conf.blk) / s->blocksize;
|
|
|
ae23c9 |
-
|
|
|
ae23c9 |
- assert(max_transfer);
|
|
|
ae23c9 |
- stl_be_p(&r->buf[8], max_transfer);
|
|
|
ae23c9 |
- /* Also take care of the opt xfer len. */
|
|
|
ae23c9 |
- stl_be_p(&r->buf[12],
|
|
|
ae23c9 |
- MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12])));
|
|
|
ae23c9 |
- }
|
|
|
ae23c9 |
+ scsi_handle_inquiry_reply(r, s);
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
scsi_req_data(&r->req, len);
|
|
|
ae23c9 |
scsi_req_unref(&r->req);
|
|
|
ae23c9 |
diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h
|
|
|
ae23c9 |
index 1a7290d..5930a43 100644
|
|
|
ae23c9 |
--- a/include/hw/scsi/scsi.h
|
|
|
ae23c9 |
+++ b/include/hw/scsi/scsi.h
|
|
|
ae23c9 |
@@ -188,6 +188,7 @@ void scsi_device_report_change(SCSIDevice *dev, SCSISense sense);
|
|
|
ae23c9 |
void scsi_device_unit_attention_reported(SCSIDevice *dev);
|
|
|
ae23c9 |
void scsi_generic_read_device_identification(SCSIDevice *dev);
|
|
|
ae23c9 |
int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed);
|
|
|
ae23c9 |
+int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf);
|
|
|
ae23c9 |
SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun);
|
|
|
ae23c9 |
|
|
|
ae23c9 |
/* scsi-generic.c. */
|
|
|
ae23c9 |
--
|
|
|
ae23c9 |
1.8.3.1
|
|
|
ae23c9 |
|