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