]> git.proxmox.com Git - qemu.git/blobdiff - hw/scsi-disk.c
scsi-disk: make discard asynchronous
[qemu.git] / hw / scsi-disk.c
index a029ab6e84a7da8476cc88d637d6389b83e2206e..f8e9dc14191855da6df71f19b1d6e1600767dff1 100644 (file)
@@ -28,16 +28,13 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
 #define DPRINTF(fmt, ...) do {} while(0)
 #endif
 
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
-
 #include "qemu-common.h"
 #include "qemu-error.h"
 #include "scsi.h"
 #include "scsi-defs.h"
 #include "sysemu.h"
 #include "blockdev.h"
-#include "block_int.h"
+#include "hw/block-common.h"
 #include "dma.h"
 
 #ifdef __linux
@@ -61,16 +58,22 @@ typedef struct SCSIDiskReq {
     BlockAcctCookie acct;
 } SCSIDiskReq;
 
+#define SCSI_DISK_F_REMOVABLE   0
+#define SCSI_DISK_F_DPOFUA      1
+
 struct SCSIDiskState
 {
     SCSIDevice qdev;
-    uint32_t removable;
+    uint32_t features;
     bool media_changed;
     bool media_event;
     bool eject_request;
+    uint64_t wwn;
     QEMUBH *bh;
     char *version;
     char *serial;
+    char *vendor;
+    char *product;
     bool tray_open;
     bool tray_locked;
 };
@@ -132,8 +135,14 @@ static void scsi_disk_save_request(QEMUFile *f, SCSIRequest *req)
     qemu_put_be64s(f, &r->sector);
     qemu_put_be32s(f, &r->sector_count);
     qemu_put_be32s(f, &r->buflen);
-    if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
-        qemu_put_buffer(f, r->iov.iov_base, r->iov.iov_len);
+    if (r->buflen) {
+        if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+            qemu_put_buffer(f, r->iov.iov_base, r->iov.iov_len);
+        } else if (!req->retry) {
+            uint32_t len = r->iov.iov_len;
+            qemu_put_be32s(f, &len);
+            qemu_put_buffer(f, r->iov.iov_base, r->iov.iov_len);
+        }
     }
 }
 
@@ -148,13 +157,19 @@ static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req)
         scsi_init_iovec(r, r->buflen);
         if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
             qemu_get_buffer(f, r->iov.iov_base, r->iov.iov_len);
+        } else if (!r->req.retry) {
+            uint32_t len;
+            qemu_get_be32s(f, &len);
+            r->iov.iov_len = len;
+            assert(r->iov.iov_len <= r->buflen);
+            qemu_get_buffer(f, r->iov.iov_base, r->iov.iov_len);
         }
     }
 
     qemu_iovec_init_external(&r->qiov, &r->iov, 1);
 }
 
-static void scsi_flush_complete(void * opaque, int ret)
+static void scsi_aio_complete(void *opaque, int ret)
 {
     SCSIDiskReq *r = (SCSIDiskReq *)opaque;
     SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
@@ -207,7 +222,7 @@ static void scsi_write_do_fua(SCSIDiskReq *r)
 
     if (scsi_is_cmd_fua(&r->req.cmd)) {
         bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH);
-        r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_flush_complete, r);
+        r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_aio_complete, r);
         return;
     }
 
@@ -296,6 +311,13 @@ static void scsi_do_read(void *opaque, int ret)
         }
     }
 
+    if (r->req.io_canceled) {
+        return;
+    }
+
+    /* The request is used as the AIO opaque value, so add a ref.  */
+    scsi_req_ref(&r->req);
+
     if (r->req.sg) {
         dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_READ);
         r->req.resid -= r->req.sg->size;
@@ -504,33 +526,23 @@ 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] & 0x2) {
-        /* Command support data - optional, not implemented */
-        BADF("optional INQUIRY command support request not implemented\n");
-        return -1;
-    }
+    int start;
 
     if (req->cmd.buf[1] & 0x1) {
         /* Vital product data */
         uint8_t page_code = req->cmd.buf[2];
-        if (req->cmd.xfer < 4) {
-            BADF("Error: Inquiry (EVPD[%02X]) buffer size %zd is "
-                 "less than 4\n", page_code, req->cmd.xfer);
-            return -1;
-        }
 
         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 */
         {
-            int pages;
             DPRINTF("Inquiry EVPD[Supported pages] "
                     "buffer size %zd\n", req->cmd.xfer);
-            pages = buflen++;
             outbuf[buflen++] = 0x00; // list of supported pages (this page)
             if (s->serial) {
                 outbuf[buflen++] = 0x80; // unit serial number
@@ -540,7 +552,6 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
                 outbuf[buflen++] = 0xb0; // block limits
                 outbuf[buflen++] = 0xb2; // thin provisioning
             }
-            outbuf[pages] = buflen - pages - 1; // number of pages
             break;
         }
         case 0x80: /* Device serial number, optional */
@@ -559,7 +570,6 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
 
             DPRINTF("Inquiry EVPD[Serial number] "
                     "buffer size %zd\n", req->cmd.xfer);
-            outbuf[buflen++] = l;
             memcpy(outbuf+buflen, s->serial, l);
             buflen += l;
             break;
@@ -577,14 +587,21 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
             DPRINTF("Inquiry EVPD[Device identification] "
                     "buffer size %zd\n", req->cmd.xfer);
 
-            outbuf[buflen++] = 4 + id_len;
             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->wwn) {
+                outbuf[buflen++] = 0x1; // Binary
+                outbuf[buflen++] = 0x3; // NAA
+                outbuf[buflen++] = 0;   // reserved
+                outbuf[buflen++] = 8;
+                stq_be_p(&outbuf[buflen], s->wwn);
+                buflen += 8;
+            }
             break;
         }
         case 0xb0: /* block limits */
@@ -602,8 +619,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
                 return -1;
             }
             /* required VPD size with unmap support */
-            outbuf[3] = buflen = 0x3c;
-
+            buflen = 0x40;
             memset(outbuf + 4, 0, buflen - 4);
 
             /* optimal transfer length granularity */
@@ -625,7 +641,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
         }
         case 0xb2: /* thin provisioning */
         {
-            outbuf[3] = buflen = 8;
+            buflen = 8;
             outbuf[4] = 0;
             outbuf[5] = 0x60; /* write_same 10/16 supported */
             outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1;
@@ -633,28 +649,20 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
             break;
         }
         default:
-            BADF("Error: unsupported Inquiry (EVPD[%02X]) "
-                 "buffer size %zd\n", page_code, req->cmd.xfer);
             return -1;
         }
         /* done with EVPD */
+        assert(buflen - start <= 255);
+        outbuf[start - 1] = buflen - start;
         return buflen;
     }
 
     /* Standard INQUIRY data */
     if (req->cmd.buf[2] != 0) {
-        BADF("Error: Inquiry (STANDARD) page or code "
-             "is non-zero [%02X]\n", req->cmd.buf[2]);
         return -1;
     }
 
     /* PAGE CODE == 0 */
-    if (req->cmd.xfer < 5) {
-        BADF("Error: Inquiry (STANDARD) buffer size %zd "
-             "is less than 5\n", req->cmd.xfer);
-        return -1;
-    }
-
     buflen = req->cmd.xfer;
     if (buflen > SCSI_MAX_INQUIRY_LEN) {
         buflen = SCSI_MAX_INQUIRY_LEN;
@@ -662,13 +670,11 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
     memset(outbuf, 0, buflen);
 
     outbuf[0] = s->qdev.type & 0x1f;
-    outbuf[1] = s->removable ? 0x80 : 0;
-    if (s->qdev.type == TYPE_ROM) {
-        memcpy(&outbuf[16], "QEMU CD-ROM     ", 16);
-    } else {
-        memcpy(&outbuf[16], "QEMU HARDDISK   ", 16);
-    }
-    memcpy(&outbuf[8], "QEMU    ", 8);
+    outbuf[1] = (s->features & (1 << SCSI_DISK_F_REMOVABLE)) ? 0x80 : 0;
+
+    strpadcpy((char *) &outbuf[16], 16, s->product, ' ');
+    strpadcpy((char *) &outbuf[8], 8, s->vendor, ' ');
+
     memset(&outbuf[32], 0, 4);
     memcpy(&outbuf[32], s->version, MIN(4, strlen(s->version)));
     /*
@@ -719,6 +725,39 @@ static inline bool media_is_cd(SCSIDiskState *s)
     return nb_sectors <= CD_MAX_SECTORS;
 }
 
+static int scsi_read_disc_information(SCSIDiskState *s, SCSIDiskReq *r,
+                                      uint8_t *outbuf)
+{
+    uint8_t type = r->req.cmd.buf[1] & 7;
+
+    if (s->qdev.type != TYPE_ROM) {
+        return -1;
+    }
+
+    /* Types 1/2 are only defined for Blu-Ray.  */
+    if (type != 0) {
+        scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
+        return -1;
+    }
+
+    memset(outbuf, 0, 34);
+    outbuf[1] = 32;
+    outbuf[2] = 0xe; /* last session complete, disc finalized */
+    outbuf[3] = 1;   /* first track on disc */
+    outbuf[4] = 1;   /* # of sessions */
+    outbuf[5] = 1;   /* first track of last session */
+    outbuf[6] = 1;   /* last track of last session */
+    outbuf[7] = 0x20; /* unrestricted use */
+    outbuf[8] = 0x00; /* CD-ROM or DVD-ROM */
+    /* 9-10-11: most significant byte corresponding bytes 4-5-6 */
+    /* 12-23: not meaningful for CD-ROM or DVD-ROM */
+    /* 24-31: disc bar code */
+    /* 32: disc application code */
+    /* 33: number of OPC tables */
+
+    return 34;
+}
+
 static int scsi_read_dvd_structure(SCSIDiskState *s, SCSIDiskReq *r,
                                    uint8_t *outbuf)
 {
@@ -927,9 +966,6 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf,
         [MODE_PAGE_AUDIO_CTL]              = (1 << TYPE_ROM),
         [MODE_PAGE_CAPABILITIES]           = (1 << TYPE_ROM),
     };
-
-    BlockDriverState *bdrv = s->qdev.conf.bs;
-    int cylinders, heads, secs;
     uint8_t *p = *p_outbuf;
 
     if ((mode_sense_valid[page] & (1 << s->qdev.type)) == 0) {
@@ -951,19 +987,18 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf,
             break;
         }
         /* if a geometry hint is available, use it */
-        bdrv_guess_geometry(bdrv, &cylinders, &heads, &secs);
-        p[2] = (cylinders >> 16) & 0xff;
-        p[3] = (cylinders >> 8) & 0xff;
-        p[4] = cylinders & 0xff;
-        p[5] = heads & 0xff;
+        p[2] = (s->qdev.conf.cyls >> 16) & 0xff;
+        p[3] = (s->qdev.conf.cyls >> 8) & 0xff;
+        p[4] = s->qdev.conf.cyls & 0xff;
+        p[5] = s->qdev.conf.heads & 0xff;
         /* Write precomp start cylinder, disabled */
-        p[6] = (cylinders >> 16) & 0xff;
-        p[7] = (cylinders >> 8) & 0xff;
-        p[8] = cylinders & 0xff;
+        p[6] = (s->qdev.conf.cyls >> 16) & 0xff;
+        p[7] = (s->qdev.conf.cyls >> 8) & 0xff;
+        p[8] = s->qdev.conf.cyls & 0xff;
         /* Reduced current start cylinder, disabled */
-        p[9] = (cylinders >> 16) & 0xff;
-        p[10] = (cylinders >> 8) & 0xff;
-        p[11] = cylinders & 0xff;
+        p[9] = (s->qdev.conf.cyls >> 16) & 0xff;
+        p[10] = (s->qdev.conf.cyls >> 8) & 0xff;
+        p[11] = s->qdev.conf.cyls & 0xff;
         /* Device step rate [ns], 200ns */
         p[12] = 0;
         p[13] = 200;
@@ -985,18 +1020,17 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf,
         p[2] = 5000 >> 8;
         p[3] = 5000 & 0xff;
         /* if a geometry hint is available, use it */
-        bdrv_guess_geometry(bdrv, &cylinders, &heads, &secs);
-        p[4] = heads & 0xff;
-        p[5] = secs & 0xff;
+        p[4] = s->qdev.conf.heads & 0xff;
+        p[5] = s->qdev.conf.secs & 0xff;
         p[6] = s->qdev.blocksize >> 8;
-        p[8] = (cylinders >> 8) & 0xff;
-        p[9] = cylinders & 0xff;
+        p[8] = (s->qdev.conf.cyls >> 8) & 0xff;
+        p[9] = s->qdev.conf.cyls & 0xff;
         /* Write precomp start cylinder, disabled */
-        p[10] = (cylinders >> 8) & 0xff;
-        p[11] = cylinders & 0xff;
+        p[10] = (s->qdev.conf.cyls >> 8) & 0xff;
+        p[11] = s->qdev.conf.cyls & 0xff;
         /* Reduced current start cylinder, disabled */
-        p[12] = (cylinders >> 8) & 0xff;
-        p[13] = cylinders & 0xff;
+        p[12] = (s->qdev.conf.cyls >> 8) & 0xff;
+        p[13] = s->qdev.conf.cyls & 0xff;
         /* Device step rate [100us], 100us */
         p[14] = 0;
         p[15] = 1;
@@ -1094,7 +1128,7 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf)
     p = outbuf;
 
     if (s->qdev.type == TYPE_DISK) {
-        dev_specific_param = 0x10; /* DPOFUA */
+        dev_specific_param = s->features & (1 << SCSI_DISK_F_DPOFUA) ? 0x10 : 0;
         if (bdrv_is_read_only(s->qdev.conf.bs)) {
             dev_specific_param |= 0x80; /* Readonly.  */
         }
@@ -1358,6 +1392,12 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r)
             goto illegal_request;
         }
         break;
+    case READ_DISC_INFORMATION:
+        buflen = scsi_read_disc_information(s, r, outbuf);
+        if (buflen < 0) {
+            goto illegal_request;
+        }
+        break;
     case READ_DVD_STRUCTURE:
         buflen = scsi_read_dvd_structure(s, r, outbuf);
         if (buflen < 0) {
@@ -1485,6 +1525,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
     case ALLOW_MEDIUM_REMOVAL:
     case READ_CAPACITY_10:
     case READ_TOC:
+    case READ_DISC_INFORMATION:
     case READ_DVD_STRUCTURE:
     case GET_CONFIGURATION:
     case GET_EVENT_STATUS_NOTIFICATION:
@@ -1502,7 +1543,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
         /* The request is used as the AIO opaque value, so add a ref.  */
         scsi_req_ref(&r->req);
         bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH);
-        r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_flush_complete, r);
+        r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_aio_complete, r);
         return 0;
     case READ_6:
     case READ_10:
@@ -1559,8 +1600,11 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
         }
         break;
     case WRITE_SAME_10:
+        len = lduw_be_p(&buf[7]);
+        goto write_same;
     case WRITE_SAME_16:
-        len = r->req.cmd.xfer / s->qdev.blocksize;
+        len = ldl_be_p(&buf[10]) & 0xffffffffULL;
+    write_same:
 
         DPRINTF("WRITE SAME() (sector %" PRId64 ", count %d)\n",
                 r->req.cmd.lba, len);
@@ -1576,15 +1620,13 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
             goto fail;
         }
 
-        rc = bdrv_discard(s->qdev.conf.bs,
-                          r->req.cmd.lba * (s->qdev.blocksize / 512),
-                          len * (s->qdev.blocksize / 512));
-        if (rc < 0) {
-            /* XXX: better error code ?*/
-            goto fail;
-        }
-
-        break;
+        /* The request is used as the AIO opaque value, so add a ref.  */
+        scsi_req_ref(&r->req);
+        r->req.aiocb = bdrv_aio_discard(s->qdev.conf.bs,
+                                        r->req.cmd.lba * (s->qdev.blocksize / 512),
+                                        len * (s->qdev.blocksize / 512),
+                                        scsi_aio_complete, r);
+        return 0;
     default:
         DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
         scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE));
@@ -1693,28 +1735,28 @@ static void scsi_disk_unit_attention_reported(SCSIDevice *dev)
 static int scsi_initfn(SCSIDevice *dev)
 {
     SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
-    DriveInfo *dinfo;
 
     if (!s->qdev.conf.bs) {
         error_report("drive property not set");
         return -1;
     }
 
-    if (!s->removable && !bdrv_is_inserted(s->qdev.conf.bs)) {
+    if (!(s->features & (1 << SCSI_DISK_F_REMOVABLE)) &&
+        !bdrv_is_inserted(s->qdev.conf.bs)) {
         error_report("Device needs media, but drive is empty");
         return -1;
     }
 
-    if (!s->serial) {
-        /* try to fall back to value set with legacy -drive serial=... */
-        dinfo = drive_get_by_blockdev(s->qdev.conf.bs);
-        if (*dinfo->serial) {
-            s->serial = g_strdup(dinfo->serial);
-        }
+    blkconf_serial(&s->qdev.conf, &s->serial);
+    if (blkconf_geometry(&dev->conf, NULL, 65535, 255, 255) < 0) {
+        return -1;
     }
 
     if (!s->version) {
-        s->version = g_strdup(QEMU_VERSION);
+        s->version = g_strdup(qemu_get_version());
+    }
+    if (!s->vendor) {
+        s->vendor = g_strdup("QEMU");
     }
 
     if (bdrv_is_sg(s->qdev.conf.bs)) {
@@ -1722,7 +1764,7 @@ static int scsi_initfn(SCSIDevice *dev)
         return -1;
     }
 
-    if (s->removable) {
+    if (s->features & (1 << SCSI_DISK_F_REMOVABLE)) {
         bdrv_set_dev_ops(s->qdev.conf.bs, &scsi_cd_block_ops, s);
     }
     bdrv_set_buffer_alignment(s->qdev.conf.bs, s->qdev.blocksize);
@@ -1737,6 +1779,9 @@ static int scsi_hd_initfn(SCSIDevice *dev)
     SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
     s->qdev.blocksize = s->qdev.conf.logical_block_size;
     s->qdev.type = TYPE_DISK;
+    if (!s->product) {
+        s->product = g_strdup("QEMU HARDDISK");
+    }
     return scsi_initfn(&s->qdev);
 }
 
@@ -1745,7 +1790,10 @@ static int scsi_cd_initfn(SCSIDevice *dev)
     SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
     s->qdev.blocksize = 2048;
     s->qdev.type = TYPE_ROM;
-    s->removable = true;
+    s->features |= 1 << SCSI_DISK_F_REMOVABLE;
+    if (!s->product) {
+        s->product = g_strdup("QEMU CD-ROM");
+    }
     return scsi_initfn(&s->qdev);
 }
 
@@ -1818,7 +1866,9 @@ static int get_device_type(SCSIDiskState *s)
         return -1;
     }
     s->qdev.type = buf[0];
-    s->removable = (buf[1] & 0x80) != 0;
+    if (buf[1] & 0x80) {
+        s->features |= 1 << SCSI_DISK_F_REMOVABLE;
+    }
     return 0;
 }
 
@@ -1885,7 +1935,7 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag,
         * ones (such as WRITE SAME or EXTENDED COPY, etc.).  So, without
         * O_DIRECT everything must go through SG_IO.
          */
-        if (!(s->qdev.conf.bs->open_flags & BDRV_O_NOCACHE)) {
+        if (bdrv_get_flags(s->qdev.conf.bs) & BDRV_O_NOCACHE) {
             break;
         }
 
@@ -1911,14 +1961,21 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag,
 }
 #endif
 
-#define DEFINE_SCSI_DISK_PROPERTIES()                           \
-    DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf),          \
-    DEFINE_PROP_STRING("ver",  SCSIDiskState, version),         \
-    DEFINE_PROP_STRING("serial",  SCSIDiskState, serial)
+#define DEFINE_SCSI_DISK_PROPERTIES()                                \
+    DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf),               \
+    DEFINE_PROP_STRING("ver", SCSIDiskState, version),               \
+    DEFINE_PROP_STRING("serial", SCSIDiskState, serial),             \
+    DEFINE_PROP_STRING("vendor", SCSIDiskState, vendor),             \
+    DEFINE_PROP_STRING("product", SCSIDiskState, product)
 
 static Property scsi_hd_properties[] = {
     DEFINE_SCSI_DISK_PROPERTIES(),
-    DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
+    DEFINE_PROP_BIT("removable", SCSIDiskState, features,
+                    SCSI_DISK_F_REMOVABLE, false),
+    DEFINE_PROP_BIT("dpofua", SCSIDiskState, features,
+                    SCSI_DISK_F_DPOFUA, false),
+    DEFINE_PROP_HEX64("wwn", SCSIDiskState, wwn, 0),
+    DEFINE_BLOCK_CHS_PROPERTIES(SCSIDiskState, qdev.conf),
     DEFINE_PROP_END_OF_LIST(),
 };
 
@@ -1963,6 +2020,7 @@ static TypeInfo scsi_hd_info = {
 
 static Property scsi_cd_properties[] = {
     DEFINE_SCSI_DISK_PROPERTIES(),
+    DEFINE_PROP_HEX64("wwn", SCSIDiskState, wwn, 0),
     DEFINE_PROP_END_OF_LIST(),
 };
 
@@ -1991,7 +2049,7 @@ static TypeInfo scsi_cd_info = {
 
 #ifdef __linux__
 static Property scsi_block_properties[] = {
-    DEFINE_SCSI_DISK_PROPERTIES(),
+    DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.conf.bs),
     DEFINE_PROP_END_OF_LIST(),
 };
 
@@ -2020,7 +2078,11 @@ static TypeInfo scsi_block_info = {
 
 static Property scsi_disk_properties[] = {
     DEFINE_SCSI_DISK_PROPERTIES(),
-    DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
+    DEFINE_PROP_BIT("removable", SCSIDiskState, features,
+                    SCSI_DISK_F_REMOVABLE, false),
+    DEFINE_PROP_BIT("dpofua", SCSIDiskState, features,
+                    SCSI_DISK_F_DPOFUA, false),
+    DEFINE_PROP_HEX64("wwn", SCSIDiskState, wwn, 0),
     DEFINE_PROP_END_OF_LIST(),
 };