aboutsummaryrefslogtreecommitdiff
path: root/hw/scsi
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2018-11-08 10:01:51 +0000
committerPeter Maydell <peter.maydell@linaro.org>2018-11-08 10:01:51 +0000
commitfa27257432689e8927cb993b251d380d654dcc86 (patch)
tree4f84ce4569599910e6d9b68e4c4ebb517fe20892 /hw/scsi
parent4de6bb0c02ad3f0ec48f0f84ba1a65ab06e81b86 (diff)
parenta458774ad711bceabefbf01e8f0b91d86ec72e0c (diff)
Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging
* icount fix (Clement) * dumping fixes for non-volatile memory (Marc-André, myself) * x86 emulation fix (Rudolf) * recent Hyper-V CPUID flag (Vitaly) * Q35 doc fix (Daniel) * lsi fix (Prasad) * SCSI block limits emulation fixes (myself) * qemu_thread_atexit rework (Peter) * ivshmem memory leak fix (Igor) # gpg: Signature made Tue 06 Nov 2018 21:34:30 GMT # gpg: using RSA key BFFBD25F78C7AE83 # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1 # Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83 * remotes/bonzini/tags/for-upstream: util/qemu-thread-posix: Fix qemu_thread_atexit* for OSX include/qemu/thread.h: Document qemu_thread_atexit* API scsi-generic: do not do VPD emulation for sense other than ILLEGAL_REQUEST scsi-generic: avoid invalid access to struct when emulating block limits scsi-generic: avoid out-of-bounds access to VPD page list scsi-generic: keep VPD page list sorted lsi53c895a: check message length value is valid scripts/dump-guest-memory: Synchronize with guest_phys_blocks_region_add memory-mapping: skip non-volatile memory regions in GuestPhysBlockList nvdimm: set non-volatile on the memory region memory: learn about non-volatile memory region target/i386: Clear RF on SYSCALL instruction MAINTAINERS: remove or downgrade myself to reviewer from some subsystems ivshmem: fix memory backend leak i386: clarify that the Q35 machine type implements a P35 chipset x86: hv_evmcs CPU flag support icount: fix deadlock when all cpus are sleeping Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/scsi')
-rw-r--r--hw/scsi/Makefile.objs2
-rw-r--r--hw/scsi/emulation.c42
-rw-r--r--hw/scsi/lsi53c895a.c19
-rw-r--r--hw/scsi/scsi-disk.c92
-rw-r--r--hw/scsi/scsi-generic.c60
5 files changed, 124 insertions, 91 deletions
diff --git a/hw/scsi/Makefile.objs b/hw/scsi/Makefile.objs
index 718b4c2a68..45167baeaf 100644
--- a/hw/scsi/Makefile.objs
+++ b/hw/scsi/Makefile.objs
@@ -1,4 +1,4 @@
-common-obj-y += scsi-disk.o
+common-obj-y += scsi-disk.o emulation.o
common-obj-y += scsi-generic.o scsi-bus.o
common-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o
common-obj-$(CONFIG_MPTSAS_SCSI_PCI) += mptsas.o mptconfig.o mptendian.o
diff --git a/hw/scsi/emulation.c b/hw/scsi/emulation.c
new file mode 100644
index 0000000000..06d62f3c38
--- /dev/null
+++ b/hw/scsi/emulation.c
@@ -0,0 +1,42 @@
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qemu/bswap.h"
+#include "hw/scsi/emulation.h"
+
+int scsi_emulate_block_limits(uint8_t *outbuf, const SCSIBlockLimits *bl)
+{
+ /* required VPD size with unmap support */
+ memset(outbuf, 0, 0x3c);
+
+ outbuf[0] = bl->wsnz; /* wsnz */
+
+ if (bl->max_io_sectors) {
+ /* optimal transfer length granularity. This field and the optimal
+ * transfer length can't be greater than maximum transfer length.
+ */
+ stw_be_p(outbuf + 2, MIN(bl->min_io_size, bl->max_io_sectors));
+
+ /* maximum transfer length */
+ stl_be_p(outbuf + 4, bl->max_io_sectors);
+
+ /* optimal transfer length */
+ stl_be_p(outbuf + 8, MIN(bl->opt_io_size, bl->max_io_sectors));
+ } else {
+ stw_be_p(outbuf + 2, bl->min_io_size);
+ stl_be_p(outbuf + 8, bl->opt_io_size);
+ }
+
+ /* max unmap LBA count */
+ stl_be_p(outbuf + 16, bl->max_unmap_sectors);
+
+ /* max unmap descriptors */
+ stl_be_p(outbuf + 20, bl->max_unmap_descr);
+
+ /* optimal unmap granularity; alignment is zero */
+ stl_be_p(outbuf + 24, bl->unmap_sectors);
+
+ /* max write same size, make it the same as maximum transfer length */
+ stl_be_p(outbuf + 36, bl->max_io_sectors);
+
+ return 0x3c;
+}
diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c
index d1e6534311..3f207f607c 100644
--- a/hw/scsi/lsi53c895a.c
+++ b/hw/scsi/lsi53c895a.c
@@ -861,10 +861,11 @@ static void lsi_do_status(LSIState *s)
static void lsi_do_msgin(LSIState *s)
{
- int len;
+ uint8_t len;
trace_lsi_do_msgin(s->dbc, s->msg_len);
s->sfbr = s->msg[0];
len = s->msg_len;
+ assert(len > 0 && len <= LSI_MAX_MSGIN_LEN);
if (len > s->dbc)
len = s->dbc;
pci_dma_write(PCI_DEVICE(s), s->dnad, s->msg, len);
@@ -1705,8 +1706,10 @@ static uint8_t lsi_reg_readb(LSIState *s, int offset)
break;
case 0x58: /* SBDL */
/* Some drivers peek at the data bus during the MSG IN phase. */
- if ((s->sstat1 & PHASE_MASK) == PHASE_MI)
+ if ((s->sstat1 & PHASE_MASK) == PHASE_MI) {
+ assert(s->msg_len > 0);
return s->msg[0];
+ }
ret = 0;
break;
case 0x59: /* SBDL high */
@@ -2103,11 +2106,23 @@ static int lsi_pre_save(void *opaque)
return 0;
}
+static int lsi_post_load(void *opaque, int version_id)
+{
+ LSIState *s = opaque;
+
+ if (s->msg_len < 0 || s->msg_len > LSI_MAX_MSGIN_LEN) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static const VMStateDescription vmstate_lsi_scsi = {
.name = "lsiscsi",
.version_id = 0,
.minimum_version_id = 0,
.pre_save = lsi_pre_save,
+ .post_load = lsi_post_load,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(parent_obj, LSIState),
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index e2c5408aa2..6eb258d3f3 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -33,6 +33,7 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "hw/scsi/scsi.h"
+#include "hw/scsi/emulation.h"
#include "scsi/constants.h"
#include "sysemu/sysemu.h"
#include "sysemu/block-backend.h"
@@ -589,7 +590,7 @@ static uint8_t *scsi_get_buf(SCSIRequest *req)
return (uint8_t *)r->iov.iov_base;
}
-int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf)
+static int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
uint8_t page_code = req->cmd.buf[2];
@@ -691,89 +692,36 @@ int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf)
}
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;
+ SCSIBlockLimits bl = {};
if (s->qdev.type == TYPE_ROM) {
DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n",
page_code);
return -1;
}
+ bl.wsnz = 1;
+ bl.unmap_sectors =
+ s->qdev.conf.discard_granularity / s->qdev.blocksize;
+ bl.min_io_size =
+ s->qdev.conf.min_io_size / s->qdev.blocksize;
+ bl.opt_io_size =
+ s->qdev.conf.opt_io_size / s->qdev.blocksize;
+ bl.max_unmap_sectors =
+ s->max_unmap_size / s->qdev.blocksize;
+ bl.max_io_sectors =
+ s->max_io_size / s->qdev.blocksize;
+ /* 255 descriptors fit in 4 KiB with an 8-byte header */
+ bl.max_unmap_descr = 255;
+
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);
- }
+ bl.max_io_sectors =
+ MIN_NON_ZERO(max_io_sectors_blk, bl.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;
+ buflen += scsi_emulate_block_limits(outbuf + buflen, &bl);
break;
}
case 0xb1: /* block device characteristics */
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
index d60c4d0fcf..7237b4162e 100644
--- a/hw/scsi/scsi-generic.c
+++ b/hw/scsi/scsi-generic.c
@@ -16,6 +16,7 @@
#include "qemu-common.h"
#include "qemu/error-report.h"
#include "hw/scsi/scsi.h"
+#include "hw/scsi/emulation.h"
#include "sysemu/block-backend.h"
#ifdef __linux__
@@ -144,7 +145,7 @@ static int execute_command(BlockBackend *blk,
static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s)
{
- uint8_t page, page_len;
+ uint8_t page, page_idx;
/*
* EVPD set to zero returns the standard INQUIRY data.
@@ -181,7 +182,7 @@ static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s)
/* 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) {
+ } else if (s->needs_vpd_bl_emulation && page == 0x00) {
/*
* Now we're capable of supplying the VPD Block Limits
* response if the hardware can't. Add it in the INQUIRY
@@ -190,17 +191,43 @@ static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s)
*
* This way, the guest kernel will be aware of the support
* and will use it to proper setup the SCSI device.
+ *
+ * VPD page numbers must be sorted, so insert 0xb0 at the
+ * right place with an in-place insert. After the initialization
+ * part of the for loop is executed, the device response is
+ * at r[0] to r[page_idx - 1].
*/
- page_len = r->buf[3];
- r->buf[page_len + 4] = 0xb0;
- r->buf[3] = ++page_len;
+ for (page_idx = lduw_be_p(r->buf + 2) + 4;
+ page_idx > 4 && r->buf[page_idx - 1] >= 0xb0;
+ page_idx--) {
+ if (page_idx < r->buflen) {
+ r->buf[page_idx] = r->buf[page_idx - 1];
+ }
+ }
+ r->buf[page_idx] = 0xb0;
+ stw_be_p(r->buf + 2, lduw_be_p(r->buf + 2) + 1);
}
}
}
-static int scsi_emulate_block_limits(SCSIGenericReq *r)
+static int scsi_generic_emulate_block_limits(SCSIGenericReq *r, SCSIDevice *s)
{
- r->buflen = scsi_disk_emulate_vpd_page(&r->req, r->buf);
+ int len;
+ uint8_t buf[64];
+
+ SCSIBlockLimits bl = {
+ .max_io_sectors = blk_get_max_transfer(s->conf.blk) / s->blocksize
+ };
+
+ memset(r->buf, 0, r->buflen);
+ stb_p(buf, s->type);
+ stb_p(buf + 1, 0xb0);
+ len = scsi_emulate_block_limits(buf + 4, &bl);
+ assert(len <= sizeof(buf) - 4);
+ stw_be_p(buf + 2, len);
+
+ memcpy(r->buf, buf, MIN(r->buflen, len + 4));
+
r->io_header.sb_len_wr = 0;
/*
@@ -219,7 +246,6 @@ 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);
@@ -242,13 +268,15 @@ static void scsi_read_complete(void * opaque, int ret)
* 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);
+ if (s->needs_vpd_bl_emulation && ret == 0 &&
+ (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) &&
+ r->req.cmd.buf[0] == INQUIRY &&
+ (r->req.cmd.buf[1] & 0x01) &&
+ r->req.cmd.buf[2] == 0xb0) {
+ SCSISense sense =
+ scsi_parse_sense_buf(r->req.sense, r->io_header.sb_len_wr);
+ if (sense.key == ILLEGAL_REQUEST) {
+ len = scsi_generic_emulate_block_limits(r, s);
/*
* No need to let scsi_read_complete go on and handle an
* INQUIRY VPD BL request we created manually.
@@ -527,7 +555,7 @@ static void scsi_generic_set_vpd_bl_emulation(SCSIDevice *s)
}
page_len = buf[3];
- for (i = 4; i < page_len + 4; i++) {
+ for (i = 4; i < MIN(sizeof(buf), page_len + 4); i++) {
if (buf[i] == 0xb0) {
s->needs_vpd_bl_emulation = false;
return;