diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2018-11-08 10:01:51 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2018-11-08 10:01:51 +0000 |
commit | fa27257432689e8927cb993b251d380d654dcc86 (patch) | |
tree | 4f84ce4569599910e6d9b68e4c4ebb517fe20892 /hw/scsi | |
parent | 4de6bb0c02ad3f0ec48f0f84ba1a65ab06e81b86 (diff) | |
parent | a458774ad711bceabefbf01e8f0b91d86ec72e0c (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.objs | 2 | ||||
-rw-r--r-- | hw/scsi/emulation.c | 42 | ||||
-rw-r--r-- | hw/scsi/lsi53c895a.c | 19 | ||||
-rw-r--r-- | hw/scsi/scsi-disk.c | 92 | ||||
-rw-r--r-- | hw/scsi/scsi-generic.c | 60 |
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; |