diff options
-rw-r--r-- | MAINTAINERS | 9 | ||||
-rw-r--r-- | Makefile.target | 1 | ||||
-rw-r--r-- | console.c | 26 | ||||
-rw-r--r-- | console.h | 2 | ||||
-rw-r--r-- | default-configs/pci.mak | 1 | ||||
-rw-r--r-- | default-configs/s390x-softmmu.mak | 1 | ||||
-rw-r--r-- | dma-helpers.c | 37 | ||||
-rw-r--r-- | dma.h | 20 | ||||
-rw-r--r-- | hw/baum.c | 7 | ||||
-rw-r--r-- | hw/baum.h | 2 | ||||
-rw-r--r-- | hw/esp.c | 3 | ||||
-rw-r--r-- | hw/ide/ahci.c | 82 | ||||
-rw-r--r-- | hw/lsi53c895a.c | 2 | ||||
-rw-r--r-- | hw/msmouse.c | 5 | ||||
-rw-r--r-- | hw/msmouse.h | 2 | ||||
-rw-r--r-- | hw/pci.h | 1 | ||||
-rw-r--r-- | hw/s390-virtio-bus.c | 33 | ||||
-rw-r--r-- | hw/s390-virtio-bus.h | 2 | ||||
-rw-r--r-- | hw/scsi-bus.c | 159 | ||||
-rw-r--r-- | hw/scsi-disk.c | 138 | ||||
-rw-r--r-- | hw/scsi-generic.c | 25 | ||||
-rw-r--r-- | hw/scsi.h | 22 | ||||
-rw-r--r-- | hw/spapr_vscsi.c | 2 | ||||
-rw-r--r-- | hw/usb-msd.c | 2 | ||||
-rw-r--r-- | hw/virtio-pci.c | 56 | ||||
-rw-r--r-- | hw/virtio-pci.h | 2 | ||||
-rw-r--r-- | hw/virtio-scsi.c | 617 | ||||
-rw-r--r-- | hw/virtio-scsi.h | 36 | ||||
-rw-r--r-- | hw/virtio.h | 3 | ||||
-rw-r--r-- | qemu-char.c | 140 | ||||
-rw-r--r-- | qemu-sockets.c | 55 | ||||
-rw-r--r-- | qom/object.c | 11 | ||||
-rw-r--r-- | spice-qemu-char.c | 9 | ||||
-rw-r--r-- | sysconfigs/target/target-x86_64.conf | 52 | ||||
-rw-r--r-- | target-i386/cpuid.c | 72 | ||||
-rw-r--r-- | ui/qemu-spice.h | 2 | ||||
-rw-r--r-- | vl.c | 14 |
37 files changed, 1317 insertions, 336 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 647c41363d..0b3b3d8dc6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -420,11 +420,16 @@ F: hw/pci* F: hw/piix* SCSI +M: Paolo Bonzini <pbonzini@redhat.com> +S: Supported +F: hw/virtio-scsi.* +F: hw/scsi* +T: git://github.com/bonzini/qemu.git scsi-next + +LSI53C895A M: Paul Brook <paul@codesourcery.com> -M: Kevin Wolf <kwolf@redhat.com> S: Odd Fixes F: hw/lsi53c895a.c -F: hw/scsi* USB M: Gerd Hoffmann <kraxel@redhat.com> diff --git a/Makefile.target b/Makefile.target index d5eb70de3b..68a5641183 100644 --- a/Makefile.target +++ b/Makefile.target @@ -200,6 +200,7 @@ obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o ioport.o # need to fix this properly obj-$(CONFIG_NO_PCI) += pci-stub.o obj-$(CONFIG_VIRTIO) += virtio.o virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o +obj-$(CONFIG_VIRTIO_SCSI) += virtio-scsi.o obj-y += vhost_net.o obj-$(CONFIG_VHOST_NET) += vhost.o obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/virtio-9p-device.o @@ -1441,9 +1441,6 @@ void console_color_init(DisplayState *ds) } } -static int n_text_consoles; -static CharDriverState *text_consoles[128]; - static void text_console_set_echo(CharDriverState *chr, bool echo) { TextConsole *s = chr->opaque; @@ -1510,7 +1507,7 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds) chr->init(chr); } -int text_console_init(QemuOpts *opts, CharDriverState **_chr) +CharDriverState *text_console_init(QemuOpts *opts) { CharDriverState *chr; TextConsole *s; @@ -1519,13 +1516,6 @@ int text_console_init(QemuOpts *opts, CharDriverState **_chr) chr = g_malloc0(sizeof(CharDriverState)); - if (n_text_consoles == 128) { - fprintf(stderr, "Too many text consoles\n"); - exit(1); - } - text_consoles[n_text_consoles] = chr; - n_text_consoles++; - width = qemu_opt_get_number(opts, "width", 0); if (width == 0) width = qemu_opt_get_number(opts, "cols", 0) * FONT_WIDTH; @@ -1542,7 +1532,7 @@ int text_console_init(QemuOpts *opts, CharDriverState **_chr) if (!s) { g_free(chr); - return -EBUSY; + return NULL; } s->chr = chr; @@ -1550,20 +1540,18 @@ int text_console_init(QemuOpts *opts, CharDriverState **_chr) s->g_height = height; chr->opaque = s; chr->chr_set_echo = text_console_set_echo; - - *_chr = chr; - return 0; + return chr; } void text_consoles_set_display(DisplayState *ds) { int i; - for (i = 0; i < n_text_consoles; i++) { - text_console_do_init(text_consoles[i], ds); + for (i = 0; i < nb_consoles; i++) { + if (consoles[i]->console_type != GRAPHIC_CONSOLE) { + text_console_do_init(consoles[i]->chr, ds); + } } - - n_text_consoles = 0; } void qemu_console_resize(DisplayState *ds, int width, int height) @@ -356,7 +356,7 @@ void vga_hw_text_update(console_ch_t *chardata); int is_graphic_console(void); int is_fixedsize_console(void); -int text_console_init(QemuOpts *opts, CharDriverState **_chr); +CharDriverState *text_console_init(QemuOpts *opts); void text_consoles_set_display(DisplayState *ds); void console_select(unsigned int index); void console_color_init(DisplayState *ds); diff --git a/default-configs/pci.mak b/default-configs/pci.mak index 9d3e1dbda1..21e4ccfb00 100644 --- a/default-configs/pci.mak +++ b/default-configs/pci.mak @@ -1,5 +1,6 @@ CONFIG_PCI=y CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_SCSI=y CONFIG_VIRTIO=y CONFIG_USB_UHCI=y CONFIG_USB_OHCI=y diff --git a/default-configs/s390x-softmmu.mak b/default-configs/s390x-softmmu.mak index 3005729204..e5888037cf 100644 --- a/default-configs/s390x-softmmu.mak +++ b/default-configs/s390x-softmmu.mak @@ -1 +1,2 @@ CONFIG_VIRTIO=y +CONFIG_VIRTIO_SCSI=y diff --git a/dma-helpers.c b/dma-helpers.c index f08cdb5454..c29ea6d3ab 100644 --- a/dma-helpers.c +++ b/dma-helpers.c @@ -204,3 +204,40 @@ BlockDriverAIOCB *dma_bdrv_write(BlockDriverState *bs, { return dma_bdrv_io(bs, sg, sector, bdrv_aio_writev, cb, opaque, true); } + + +static uint64_t dma_buf_rw(uint8_t *ptr, int32_t len, QEMUSGList *sg, bool to_dev) +{ + uint64_t resid; + int sg_cur_index; + + resid = sg->size; + sg_cur_index = 0; + len = MIN(len, resid); + while (len > 0) { + ScatterGatherEntry entry = sg->sg[sg_cur_index++]; + int32_t xfer = MIN(len, entry.len); + cpu_physical_memory_rw(entry.base, ptr, xfer, !to_dev); + ptr += xfer; + len -= xfer; + resid -= xfer; + } + + return resid; +} + +uint64_t dma_buf_read(uint8_t *ptr, int32_t len, QEMUSGList *sg) +{ + return dma_buf_rw(ptr, len, sg, 0); +} + +uint64_t dma_buf_write(uint8_t *ptr, int32_t len, QEMUSGList *sg) +{ + return dma_buf_rw(ptr, len, sg, 1); +} + +void dma_acct_start(BlockDriverState *bs, BlockAcctCookie *cookie, + QEMUSGList *sg, enum BlockAcctType type) +{ + bdrv_acct_start(bs, cookie, sg->size, type); +} @@ -17,6 +17,13 @@ typedef struct ScatterGatherEntry ScatterGatherEntry; +struct QEMUSGList { + ScatterGatherEntry *sg; + int nsg; + int nalloc; + size_t size; +}; + #if defined(TARGET_PHYS_ADDR_BITS) typedef target_phys_addr_t dma_addr_t; @@ -32,13 +39,6 @@ struct ScatterGatherEntry { dma_addr_t len; }; -struct QEMUSGList { - ScatterGatherEntry *sg; - int nsg; - int nalloc; - dma_addr_t size; -}; - void qemu_sglist_init(QEMUSGList *qsg, int alloc_hint); void qemu_sglist_add(QEMUSGList *qsg, dma_addr_t base, dma_addr_t len); void qemu_sglist_destroy(QEMUSGList *qsg); @@ -58,4 +58,10 @@ BlockDriverAIOCB *dma_bdrv_read(BlockDriverState *bs, BlockDriverAIOCB *dma_bdrv_write(BlockDriverState *bs, QEMUSGList *sg, uint64_t sector, BlockDriverCompletionFunc *cb, void *opaque); +uint64_t dma_buf_read(uint8_t *ptr, int32_t len, QEMUSGList *sg); +uint64_t dma_buf_write(uint8_t *ptr, int32_t len, QEMUSGList *sg); + +void dma_acct_start(BlockDriverState *bs, BlockAcctCookie *cookie, + QEMUSGList *sg, enum BlockAcctType type); + #endif @@ -562,7 +562,7 @@ static void baum_close(struct CharDriverState *chr) g_free(baum); } -int chr_baum_init(QemuOpts *opts, CharDriverState **_chr) +CharDriverState *chr_baum_init(QemuOpts *opts) { BaumDriverState *baum; CharDriverState *chr; @@ -614,8 +614,7 @@ int chr_baum_init(QemuOpts *opts, CharDriverState **_chr) qemu_chr_generic_open(chr); - *_chr = chr; - return 0; + return chr; fail: qemu_free_timer(baum->cellCount_timer); @@ -624,5 +623,5 @@ fail_handle: g_free(handle); g_free(chr); g_free(baum); - return -EIO; + return NULL; } @@ -23,4 +23,4 @@ */ /* char device */ -int chr_baum_init(QemuOpts *opts, CharDriverState **_chr); +CharDriverState *chr_baum_init(QemuOpts *opts); @@ -390,7 +390,8 @@ static void esp_do_dma(ESPState *s) esp_dma_done(s); } -static void esp_command_complete(SCSIRequest *req, uint32_t status) +static void esp_command_complete(SCSIRequest *req, uint32_t status, + size_t resid) { ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent); diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index b515f417b1..041ce1e89f 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -428,55 +428,6 @@ static void ahci_reg_init(AHCIState *s) } } -static uint32_t read_from_sglist(uint8_t *buffer, uint32_t len, - QEMUSGList *sglist) -{ - uint32_t i = 0; - uint32_t total = 0, once; - ScatterGatherEntry *cur_prd; - uint32_t sgcount; - - cur_prd = sglist->sg; - sgcount = sglist->nsg; - for (i = 0; len && sgcount; i++) { - once = MIN(cur_prd->len, len); - cpu_physical_memory_read(cur_prd->base, buffer, once); - cur_prd++; - sgcount--; - len -= once; - buffer += once; - total += once; - } - - return total; -} - -static uint32_t write_to_sglist(uint8_t *buffer, uint32_t len, - QEMUSGList *sglist) -{ - uint32_t i = 0; - uint32_t total = 0, once; - ScatterGatherEntry *cur_prd; - uint32_t sgcount; - - DPRINTF(-1, "total: 0x%x bytes\n", len); - - cur_prd = sglist->sg; - sgcount = sglist->nsg; - for (i = 0; len && sgcount; i++) { - once = MIN(cur_prd->len, len); - DPRINTF(-1, "write 0x%x bytes to 0x%lx\n", once, (long)cur_prd->base); - cpu_physical_memory_write(cur_prd->base, buffer, once); - cur_prd++; - sgcount--; - len -= once; - buffer += once; - total += once; - } - - return total; -} - static void check_cmd(AHCIState *s, int port) { AHCIPortRegs *pr = &s->dev[port].port_regs; @@ -802,9 +753,8 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis, DPRINTF(port, "tag %d aio read %"PRId64"\n", ncq_tfs->tag, ncq_tfs->lba); - bdrv_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct, - (ncq_tfs->sector_count-1) * BDRV_SECTOR_SIZE, - BDRV_ACCT_READ); + dma_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct, + &ncq_tfs->sglist, BDRV_ACCT_READ); ncq_tfs->aiocb = dma_bdrv_read(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->sglist, ncq_tfs->lba, ncq_cb, ncq_tfs); @@ -816,9 +766,8 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis, DPRINTF(port, "tag %d aio write %"PRId64"\n", ncq_tfs->tag, ncq_tfs->lba); - bdrv_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct, - (ncq_tfs->sector_count-1) * BDRV_SECTOR_SIZE, - BDRV_ACCT_WRITE); + dma_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct, + &ncq_tfs->sglist, BDRV_ACCT_WRITE); ncq_tfs->aiocb = dma_bdrv_write(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->sglist, ncq_tfs->lba, ncq_cb, ncq_tfs); @@ -1023,12 +972,12 @@ static int ahci_start_transfer(IDEDMA *dma) is_write ? "writ" : "read", size, is_atapi ? "atapi" : "ata", has_sglist ? "" : "o"); - if (is_write && has_sglist && (s->data_ptr < s->data_end)) { - read_from_sglist(s->data_ptr, size, &s->sg); - } - - if (!is_write && has_sglist && (s->data_ptr < s->data_end)) { - write_to_sglist(s->data_ptr, size, &s->sg); + if (has_sglist && size) { + if (is_write) { + dma_buf_write(s->data_ptr, size, &s->sg); + } else { + dma_buf_read(s->data_ptr, size, &s->sg); + } } /* update number of transferred bytes */ @@ -1067,14 +1016,9 @@ static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); IDEState *s = &ad->port.ifs[0]; - int i; ahci_populate_sglist(ad, &s->sg); - - s->io_buffer_size = 0; - for (i = 0; i < s->sg.nsg; i++) { - s->io_buffer_size += s->sg.sg[i].len; - } + s->io_buffer_size = s->sg.size; DPRINTF(ad->port_no, "len=%#x\n", s->io_buffer_size); return s->io_buffer_size != 0; @@ -1092,9 +1036,9 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) } if (is_write) { - write_to_sglist(p, l, &s->sg); + dma_buf_read(p, l, &s->sg); } else { - read_from_sglist(p, l, &s->sg); + dma_buf_write(p, l, &s->sg); } /* update number of transferred bytes */ diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index 0acd1d06df..edc09b7307 100644 --- a/hw/lsi53c895a.c +++ b/hw/lsi53c895a.c @@ -699,7 +699,7 @@ static int lsi_queue_req(LSIState *s, SCSIRequest *req, uint32_t len) } /* Callback to indicate that the SCSI layer has completed a command. */ -static void lsi_command_complete(SCSIRequest *req, uint32_t status) +static void lsi_command_complete(SCSIRequest *req, uint32_t status, size_t resid) { LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent); int out; diff --git a/hw/msmouse.c b/hw/msmouse.c index c3b57ea31c..9c492a4637 100644 --- a/hw/msmouse.c +++ b/hw/msmouse.c @@ -64,7 +64,7 @@ static void msmouse_chr_close (struct CharDriverState *chr) g_free (chr); } -int qemu_chr_open_msmouse(QemuOpts *opts, CharDriverState **_chr) +CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts) { CharDriverState *chr; @@ -74,6 +74,5 @@ int qemu_chr_open_msmouse(QemuOpts *opts, CharDriverState **_chr) qemu_add_mouse_event_handler(msmouse_event, chr, 0, "QEMU Microsoft Mouse"); - *_chr = chr; - return 0; + return chr; } diff --git a/hw/msmouse.h b/hw/msmouse.h index 8b853b35bf..456cb21424 100644 --- a/hw/msmouse.h +++ b/hw/msmouse.h @@ -1,2 +1,2 @@ /* msmouse.c */ -int qemu_chr_open_msmouse(QemuOpts *opts, CharDriverState **_chr); +CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts); @@ -75,6 +75,7 @@ #define PCI_DEVICE_ID_VIRTIO_BLOCK 0x1001 #define PCI_DEVICE_ID_VIRTIO_BALLOON 0x1002 #define PCI_DEVICE_ID_VIRTIO_CONSOLE 0x1003 +#define PCI_DEVICE_ID_VIRTIO_SCSI 0x1004 #define FMT_PCIBUS PRIx64 diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c index 9d480564d7..c450e4bb5b 100644 --- a/hw/s390-virtio-bus.c +++ b/hw/s390-virtio-bus.c @@ -169,6 +169,18 @@ static int s390_virtio_serial_init(VirtIOS390Device *dev) return r; } +static int s390_virtio_scsi_init(VirtIOS390Device *dev) +{ + VirtIODevice *vdev; + + vdev = virtio_scsi_init((DeviceState *)dev, &dev->scsi); + if (!vdev) { + return -1; + } + + return s390_virtio_device_init(dev, vdev); +} + static uint64_t s390_virtio_device_vq_token(VirtIOS390Device *dev, int vq) { ram_addr_t token_off; @@ -433,6 +445,26 @@ static TypeInfo virtio_s390_device_info = { .abstract = true, }; +static Property s390_virtio_scsi_properties[] = { + DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOS390Device, host_features, scsi), + DEFINE_PROP_END_OF_LIST(), +}; + +static void s390_virtio_scsi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass); + + k->init = s390_virtio_scsi_init; + dc->props = s390_virtio_scsi_properties; +} + +static TypeInfo s390_virtio_scsi = { + .name = "virtio-scsi-s390", + .parent = TYPE_VIRTIO_S390_DEVICE, + .instance_size = sizeof(VirtIOS390Device), + .class_init = s390_virtio_scsi_class_init, +}; /***************** S390 Virtio Bus Bridge Device *******************/ /* Only required to have the virtio bus as child in the system bus */ @@ -465,6 +497,7 @@ static void s390_virtio_register_types(void) type_register_static(&s390_virtio_serial); type_register_static(&s390_virtio_blk); type_register_static(&s390_virtio_net); + type_register_static(&s390_virtio_scsi); type_register_static(&s390_virtio_bridge_info); } diff --git a/hw/s390-virtio-bus.h b/hw/s390-virtio-bus.h index b5e59b7d4b..0e60bc0fa2 100644 --- a/hw/s390-virtio-bus.h +++ b/hw/s390-virtio-bus.h @@ -19,6 +19,7 @@ #include "virtio-net.h" #include "virtio-serial.h" +#include "virtio-scsi.h" #define VIRTIO_DEV_OFFS_TYPE 0 /* 8 bits */ #define VIRTIO_DEV_OFFS_NUM_VQ 1 /* 8 bits */ @@ -67,6 +68,7 @@ struct VirtIOS390Device { uint32_t host_features; virtio_serial_conf serial; virtio_net_conf net; + VirtIOSCSIConf scsi; }; typedef struct VirtIOS390Bus { diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index b3e97ceeec..2cb5a18da2 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -5,6 +5,7 @@ #include "qdev.h" #include "blockdev.h" #include "trace.h" +#include "dma.h" static char *scsibus_get_fw_dev_path(DeviceState *dev); static int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf); @@ -86,6 +87,7 @@ static void scsi_dma_restart_bh(void *opaque) scsi_req_continue(req); break; case SCSI_XFER_NONE: + assert(!req->sg); scsi_req_dequeue(req); scsi_req_enqueue(req); break; @@ -130,6 +132,10 @@ static int scsi_qdev_init(DeviceState *qdev) error_report("bad scsi device id: %d", dev->id); goto err; } + if (dev->lun != -1 && dev->lun > bus->info->max_lun) { + error_report("bad scsi device lun: %d", dev->lun); + goto err; + } if (dev->id == -1) { int id = -1; @@ -138,8 +144,8 @@ static int scsi_qdev_init(DeviceState *qdev) } do { d = scsi_device_find(bus, dev->channel, ++id, dev->lun); - } while (d && d->lun == dev->lun && id <= bus->info->max_target); - if (id > bus->info->max_target) { + } while (d && d->lun == dev->lun && id < bus->info->max_target); + if (d && d->lun == dev->lun) { error_report("no free target"); goto err; } @@ -149,14 +155,15 @@ static int scsi_qdev_init(DeviceState *qdev) do { d = scsi_device_find(bus, dev->channel, dev->id, ++lun); } while (d && d->lun == lun && lun < bus->info->max_lun); - if (lun > bus->info->max_lun) { + if (d && d->lun == lun) { error_report("no free lun"); goto err; } dev->lun = lun; } else { d = scsi_device_find(bus, dev->channel, dev->id, dev->lun); - if (dev->lun == d->lun && dev != d) { + assert(d); + if (d->lun == dev->lun && dev != d) { qdev_free(&d->qdev); } } @@ -215,7 +222,7 @@ int scsi_bus_legacy_handle_cmdline(SCSIBus *bus) int res = 0, unit; loc_push_none(&loc); - for (unit = 0; unit < bus->info->max_target; unit++) { + for (unit = 0; unit <= bus->info->max_target; unit++) { dinfo = drive_get(IF_SCSI, bus->busnr, unit); if (dinfo == NULL) { continue; @@ -378,7 +385,7 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r) /* PAGE CODE == 0 */ if (r->req.cmd.xfer < 5) { - return -1; + return false; } r->len = MIN(r->req.cmd.xfer, 36); @@ -533,6 +540,8 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, } req->cmd = cmd; + req->resid = req->cmd.xfer; + switch (buf[0]) { case INQUIRY: trace_scsi_inquiry(d->id, lun, tag, cmd.buf[1], cmd.buf[2]); @@ -643,15 +652,25 @@ void scsi_req_build_sense(SCSIRequest *req, SCSISense sense) req->sense_len = 18; } -int32_t scsi_req_enqueue(SCSIRequest *req) +static void scsi_req_enqueue_internal(SCSIRequest *req) { - int32_t rc; - assert(!req->enqueued); scsi_req_ref(req); + if (req->bus->info->get_sg_list) { + req->sg = req->bus->info->get_sg_list(req); + } else { + req->sg = NULL; + } req->enqueued = true; QTAILQ_INSERT_TAIL(&req->dev->requests, req, next); +} +int32_t scsi_req_enqueue(SCSIRequest *req) +{ + int32_t rc; + + assert(!req->retry); + scsi_req_enqueue_internal(req); scsi_req_ref(req); rc = req->ops->send_command(req, req->cmd.buf); scsi_req_unref(req); @@ -1273,12 +1292,32 @@ void scsi_req_continue(SCSIRequest *req) Once it completes, calling scsi_req_continue will restart I/O. */ void scsi_req_data(SCSIRequest *req, int len) { + uint8_t *buf; if (req->io_canceled) { trace_scsi_req_data_canceled(req->dev->id, req->lun, req->tag, len); - } else { - trace_scsi_req_data(req->dev->id, req->lun, req->tag, len); + return; + } + trace_scsi_req_data(req->dev->id, req->lun, req->tag, len); + assert(req->cmd.mode != SCSI_XFER_NONE); + if (!req->sg) { + req->resid -= len; req->bus->info->transfer_data(req, len); + return; } + + /* If the device calls scsi_req_data and the HBA specified a + * scatter/gather list, the transfer has to happen in a single + * step. */ + assert(!req->dma_started); + req->dma_started = true; + + buf = scsi_req_get_buf(req); + if (req->cmd.mode == SCSI_XFER_FROM_DEV) { + req->resid = dma_buf_read(buf, len, req->sg); + } else { + req->resid = dma_buf_write(buf, len, req->sg); + } + scsi_req_continue(req); } void scsi_req_print(SCSIRequest *req) @@ -1337,7 +1376,7 @@ void scsi_req_complete(SCSIRequest *req, int status) scsi_req_ref(req); scsi_req_dequeue(req); - req->bus->info->complete(req, req->status); + req->bus->info->complete(req, req->status, req->resid); scsi_req_unref(req); } @@ -1413,6 +1452,102 @@ SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int id, int lun) return target_dev; } +/* SCSI request list. For simplicity, pv points to the whole device */ + +static void put_scsi_requests(QEMUFile *f, void *pv, size_t size) +{ + SCSIDevice *s = pv; + SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus); + SCSIRequest *req; + + QTAILQ_FOREACH(req, &s->requests, next) { + assert(!req->io_canceled); + assert(req->status == -1); + assert(req->retry); + assert(req->enqueued); + + qemu_put_sbyte(f, 1); + qemu_put_buffer(f, req->cmd.buf, sizeof(req->cmd.buf)); + qemu_put_be32s(f, &req->tag); + qemu_put_be32s(f, &req->lun); + if (bus->info->save_request) { + bus->info->save_request(f, req); + } + if (req->ops->save_request) { + req->ops->save_request(f, req); + } + } + qemu_put_sbyte(f, 0); +} + +static int get_scsi_requests(QEMUFile *f, void *pv, size_t size) +{ + SCSIDevice *s = pv; + SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus); + + while (qemu_get_sbyte(f)) { + uint8_t buf[SCSI_CMD_BUF_SIZE]; + uint32_t tag; + uint32_t lun; + SCSIRequest *req; + + qemu_get_buffer(f, buf, sizeof(buf)); + qemu_get_be32s(f, &tag); + qemu_get_be32s(f, &lun); + req = scsi_req_new(s, tag, lun, buf, NULL); + if (bus->info->load_request) { + req->hba_private = bus->info->load_request(f, req); + } + if (req->ops->load_request) { + req->ops->load_request(f, req); + } + + /* Just restart it later. */ + req->retry = true; + scsi_req_enqueue_internal(req); + + /* At this point, the request will be kept alive by the reference + * added by scsi_req_enqueue_internal, so we can release our reference. + * The HBA of course will add its own reference in the load_request + * callback if it needs to hold on the SCSIRequest. + */ + scsi_req_unref(req); + } + + return 0; +} + +const VMStateInfo vmstate_info_scsi_requests = { + .name = "scsi-requests", + .get = get_scsi_requests, + .put = put_scsi_requests, +}; + +const VMStateDescription vmstate_scsi_device = { + .name = "SCSIDevice", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(unit_attention.key, SCSIDevice), + VMSTATE_UINT8(unit_attention.asc, SCSIDevice), + VMSTATE_UINT8(unit_attention.ascq, SCSIDevice), + VMSTATE_BOOL(sense_is_ua, SCSIDevice), + VMSTATE_UINT8_ARRAY(sense, SCSIDevice, SCSI_SENSE_BUF_SIZE), + VMSTATE_UINT32(sense_len, SCSIDevice), + { + .name = "requests", + .version_id = 0, + .field_exists = NULL, + .size = 0, /* ouch */ + .info = &vmstate_info_scsi_requests, + .flags = VMS_SINGLE, + .offset = 0, + }, + VMSTATE_END_OF_LIST() + } +}; + static void scsi_device_class_init(ObjectClass *klass, void *data) { DeviceClass *k = DEVICE_CLASS(klass); diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index c12e3a6cb3..4101a59d34 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -38,6 +38,7 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0) #include "sysemu.h" #include "blockdev.h" #include "block_int.h" +#include "dma.h" #ifdef __linux #include <scsi/sg.h> @@ -110,12 +111,12 @@ static void scsi_cancel_io(SCSIRequest *req) r->req.aiocb = NULL; } -static uint32_t scsi_init_iovec(SCSIDiskReq *r) +static uint32_t scsi_init_iovec(SCSIDiskReq *r, size_t size) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); if (!r->iov.iov_base) { - r->buflen = SCSI_DMA_BUF_SIZE; + r->buflen = size; r->iov.iov_base = qemu_blockalign(s->qdev.conf.bs, r->buflen); } r->iov.iov_len = MIN(r->sector_count * 512, r->buflen); @@ -123,6 +124,56 @@ static uint32_t scsi_init_iovec(SCSIDiskReq *r) return r->qiov.size / 512; } +static void scsi_disk_save_request(QEMUFile *f, SCSIRequest *req) +{ + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, 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); + } +} + +static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req) +{ + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); + + qemu_get_be64s(f, &r->sector); + qemu_get_be32s(f, &r->sector_count); + qemu_get_be32s(f, &r->buflen); + if (r->buflen) { + 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); + } + } + + qemu_iovec_init_external(&r->qiov, &r->iov, 1); +} + +static void scsi_dma_complete(void *opaque, int ret) +{ + SCSIDiskReq *r = (SCSIDiskReq *)opaque; + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + + bdrv_acct_done(s->qdev.conf.bs, &r->acct); + + if (ret) { + if (scsi_handle_rw_error(r, -ret)) { + goto done; + } + } + + r->sector += r->sector_count; + r->sector_count = 0; + scsi_req_complete(&r->req, GOOD); + +done: + scsi_req_unref(&r->req); +} + static void scsi_read_complete(void * opaque, int ret) { SCSIDiskReq *r = (SCSIDiskReq *)opaque; @@ -213,10 +264,17 @@ static void scsi_read_data(SCSIRequest *req) return; } - n = scsi_init_iovec(r); - bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); - r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n, - scsi_read_complete, r); + 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; + r->req.aiocb = dma_bdrv_read(s->qdev.conf.bs, r->req.sg, r->sector, + scsi_dma_complete, r); + } else { + n = scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); + bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); + r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n, + scsi_read_complete, r); + } } /* @@ -287,7 +345,7 @@ static void scsi_write_complete(void * opaque, int ret) if (r->sector_count == 0) { scsi_req_complete(&r->req, GOOD); } else { - scsi_init_iovec(r); + scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, r->qiov.size); scsi_req_data(&r->req, r->qiov.size); } @@ -315,18 +373,26 @@ static void scsi_write_data(SCSIRequest *req) return; } - n = r->qiov.size / 512; - if (n) { - if (s->tray_open) { - scsi_write_complete(r, -ENOMEDIUM); - return; - } + if (!r->req.sg && !r->qiov.size) { + /* Called for the first time. Ask the driver to send us more data. */ + scsi_write_complete(r, 0); + return; + } + if (s->tray_open) { + scsi_write_complete(r, -ENOMEDIUM); + return; + } + + if (r->req.sg) { + dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_WRITE); + r->req.resid -= r->req.sg->size; + r->req.aiocb = dma_bdrv_write(s->qdev.conf.bs, r->req.sg, r->sector, + scsi_dma_complete, r); + } else { + n = r->qiov.size / 512; bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_WRITE); r->req.aiocb = bdrv_aio_writev(s->qdev.conf.bs, r->sector, &r->qiov, n, scsi_write_complete, r); - } else { - /* Called for the first time. Ask the driver to send us more data. */ - scsi_write_complete(r, 0); } } @@ -1584,6 +1650,8 @@ static const SCSIReqOps scsi_disk_reqops = { .write_data = scsi_write_data, .cancel_io = scsi_cancel_io, .get_buf = scsi_get_buf, + .load_request = scsi_disk_load_request, + .save_request = scsi_disk_save_request, }; static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, @@ -1686,6 +1754,15 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag, case WRITE_VERIFY_10: case WRITE_VERIFY_12: case WRITE_VERIFY_16: + /* If we are not using O_DIRECT, we might read stale data from the + * host cache if writes were made using other commands than these + * 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)) { + break; + } + /* MMC writing cannot be done via pread/pwrite, because it sometimes * involves writing beyond the maximum LBA or to negative LBA (lead-in). * And once you do these writes, reading from the block device is @@ -1696,10 +1773,11 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag, * seen, but performance usually isn't paramount on optical media. So, * just make scsi-block operate the same as scsi-generic for them. */ - if (s->qdev.type != TYPE_ROM) { - return scsi_req_alloc(&scsi_disk_reqops, &s->qdev, tag, lun, - hba_private); - } + if (s->qdev.type == TYPE_ROM) { + break; + } + return scsi_req_alloc(&scsi_disk_reqops, &s->qdev, tag, lun, + hba_private); } return scsi_req_alloc(&scsi_generic_req_ops, &s->qdev, tag, lun, @@ -1718,6 +1796,22 @@ static Property scsi_hd_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static const VMStateDescription vmstate_scsi_disk_state = { + .name = "scsi-disk", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_SCSI_DEVICE(qdev, SCSIDiskState), + VMSTATE_BOOL(media_changed, SCSIDiskState), + VMSTATE_BOOL(media_event, SCSIDiskState), + VMSTATE_BOOL(eject_request, SCSIDiskState), + VMSTATE_BOOL(tray_open, SCSIDiskState), + VMSTATE_BOOL(tray_locked, SCSIDiskState), + VMSTATE_END_OF_LIST() + } +}; + static void scsi_hd_class_initfn(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1731,6 +1825,7 @@ static void scsi_hd_class_initfn(ObjectClass *klass, void *data) dc->desc = "virtual SCSI disk"; dc->reset = scsi_disk_reset; dc->props = scsi_hd_properties; + dc->vmsd = &vmstate_scsi_disk_state; } static TypeInfo scsi_hd_info = { @@ -1758,6 +1853,7 @@ static void scsi_cd_class_initfn(ObjectClass *klass, void *data) dc->desc = "virtual SCSI CD-ROM"; dc->reset = scsi_disk_reset; dc->props = scsi_cd_properties; + dc->vmsd = &vmstate_scsi_disk_state; } static TypeInfo scsi_cd_info = { @@ -1785,6 +1881,7 @@ static void scsi_block_class_initfn(ObjectClass *klass, void *data) dc->desc = "SCSI block device passthrough"; dc->reset = scsi_disk_reset; dc->props = scsi_block_properties; + dc->vmsd = &vmstate_scsi_disk_state; } static TypeInfo scsi_block_info = { @@ -1814,6 +1911,7 @@ static void scsi_disk_class_initfn(ObjectClass *klass, void *data) dc->desc = "virtual SCSI disk or CD-ROM (legacy)"; dc->reset = scsi_disk_reset; dc->props = scsi_disk_properties; + dc->vmsd = &vmstate_scsi_disk_state; } static TypeInfo scsi_disk_info = { diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index 86014aa893..d856d23b3b 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -59,6 +59,28 @@ typedef struct SCSIGenericReq { sg_io_hdr_t io_header; } SCSIGenericReq; +static void scsi_generic_save_request(QEMUFile *f, SCSIRequest *req) +{ + SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); + + qemu_put_sbe32s(f, &r->buflen); + if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) { + assert(!r->req.sg); + qemu_put_buffer(f, r->buf, r->req.cmd.xfer); + } +} + +static void scsi_generic_load_request(QEMUFile *f, SCSIRequest *req) +{ + SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); + + qemu_get_sbe32s(f, &r->buflen); + if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) { + assert(!r->req.sg); + qemu_get_buffer(f, r->buf, r->req.cmd.xfer); + } +} + static void scsi_free_request(SCSIRequest *req) { SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); @@ -446,6 +468,8 @@ const SCSIReqOps scsi_generic_req_ops = { .write_data = scsi_write_data, .cancel_io = scsi_cancel_io, .get_buf = scsi_get_buf, + .load_request = scsi_generic_load_request, + .save_request = scsi_generic_save_request, }; static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, @@ -474,6 +498,7 @@ static void scsi_generic_class_initfn(ObjectClass *klass, void *data) dc->desc = "pass through generic scsi device (/dev/sg*)"; dc->reset = scsi_generic_reset; dc->props = scsi_generic_properties; + dc->vmsd = &vmstate_scsi_device; } static TypeInfo scsi_generic_info = { @@ -46,8 +46,11 @@ struct SCSIRequest { uint32_t tag; uint32_t lun; uint32_t status; + size_t resid; SCSICommand cmd; BlockDriverAIOCB *aiocb; + QEMUSGList *sg; + bool dma_started; uint8_t sense[SCSI_SENSE_BUF_SIZE]; uint32_t sense_len; bool enqueued; @@ -93,6 +96,16 @@ struct SCSIDevice uint64_t max_lba; }; +extern const VMStateDescription vmstate_scsi_device; + +#define VMSTATE_SCSI_DEVICE(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(SCSIDevice), \ + .vmsd = &vmstate_scsi_device, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, SCSIDevice), \ +} + /* cdrom.c */ int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track); int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num); @@ -106,14 +119,21 @@ struct SCSIReqOps { void (*write_data)(SCSIRequest *req); void (*cancel_io)(SCSIRequest *req); uint8_t *(*get_buf)(SCSIRequest *req); + + void (*save_request)(QEMUFile *f, SCSIRequest *req); + void (*load_request)(QEMUFile *f, SCSIRequest *req); }; struct SCSIBusInfo { int tcq; int max_channel, max_target, max_lun; void (*transfer_data)(SCSIRequest *req, uint32_t arg); - void (*complete)(SCSIRequest *req, uint32_t arg); + void (*complete)(SCSIRequest *req, uint32_t arg, size_t resid); void (*cancel)(SCSIRequest *req); + QEMUSGList *(*get_sg_list)(SCSIRequest *req); + + void (*save_request)(QEMUFile *f, SCSIRequest *req); + void *(*load_request)(QEMUFile *f, SCSIRequest *req); }; struct SCSIBus { diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c index ffce261f98..21670170e8 100644 --- a/hw/spapr_vscsi.c +++ b/hw/spapr_vscsi.c @@ -494,7 +494,7 @@ static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len) } /* Callback to indicate that the SCSI layer has completed a transfer. */ -static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status) +static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status, size_t resid) { VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent); vscsi_req *req = sreq->hba_private; diff --git a/hw/usb-msd.c b/hw/usb-msd.c index c933efe19a..5fbd2d021b 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -223,7 +223,7 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) } } -static void usb_msd_command_complete(SCSIRequest *req, uint32_t status) +static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t resid) { MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); USBPacket *p = s->packet; diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index 907b52a25d..a0fb7c1b9c 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -21,6 +21,7 @@ #include "virtio-blk.h" #include "virtio-net.h" #include "virtio-serial.h" +#include "virtio-scsi.h" #include "pci.h" #include "qemu-error.h" #include "msix.h" @@ -930,12 +931,67 @@ static TypeInfo virtio_balloon_info = { .class_init = virtio_balloon_class_init, }; +static int virtio_scsi_init_pci(PCIDevice *pci_dev) +{ + VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); + VirtIODevice *vdev; + + vdev = virtio_scsi_init(&pci_dev->qdev, &proxy->scsi); + if (!vdev) { + return -EINVAL; + } + + vdev->nvectors = proxy->nvectors; + virtio_init_pci(proxy, vdev); + + /* make the actual value visible */ + proxy->nvectors = vdev->nvectors; + return 0; +} + +static int virtio_scsi_exit_pci(PCIDevice *pci_dev) +{ + VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); + + virtio_scsi_exit(proxy->vdev); + return virtio_exit_pci(pci_dev); +} + +static Property virtio_scsi_properties[] = { + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), + DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOPCIProxy, host_features, scsi), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_scsi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = virtio_scsi_init_pci; + k->exit = virtio_scsi_exit_pci; + k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI; + k->revision = 0x00; + k->class_id = PCI_CLASS_STORAGE_SCSI; + dc->reset = virtio_pci_reset; + dc->props = virtio_scsi_properties; +} + +static TypeInfo virtio_scsi_info = { + .name = "virtio-scsi-pci", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(VirtIOPCIProxy), + .class_init = virtio_scsi_class_init, +}; + static void virtio_pci_register_types(void) { type_register_static(&virtio_blk_info); type_register_static(&virtio_net_info); type_register_static(&virtio_serial_info); type_register_static(&virtio_balloon_info); + type_register_static(&virtio_scsi_info); } type_init(virtio_pci_register_types) diff --git a/hw/virtio-pci.h b/hw/virtio-pci.h index 344c22b68f..e5604282e5 100644 --- a/hw/virtio-pci.h +++ b/hw/virtio-pci.h @@ -17,6 +17,7 @@ #include "virtio-net.h" #include "virtio-serial.h" +#include "virtio-scsi.h" /* Performance improves when virtqueue kick processing is decoupled from the * vcpu thread using ioeventfd for some devices. */ @@ -40,6 +41,7 @@ typedef struct { #endif virtio_serial_conf serial; virtio_net_conf net; + VirtIOSCSIConf scsi; bool ioeventfd_disabled; bool ioeventfd_started; } VirtIOPCIProxy; diff --git a/hw/virtio-scsi.c b/hw/virtio-scsi.c new file mode 100644 index 0000000000..e607edc915 --- /dev/null +++ b/hw/virtio-scsi.c @@ -0,0 +1,617 @@ +/* + * Virtio SCSI HBA + * + * Copyright IBM, Corp. 2010 + * Copyright Red Hat, Inc. 2011 + * + * Authors: + * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> + * Paolo Bonzini <pbonzini@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "virtio-scsi.h" +#include <hw/scsi.h> +#include <hw/scsi-defs.h> + +#define VIRTIO_SCSI_VQ_SIZE 128 +#define VIRTIO_SCSI_CDB_SIZE 32 +#define VIRTIO_SCSI_SENSE_SIZE 96 +#define VIRTIO_SCSI_MAX_CHANNEL 0 +#define VIRTIO_SCSI_MAX_TARGET 255 +#define VIRTIO_SCSI_MAX_LUN 16383 + +/* Response codes */ +#define VIRTIO_SCSI_S_OK 0 +#define VIRTIO_SCSI_S_OVERRUN 1 +#define VIRTIO_SCSI_S_ABORTED 2 +#define VIRTIO_SCSI_S_BAD_TARGET 3 +#define VIRTIO_SCSI_S_RESET 4 +#define VIRTIO_SCSI_S_BUSY 5 +#define VIRTIO_SCSI_S_TRANSPORT_FAILURE 6 +#define VIRTIO_SCSI_S_TARGET_FAILURE 7 +#define VIRTIO_SCSI_S_NEXUS_FAILURE 8 +#define VIRTIO_SCSI_S_FAILURE 9 +#define VIRTIO_SCSI_S_FUNCTION_SUCCEEDED 10 +#define VIRTIO_SCSI_S_FUNCTION_REJECTED 11 +#define VIRTIO_SCSI_S_INCORRECT_LUN 12 + +/* Controlq type codes. */ +#define VIRTIO_SCSI_T_TMF 0 +#define VIRTIO_SCSI_T_AN_QUERY 1 +#define VIRTIO_SCSI_T_AN_SUBSCRIBE 2 + +/* Valid TMF subtypes. */ +#define VIRTIO_SCSI_T_TMF_ABORT_TASK 0 +#define VIRTIO_SCSI_T_TMF_ABORT_TASK_SET 1 +#define VIRTIO_SCSI_T_TMF_CLEAR_ACA 2 +#define VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET 3 +#define VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET 4 +#define VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET 5 +#define VIRTIO_SCSI_T_TMF_QUERY_TASK 6 +#define VIRTIO_SCSI_T_TMF_QUERY_TASK_SET 7 + +/* Events. */ +#define VIRTIO_SCSI_T_EVENTS_MISSED 0x80000000 +#define VIRTIO_SCSI_T_NO_EVENT 0 +#define VIRTIO_SCSI_T_TRANSPORT_RESET 1 +#define VIRTIO_SCSI_T_ASYNC_NOTIFY 2 + +/* SCSI command request, followed by data-out */ +typedef struct { + uint8_t lun[8]; /* Logical Unit Number */ + uint64_t tag; /* Command identifier */ + uint8_t task_attr; /* Task attribute */ + uint8_t prio; + uint8_t crn; + uint8_t cdb[]; +} QEMU_PACKED VirtIOSCSICmdReq; + +/* Response, followed by sense data and data-in */ +typedef struct { + uint32_t sense_len; /* Sense data length */ + uint32_t resid; /* Residual bytes in data buffer */ + uint16_t status_qualifier; /* Status qualifier */ + uint8_t status; /* Command completion status */ + uint8_t response; /* Response values */ + uint8_t sense[]; +} QEMU_PACKED VirtIOSCSICmdResp; + +/* Task Management Request */ +typedef struct { + uint32_t type; + uint32_t subtype; + uint8_t lun[8]; + uint64_t tag; +} QEMU_PACKED VirtIOSCSICtrlTMFReq; + +typedef struct { + uint8_t response; +} QEMU_PACKED VirtIOSCSICtrlTMFResp; + +/* Asynchronous notification query/subscription */ +typedef struct { + uint32_t type; + uint8_t lun[8]; + uint32_t event_requested; +} QEMU_PACKED VirtIOSCSICtrlANReq; + +typedef struct { + uint32_t event_actual; + uint8_t response; +} QEMU_PACKED VirtIOSCSICtrlANResp; + +typedef struct { + uint32_t event; + uint8_t lun[8]; + uint32_t reason; +} QEMU_PACKED VirtIOSCSIEvent; + +typedef struct { + uint32_t num_queues; + uint32_t seg_max; + uint32_t max_sectors; + uint32_t cmd_per_lun; + uint32_t event_info_size; + uint32_t sense_size; + uint32_t cdb_size; + uint16_t max_channel; + uint16_t max_target; + uint32_t max_lun; +} QEMU_PACKED VirtIOSCSIConfig; + +typedef struct { + VirtIODevice vdev; + DeviceState *qdev; + VirtIOSCSIConf *conf; + + SCSIBus bus; + VirtQueue *ctrl_vq; + VirtQueue *event_vq; + VirtQueue *cmd_vq; + uint32_t sense_size; + uint32_t cdb_size; + int resetting; +} VirtIOSCSI; + +typedef struct VirtIOSCSIReq { + VirtIOSCSI *dev; + VirtQueue *vq; + VirtQueueElement elem; + QEMUSGList qsgl; + SCSIRequest *sreq; + union { + char *buf; + VirtIOSCSICmdReq *cmd; + VirtIOSCSICtrlTMFReq *tmf; + VirtIOSCSICtrlANReq *an; + } req; + union { + char *buf; + VirtIOSCSICmdResp *cmd; + VirtIOSCSICtrlTMFResp *tmf; + VirtIOSCSICtrlANResp *an; + VirtIOSCSIEvent *event; + } resp; +} VirtIOSCSIReq; + +static inline int virtio_scsi_get_lun(uint8_t *lun) +{ + return ((lun[2] << 8) | lun[3]) & 0x3FFF; +} + +static inline SCSIDevice *virtio_scsi_device_find(VirtIOSCSI *s, uint8_t *lun) +{ + if (lun[0] != 1) { + return NULL; + } + if (lun[2] != 0 && !(lun[2] >= 0x40 && lun[2] < 0x80)) { + return NULL; + } + return scsi_device_find(&s->bus, 0, lun[1], virtio_scsi_get_lun(lun)); +} + +static void virtio_scsi_complete_req(VirtIOSCSIReq *req) +{ + VirtIOSCSI *s = req->dev; + VirtQueue *vq = req->vq; + virtqueue_push(vq, &req->elem, req->qsgl.size + req->elem.in_sg[0].iov_len); + qemu_sglist_destroy(&req->qsgl); + if (req->sreq) { + req->sreq->hba_private = NULL; + scsi_req_unref(req->sreq); + } + g_free(req); + virtio_notify(&s->vdev, vq); +} + +static void virtio_scsi_bad_req(void) +{ + error_report("wrong size for virtio-scsi headers"); + exit(1); +} + +static void qemu_sgl_init_external(QEMUSGList *qsgl, struct iovec *sg, + target_phys_addr_t *addr, int num) +{ + memset(qsgl, 0, sizeof(*qsgl)); + while (num--) { + qemu_sglist_add(qsgl, *(addr++), (sg++)->iov_len); + } +} + +static void virtio_scsi_parse_req(VirtIOSCSI *s, VirtQueue *vq, + VirtIOSCSIReq *req) +{ + assert(req->elem.out_num && req->elem.in_num); + req->vq = vq; + req->dev = s; + req->sreq = NULL; + req->req.buf = req->elem.out_sg[0].iov_base; + req->resp.buf = req->elem.in_sg[0].iov_base; + + if (req->elem.out_num > 1) { + qemu_sgl_init_external(&req->qsgl, &req->elem.out_sg[1], + &req->elem.out_addr[1], + req->elem.out_num - 1); + } else { + qemu_sgl_init_external(&req->qsgl, &req->elem.in_sg[1], + &req->elem.in_addr[1], + req->elem.in_num - 1); + } +} + +static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq) +{ + VirtIOSCSIReq *req; + req = g_malloc(sizeof(*req)); + if (!virtqueue_pop(vq, &req->elem)) { + g_free(req); + return NULL; + } + + virtio_scsi_parse_req(s, vq, req); + return req; +} + +static void virtio_scsi_save_request(QEMUFile *f, SCSIRequest *sreq) +{ + VirtIOSCSIReq *req = sreq->hba_private; + + qemu_put_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem)); +} + +static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq) +{ + SCSIBus *bus = sreq->bus; + VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); + VirtIOSCSIReq *req; + + req = g_malloc(sizeof(*req)); + qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem)); + virtio_scsi_parse_req(s, s->cmd_vq, req); + + scsi_req_ref(sreq); + req->sreq = sreq; + if (req->sreq->cmd.mode != SCSI_XFER_NONE) { + int req_mode = + (req->elem.in_num > 1 ? SCSI_XFER_FROM_DEV : SCSI_XFER_TO_DEV); + + assert(req->sreq->cmd.mode == req_mode); + } + return req; +} + +static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) +{ + SCSIDevice *d = virtio_scsi_device_find(s, req->req.tmf->lun); + SCSIRequest *r, *next; + DeviceState *qdev; + int target; + + /* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE". */ + req->resp.tmf->response = VIRTIO_SCSI_S_OK; + + switch (req->req.tmf->subtype) { + case VIRTIO_SCSI_T_TMF_ABORT_TASK: + case VIRTIO_SCSI_T_TMF_QUERY_TASK: + if (!d) { + goto fail; + } + if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) { + goto incorrect_lun; + } + QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { + if (r->tag == req->req.tmf->tag) { + break; + } + } + if (r && r->hba_private) { + if (req->req.tmf->subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK) { + /* "If the specified command is present in the task set, then + * return a service response set to FUNCTION SUCCEEDED". + */ + req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; + } else { + scsi_req_cancel(r); + } + } + break; + + case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET: + if (!d) { + goto fail; + } + if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) { + goto incorrect_lun; + } + s->resetting++; + qdev_reset_all(&d->qdev); + s->resetting--; + break; + + case VIRTIO_SCSI_T_TMF_ABORT_TASK_SET: + case VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET: + case VIRTIO_SCSI_T_TMF_QUERY_TASK_SET: + if (!d) { + goto fail; + } + if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) { + goto incorrect_lun; + } + QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { + if (r->hba_private) { + if (req->req.tmf->subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK_SET) { + /* "If there is any command present in the task set, then + * return a service response set to FUNCTION SUCCEEDED". + */ + req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; + break; + } else { + scsi_req_cancel(r); + } + } + } + break; + + case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET: + target = req->req.tmf->lun[1]; + s->resetting++; + QTAILQ_FOREACH(qdev, &s->bus.qbus.children, sibling) { + d = DO_UPCAST(SCSIDevice, qdev, qdev); + if (d->channel == 0 && d->id == target) { + qdev_reset_all(&d->qdev); + } + } + s->resetting--; + break; + + case VIRTIO_SCSI_T_TMF_CLEAR_ACA: + default: + req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_REJECTED; + break; + } + + return; + +incorrect_lun: + req->resp.tmf->response = VIRTIO_SCSI_S_INCORRECT_LUN; + return; + +fail: + req->resp.tmf->response = VIRTIO_SCSI_S_BAD_TARGET; +} + +static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOSCSI *s = (VirtIOSCSI *)vdev; + VirtIOSCSIReq *req; + + while ((req = virtio_scsi_pop_req(s, vq))) { + int out_size, in_size; + if (req->elem.out_num < 1 || req->elem.in_num < 1) { + virtio_scsi_bad_req(); + continue; + } + + out_size = req->elem.out_sg[0].iov_len; + in_size = req->elem.in_sg[0].iov_len; + if (req->req.tmf->type == VIRTIO_SCSI_T_TMF) { + if (out_size < sizeof(VirtIOSCSICtrlTMFReq) || + in_size < sizeof(VirtIOSCSICtrlTMFResp)) { + virtio_scsi_bad_req(); + } + virtio_scsi_do_tmf(s, req); + + } else if (req->req.tmf->type == VIRTIO_SCSI_T_AN_QUERY || + req->req.tmf->type == VIRTIO_SCSI_T_AN_SUBSCRIBE) { + if (out_size < sizeof(VirtIOSCSICtrlANReq) || + in_size < sizeof(VirtIOSCSICtrlANResp)) { + virtio_scsi_bad_req(); + } + req->resp.an->event_actual = 0; + req->resp.an->response = VIRTIO_SCSI_S_OK; + } + virtio_scsi_complete_req(req); + } +} + +static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status, + size_t resid) +{ + VirtIOSCSIReq *req = r->hba_private; + + req->resp.cmd->response = VIRTIO_SCSI_S_OK; + req->resp.cmd->status = status; + if (req->resp.cmd->status == GOOD) { + req->resp.cmd->resid = resid; + } else { + req->resp.cmd->resid = 0; + req->resp.cmd->sense_len = + scsi_req_get_sense(r, req->resp.cmd->sense, VIRTIO_SCSI_SENSE_SIZE); + } + virtio_scsi_complete_req(req); +} + +static QEMUSGList *virtio_scsi_get_sg_list(SCSIRequest *r) +{ + VirtIOSCSIReq *req = r->hba_private; + + return &req->qsgl; +} + +static void virtio_scsi_request_cancelled(SCSIRequest *r) +{ + VirtIOSCSIReq *req = r->hba_private; + + if (!req) { + return; + } + if (req->dev->resetting) { + req->resp.cmd->response = VIRTIO_SCSI_S_RESET; + } else { + req->resp.cmd->response = VIRTIO_SCSI_S_ABORTED; + } + virtio_scsi_complete_req(req); +} + +static void virtio_scsi_fail_cmd_req(VirtIOSCSIReq *req) +{ + req->resp.cmd->response = VIRTIO_SCSI_S_FAILURE; + virtio_scsi_complete_req(req); +} + +static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOSCSI *s = (VirtIOSCSI *)vdev; + VirtIOSCSIReq *req; + int n; + + while ((req = virtio_scsi_pop_req(s, vq))) { + SCSIDevice *d; + int out_size, in_size; + if (req->elem.out_num < 1 || req->elem.in_num < 1) { + virtio_scsi_bad_req(); + } + + out_size = req->elem.out_sg[0].iov_len; + in_size = req->elem.in_sg[0].iov_len; + if (out_size < sizeof(VirtIOSCSICmdReq) + s->cdb_size || + in_size < sizeof(VirtIOSCSICmdResp) + s->sense_size) { + virtio_scsi_bad_req(); + } + + if (req->elem.out_num > 1 && req->elem.in_num > 1) { + virtio_scsi_fail_cmd_req(req); + continue; + } + + d = virtio_scsi_device_find(s, req->req.cmd->lun); + if (!d) { + req->resp.cmd->response = VIRTIO_SCSI_S_BAD_TARGET; + virtio_scsi_complete_req(req); + continue; + } + req->sreq = scsi_req_new(d, req->req.cmd->tag, + virtio_scsi_get_lun(req->req.cmd->lun), + req->req.cmd->cdb, req); + + if (req->sreq->cmd.mode != SCSI_XFER_NONE) { + int req_mode = + (req->elem.in_num > 1 ? SCSI_XFER_FROM_DEV : SCSI_XFER_TO_DEV); + + if (req->sreq->cmd.mode != req_mode || + req->sreq->cmd.xfer > req->qsgl.size) { + req->resp.cmd->response = VIRTIO_SCSI_S_OVERRUN; + virtio_scsi_complete_req(req); + continue; + } + } + + n = scsi_req_enqueue(req->sreq); + if (n) { + scsi_req_continue(req->sreq); + } + } +} + +static void virtio_scsi_get_config(VirtIODevice *vdev, + uint8_t *config) +{ + VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config; + VirtIOSCSI *s = (VirtIOSCSI *)vdev; + + stl_raw(&scsiconf->num_queues, s->conf->num_queues); + stl_raw(&scsiconf->seg_max, 128 - 2); + stl_raw(&scsiconf->max_sectors, s->conf->max_sectors); + stl_raw(&scsiconf->cmd_per_lun, s->conf->cmd_per_lun); + stl_raw(&scsiconf->event_info_size, sizeof(VirtIOSCSIEvent)); + stl_raw(&scsiconf->sense_size, s->sense_size); + stl_raw(&scsiconf->cdb_size, s->cdb_size); + stl_raw(&scsiconf->max_channel, VIRTIO_SCSI_MAX_CHANNEL); + stl_raw(&scsiconf->max_target, VIRTIO_SCSI_MAX_TARGET); + stl_raw(&scsiconf->max_lun, VIRTIO_SCSI_MAX_LUN); +} + +static void virtio_scsi_set_config(VirtIODevice *vdev, + const uint8_t *config) +{ + VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config; + VirtIOSCSI *s = (VirtIOSCSI *)vdev; + + if ((uint32_t) ldl_raw(&scsiconf->sense_size) >= 65536 || + (uint32_t) ldl_raw(&scsiconf->cdb_size) >= 256) { + error_report("bad data written to virtio-scsi configuration space"); + exit(1); + } + + s->sense_size = ldl_raw(&scsiconf->sense_size); + s->cdb_size = ldl_raw(&scsiconf->cdb_size); +} + +static uint32_t virtio_scsi_get_features(VirtIODevice *vdev, + uint32_t requested_features) +{ + return requested_features; +} + +static void virtio_scsi_reset(VirtIODevice *vdev) +{ + VirtIOSCSI *s = (VirtIOSCSI *)vdev; + + s->sense_size = VIRTIO_SCSI_SENSE_SIZE; + s->cdb_size = VIRTIO_SCSI_CDB_SIZE; +} + +/* The device does not have anything to save beyond the virtio data. + * Request data is saved with callbacks from SCSI devices. + */ +static void virtio_scsi_save(QEMUFile *f, void *opaque) +{ + VirtIOSCSI *s = opaque; + virtio_save(&s->vdev, f); +} + +static int virtio_scsi_load(QEMUFile *f, void *opaque, int version_id) +{ + VirtIOSCSI *s = opaque; + virtio_load(&s->vdev, f); + return 0; +} + +static struct SCSIBusInfo virtio_scsi_scsi_info = { + .tcq = true, + .max_channel = VIRTIO_SCSI_MAX_CHANNEL, + .max_target = VIRTIO_SCSI_MAX_TARGET, + .max_lun = VIRTIO_SCSI_MAX_LUN, + + .complete = virtio_scsi_command_complete, + .cancel = virtio_scsi_request_cancelled, + .get_sg_list = virtio_scsi_get_sg_list, + .save_request = virtio_scsi_save_request, + .load_request = virtio_scsi_load_request, +}; + +VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *proxyconf) +{ + VirtIOSCSI *s; + static int virtio_scsi_id; + + s = (VirtIOSCSI *)virtio_common_init("virtio-scsi", VIRTIO_ID_SCSI, + sizeof(VirtIOSCSIConfig), + sizeof(VirtIOSCSI)); + + s->qdev = dev; + s->conf = proxyconf; + + /* TODO set up vdev function pointers */ + s->vdev.get_config = virtio_scsi_get_config; + s->vdev.set_config = virtio_scsi_set_config; + s->vdev.get_features = virtio_scsi_get_features; + s->vdev.reset = virtio_scsi_reset; + + s->ctrl_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE, + virtio_scsi_handle_ctrl); + s->event_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE, + NULL); + s->cmd_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE, + virtio_scsi_handle_cmd); + + scsi_bus_new(&s->bus, dev, &virtio_scsi_scsi_info); + if (!dev->hotplugged) { + scsi_bus_legacy_handle_cmdline(&s->bus); + } + + register_savevm(dev, "virtio-scsi", virtio_scsi_id++, 1, + virtio_scsi_save, virtio_scsi_load, s); + + return &s->vdev; +} + +void virtio_scsi_exit(VirtIODevice *vdev) +{ + virtio_cleanup(vdev); +} diff --git a/hw/virtio-scsi.h b/hw/virtio-scsi.h new file mode 100644 index 0000000000..4bc889de02 --- /dev/null +++ b/hw/virtio-scsi.h @@ -0,0 +1,36 @@ +/* + * Virtio SCSI HBA + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef _QEMU_VIRTIO_SCSI_H +#define _QEMU_VIRTIO_SCSI_H + +#include "virtio.h" +#include "net.h" +#include "pci.h" + +/* The ID for virtio_scsi */ +#define VIRTIO_ID_SCSI 8 + +struct VirtIOSCSIConf { + uint32_t num_queues; + uint32_t max_sectors; + uint32_t cmd_per_lun; +}; + +#define DEFINE_VIRTIO_SCSI_PROPERTIES(_state, _features_field, _conf_field) \ + DEFINE_VIRTIO_COMMON_FEATURES(_state, _features_field), \ + DEFINE_PROP_UINT32("num_queues", _state, _conf_field.num_queues, 1), \ + DEFINE_PROP_UINT32("max_sectors", _state, _conf_field.max_sectors, 0xFFFF), \ + DEFINE_PROP_UINT32("cmd_per_lun", _state, _conf_field.cmd_per_lun, 128) + +#endif /* _QEMU_VIRTIO_SCSI_H */ diff --git a/hw/virtio.h b/hw/virtio.h index 25f55647b4..400c092c95 100644 --- a/hw/virtio.h +++ b/hw/virtio.h @@ -199,6 +199,8 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, typedef struct virtio_serial_conf virtio_serial_conf; VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *serial); VirtIODevice *virtio_balloon_init(DeviceState *dev); +typedef struct VirtIOSCSIConf VirtIOSCSIConf; +VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *conf); #ifdef CONFIG_LINUX VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf); #endif @@ -208,6 +210,7 @@ void virtio_net_exit(VirtIODevice *vdev); void virtio_blk_exit(VirtIODevice *vdev); void virtio_serial_exit(VirtIODevice *vdev); void virtio_balloon_exit(VirtIODevice *vdev); +void virtio_scsi_exit(VirtIODevice *vdev); #define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \ DEFINE_PROP_BIT("indirect_desc", _state, _field, \ diff --git a/qemu-char.c b/qemu-char.c index b1d80dd24e..bb9e3f50a8 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -218,15 +218,13 @@ static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len) return len; } -static int qemu_chr_open_null(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_null(QemuOpts *opts) { CharDriverState *chr; chr = g_malloc0(sizeof(CharDriverState)); chr->chr_write = null_chr_write; - - *_chr= chr; - return 0; + return chr; } /* MUX driver for serial I/O splitting */ @@ -636,21 +634,19 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out) return chr; } -static int qemu_chr_open_file_out(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_file_out(QemuOpts *opts) { int fd_out; TFR(fd_out = qemu_open(qemu_opt_get(opts, "path"), O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0666)); if (fd_out < 0) { - return -errno; + return NULL; } - - *_chr = qemu_chr_open_fd(-1, fd_out); - return 0; + return qemu_chr_open_fd(-1, fd_out); } -static int qemu_chr_open_pipe(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_pipe(QemuOpts *opts) { int fd_in, fd_out; char filename_in[256], filename_out[256]; @@ -658,7 +654,7 @@ static int qemu_chr_open_pipe(QemuOpts *opts, CharDriverState **_chr) if (filename == NULL) { fprintf(stderr, "chardev: pipe: no filename given\n"); - return -EINVAL; + return NULL; } snprintf(filename_in, 256, "%s.in", filename); @@ -672,12 +668,10 @@ static int qemu_chr_open_pipe(QemuOpts *opts, CharDriverState **_chr) close(fd_out); TFR(fd_in = fd_out = qemu_open(filename, O_RDWR | O_BINARY)); if (fd_in < 0) { - return -errno; + return NULL; } } - - *_chr = qemu_chr_open_fd(fd_in, fd_out); - return 0; + return qemu_chr_open_fd(fd_in, fd_out); } @@ -768,14 +762,13 @@ static void qemu_chr_close_stdio(struct CharDriverState *chr) fd_chr_close(chr); } -static int qemu_chr_open_stdio(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_stdio(QemuOpts *opts) { CharDriverState *chr; if (stdio_nb_clients >= STDIO_MAX_CLIENTS) { - return -EBUSY; + return NULL; } - if (stdio_nb_clients == 0) { old_fd0_flags = fcntl(0, F_GETFL); tcgetattr (0, &oldtty); @@ -792,8 +785,7 @@ static int qemu_chr_open_stdio(QemuOpts *opts, CharDriverState **_chr) display_type != DT_NOGRAPHIC); qemu_chr_fe_set_echo(chr, false); - *_chr = chr; - return 0; + return chr; } #ifdef __sun__ @@ -980,7 +972,7 @@ static void pty_chr_close(struct CharDriverState *chr) qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static int qemu_chr_open_pty(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) { CharDriverState *chr; PtyCharDriver *s; @@ -995,7 +987,7 @@ static int qemu_chr_open_pty(QemuOpts *opts, CharDriverState **_chr) #endif if (openpty(&master_fd, &slave_fd, pty_name, NULL, NULL) < 0) { - return -errno; + return NULL; } /* Set raw attributes on the pty. */ @@ -1021,8 +1013,7 @@ static int qemu_chr_open_pty(QemuOpts *opts, CharDriverState **_chr) s->fd = master_fd; s->timer = qemu_new_timer_ms(rt_clock, pty_chr_timer, chr); - *_chr = chr; - return 0; + return chr; } static void tty_serial_init(int fd, int speed, @@ -1223,7 +1214,7 @@ static void qemu_chr_close_tty(CharDriverState *chr) } } -static int qemu_chr_open_tty(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_tty(QemuOpts *opts) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; @@ -1231,20 +1222,18 @@ static int qemu_chr_open_tty(QemuOpts *opts, CharDriverState **_chr) TFR(fd = qemu_open(filename, O_RDWR | O_NONBLOCK)); if (fd < 0) { - return -errno; + return NULL; } tty_serial_init(fd, 115200, 'N', 8, 1); chr = qemu_chr_open_fd(fd, fd); chr->chr_ioctl = tty_serial_ioctl; chr->chr_close = qemu_chr_close_tty; - - *_chr = chr; - return 0; + return chr; } #else /* ! __linux__ && ! __sun__ */ -static int qemu_chr_open_pty(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) { - return -ENOTSUP; + return NULL; } #endif /* __linux__ || __sun__ */ @@ -1358,21 +1347,21 @@ static void pp_close(CharDriverState *chr) qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static int qemu_chr_open_pp(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; ParallelCharDriver *drv; int fd; - TFR(fd = open(filename, O_RDWR)); + TFR(fd = qemu_open(filename, O_RDWR)); if (fd < 0) { - return -errno; + return NULL; } if (ioctl(fd, PPCLAIM) < 0) { close(fd); - return -errno; + return NULL; } drv = g_malloc0(sizeof(ParallelCharDriver)); @@ -1387,8 +1376,7 @@ static int qemu_chr_open_pp(QemuOpts *opts, CharDriverState **_chr) qemu_chr_generic_open(chr); - *_chr = chr; - return 0; + return chr; } #endif /* __linux__ */ @@ -1430,7 +1418,7 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) return 0; } -static int qemu_chr_open_pp(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; @@ -1438,16 +1426,14 @@ static int qemu_chr_open_pp(QemuOpts *opts, CharDriverState **_chr) fd = qemu_open(filename, O_RDWR); if (fd < 0) { - return -errno; + return NULL; } chr = g_malloc0(sizeof(CharDriverState)); chr->opaque = (void *)(intptr_t)fd; chr->chr_write = null_chr_write; chr->chr_ioctl = pp_ioctl; - - *_chr = chr; - return 0; + return chr; } #endif @@ -1663,7 +1649,7 @@ static int win_chr_poll(void *opaque) return 0; } -static int qemu_chr_open_win(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_win(QemuOpts *opts) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; @@ -1678,12 +1664,10 @@ static int qemu_chr_open_win(QemuOpts *opts, CharDriverState **_chr) if (win_chr_init(chr, filename) < 0) { g_free(s); g_free(chr); - return -EIO; + return NULL; } qemu_chr_generic_open(chr); - - *_chr = chr; - return 0; + return chr; } static int win_chr_pipe_poll(void *opaque) @@ -1765,7 +1749,7 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename) } -static int qemu_chr_open_win_pipe(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_win_pipe(QemuOpts *opts) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; @@ -1780,15 +1764,13 @@ static int qemu_chr_open_win_pipe(QemuOpts *opts, CharDriverState **_chr) if (win_chr_pipe_init(chr, filename) < 0) { g_free(s); g_free(chr); - return -EIO; + return NULL; } qemu_chr_generic_open(chr); - - *_chr = chr; - return 0; + return chr; } -static int qemu_chr_open_win_file(HANDLE fd_out, CharDriverState **pchr) +static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out) { CharDriverState *chr; WinCharState *s; @@ -1799,16 +1781,15 @@ static int qemu_chr_open_win_file(HANDLE fd_out, CharDriverState **pchr) chr->opaque = s; chr->chr_write = win_chr_write; qemu_chr_generic_open(chr); - *pchr = chr; - return 0; + return chr; } -static int qemu_chr_open_win_con(QemuOpts *opts, CharDriverState **chr) +static CharDriverState *qemu_chr_open_win_con(QemuOpts *opts) { - return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE), chr); + return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE)); } -static int qemu_chr_open_win_file_out(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_win_file_out(QemuOpts *opts) { const char *file_out = qemu_opt_get(opts, "path"); HANDLE fd_out; @@ -1816,10 +1797,10 @@ static int qemu_chr_open_win_file_out(QemuOpts *opts, CharDriverState **_chr) fd_out = CreateFile(file_out, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (fd_out == INVALID_HANDLE_VALUE) { - return -EIO; + return NULL; } - return qemu_chr_open_win_file(fd_out, _chr); + return qemu_chr_open_win_file(fd_out); } static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len) @@ -1960,7 +1941,7 @@ static void win_stdio_close(CharDriverState *chr) stdio_nb_clients--; } -static int qemu_chr_open_win_stdio(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_win_stdio(QemuOpts *opts) { CharDriverState *chr; WinStdioCharState *stdio; @@ -1969,7 +1950,7 @@ static int qemu_chr_open_win_stdio(QemuOpts *opts, CharDriverState **_chr) if (stdio_nb_clients >= STDIO_MAX_CLIENTS || ((display_type != DT_NOGRAPHIC) && (stdio_nb_clients != 0))) { - return -EIO; + return NULL; } chr = g_malloc0(sizeof(CharDriverState)); @@ -2028,9 +2009,7 @@ static int qemu_chr_open_win_stdio(QemuOpts *opts, CharDriverState **_chr) chr->chr_set_echo = qemu_chr_set_echo_win_stdio; qemu_chr_fe_set_echo(chr, false); - *_chr = chr; - - return 0; + return chr; } #endif /* !_WIN32 */ @@ -2111,12 +2090,11 @@ static void udp_chr_close(CharDriverState *chr) qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static int qemu_chr_open_udp(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_udp(QemuOpts *opts) { CharDriverState *chr = NULL; NetCharDriver *s = NULL; int fd = -1; - int ret; chr = g_malloc0(sizeof(CharDriverState)); s = g_malloc0(sizeof(NetCharDriver)); @@ -2124,7 +2102,6 @@ static int qemu_chr_open_udp(QemuOpts *opts, CharDriverState **_chr) fd = inet_dgram_opts(opts); if (fd < 0) { fprintf(stderr, "inet_dgram_opts failed\n"); - ret = -errno; goto return_err; } @@ -2135,9 +2112,7 @@ static int qemu_chr_open_udp(QemuOpts *opts, CharDriverState **_chr) chr->chr_write = udp_chr_write; chr->chr_update_read_handler = udp_chr_update_read_handler; chr->chr_close = udp_chr_close; - - *_chr = chr; - return 0; + return chr; return_err: g_free(chr); @@ -2145,7 +2120,7 @@ return_err: if (fd >= 0) { closesocket(fd); } - return ret; + return NULL; } /***********************************************************/ @@ -2436,7 +2411,7 @@ static void tcp_chr_close(CharDriverState *chr) qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static int qemu_chr_open_socket(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) { CharDriverState *chr = NULL; TCPCharDriver *s = NULL; @@ -2446,7 +2421,6 @@ static int qemu_chr_open_socket(QemuOpts *opts, CharDriverState **_chr) int do_nodelay; int is_unix; int is_telnet; - int ret; is_listen = qemu_opt_get_bool(opts, "server", 0); is_waitconnect = qemu_opt_get_bool(opts, "wait", 1); @@ -2473,7 +2447,6 @@ static int qemu_chr_open_socket(QemuOpts *opts, CharDriverState **_chr) } } if (fd < 0) { - ret = -errno; goto fail; } @@ -2528,16 +2501,14 @@ static int qemu_chr_open_socket(QemuOpts *opts, CharDriverState **_chr) tcp_chr_accept(chr); socket_set_nonblock(s->listen_fd); } - - *_chr = chr; - return 0; + return chr; fail: if (fd >= 0) closesocket(fd); g_free(s); g_free(chr); - return ret; + return NULL; } /***********************************************************/ @@ -2730,7 +2701,7 @@ fail: static const struct { const char *name; - int (*open)(QemuOpts *opts, CharDriverState **chr); + CharDriverState *(*open)(QemuOpts *opts); } backend_table[] = { { .name = "null", .open = qemu_chr_open_null }, { .name = "socket", .open = qemu_chr_open_socket }, @@ -2771,7 +2742,6 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, { CharDriverState *chr; int i; - int ret; if (qemu_opts_id(opts) == NULL) { fprintf(stderr, "chardev: no id specified\n"); @@ -2793,10 +2763,10 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, return NULL; } - ret = backend_table[i].open(opts, &chr); - if (ret < 0) { - fprintf(stderr, "chardev: opening backend \"%s\" failed: %s\n", - qemu_opt_get(opts, "backend"), strerror(-ret)); + chr = backend_table[i].open(opts); + if (!chr) { + fprintf(stderr, "chardev: opening backend \"%s\" failed\n", + qemu_opt_get(opts, "backend")); return NULL; } diff --git a/qemu-sockets.c b/qemu-sockets.c index 61b2247077..6bcb8e3e0f 100644 --- a/qemu-sockets.c +++ b/qemu-sockets.c @@ -26,7 +26,6 @@ # define AI_ADDRCONFIG 0 #endif -static int sockets_debug = 0; static const int on=1, off=0; /* used temporarely until all users are converted to QemuOpts */ @@ -101,21 +100,6 @@ const char *inet_strfamily(int family) return "unknown"; } -static void inet_print_addrinfo(const char *tag, struct addrinfo *res) -{ - struct addrinfo *e; - char uaddr[INET6_ADDRSTRLEN+1]; - char uport[33]; - - for (e = res; e != NULL; e = e->ai_next) { - getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen, - uaddr,INET6_ADDRSTRLEN,uport,32, - NI_NUMERICHOST | NI_NUMERICSERV); - fprintf(stderr,"%s: getaddrinfo: family %s, host %s, port %s\n", - tag, inet_strfamily(e->ai_family), uaddr, uport); - } -} - int inet_listen_opts(QemuOpts *opts, int port_offset) { struct addrinfo ai,*res,*e; @@ -123,7 +107,7 @@ int inet_listen_opts(QemuOpts *opts, int port_offset) char port[33]; char uaddr[INET6_ADDRSTRLEN+1]; char uport[33]; - int slisten,rc,to,try_next; + int slisten, rc, to, port_min, port_max, p; memset(&ai,0, sizeof(ai)); ai.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; @@ -153,8 +137,6 @@ int inet_listen_opts(QemuOpts *opts, int port_offset) gai_strerror(rc)); return -1; } - if (sockets_debug) - inet_print_addrinfo(__FUNCTION__, res); /* create socket + bind */ for (e = res; e != NULL; e = e->ai_next) { @@ -177,23 +159,18 @@ int inet_listen_opts(QemuOpts *opts, int port_offset) } #endif - for (;;) { + port_min = inet_getport(e); + port_max = to ? to + port_offset : port_min; + for (p = port_min; p <= port_max; p++) { + inet_setport(e, p); if (bind(slisten, e->ai_addr, e->ai_addrlen) == 0) { - if (sockets_debug) - fprintf(stderr,"%s: bind(%s,%s,%d): OK\n", __FUNCTION__, - inet_strfamily(e->ai_family), uaddr, inet_getport(e)); goto listen; } - try_next = to && (inet_getport(e) <= to + port_offset); - if (!try_next || sockets_debug) + if (p == port_max) { fprintf(stderr,"%s: bind(%s,%s,%d): %s\n", __FUNCTION__, inet_strfamily(e->ai_family), uaddr, inet_getport(e), strerror(errno)); - if (try_next) { - inet_setport(e, inet_getport(e) + 1); - continue; } - break; } closesocket(slisten); } @@ -249,8 +226,6 @@ int inet_connect_opts(QemuOpts *opts) gai_strerror(rc)); return -1; } - if (sockets_debug) - inet_print_addrinfo(__FUNCTION__, res); for (e = res; e != NULL; e = e->ai_next) { if (getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen, @@ -269,17 +244,13 @@ int inet_connect_opts(QemuOpts *opts) /* connect to peer */ if (connect(sock,e->ai_addr,e->ai_addrlen) < 0) { - if (sockets_debug || NULL == e->ai_next) + if (NULL == e->ai_next) fprintf(stderr, "%s: connect(%s,%s,%s,%s): %s\n", __FUNCTION__, inet_strfamily(e->ai_family), e->ai_canonname, uaddr, uport, strerror(errno)); closesocket(sock); continue; } - if (sockets_debug) - fprintf(stderr, "%s: connect(%s,%s,%s,%s): OK\n", __FUNCTION__, - inet_strfamily(e->ai_family), - e->ai_canonname, uaddr, uport); freeaddrinfo(res); return sock; } @@ -322,10 +293,6 @@ int inet_dgram_opts(QemuOpts *opts) gai_strerror(rc)); return -1; } - if (sockets_debug) { - fprintf(stderr, "%s: peer (%s:%s)\n", __FUNCTION__, addr, port); - inet_print_addrinfo(__FUNCTION__, peer); - } /* lookup local addr */ memset(&ai,0, sizeof(ai)); @@ -346,10 +313,6 @@ int inet_dgram_opts(QemuOpts *opts) gai_strerror(rc)); return -1; } - if (sockets_debug) { - fprintf(stderr, "%s: local (%s:%s)\n", __FUNCTION__, addr, port); - inet_print_addrinfo(__FUNCTION__, local); - } /* create socket */ sock = qemu_socket(peer->ai_family, peer->ai_socktype, peer->ai_protocol); @@ -541,8 +504,6 @@ int unix_listen_opts(QemuOpts *opts) goto err; } - if (sockets_debug) - fprintf(stderr, "bind(unix:%s): OK\n", un.sun_path); return sock; err: @@ -576,8 +537,6 @@ int unix_connect_opts(QemuOpts *opts) return -1; } - if (sockets_debug) - fprintf(stderr, "connect(unix:%s): OK\n", path); return sock; } diff --git a/qom/object.c b/qom/object.c index d858c04705..aa037d299f 100644 --- a/qom/object.c +++ b/qom/object.c @@ -892,6 +892,7 @@ static void object_set_link_property(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { Object **child = opaque; + Object *old_target; bool ambiguous = false; const char *type; char *path; @@ -901,10 +902,8 @@ static void object_set_link_property(Object *obj, Visitor *v, void *opaque, visit_type_str(v, &path, name, errp); - if (*child) { - object_unref(*child); - *child = NULL; - } + old_target = *child; + *child = NULL; if (strcmp(path, "") != 0) { Object *target; @@ -930,6 +929,10 @@ static void object_set_link_property(Object *obj, Visitor *v, void *opaque, } g_free(path); + + if (old_target != NULL) { + object_unref(old_target); + } } void object_property_add_link(Object *obj, const char *name, diff --git a/spice-qemu-char.c b/spice-qemu-char.c index 7e8eaa9fd8..1e735ffd09 100644 --- a/spice-qemu-char.c +++ b/spice-qemu-char.c @@ -188,7 +188,7 @@ static void print_allowed_subtypes(void) fprintf(stderr, "\n"); } -int qemu_chr_open_spice(QemuOpts *opts, CharDriverState **_chr) +CharDriverState *qemu_chr_open_spice(QemuOpts *opts) { CharDriverState *chr; SpiceCharDriver *s; @@ -200,7 +200,7 @@ int qemu_chr_open_spice(QemuOpts *opts, CharDriverState **_chr) if (name == NULL) { fprintf(stderr, "spice-qemu-char: missing name parameter\n"); print_allowed_subtypes(); - return -EINVAL; + return NULL; } for(;*psubtype != NULL; ++psubtype) { if (strcmp(name, *psubtype) == 0) { @@ -211,7 +211,7 @@ int qemu_chr_open_spice(QemuOpts *opts, CharDriverState **_chr) if (subtype == NULL) { fprintf(stderr, "spice-qemu-char: unsupported name\n"); print_allowed_subtypes(); - return -EINVAL; + return NULL; } chr = g_malloc0(sizeof(CharDriverState)); @@ -233,6 +233,5 @@ int qemu_chr_open_spice(QemuOpts *opts, CharDriverState **_chr) } #endif - *_chr = chr; - return 0; + return chr; } diff --git a/sysconfigs/target/target-x86_64.conf b/sysconfigs/target/target-x86_64.conf index 43ad282b5c..d0503804c2 100644 --- a/sysconfigs/target/target-x86_64.conf +++ b/sysconfigs/target/target-x86_64.conf @@ -7,9 +7,9 @@ family = "6" model = "2" stepping = "3" - feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36" - feature_ecx = "sse3 ssse3" - extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx" + feature_edx = "sse2 sse fxsr mmx clflush pse36 pat cmov mca pge mtrr sep apic cx8 mce pae msr tsc pse de fpu" + feature_ecx = "ssse3 sse3" + extfeature_edx = "i64 xd syscall" extfeature_ecx = "lahf_lm" xlevel = "0x8000000A" model_id = "Intel Celeron_4x0 (Conroe/Merom Class Core 2)" @@ -21,9 +21,9 @@ family = "6" model = "2" stepping = "3" - feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36" - feature_ecx = "sse3 cx16 ssse3 sse4.1" - extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx" + feature_edx = "sse2 sse fxsr mmx clflush pse36 pat cmov mca pge mtrr sep apic cx8 mce pae msr tsc pse de fpu" + feature_ecx = "sse4.1 cx16 ssse3 sse3" + extfeature_edx = "i64 xd syscall" extfeature_ecx = "lahf_lm" xlevel = "0x8000000A" model_id = "Intel Core 2 Duo P9xxx (Penryn Class Core 2)" @@ -35,24 +35,38 @@ family = "6" model = "2" stepping = "3" - feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36" - feature_ecx = "sse3 cx16 ssse3 sse4.1 sse4.2 popcnt" - extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx" + feature_edx = "sse2 sse fxsr mmx clflush pse36 pat cmov mca pge mtrr sep apic cx8 mce pae msr tsc pse de fpu" + feature_ecx = "popcnt sse4.2 sse4.1 cx16 ssse3 sse3" + extfeature_edx = "i64 syscall xd" extfeature_ecx = "lahf_lm" xlevel = "0x8000000A" model_id = "Intel Core i7 9xx (Nehalem Class Core i7)" [cpudef] + name = "Westmere" + level = "11" + vendor = "GenuineIntel" + family = "6" + model = "44" + stepping = "1" + feature_edx = "sse2 sse fxsr mmx clflush pse36 pat cmov mca pge mtrr sep apic cx8 mce pae msr tsc pse de fpu" + feature_ecx = "aes popcnt sse4.2 sse4.1 cx16 ssse3 sse3" + extfeature_edx = "i64 syscall xd" + extfeature_ecx = "lahf_lm" + xlevel = "0x8000000A" + model_id = "Westmere E56xx/L56xx/X56xx (Nehalem-C)" + +[cpudef] name = "Opteron_G1" level = "5" vendor = "AuthenticAMD" family = "15" model = "6" stepping = "1" - feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36" + feature_edx = "sse2 sse fxsr mmx clflush pse36 pat cmov mca pge mtrr sep apic cx8 mce pae msr tsc pse de fpu" feature_ecx = "sse3" - extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx" -# extfeature_ecx = "" + extfeature_edx = "lm fxsr mmx nx pse36 pat cmov mca pge mtrr syscall apic cx8 mce pae msr tsc pse de fpu" + extfeature_ecx = " " xlevel = "0x80000008" model_id = "AMD Opteron 240 (Gen 1 Class Opteron)" @@ -63,9 +77,9 @@ family = "15" model = "6" stepping = "1" - feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36" - feature_ecx = "sse3 cx16" - extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx rdtscp" + feature_edx = "sse2 sse fxsr mmx clflush pse36 pat cmov mca pge mtrr sep apic cx8 mce pae msr tsc pse de fpu" + feature_ecx = "cx16 sse3" + extfeature_edx = "lm rdtscp fxsr mmx nx pse36 pat cmov mca pge mtrr syscall apic cx8 mce pae msr tsc pse de fpu" extfeature_ecx = "svm lahf_lm" xlevel = "0x80000008" model_id = "AMD Opteron 22xx (Gen 2 Class Opteron)" @@ -77,10 +91,10 @@ family = "15" model = "6" stepping = "1" - feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36" - feature_ecx = "sse3 cx16 monitor popcnt" - extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx rdtscp" - extfeature_ecx = "svm sse4a abm misalignsse lahf_lm" + feature_edx = "sse2 sse fxsr mmx clflush pse36 pat cmov mca pge mtrr sep apic cx8 mce pae msr tsc pse de fpu" + feature_ecx = "popcnt cx16 monitor sse3" + extfeature_edx = "lm rdtscp fxsr mmx nx pse36 pat cmov mca pge mtrr syscall apic cx8 mce pae msr tsc pse de fpu" + extfeature_ecx = "misalignsse sse4a abm svm lahf_lm" xlevel = "0x80000008" model_id = "AMD Opteron 23xx (Gen 3 Class Opteron)" diff --git a/target-i386/cpuid.c b/target-i386/cpuid.c index b9bfeaff70..c2edb646fe 100644 --- a/target-i386/cpuid.c +++ b/target-i386/cpuid.c @@ -44,7 +44,7 @@ static const char *feature_name[] = { "ht" /* Intel htt */, "tm", "ia64", "pbe", }; static const char *ext_feature_name[] = { - "pni|sse3" /* Intel,AMD sse3 */, "pclmuldq", "dtes64", "monitor", + "pni|sse3" /* Intel,AMD sse3 */, "pclmulqdq|pclmuldq", "dtes64", "monitor", "ds_cpl", "vmx", "smx", "est", "tm2", "ssse3", "cid", NULL, "fma", "cx16", "xtpr", "pdcm", @@ -59,9 +59,9 @@ static const char *ext2_feature_name[] = { "cx8" /* AMD CMPXCHG8B */, "apic", NULL, "syscall", "mtrr", "pge", "mca", "cmov", "pat", "pse36", NULL, NULL /* Linux mp */, - "nx" /* Intel xd */, NULL, "mmxext", "mmx", - "fxsr", "fxsr_opt" /* AMD ffxsr */, "pdpe1gb" /* AMD Page1GB */, "rdtscp", - NULL, "lm" /* Intel 64 */, "3dnowext", "3dnow", + "nx|xd", NULL, "mmxext", "mmx", + "fxsr", "fxsr_opt|ffxsr", "pdpe1gb" /* AMD Page1GB */, "rdtscp", + NULL, "lm|i64", "3dnowext", "3dnow", }; static const char *ext3_feature_name[] = { "lahf_lm" /* AMD LahfSahf */, "cmp_legacy", "svm", "extapic" /* AMD ExtApicSpace */, @@ -597,6 +597,46 @@ static int check_features_against_host(x86_def_t *guest_def) return rv; } +static void x86_cpuid_version_set_family(CPUX86State *env, int family) +{ + env->cpuid_version &= ~0xff00f00; + if (family > 0x0f) { + env->cpuid_version |= 0xf00 | ((family - 0x0f) << 20); + } else { + env->cpuid_version |= family << 8; + } +} + +static void x86_cpuid_version_set_model(CPUX86State *env, int model) +{ + env->cpuid_version &= ~0xf00f0; + env->cpuid_version |= ((model & 0xf) << 4) | ((model >> 4) << 16); +} + +static void x86_cpuid_version_set_stepping(CPUX86State *env, int stepping) +{ + env->cpuid_version &= ~0xf; + env->cpuid_version |= stepping & 0xf; +} + +static void x86_cpuid_set_model_id(CPUX86State *env, const char *model_id) +{ + int c, len, i; + + if (model_id == NULL) { + model_id = ""; + } + len = strlen(model_id); + for (i = 0; i < 48; i++) { + if (i >= len) { + c = '\0'; + } else { + c = (uint8_t)model_id[i]; + } + env->cpuid_model[i >> 2] |= c << (8 * (i & 3)); + } +} + static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model) { unsigned int i; @@ -883,12 +923,9 @@ int cpu_x86_register (CPUX86State *env, const char *cpu_model) } env->cpuid_vendor_override = def->vendor_override; env->cpuid_level = def->level; - if (def->family > 0x0f) - env->cpuid_version = 0xf00 | ((def->family - 0x0f) << 20); - else - env->cpuid_version = def->family << 8; - env->cpuid_version |= ((def->model & 0xf) << 4) | ((def->model >> 4) << 16); - env->cpuid_version |= def->stepping; + x86_cpuid_version_set_family(env, def->family); + x86_cpuid_version_set_model(env, def->model); + x86_cpuid_version_set_stepping(env, def->stepping); env->cpuid_features = def->features; env->cpuid_ext_features = def->ext_features; env->cpuid_ext2_features = def->ext2_features; @@ -910,20 +947,7 @@ int cpu_x86_register (CPUX86State *env, const char *cpu_model) env->cpuid_ext3_features &= TCG_EXT3_FEATURES; env->cpuid_svm_features &= TCG_SVM_FEATURES; } - { - const char *model_id = def->model_id; - int c, len, i; - if (!model_id) - model_id = ""; - len = strlen(model_id); - for(i = 0; i < 48; i++) { - if (i >= len) - c = '\0'; - else - c = (uint8_t)model_id[i]; - env->cpuid_model[i >> 2] |= c << (8 * (i & 3)); - } - } + x86_cpuid_set_model_id(env, def->model_id); return 0; } diff --git a/ui/qemu-spice.h b/ui/qemu-spice.h index 680206a585..3299da87d6 100644 --- a/ui/qemu-spice.h +++ b/ui/qemu-spice.h @@ -45,7 +45,7 @@ int qemu_spice_migrate_info(const char *hostname, int port, int tls_port, void do_info_spice_print(Monitor *mon, const QObject *data); void do_info_spice(Monitor *mon, QObject **ret_data); -int qemu_chr_open_spice(QemuOpts *opts, CharDriverState **_chr); +CharDriverState *qemu_chr_open_spice(QemuOpts *opts); #else /* CONFIG_SPICE */ #include "monitor.h" @@ -1857,8 +1857,10 @@ struct device_config { DEV_PARALLEL, /* -parallel */ DEV_VIRTCON, /* -virtioconsole */ DEV_DEBUGCON, /* -debugcon */ + DEV_GDB, /* -gdb, -s */ } type; const char *cmdline; + Location loc; QTAILQ_ENTRY(device_config) next; }; QTAILQ_HEAD(, device_config) device_configs = QTAILQ_HEAD_INITIALIZER(device_configs); @@ -1870,6 +1872,7 @@ static void add_device_config(int type, const char *cmdline) conf = g_malloc0(sizeof(*conf)); conf->type = type; conf->cmdline = cmdline; + loc_save(&conf->loc); QTAILQ_INSERT_TAIL(&device_configs, conf, next); } @@ -1881,7 +1884,9 @@ static int foreach_device_config(int type, int (*func)(const char *cmdline)) QTAILQ_FOREACH(conf, &device_configs, next) { if (conf->type != type) continue; + loc_push_restore(&conf->loc); rc = func(conf->cmdline); + loc_pop(&conf->loc); if (0 != rc) return rc; } @@ -2178,7 +2183,6 @@ int qemu_init_main_loop(void) int main(int argc, char **argv, char **envp) { - const char *gdbstub_dev = NULL; int i; int snapshot, linux_boot; const char *icount_option = NULL; @@ -2603,10 +2607,10 @@ int main(int argc, char **argv, char **envp) log_file = optarg; break; case QEMU_OPTION_s: - gdbstub_dev = "tcp::" DEFAULT_GDBSTUB_PORT; + add_device_config(DEV_GDB, "tcp::" DEFAULT_GDBSTUB_PORT); break; case QEMU_OPTION_gdb: - gdbstub_dev = optarg; + add_device_config(DEV_GDB, optarg); break; case QEMU_OPTION_L: data_dir = optarg; @@ -3496,9 +3500,7 @@ int main(int argc, char **argv, char **envp) } text_consoles_set_display(ds); - if (gdbstub_dev && gdbserver_start(gdbstub_dev) < 0) { - fprintf(stderr, "qemu: could not open gdbserver on device '%s'\n", - gdbstub_dev); + if (foreach_device_config(DEV_GDB, gdbserver_start) < 0) { exit(1); } |