summaryrefslogtreecommitdiffstats
path: root/hw/scsi-disk.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/scsi-disk.c')
-rw-r--r--hw/scsi-disk.c143
1 files changed, 99 insertions, 44 deletions
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 045c764d9b..525816cb76 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -34,7 +34,7 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
#include "scsi-defs.h"
#include "sysemu.h"
#include "blockdev.h"
-#include "block_int.h"
+#include "hw/block-common.h"
#include "dma.h"
#ifdef __linux
@@ -68,6 +68,7 @@ struct SCSIDiskState
bool media_changed;
bool media_event;
bool eject_request;
+ uint64_t wwn;
QEMUBH *bh;
char *version;
char *serial;
@@ -132,8 +133,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,6 +155,12 @@ 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);
}
}
@@ -511,6 +524,7 @@ static int scsi_disk_emulate_inquiry(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 */
@@ -519,14 +533,14 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
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
@@ -536,7 +550,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 */
@@ -555,7 +568,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;
@@ -573,14 +585,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 */
@@ -598,8 +617,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 */
@@ -621,7 +639,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;
@@ -632,6 +650,8 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
return -1;
}
/* done with EVPD */
+ assert(buflen - start <= 255);
+ outbuf[start - 1] = buflen - start;
return buflen;
}
@@ -705,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)
{
@@ -913,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) {
@@ -937,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;
@@ -971,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;
@@ -1344,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) {
@@ -1471,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:
@@ -1682,7 +1737,6 @@ 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");
@@ -1695,16 +1749,13 @@ static int scsi_initfn(SCSIDevice *dev)
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 (bdrv_is_sg(s->qdev.conf.bs)) {
@@ -1877,7 +1928,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;
}
@@ -1914,6 +1965,8 @@ static Property scsi_hd_properties[] = {
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(),
};
@@ -1958,6 +2011,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(),
};
@@ -2019,6 +2073,7 @@ static Property scsi_disk_properties[] = {
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(),
};