diff options
Diffstat (limited to 'hw')
37 files changed, 959 insertions, 204 deletions
diff --git a/hw/arm/virt.c b/hw/arm/virt.c index bd206a019a..d6fffc75bd 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -541,7 +541,7 @@ static QEMUMachine machvirt_a15_machine = { .name = "virt", .desc = "ARM Virtual Machine", .init = machvirt_init, - .max_cpus = 4, + .max_cpus = 8, }; static void machvirt_machine_init(void) diff --git a/hw/block/block.c b/hw/block/block.c index 33dd3f33b6..b6a6dc6baa 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -22,8 +22,9 @@ void blkconf_serial(BlockConf *conf, char **serial) } } -int blkconf_geometry(BlockConf *conf, int *ptrans, - unsigned cyls_max, unsigned heads_max, unsigned secs_max) +void blkconf_geometry(BlockConf *conf, int *ptrans, + unsigned cyls_max, unsigned heads_max, unsigned secs_max, + Error **errp) { DriveInfo *dinfo; @@ -46,17 +47,16 @@ int blkconf_geometry(BlockConf *conf, int *ptrans, } if (conf->cyls || conf->heads || conf->secs) { if (conf->cyls < 1 || conf->cyls > cyls_max) { - error_report("cyls must be between 1 and %u", cyls_max); - return -1; + error_setg(errp, "cyls must be between 1 and %u", cyls_max); + return; } if (conf->heads < 1 || conf->heads > heads_max) { - error_report("heads must be between 1 and %u", heads_max); - return -1; + error_setg(errp, "heads must be between 1 and %u", heads_max); + return; } if (conf->secs < 1 || conf->secs > secs_max) { - error_report("secs must be between 1 and %u", secs_max); - return -1; + error_setg(errp, "secs must be between 1 and %u", secs_max); + return; } } - return 0; } diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index 24a6b71395..b55188cb82 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -193,6 +193,8 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk, error_setg(&s->blocker, "block device is in use by data plane"); bdrv_op_block_all(blk->conf.bs, s->blocker); + bdrv_op_unblock(blk->conf.bs, BLOCK_OP_TYPE_RESIZE, s->blocker); + bdrv_op_unblock(blk->conf.bs, BLOCK_OP_TYPE_DRIVE_DEL, s->blocker); *dataplane = s; } diff --git a/hw/block/nvme.c b/hw/block/nvme.c index 6d9a0651d8..04459e583c 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -319,7 +319,7 @@ static void nvme_init_sq(NvmeSQueue *sq, NvmeCtrl *n, uint64_t dma_addr, sq->size = size; sq->cqid = cqid; sq->head = sq->tail = 0; - sq->io_req = g_malloc(sq->size * sizeof(*sq->io_req)); + sq->io_req = g_new(NvmeRequest, sq->size); QTAILQ_INIT(&sq->req_list); QTAILQ_INIT(&sq->out_req_list); @@ -773,9 +773,9 @@ static int nvme_init(PCIDevice *pci_dev) n->reg_size = 1 << qemu_fls(0x1004 + 2 * (n->num_queues + 1) * 4); n->ns_size = bs_size / (uint64_t)n->num_namespaces; - n->namespaces = g_malloc0(sizeof(*n->namespaces)*n->num_namespaces); - n->sq = g_malloc0(sizeof(*n->sq)*n->num_queues); - n->cq = g_malloc0(sizeof(*n->cq)*n->num_queues); + n->namespaces = g_new0(NvmeNamespace, n->num_namespaces); + n->sq = g_new0(NvmeSQueue *, n->num_queues); + n->cq = g_new0(NvmeCQueue *, n->num_queues); memory_region_init_io(&n->iomem, OBJECT(n), &nvme_mmio_ops, n, "nvme", n->reg_size); diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 302c39e2be..a7f28275f4 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -469,8 +469,9 @@ static void virtio_blk_dma_restart_bh(void *opaque) s->rq = NULL; while (req) { + VirtIOBlockReq *next = req->next; virtio_blk_handle_request(req, &mrb); - req = req->next; + req = next; } virtio_submit_multiwrite(s->bs, &mrb); @@ -728,9 +729,7 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOBlock *s = VIRTIO_BLK(dev); VirtIOBlkConf *blk = &(s->blk); -#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE Error *err = NULL; -#endif static int virtio_blk_id; if (!blk->conf.bs) { @@ -744,8 +743,9 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) blkconf_serial(&blk->conf, &blk->serial); s->original_wce = bdrv_enable_write_cache(blk->conf.bs); - if (blkconf_geometry(&blk->conf, NULL, 65535, 255, 255) < 0) { - error_setg(errp, "Error setting geometry"); + blkconf_geometry(&blk->conf, NULL, 65535, 255, 255, &err); + if (err) { + error_propagate(errp, err); return; } diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs index 5377d052e9..17845df3f0 100644 --- a/hw/core/Makefile.objs +++ b/hw/core/Makefile.objs @@ -4,6 +4,7 @@ common-obj-y += fw-path-provider.o # irq.o needed for qdev GPIO handling: common-obj-y += irq.o common-obj-y += hotplug.o +common-obj-y += nmi.o common-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o common-obj-$(CONFIG_XILINX_AXI) += stream.o diff --git a/hw/core/nmi.c b/hw/core/nmi.c new file mode 100644 index 0000000000..3dff020659 --- /dev/null +++ b/hw/core/nmi.c @@ -0,0 +1,84 @@ +/* + * NMI monitor handler class and helpers. + * + * Copyright IBM Corp., 2014 + * + * Author: Alexey Kardashevskiy <aik@ozlabs.ru> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "hw/nmi.h" +#include "qapi/qmp/qerror.h" + +struct do_nmi_s { + int cpu_index; + Error *errp; + bool handled; +}; + +static void nmi_children(Object *o, struct do_nmi_s *ns); + +static int do_nmi(Object *o, void *opaque) +{ + struct do_nmi_s *ns = opaque; + NMIState *n = (NMIState *) object_dynamic_cast(o, TYPE_NMI); + + if (n) { + NMIClass *nc = NMI_GET_CLASS(n); + + ns->handled = true; + nc->nmi_monitor_handler(n, ns->cpu_index, &ns->errp); + if (ns->errp) { + return -1; + } + } + nmi_children(o, ns); + + return 0; +} + +static void nmi_children(Object *o, struct do_nmi_s *ns) +{ + object_child_foreach(o, do_nmi, ns); +} + +void nmi_monitor_handle(int cpu_index, Error **errp) +{ + struct do_nmi_s ns = { + .cpu_index = cpu_index, + .errp = NULL, + .handled = false + }; + + nmi_children(object_get_root(), &ns); + if (ns.handled) { + error_propagate(errp, ns.errp); + } else { + error_set(errp, QERR_UNSUPPORTED); + } +} + +static const TypeInfo nmi_info = { + .name = TYPE_NMI, + .parent = TYPE_INTERFACE, + .class_size = sizeof(NMIClass), +}; + +static void nmi_register_types(void) +{ + type_register_static(&nmi_info); +} + +type_init(nmi_register_types) diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index cc2c2b1dbc..bcc5c3701a 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -138,7 +138,9 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) if (qemu_spice_rect_is_empty(qxl->dirty+i)) { break; } - if (qxl->dirty[i].left > qxl->dirty[i].right || + if (qxl->dirty[i].left < 0 || + qxl->dirty[i].top < 0 || + qxl->dirty[i].left > qxl->dirty[i].right || qxl->dirty[i].top > qxl->dirty[i].bottom || qxl->dirty[i].right > qxl->guest_primary.surface.width || qxl->dirty[i].bottom > qxl->guest_primary.surface.height) { diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c index ee60d3ff39..d06002dde8 100644 --- a/hw/dma/xilinx_axidma.c +++ b/hw/dma/xilinx_axidma.c @@ -553,10 +553,12 @@ static void xilinx_axidma_realize(DeviceState *dev, Error **errp) int i; for (i = 0; i < 2; i++) { - s->streams[i].nr = i; - s->streams[i].bh = qemu_bh_new(timer_hit, &s->streams[i]); - s->streams[i].ptimer = ptimer_init(s->streams[i].bh); - ptimer_set_freq(s->streams[i].ptimer, s->freqhz); + struct Stream *st = &s->streams[i]; + + st->nr = i; + st->bh = qemu_bh_new(timer_hit, st); + st->ptimer = ptimer_init(st->bh); + ptimer_set_freq(st->ptimer, s->freqhz); } return; diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 932b0d508c..0ee713b0ff 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1203,7 +1203,7 @@ void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports) s->as = as; s->ports = ports; - s->dev = g_malloc0(sizeof(AHCIDevice) * ports); + s->dev = g_new0(AHCIDevice, ports); ahci_reg_init(s); /* XXX BAR size should be 1k, but that breaks, so bump it to 4k for now */ memory_region_init_io(&s->mem, OBJECT(qdev), &ahci_mem_ops, s, diff --git a/hw/ide/microdrive.c b/hw/ide/microdrive.c index 2d70ddb757..15671b8c4d 100644 --- a/hw/ide/microdrive.c +++ b/hw/ide/microdrive.c @@ -567,7 +567,7 @@ PCMCIACardState *dscm1xxxx_init(DriveInfo *dinfo) } md->bus.ifs[0].drive_kind = IDE_CFATA; md->bus.ifs[0].mdata_size = METADATA_SIZE; - md->bus.ifs[0].mdata_storage = (uint8_t *) g_malloc0(METADATA_SIZE); + md->bus.ifs[0].mdata_storage = g_malloc0(METADATA_SIZE); return PCMCIA_CARD(md); } diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index 6e475e6970..efab95b320 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -59,7 +59,7 @@ static char *idebus_get_fw_dev_path(DeviceState *dev) { char path[30]; - snprintf(path, sizeof(path), "%s@%d", qdev_fw_name(dev), + snprintf(path, sizeof(path), "%s@%x", qdev_fw_name(dev), ((IDEBus*)dev->parent_bus)->bus_id); return g_strdup(path); @@ -151,6 +151,7 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind) { IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus); IDEState *s = bus->ifs + dev->unit; + Error *err = NULL; if (dev->conf.discard_granularity == -1) { dev->conf.discard_granularity = 512; @@ -161,9 +162,13 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind) } blkconf_serial(&dev->conf, &dev->serial); - if (kind != IDE_CD - && blkconf_geometry(&dev->conf, &dev->chs_trans, 65536, 16, 255) < 0) { - return -1; + if (kind != IDE_CD) { + blkconf_geometry(&dev->conf, &dev->chs_trans, 65536, 16, 255, &err); + if (err) { + error_report("%s", error_get_pretty(err)); + error_free(err); + return -1; + } } if (ide_init_drive(s, dev->conf.bs, kind, diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 1532ef9482..db9110c1c4 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -66,7 +66,8 @@ void gic_update(GICState *s) best_prio = 0x100; best_irq = 1023; for (irq = 0; irq < s->num_irq; irq++) { - if (GIC_TEST_ENABLED(irq, cm) && gic_test_pending(s, irq, cm)) { + if (GIC_TEST_ENABLED(irq, cm) && gic_test_pending(s, irq, cm) && + (irq < GIC_INTERNAL || GIC_TARGET(irq) & cm)) { if (GIC_GET_PRIORITY(irq, cpu) < best_prio) { best_prio = GIC_GET_PRIORITY(irq, cpu); best_irq = irq; @@ -372,7 +373,7 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset) } } else if (offset < 0xf00) { /* Interrupt Configuration. */ - irq = (offset - 0xc00) * 2 + GIC_BASE_IRQ; + irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ; if (irq >= s->num_irq) goto bad_reg; res = 0; @@ -558,13 +559,15 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ; if (irq >= s->num_irq) goto bad_reg; - if (irq < GIC_INTERNAL) + if (irq < GIC_NR_SGIS) value |= 0xaa; for (i = 0; i < 4; i++) { - if (value & (1 << (i * 2))) { - GIC_SET_MODEL(irq + i); - } else { - GIC_CLEAR_MODEL(irq + i); + if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) { + if (value & (1 << (i * 2))) { + GIC_SET_MODEL(irq + i); + } else { + GIC_CLEAR_MODEL(irq + i); + } } if (value & (2 << (i * 2))) { GIC_SET_EDGE_TRIGGER(irq + i); diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index 6d884eca3b..18b01ba0c7 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -128,7 +128,7 @@ static void arm_gic_common_reset(DeviceState *dev) s->running_priority[i] = 0x100; s->cpu_enabled[i] = false; } - for (i = 0; i < 16; i++) { + for (i = 0; i < GIC_NR_SGIS; i++) { GIC_SET_ENABLED(i, ALL_CPU_MASK); GIC_SET_EDGE_TRIGGER(i); } diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index aea9c5b49f..6843abf547 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -89,7 +89,6 @@ petalogix_ml605_init(MachineState *machine) SysBusDevice *busdev; DriveInfo *dinfo; int i; - hwaddr ddr_base = MEMORY_BASEADDR; MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1); MemoryRegion *phys_ram = g_new(MemoryRegion, 1); qemu_irq irq[32]; @@ -106,7 +105,7 @@ petalogix_ml605_init(MachineState *machine) memory_region_init_ram(phys_ram, NULL, "petalogix_ml605.ram", ram_size); vmstate_register_ram_global(phys_ram); - memory_region_add_subregion(address_space_mem, ddr_base, phys_ram); + memory_region_add_subregion(address_space_mem, MEMORY_BASEADDR, phys_ram); dinfo = drive_get(IF_PFLASH, 0, 0); /* 5th parameter 2 means bank-width @@ -201,7 +200,7 @@ petalogix_ml605_init(MachineState *machine) } } - microblaze_load_kernel(cpu, ddr_base, ram_size, + microblaze_load_kernel(cpu, MEMORY_BASEADDR, ram_size, machine->initrd_filename, BINARY_DEVICE_TREE_FILE, machine_cpu_reset); diff --git a/hw/misc/vfio.c b/hw/misc/vfio.c index 0617b70ea6..40dcaa6558 100644 --- a/hw/misc/vfio.c +++ b/hw/misc/vfio.c @@ -2194,9 +2194,13 @@ static void vfio_probe_nvidia_bar0_88000_quirk(VFIODevice *vdev, int nr) { PCIDevice *pdev = &vdev->pdev; VFIOQuirk *quirk; + uint16_t vendor, class; - if (!vdev->has_vga || nr != 0 || - pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_NVIDIA) { + vendor = pci_get_word(pdev->config + PCI_VENDOR_ID); + class = pci_get_word(pdev->config + PCI_CLASS_DEVICE); + + if (nr != 0 || vendor != PCI_VENDOR_ID_NVIDIA || + class != PCI_CLASS_DISPLAY_VGA) { return; } diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 791321fa49..f246fa1c45 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -34,6 +34,7 @@ #define PCI_DEVICE_ID_VMWARE_VMXNET3_REVISION 0x1 #define VMXNET3_MSIX_BAR_SIZE 0x2000 +#define MIN_BUF_SIZE 60 #define VMXNET3_BAR0_IDX (0) #define VMXNET3_BAR1_IDX (1) @@ -1871,12 +1872,21 @@ vmxnet3_receive(NetClientState *nc, const uint8_t *buf, size_t size) { VMXNET3State *s = qemu_get_nic_opaque(nc); size_t bytes_indicated; + uint8_t min_buf[MIN_BUF_SIZE]; if (!vmxnet3_can_receive(nc)) { VMW_PKPRN("Cannot receive now"); return -1; } + /* Pad to minimum Ethernet frame length */ + if (size < sizeof(min_buf)) { + memcpy(min_buf, buf, size); + memset(&min_buf[size], 0, sizeof(min_buf) - size); + buf = min_buf; + size = sizeof(min_buf); + } + if (s->peer_has_vhdr) { vmxnet_rx_pkt_set_vhdr(s->rx_pkt, (struct virtio_net_hdr *)buf); buf += sizeof(struct virtio_net_hdr); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index d01978f3dc..5cb452f234 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -55,6 +55,7 @@ #include "qemu/config-file.h" #include "qemu/error-report.h" #include "trace.h" +#include "hw/nmi.h" #include <libfdt.h> @@ -1576,10 +1577,28 @@ static void spapr_machine_initfn(Object *obj) spapr_get_kvm_type, spapr_set_kvm_type, NULL); } +static void ppc_cpu_do_nmi_on_cpu(void *arg) +{ + CPUState *cs = arg; + + cpu_synchronize_state(cs); + ppc_cpu_do_system_reset(cs); +} + +static void spapr_nmi(NMIState *n, int cpu_index, Error **errp) +{ + CPUState *cs; + + CPU_FOREACH(cs) { + async_run_on_cpu(cs, ppc_cpu_do_nmi_on_cpu, cs); + } +} + static void spapr_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc); + NMIClass *nc = NMI_CLASS(oc); mc->name = "pseries"; mc->desc = "pSeries Logical Partition (PAPR compliant)"; @@ -1593,6 +1612,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) mc->kvm_type = spapr_kvm_type; fwc->get_dev_path = spapr_get_fw_dev_path; + nc->nmi_monitor_handler = spapr_nmi; } static const TypeInfo spapr_machine_info = { @@ -1603,6 +1623,7 @@ static const TypeInfo spapr_machine_info = { .class_init = spapr_machine_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_FW_PATH_PROVIDER }, + { TYPE_NMI }, { } }, }; diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 42f5cec4c1..e538b1f686 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -17,6 +17,9 @@ #include "ioinst.h" #include "css.h" #include "virtio-ccw.h" +#include "qemu/config-file.h" + +#define TYPE_S390_CCW_MACHINE "s390-ccw-machine" void io_subsystem_reset(void) { @@ -84,17 +87,35 @@ static void ccw_init(MachineState *machine) ram_addr_t my_ram_size = machine->ram_size; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); - int shift = 0; + sclpMemoryHotplugDev *mhd = init_sclp_memory_hotplug_dev(); uint8_t *storage_keys; int ret; VirtualCssBus *css_bus; - - /* s390x ram size detection needs a 16bit multiplier + an increment. So - guests > 64GB can be specified in 2MB steps etc. */ - while ((my_ram_size >> (20 + shift)) > 65535) { - shift++; + QemuOpts *opts = qemu_opts_find(qemu_find_opts("memory"), NULL); + ram_addr_t pad_size = 0; + ram_addr_t maxmem = qemu_opt_get_size(opts, "maxmem", my_ram_size); + ram_addr_t standby_mem_size = maxmem - my_ram_size; + + /* The storage increment size is a multiple of 1M and is a power of 2. + * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer. + * The variable 'mhd->increment_size' is an exponent of 2 that can be + * used to calculate the size (in bytes) of an increment. */ + mhd->increment_size = 20; + while ((my_ram_size >> mhd->increment_size) > MAX_STORAGE_INCREMENTS) { + mhd->increment_size++; + } + while ((standby_mem_size >> mhd->increment_size) > MAX_STORAGE_INCREMENTS) { + mhd->increment_size++; } - my_ram_size = my_ram_size >> (20 + shift) << (20 + shift); + + /* The core and standby memory areas need to be aligned with + * the increment size. In effect, this can cause the + * user-specified memory size to be rounded down to align + * with the nearest increment boundary. */ + standby_mem_size = standby_mem_size >> mhd->increment_size + << mhd->increment_size; + my_ram_size = my_ram_size >> mhd->increment_size + << mhd->increment_size; /* let's propagate the changed ram size into the global variable. */ ram_size = my_ram_size; @@ -109,11 +130,22 @@ static void ccw_init(MachineState *machine) /* register hypercalls */ virtio_ccw_register_hcalls(); - /* allocate RAM */ + /* allocate RAM for core */ memory_region_init_ram(ram, NULL, "s390.ram", my_ram_size); vmstate_register_ram_global(ram); memory_region_add_subregion(sysmem, 0, ram); + /* If the size of ram is not on a MEM_SECTION_SIZE boundary, + calculate the pad size necessary to force this boundary. */ + if (standby_mem_size) { + if (my_ram_size % MEM_SECTION_SIZE) { + pad_size = MEM_SECTION_SIZE - my_ram_size % MEM_SECTION_SIZE; + } + my_ram_size += standby_mem_size + pad_size; + mhd->pad_size = pad_size; + mhd->standby_mem_size = standby_mem_size; + } + /* allocate storage keys */ storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE); @@ -134,24 +166,39 @@ static void ccw_init(MachineState *machine) s390_create_virtio_net(BUS(css_bus), "virtio-net-ccw"); } -static QEMUMachine ccw_machine = { - .name = "s390-ccw-virtio", - .alias = "s390-ccw", - .desc = "VirtIO-ccw based S390 machine", - .init = ccw_init, - .block_default_type = IF_VIRTIO, - .no_cdrom = 1, - .no_floppy = 1, - .no_serial = 1, - .no_parallel = 1, - .no_sdcard = 1, - .use_sclp = 1, - .max_cpus = 255, +static void ccw_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + NMIClass *nc = NMI_CLASS(oc); + + mc->name = "s390-ccw-virtio"; + mc->alias = "s390-ccw"; + mc->desc = "VirtIO-ccw based S390 machine"; + mc->init = ccw_init; + mc->block_default_type = IF_VIRTIO; + mc->no_cdrom = 1; + mc->no_floppy = 1; + mc->no_serial = 1; + mc->no_parallel = 1; + mc->no_sdcard = 1; + mc->use_sclp = 1, + mc->max_cpus = 255; + nc->nmi_monitor_handler = s390_nmi; +} + +static const TypeInfo ccw_machine_info = { + .name = TYPE_S390_CCW_MACHINE, + .parent = TYPE_MACHINE, + .class_init = ccw_machine_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_NMI }, + { } + }, }; -static void ccw_machine_init(void) +static void ccw_machine_register_types(void) { - qemu_register_machine(&ccw_machine); + type_register_static(&ccw_machine_info); } -machine_init(ccw_machine_init) +type_init(ccw_machine_register_types) diff --git a/hw/s390x/s390-virtio.c b/hw/s390x/s390-virtio.c index 93c7acea72..4ca52b7190 100644 --- a/hw/s390x/s390-virtio.c +++ b/hw/s390x/s390-virtio.c @@ -51,6 +51,7 @@ #define MAX_BLK_DEVS 10 #define ZIPL_FILENAME "s390-zipl.rom" +#define TYPE_S390_MACHINE "s390-machine" static VirtIOS390Bus *s390_bus; static S390CPU **ipi_states; @@ -229,18 +230,21 @@ static void s390_init(MachineState *machine) ram_addr_t my_ram_size = machine->ram_size; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); - int shift = 0; + int increment_size = 20; uint8_t *storage_keys; void *virtio_region; hwaddr virtio_region_len; hwaddr virtio_region_start; - /* s390x ram size detection needs a 16bit multiplier + an increment. So - guests > 64GB can be specified in 2MB steps etc. */ - while ((my_ram_size >> (20 + shift)) > 65535) { - shift++; + /* + * The storage increment size is a multiple of 1M and is a power of 2. + * The number of storage increments must be MAX_STORAGE_INCREMENTS or + * fewer. + */ + while ((my_ram_size >> increment_size) > MAX_STORAGE_INCREMENTS) { + increment_size++; } - my_ram_size = my_ram_size >> (20 + shift) << (20 + shift); + my_ram_size = my_ram_size >> increment_size << increment_size; /* let's propagate the changed ram size into the global variable. */ ram_size = my_ram_size; @@ -279,25 +283,49 @@ static void s390_init(MachineState *machine) s390_create_virtio_net((BusState *)s390_bus, "virtio-net-s390"); } -static QEMUMachine s390_machine = { - .name = "s390-virtio", - .alias = "s390", - .desc = "VirtIO based S390 machine", - .init = s390_init, - .block_default_type = IF_VIRTIO, - .no_cdrom = 1, - .no_floppy = 1, - .no_serial = 1, - .no_parallel = 1, - .no_sdcard = 1, - .use_virtcon = 1, - .max_cpus = 255, - .is_default = 1, +void s390_nmi(NMIState *n, int cpu_index, Error **errp) +{ + CPUState *cs = qemu_get_cpu(cpu_index); + + if (s390_cpu_restart(S390_CPU(cs))) { + error_set(errp, QERR_UNSUPPORTED); + } +} + +static void s390_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + NMIClass *nc = NMI_CLASS(oc); + + mc->name = "s390-virtio"; + mc->alias = "s390"; + mc->desc = "VirtIO based S390 machine"; + mc->init = s390_init; + mc->block_default_type = IF_VIRTIO; + mc->max_cpus = 255; + mc->no_serial = 1; + mc->no_parallel = 1; + mc->use_virtcon = 1; + mc->no_floppy = 1; + mc->no_cdrom = 1; + mc->no_sdcard = 1; + mc->is_default = 1; + nc->nmi_monitor_handler = s390_nmi; +} + +static const TypeInfo s390_machine_info = { + .name = TYPE_S390_MACHINE, + .parent = TYPE_MACHINE, + .class_init = s390_machine_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_NMI }, + { } + }, }; -static void s390_machine_init(void) +static void s390_machine_register_types(void) { - qemu_register_machine(&s390_machine); + type_register_static(&s390_machine_info); } -machine_init(s390_machine_init); +type_init(s390_machine_register_types) diff --git a/hw/s390x/s390-virtio.h b/hw/s390x/s390-virtio.h index 5c405e7755..33847aefcf 100644 --- a/hw/s390x/s390-virtio.h +++ b/hw/s390x/s390-virtio.h @@ -12,6 +12,8 @@ #ifndef HW_S390_VIRTIO_H #define HW_S390_VIRTIO_H 1 +#include "hw/nmi.h" + #define KVM_S390_VIRTIO_NOTIFY 0 #define KVM_S390_VIRTIO_RESET 1 #define KVM_S390_VIRTIO_SET_STATUS 2 @@ -26,4 +28,5 @@ void s390_init_ipl_dev(const char *kernel_filename, const char *initrd_filename, const char *firmware); void s390_create_virtio_net(BusState *bus, const char *name); +void s390_nmi(NMIState *n, int cpu_index, Error **errp); #endif diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index d8ddf35e58..02b3275132 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -16,7 +16,8 @@ #include "sysemu/kvm.h" #include "exec/memory.h" #include "sysemu/sysemu.h" - +#include "exec/address-spaces.h" +#include "qemu/config-file.h" #include "hw/s390x/sclp.h" #include "hw/s390x/event-facility.h" @@ -33,10 +34,19 @@ static inline SCLPEventFacility *get_event_facility(void) static void read_SCP_info(SCCB *sccb) { ReadInfo *read_info = (ReadInfo *) sccb; + sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); CPUState *cpu; - int shift = 0; int cpu_count = 0; int i = 0; + int increment_size = 20; + int rnsize, rnmax; + QemuOpts *opts = qemu_opts_find(qemu_find_opts("memory"), NULL); + int slots = qemu_opt_get_number(opts, "slots", 0); + int max_avail_slots = s390_get_memslot_count(kvm_state); + + if (slots > max_avail_slots) { + slots = max_avail_slots; + } CPU_FOREACH(cpu) { cpu_count++; @@ -54,14 +64,235 @@ static void read_SCP_info(SCCB *sccb) read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO); - while ((ram_size >> (20 + shift)) > 65535) { - shift++; + /* + * The storage increment size is a multiple of 1M and is a power of 2. + * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer. + */ + while ((ram_size >> increment_size) > MAX_STORAGE_INCREMENTS) { + increment_size++; + } + rnmax = ram_size >> increment_size; + + /* Memory Hotplug is only supported for the ccw machine type */ + if (mhd) { + while ((mhd->standby_mem_size >> increment_size) > + MAX_STORAGE_INCREMENTS) { + increment_size++; + } + assert(increment_size == mhd->increment_size); + + mhd->standby_subregion_size = MEM_SECTION_SIZE; + /* Deduct the memory slot already used for core */ + if (slots > 0) { + while ((mhd->standby_subregion_size * (slots - 1) + < mhd->standby_mem_size)) { + mhd->standby_subregion_size = mhd->standby_subregion_size << 1; + } + } + /* + * Initialize mapping of guest standby memory sections indicating which + * are and are not online. Assume all standby memory begins offline. + */ + if (mhd->standby_state_map == 0) { + if (mhd->standby_mem_size % mhd->standby_subregion_size) { + mhd->standby_state_map = g_malloc0((mhd->standby_mem_size / + mhd->standby_subregion_size + 1) * + (mhd->standby_subregion_size / + MEM_SECTION_SIZE)); + } else { + mhd->standby_state_map = g_malloc0(mhd->standby_mem_size / + MEM_SECTION_SIZE); + } + } + mhd->padded_ram_size = ram_size + mhd->pad_size; + mhd->rzm = 1 << mhd->increment_size; + rnmax = ((ram_size + mhd->standby_mem_size + mhd->pad_size) + >> mhd->increment_size); + + read_info->facilities |= cpu_to_be64(SCLP_FC_ASSIGN_ATTACH_READ_STOR); + } + + rnsize = 1 << (increment_size - 20); + if (rnsize <= 128) { + read_info->rnsize = rnsize; + } else { + read_info->rnsize = 0; + read_info->rnsize2 = cpu_to_be32(rnsize); + } + + if (rnmax < 0x10000) { + read_info->rnmax = cpu_to_be16(rnmax); + } else { + read_info->rnmax = cpu_to_be16(0); + read_info->rnmax2 = cpu_to_be64(rnmax); } - read_info->rnmax = cpu_to_be16(ram_size >> (20 + shift)); - read_info->rnsize = 1 << shift; + sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION); } +static void read_storage_element0_info(SCCB *sccb) +{ + int i, assigned; + int subincrement_id = SCLP_STARTING_SUBINCREMENT_ID; + ReadStorageElementInfo *storage_info = (ReadStorageElementInfo *) sccb; + sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); + + assert(mhd); + + if ((ram_size >> mhd->increment_size) >= 0x10000) { + sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION); + return; + } + + /* Return information regarding core memory */ + storage_info->max_id = cpu_to_be16(mhd->standby_mem_size ? 1 : 0); + assigned = ram_size >> mhd->increment_size; + storage_info->assigned = cpu_to_be16(assigned); + + for (i = 0; i < assigned; i++) { + storage_info->entries[i] = cpu_to_be32(subincrement_id); + subincrement_id += SCLP_INCREMENT_UNIT; + } + sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION); +} + +static void read_storage_element1_info(SCCB *sccb) +{ + ReadStorageElementInfo *storage_info = (ReadStorageElementInfo *) sccb; + sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); + + assert(mhd); + + if ((mhd->standby_mem_size >> mhd->increment_size) >= 0x10000) { + sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION); + return; + } + + /* Return information regarding standby memory */ + storage_info->max_id = cpu_to_be16(mhd->standby_mem_size ? 1 : 0); + storage_info->assigned = cpu_to_be16(mhd->standby_mem_size >> + mhd->increment_size); + storage_info->standby = cpu_to_be16(mhd->standby_mem_size >> + mhd->increment_size); + sccb->h.response_code = cpu_to_be16(SCLP_RC_STANDBY_READ_COMPLETION); +} + +static void attach_storage_element(SCCB *sccb, uint16_t element) +{ + int i, assigned, subincrement_id; + AttachStorageElement *attach_info = (AttachStorageElement *) sccb; + sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); + + assert(mhd); + + if (element != 1) { + sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); + return; + } + + assigned = mhd->standby_mem_size >> mhd->increment_size; + attach_info->assigned = cpu_to_be16(assigned); + subincrement_id = ((ram_size >> mhd->increment_size) << 16) + + SCLP_STARTING_SUBINCREMENT_ID; + for (i = 0; i < assigned; i++) { + attach_info->entries[i] = cpu_to_be32(subincrement_id); + subincrement_id += SCLP_INCREMENT_UNIT; + } + sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); +} + +static void assign_storage(SCCB *sccb) +{ + MemoryRegion *mr = NULL; + uint64_t this_subregion_size; + AssignStorage *assign_info = (AssignStorage *) sccb; + sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); + assert(mhd); + ram_addr_t assign_addr = (assign_info->rn - 1) * mhd->rzm; + MemoryRegion *sysmem = get_system_memory(); + + if ((assign_addr % MEM_SECTION_SIZE == 0) && + (assign_addr >= mhd->padded_ram_size)) { + /* Re-use existing memory region if found */ + mr = memory_region_find(sysmem, assign_addr, 1).mr; + if (!mr) { + + MemoryRegion *standby_ram = g_new(MemoryRegion, 1); + + /* offset to align to standby_subregion_size for allocation */ + ram_addr_t offset = assign_addr - + (assign_addr - mhd->padded_ram_size) + % mhd->standby_subregion_size; + + /* strlen("standby.ram") + 4 (Max of KVM_MEMORY_SLOTS) + NULL */ + char id[16]; + snprintf(id, 16, "standby.ram%d", + (int)((offset - mhd->padded_ram_size) / + mhd->standby_subregion_size) + 1); + + /* Allocate a subregion of the calculated standby_subregion_size */ + if (offset + mhd->standby_subregion_size > + mhd->padded_ram_size + mhd->standby_mem_size) { + this_subregion_size = mhd->padded_ram_size + + mhd->standby_mem_size - offset; + } else { + this_subregion_size = mhd->standby_subregion_size; + } + + memory_region_init_ram(standby_ram, NULL, id, this_subregion_size); + vmstate_register_ram_global(standby_ram); + memory_region_add_subregion(sysmem, offset, standby_ram); + } + /* The specified subregion is no longer in standby */ + mhd->standby_state_map[(assign_addr - mhd->padded_ram_size) + / MEM_SECTION_SIZE] = 1; + } + sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); +} + +static void unassign_storage(SCCB *sccb) +{ + MemoryRegion *mr = NULL; + AssignStorage *assign_info = (AssignStorage *) sccb; + sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev(); + assert(mhd); + ram_addr_t unassign_addr = (assign_info->rn - 1) * mhd->rzm; + MemoryRegion *sysmem = get_system_memory(); + + /* if the addr is a multiple of 256 MB */ + if ((unassign_addr % MEM_SECTION_SIZE == 0) && + (unassign_addr >= mhd->padded_ram_size)) { + mhd->standby_state_map[(unassign_addr - + mhd->padded_ram_size) / MEM_SECTION_SIZE] = 0; + + /* find the specified memory region and destroy it */ + mr = memory_region_find(sysmem, unassign_addr, 1).mr; + if (mr) { + int i; + int is_removable = 1; + ram_addr_t map_offset = (unassign_addr - mhd->padded_ram_size - + (unassign_addr - mhd->padded_ram_size) + % mhd->standby_subregion_size); + /* Mark all affected subregions as 'standby' once again */ + for (i = 0; + i < (mhd->standby_subregion_size / MEM_SECTION_SIZE); + i++) { + + if (mhd->standby_state_map[i + map_offset / MEM_SECTION_SIZE]) { + is_removable = 0; + break; + } + } + if (is_removable) { + memory_region_del_subregion(sysmem, mr); + object_unparent(OBJECT(mr)); + g_free(mr); + } + } + } + sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION); +} + /* Provide information about the CPU */ static void sclp_read_cpu_info(SCCB *sccb) { @@ -103,6 +334,22 @@ static void sclp_execute(SCCB *sccb, uint32_t code) case SCLP_CMDW_READ_CPU_INFO: sclp_read_cpu_info(sccb); break; + case SCLP_READ_STORAGE_ELEMENT_INFO: + if (code & 0xff00) { + read_storage_element1_info(sccb); + } else { + read_storage_element0_info(sccb); + } + break; + case SCLP_ATTACH_STORAGE_ELEMENT: + attach_storage_element(sccb, (code & 0xff00) >> 8); + break; + case SCLP_ASSIGN_STORAGE: + assign_storage(sccb); + break; + case SCLP_UNASSIGN_STORAGE: + unassign_storage(sccb); + break; default: efc->command_handler(ef, sccb, code); break; @@ -183,3 +430,33 @@ void s390_sclp_init(void) OBJECT(dev), NULL); qdev_init_nofail(dev); } + +sclpMemoryHotplugDev *init_sclp_memory_hotplug_dev(void) +{ + DeviceState *dev; + dev = qdev_create(NULL, TYPE_SCLP_MEMORY_HOTPLUG_DEV); + object_property_add_child(qdev_get_machine(), + TYPE_SCLP_MEMORY_HOTPLUG_DEV, + OBJECT(dev), NULL); + qdev_init_nofail(dev); + return SCLP_MEMORY_HOTPLUG_DEV(object_resolve_path( + TYPE_SCLP_MEMORY_HOTPLUG_DEV, NULL)); +} + +sclpMemoryHotplugDev *get_sclp_memory_hotplug_dev(void) +{ + return SCLP_MEMORY_HOTPLUG_DEV(object_resolve_path( + TYPE_SCLP_MEMORY_HOTPLUG_DEV, NULL)); +} + +static TypeInfo sclp_memory_hotplug_dev_info = { + .name = TYPE_SCLP_MEMORY_HOTPLUG_DEV, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(sclpMemoryHotplugDev), +}; + +static void register_types(void) +{ + type_register_static(&sclp_memory_hotplug_dev_info); +} +type_init(register_types); diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index 513ea47e8a..d9b4c7ea3c 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -19,6 +19,7 @@ #include "hw/pci/pci.h" #include "hw/scsi/scsi.h" #include "sysemu/dma.h" +#include "qemu/error-report.h" //#define DEBUG_LSI //#define DEBUG_LSI_REG diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 6f4462b483..954c6072bf 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -36,20 +36,19 @@ static const TypeInfo scsi_bus_info = { }; static int next_scsi_bus; -static int scsi_device_init(SCSIDevice *s) +static void scsi_device_realize(SCSIDevice *s, Error **errp) { SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s); - if (sc->init) { - return sc->init(s); + if (sc->realize) { + sc->realize(s, errp); } - return 0; } -static void scsi_device_destroy(SCSIDevice *s) +static void scsi_device_unrealize(SCSIDevice *s, Error **errp) { SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s); - if (sc->destroy) { - sc->destroy(s); + if (sc->unrealize) { + sc->unrealize(s, errp); } } @@ -143,24 +142,24 @@ static void scsi_dma_restart_cb(void *opaque, int running, RunState state) } } -static int scsi_qdev_init(DeviceState *qdev) +static void scsi_qdev_realize(DeviceState *qdev, Error **errp) { SCSIDevice *dev = SCSI_DEVICE(qdev); SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus); SCSIDevice *d; - int rc = -1; + Error *local_err = NULL; if (dev->channel > bus->info->max_channel) { - error_report("bad scsi channel id: %d", dev->channel); - goto err; + error_setg(errp, "bad scsi channel id: %d", dev->channel); + return; } if (dev->id != -1 && dev->id > bus->info->max_target) { - error_report("bad scsi device id: %d", dev->id); - goto err; + error_setg(errp, "bad scsi device id: %d", dev->id); + return; } if (dev->lun != -1 && dev->lun > bus->info->max_lun) { - error_report("bad scsi device lun: %d", dev->lun); - goto err; + error_setg(errp, "bad scsi device lun: %d", dev->lun); + return; } if (dev->id == -1) { @@ -172,8 +171,8 @@ static int scsi_qdev_init(DeviceState *qdev) d = scsi_device_find(bus, dev->channel, ++id, dev->lun); } while (d && d->lun == dev->lun && id < bus->info->max_target); if (d && d->lun == dev->lun) { - error_report("no free target"); - goto err; + error_setg(errp, "no free target"); + return; } dev->id = id; } else if (dev->lun == -1) { @@ -182,43 +181,41 @@ static int scsi_qdev_init(DeviceState *qdev) d = scsi_device_find(bus, dev->channel, dev->id, ++lun); } while (d && d->lun == lun && lun < bus->info->max_lun); if (d && d->lun == lun) { - error_report("no free lun"); - goto err; + error_setg(errp, "no free lun"); + return; } dev->lun = lun; } else { d = scsi_device_find(bus, dev->channel, dev->id, dev->lun); assert(d); if (d->lun == dev->lun && dev != d) { - error_report("lun already used by '%s'", d->qdev.id); - goto err; + error_setg(errp, "lun already used by '%s'", d->qdev.id); + return; } } QTAILQ_INIT(&dev->requests); - rc = scsi_device_init(dev); - if (rc == 0) { - dev->vmsentry = qemu_add_vm_change_state_handler(scsi_dma_restart_cb, - dev); + scsi_device_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; } + dev->vmsentry = qemu_add_vm_change_state_handler(scsi_dma_restart_cb, + dev); if (bus->info->hotplug) { bus->info->hotplug(bus, dev); } - -err: - return rc; } -static int scsi_qdev_exit(DeviceState *qdev) +static void scsi_qdev_unrealize(DeviceState *qdev, Error **errp) { SCSIDevice *dev = SCSI_DEVICE(qdev); if (dev->vmsentry) { qemu_del_vm_change_state_handler(dev->vmsentry); } - scsi_device_destroy(dev); - return 0; + scsi_device_unrealize(dev, errp); } /* handle legacy '-drive if=scsi,...' cmd line args */ @@ -273,6 +270,7 @@ void scsi_bus_legacy_handle_cmdline(SCSIBus *bus, Error **errp) scsi_bus_legacy_add_drive(bus, dinfo->bdrv, unit, false, -1, NULL, &err); if (err != NULL) { + error_report("%s", error_get_pretty(err)); error_propagate(errp, err); break; } @@ -1992,11 +1990,11 @@ static void scsi_device_class_init(ObjectClass *klass, void *data) { DeviceClass *k = DEVICE_CLASS(klass); set_bit(DEVICE_CATEGORY_STORAGE, k->categories); - k->bus_type = TYPE_SCSI_BUS; - k->init = scsi_qdev_init; - k->unplug = scsi_qdev_unplug; - k->exit = scsi_qdev_exit; - k->props = scsi_props; + k->bus_type = TYPE_SCSI_BUS; + k->realize = scsi_qdev_realize; + k->unplug = scsi_qdev_unplug; + k->unrealize = scsi_qdev_unrealize; + k->props = scsi_props; } static const TypeInfo scsi_device_type_info = { diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index d55521dcf6..e34a54404b 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -2151,7 +2151,7 @@ static void scsi_disk_reset(DeviceState *dev) s->tray_open = 0; } -static void scsi_destroy(SCSIDevice *dev) +static void scsi_unrealize(SCSIDevice *dev, Error **errp) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); @@ -2234,25 +2234,29 @@ static void scsi_disk_unit_attention_reported(SCSIDevice *dev) } } -static int scsi_initfn(SCSIDevice *dev) +static void scsi_realize(SCSIDevice *dev, Error **errp) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); + Error *err = NULL; if (!s->qdev.conf.bs) { - error_report("drive property not set"); - return -1; + error_setg(errp, "drive property not set"); + return; } if (!(s->features & (1 << SCSI_DISK_F_REMOVABLE)) && !bdrv_is_inserted(s->qdev.conf.bs)) { - error_report("Device needs media, but drive is empty"); - return -1; + error_setg(errp, "Device needs media, but drive is empty"); + return; } blkconf_serial(&s->qdev.conf, &s->serial); - if (dev->type == TYPE_DISK - && blkconf_geometry(&dev->conf, NULL, 65535, 255, 255) < 0) { - return -1; + if (dev->type == TYPE_DISK) { + blkconf_geometry(&dev->conf, NULL, 65535, 255, 255, &err); + if (err) { + error_propagate(errp, err); + return; + } } if (s->qdev.conf.discard_granularity == -1) { @@ -2268,8 +2272,8 @@ static int scsi_initfn(SCSIDevice *dev) } if (bdrv_is_sg(s->qdev.conf.bs)) { - error_report("unwanted /dev/sg*"); - return -1; + error_setg(errp, "unwanted /dev/sg*"); + return; } if ((s->features & (1 << SCSI_DISK_F_REMOVABLE)) && @@ -2282,10 +2286,9 @@ static int scsi_initfn(SCSIDevice *dev) bdrv_iostatus_enable(s->qdev.conf.bs); add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, NULL); - return 0; } -static int scsi_hd_initfn(SCSIDevice *dev) +static void scsi_hd_realize(SCSIDevice *dev, Error **errp) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); s->qdev.blocksize = s->qdev.conf.logical_block_size; @@ -2293,10 +2296,10 @@ static int scsi_hd_initfn(SCSIDevice *dev) if (!s->product) { s->product = g_strdup("QEMU HARDDISK"); } - return scsi_initfn(&s->qdev); + scsi_realize(&s->qdev, errp); } -static int scsi_cd_initfn(SCSIDevice *dev) +static void scsi_cd_realize(SCSIDevice *dev, Error **errp) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); s->qdev.blocksize = 2048; @@ -2305,22 +2308,26 @@ static int scsi_cd_initfn(SCSIDevice *dev) if (!s->product) { s->product = g_strdup("QEMU CD-ROM"); } - return scsi_initfn(&s->qdev); + scsi_realize(&s->qdev, errp); } -static int scsi_disk_initfn(SCSIDevice *dev) +static void scsi_disk_realize(SCSIDevice *dev, Error **errp) { DriveInfo *dinfo; + Error *local_err = NULL; if (!dev->conf.bs) { - return scsi_initfn(dev); /* ... and die there */ + scsi_realize(dev, &local_err); + assert(local_err); + error_propagate(errp, local_err); + return; } dinfo = drive_get_by_blockdev(dev->conf.bs); if (dinfo->media_cd) { - return scsi_cd_initfn(dev); + scsi_cd_realize(dev, errp); } else { - return scsi_hd_initfn(dev); + scsi_hd_realize(dev, errp); } } @@ -2452,35 +2459,35 @@ static int get_device_type(SCSIDiskState *s) return 0; } -static int scsi_block_initfn(SCSIDevice *dev) +static void scsi_block_realize(SCSIDevice *dev, Error **errp) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); int sg_version; int rc; if (!s->qdev.conf.bs) { - error_report("drive property not set"); - return -1; + error_setg(errp, "drive property not set"); + return; } /* check we are using a driver managing SG_IO (version 3 and after) */ rc = bdrv_ioctl(s->qdev.conf.bs, SG_GET_VERSION_NUM, &sg_version); if (rc < 0) { - error_report("cannot get SG_IO version number: %s. " + error_setg(errp, "cannot get SG_IO version number: %s. " "Is this a SCSI device?", strerror(-rc)); - return -1; + return; } if (sg_version < 30000) { - error_report("scsi generic interface too old"); - return -1; + error_setg(errp, "scsi generic interface too old"); + return; } /* get device type from INQUIRY data */ rc = get_device_type(s); if (rc < 0) { - error_report("INQUIRY failed"); - return -1; + error_setg(errp, "INQUIRY failed"); + return; } /* Make a guess for the block size, we'll fix it when the guest sends. @@ -2498,7 +2505,7 @@ static int scsi_block_initfn(SCSIDevice *dev) */ s->features |= (1 << SCSI_DISK_F_NO_REMOVABLE_DEVOPS); - return scsi_initfn(&s->qdev); + scsi_realize(&s->qdev, errp); } static bool scsi_block_is_passthrough(SCSIDiskState *s, uint8_t *buf) @@ -2621,8 +2628,8 @@ static void scsi_hd_class_initfn(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); - sc->init = scsi_hd_initfn; - sc->destroy = scsi_destroy; + sc->realize = scsi_hd_realize; + sc->unrealize = scsi_unrealize; sc->alloc_req = scsi_new_request; sc->unit_attention_reported = scsi_disk_unit_attention_reported; dc->fw_name = "disk"; @@ -2652,8 +2659,8 @@ static void scsi_cd_class_initfn(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); - sc->init = scsi_cd_initfn; - sc->destroy = scsi_destroy; + sc->realize = scsi_cd_realize; + sc->unrealize = scsi_unrealize; sc->alloc_req = scsi_new_request; sc->unit_attention_reported = scsi_disk_unit_attention_reported; dc->fw_name = "disk"; @@ -2682,8 +2689,8 @@ static void scsi_block_class_initfn(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); - sc->init = scsi_block_initfn; - sc->destroy = scsi_destroy; + sc->realize = scsi_block_realize; + sc->unrealize = scsi_unrealize; sc->alloc_req = scsi_block_new_request; sc->parse_cdb = scsi_block_parse_cdb; dc->fw_name = "disk"; @@ -2720,8 +2727,8 @@ static void scsi_disk_class_initfn(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); - sc->init = scsi_disk_initfn; - sc->destroy = scsi_destroy; + sc->realize = scsi_disk_realize; + sc->unrealize = scsi_unrealize; sc->alloc_req = scsi_new_request; sc->unit_attention_reported = scsi_disk_unit_attention_reported; dc->fw_name = "disk"; diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 0b2ff90b90..20587b41c1 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -303,9 +303,6 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) SCSIDevice *s = r->req.dev; int ret; - DPRINTF("Command: lun=%d tag=0x%x len %zd data=0x%02x", lun, tag, - r->req.cmd.xfer, cmd[0]); - #ifdef DEBUG_SCSI { int i; @@ -386,49 +383,49 @@ static void scsi_generic_reset(DeviceState *dev) scsi_device_purge_requests(s, SENSE_CODE(RESET)); } -static void scsi_destroy(SCSIDevice *s) +static void scsi_unrealize(SCSIDevice *s, Error **errp) { scsi_device_purge_requests(s, SENSE_CODE(NO_SENSE)); blockdev_mark_auto_del(s->conf.bs); } -static int scsi_generic_initfn(SCSIDevice *s) +static void scsi_generic_realize(SCSIDevice *s, Error **errp) { int rc; int sg_version; struct sg_scsi_id scsiid; if (!s->conf.bs) { - error_report("drive property not set"); - return -1; + error_setg(errp, "drive property not set"); + return; } if (bdrv_get_on_error(s->conf.bs, 0) != BLOCKDEV_ON_ERROR_ENOSPC) { - error_report("Device doesn't support drive option werror"); - return -1; + error_setg(errp, "Device doesn't support drive option werror"); + return; } if (bdrv_get_on_error(s->conf.bs, 1) != BLOCKDEV_ON_ERROR_REPORT) { - error_report("Device doesn't support drive option rerror"); - return -1; + error_setg(errp, "Device doesn't support drive option rerror"); + return; } /* check we are using a driver managing SG_IO (version 3 and after */ rc = bdrv_ioctl(s->conf.bs, SG_GET_VERSION_NUM, &sg_version); if (rc < 0) { - error_report("cannot get SG_IO version number: %s. " - "Is this a SCSI device?", - strerror(-rc)); - return -1; + error_setg(errp, "cannot get SG_IO version number: %s. " + "Is this a SCSI device?", + strerror(-rc)); + return; } if (sg_version < 30000) { - error_report("scsi generic interface too old"); - return -1; + error_setg(errp, "scsi generic interface too old"); + return; } /* get LUN of the /dev/sg? */ if (bdrv_ioctl(s->conf.bs, SG_GET_SCSI_ID, &scsiid)) { - error_report("SG_GET_SCSI_ID ioctl failed"); - return -1; + error_setg(errp, "SG_GET_SCSI_ID ioctl failed"); + return; } /* define device state */ @@ -460,7 +457,6 @@ static int scsi_generic_initfn(SCSIDevice *s) } DPRINTF("block size %d\n", s->blocksize); - return 0; } const SCSIReqOps scsi_generic_req_ops = { @@ -501,8 +497,8 @@ static void scsi_generic_class_initfn(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); - sc->init = scsi_generic_initfn; - sc->destroy = scsi_destroy; + sc->realize = scsi_generic_realize; + sc->unrealize = scsi_unrealize; sc->alloc_req = scsi_new_request; sc->parse_cdb = scsi_generic_parse_cdb; dc->fw_name = "disk"; diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 2dd92552e0..86aba8851d 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -699,6 +699,12 @@ void virtio_scsi_common_realize(DeviceState *dev, Error **errp, virtio_init(vdev, "virtio-scsi", VIRTIO_ID_SCSI, sizeof(VirtIOSCSIConfig)); + if (s->conf.num_queues <= 0 || s->conf.num_queues > VIRTIO_PCI_QUEUE_MAX) { + error_setg(errp, "Invalid number of queues (= %" PRId32 "), " + "must be a positive integer less than %d.", + s->conf.num_queues, VIRTIO_PCI_QUEUE_MAX); + return; + } s->cmd_vqs = g_malloc0(s->conf.num_queues * sizeof(VirtQueue *)); s->sense_size = VIRTIO_SCSI_SENSE_SIZE; s->cdb_size = VIRTIO_SCSI_CDB_SIZE; diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index 233fc70d67..4df650f450 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -792,6 +792,7 @@ static void rtc_reset(void *opaque) #ifdef TARGET_I386 if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { s->irq_coalesced = 0; + s->irq_reinject_on_ack_count = 0; } #endif } diff --git a/hw/tricore/Makefile.objs b/hw/tricore/Makefile.objs new file mode 100644 index 0000000000..435e095cff --- /dev/null +++ b/hw/tricore/Makefile.objs @@ -0,0 +1 @@ +obj-y += tricore_testboard.o diff --git a/hw/tricore/tricore_testboard.c b/hw/tricore/tricore_testboard.c new file mode 100644 index 0000000000..f412e27f1c --- /dev/null +++ b/hw/tricore/tricore_testboard.c @@ -0,0 +1,124 @@ +/* + * TriCore Baseboard System emulation. + * + * Copyright (c) 2013-2014 Bastian Koppelmann C-Lab/University Paderborn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + + +#include "hw/hw.h" +#include "hw/devices.h" +#include "net/net.h" +#include "sysemu/sysemu.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "sysemu/blockdev.h" +#include "exec/address-spaces.h" +#include "hw/block/flash.h" +#include "elf.h" +#include "hw/tricore/tricore.h" +#include "qemu/error-report.h" + + +/* Board init. */ + +static struct tricore_boot_info tricoretb_binfo; + +static void tricore_load_kernel(CPUTriCoreState *env) +{ + uint64_t entry; + long kernel_size; + + kernel_size = load_elf(tricoretb_binfo.kernel_filename, NULL, + NULL, (uint64_t *)&entry, NULL, + NULL, 0, + ELF_MACHINE, 1); + if (kernel_size <= 0) { + error_report("qemu: no kernel file '%s'", + tricoretb_binfo.kernel_filename); + exit(1); + } + env->PC = entry; + +} + +static void tricore_testboard_init(MachineState *machine, int board_id) +{ + TriCoreCPU *cpu; + CPUTriCoreState *env; + + MemoryRegion *sysmem = get_system_memory(); + MemoryRegion *ext_cram = g_new(MemoryRegion, 1); + MemoryRegion *ext_dram = g_new(MemoryRegion, 1); + MemoryRegion *int_cram = g_new(MemoryRegion, 1); + MemoryRegion *int_dram = g_new(MemoryRegion, 1); + MemoryRegion *pcp_data = g_new(MemoryRegion, 1); + MemoryRegion *pcp_text = g_new(MemoryRegion, 1); + + if (!machine->cpu_model) { + machine->cpu_model = "tc1796"; + } + cpu = cpu_tricore_init(machine->cpu_model); + env = &cpu->env; + if (!cpu) { + error_report("Unable to find CPU definition"); + exit(1); + } + memory_region_init_ram(ext_cram, NULL, "powerlink_ext_c.ram", 2*1024*1024); + vmstate_register_ram_global(ext_cram); + memory_region_init_ram(ext_dram, NULL, "powerlink_ext_d.ram", 4*1024*1024); + vmstate_register_ram_global(ext_dram); + memory_region_init_ram(int_cram, NULL, "powerlink_int_c.ram", 48*1024); + vmstate_register_ram_global(int_cram); + memory_region_init_ram(int_dram, NULL, "powerlink_int_d.ram", 48*1024); + vmstate_register_ram_global(int_dram); + memory_region_init_ram(pcp_data, NULL, "powerlink_pcp_data.ram", 16*1024); + vmstate_register_ram_global(pcp_data); + memory_region_init_ram(pcp_text, NULL, "powerlink_pcp_text.ram", 32*1024); + vmstate_register_ram_global(pcp_text); + + memory_region_add_subregion(sysmem, 0x80000000, ext_cram); + memory_region_add_subregion(sysmem, 0xa1000000, ext_dram); + memory_region_add_subregion(sysmem, 0xd4000000, int_cram); + memory_region_add_subregion(sysmem, 0xd0000000, int_dram); + memory_region_add_subregion(sysmem, 0xf0050000, pcp_data); + memory_region_add_subregion(sysmem, 0xf0060000, pcp_text); + + tricoretb_binfo.ram_size = machine->ram_size; + tricoretb_binfo.kernel_filename = machine->kernel_filename; + + if (machine->kernel_filename) { + tricore_load_kernel(env); + } +} + +static void tricoreboard_init(MachineState *machine) +{ + tricore_testboard_init(machine, 0x183); +} + +static QEMUMachine ttb_machine = { + .name = "tricore_testboard", + .desc = "a minimal TriCore board", + .init = tricoreboard_init, + .is_default = 0, +}; + +static void tricore_testboard_machine_init(void) +{ + qemu_register_machine(&ttb_machine); +} + +machine_init(tricore_testboard_machine_init); diff --git a/hw/usb/bus.c b/hw/usb/bus.c index 927a47bbff..c7c4dadedd 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -87,6 +87,13 @@ void usb_bus_new(USBBus *bus, size_t bus_size, QTAILQ_INSERT_TAIL(&busses, bus, next); } +void usb_bus_release(USBBus *bus) +{ + assert(next_usb_bus > 0); + + QTAILQ_REMOVE(&busses, bus, next); +} + USBBus *usb_bus_find(int busnr) { USBBus *bus; @@ -589,11 +596,11 @@ static char *usb_get_fw_dev_path(DeviceState *qdev) nr = strtol(in, &in, 10); if (in[0] == '.') { /* some hub between root port and device */ - pos += snprintf(fw_path + pos, fw_len - pos, "hub@%ld/", nr); + pos += snprintf(fw_path + pos, fw_len - pos, "hub@%lx/", nr); in++; } else { /* the device itself */ - pos += snprintf(fw_path + pos, fw_len - pos, "%s@%ld", + pos += snprintf(fw_path + pos, fw_len - pos, "%s@%lx", qdev_fw_name(qdev), nr); break; } diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index 505741a783..289ca3b853 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -84,6 +84,19 @@ static void usb_ehci_pci_init(Object *obj) usb_ehci_init(s, DEVICE(obj)); } +static void usb_ehci_pci_exit(PCIDevice *dev) +{ + EHCIPCIState *i = PCI_EHCI(dev); + EHCIState *s = &i->ehci; + + usb_ehci_unrealize(s, DEVICE(dev), NULL); + + if (s->irq) { + g_free(s->irq); + s->irq = NULL; + } +} + static void usb_ehci_pci_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int l) { @@ -121,6 +134,7 @@ static void ehci_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); k->init = usb_ehci_pci_initfn; + k->exit = usb_ehci_pci_exit; k->class_id = PCI_CLASS_SERIAL_USB; k->config_write = usb_ehci_pci_write_config; dc->hotpluggable = false; diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 448e0073dd..bacb7ceac9 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -2468,7 +2468,34 @@ void usb_ehci_realize(EHCIState *s, DeviceState *dev, Error **errp) s->device = dev; qemu_register_reset(ehci_reset, s); - qemu_add_vm_change_state_handler(usb_ehci_vm_state_change, s); + s->vmstate = qemu_add_vm_change_state_handler(usb_ehci_vm_state_change, s); +} + +void usb_ehci_unrealize(EHCIState *s, DeviceState *dev, Error **errp) +{ + trace_usb_ehci_unrealize(); + + if (s->frame_timer) { + timer_del(s->frame_timer); + timer_free(s->frame_timer); + s->frame_timer = NULL; + } + if (s->async_bh) { + qemu_bh_delete(s->async_bh); + } + + ehci_queues_rip_all(s, 0); + ehci_queues_rip_all(s, 1); + + memory_region_del_subregion(&s->mem, &s->mem_caps); + memory_region_del_subregion(&s->mem, &s->mem_opreg); + memory_region_del_subregion(&s->mem, &s->mem_ports); + + usb_bus_release(&s->bus); + + if (s->vmstate) { + qemu_del_vm_change_state_handler(s->vmstate); + } } void usb_ehci_init(EHCIState *s, DeviceState *dev) diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index 1ad4b96cce..4858b7e80c 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -316,12 +316,14 @@ struct EHCIState { uint32_t async_stepdown; uint32_t periodic_sched_active; bool int_req_by_async; + VMChangeStateEntry *vmstate; }; extern const VMStateDescription vmstate_ehci; void usb_ehci_init(EHCIState *s, DeviceState *dev); void usb_ehci_realize(EHCIState *s, DeviceState *dev, Error **errp); +void usb_ehci_unrealize(EHCIState *s, DeviceState *dev, Error **errp); #define TYPE_PCI_EHCI "pci-ehci-usb" #define PCI_EHCI(obj) OBJECT_CHECK(EHCIPCIState, (obj), TYPE_PCI_EHCI) diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 13afdf5919..83bec34185 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -619,8 +619,8 @@ static inline int ohci_put_td(OHCIState *ohci, static inline int ohci_put_iso_td(OHCIState *ohci, dma_addr_t addr, struct ohci_iso_td *td) { - return put_dwords(ohci, addr, (uint32_t *)td, 4 || - put_words(ohci, addr + 16, td->offset, 8)); + return put_dwords(ohci, addr, (uint32_t *)td, 4) || + put_words(ohci, addr + 16, td->offset, 8); } static inline int ohci_put_hcca(OHCIState *ohci, @@ -1371,8 +1371,10 @@ static int ohci_bus_start(OHCIState *ohci) /* Stop sending SOF tokens on the bus */ static void ohci_bus_stop(OHCIState *ohci) { - if (ohci->eof_timer) + if (ohci->eof_timer) { timer_del(ohci->eof_timer); + timer_free(ohci->eof_timer); + } ohci->eof_timer = NULL; } @@ -1952,6 +1954,24 @@ static int usb_ohci_initfn_pci(PCIDevice *dev) return 0; } +static void usb_ohci_exit(PCIDevice *dev) +{ + OHCIPCIState *ohci = PCI_OHCI(dev); + OHCIState *s = &ohci->state; + + ohci_bus_stop(s); + + if (s->async_td) { + usb_cancel_packet(&s->usb_packet); + s->async_td = 0; + } + ohci_stop_endpoints(s); + + if (!ohci->masterbus) { + usb_bus_release(&s->bus); + } +} + #define TYPE_SYSBUS_OHCI "sysbus-ohci" #define SYSBUS_OHCI(obj) OBJECT_CHECK(OHCISysBusState, (obj), TYPE_SYSBUS_OHCI) @@ -2089,6 +2109,7 @@ static void ohci_pci_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); k->init = usb_ohci_initfn_pci; + k->exit = usb_ohci_exit; k->vendor_id = PCI_VENDOR_ID_APPLE; k->device_id = PCI_DEVICE_ID_APPLE_IPID_USB; k->class_id = PCI_CLASS_SERIAL_USB; diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index ee5f112d65..3b3ebcda8b 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -1256,6 +1256,29 @@ static int usb_uhci_vt82c686b_initfn(PCIDevice *dev) return usb_uhci_common_initfn(dev); } +static void usb_uhci_exit(PCIDevice *dev) +{ + UHCIState *s = DO_UPCAST(UHCIState, dev, dev); + + trace_usb_uhci_exit(); + + if (s->frame_timer) { + timer_del(s->frame_timer); + timer_free(s->frame_timer); + s->frame_timer = NULL; + } + + if (s->bh) { + qemu_bh_delete(s->bh); + } + + uhci_async_cancel_all(s); + + if (!s->masterbus) { + usb_bus_release(&s->bus); + } +} + static Property uhci_properties[] = { DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), @@ -1272,6 +1295,7 @@ static void uhci_class_init(ObjectClass *klass, void *data) UHCIInfo *info = data; k->init = info->initfn ? info->initfn : usb_uhci_common_initfn; + k->exit = info->unplug ? usb_uhci_exit : NULL; k->vendor_id = info->vendor_id; k->device_id = info->device_id; k->revision = info->revision; diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 58c4b11527..bbe4c5fb85 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1151,7 +1151,7 @@ static void xhci_reset_streams(XHCIEPContext *epctx) static void xhci_alloc_streams(XHCIEPContext *epctx, dma_addr_t base) { assert(epctx->pstreams == NULL); - epctx->nr_pstreams = 2 << (epctx->max_pstreams + 1); + epctx->nr_pstreams = 2 << epctx->max_pstreams; epctx->pstreams = xhci_alloc_stream_contexts(epctx->nr_pstreams, base); } @@ -1180,7 +1180,7 @@ static int xhci_epmask_to_eps_with_streams(XHCIState *xhci, slot = &xhci->slots[slotid - 1]; for (i = 2, j = 0; i <= 31; i++) { - if (!(epmask & (1 << i))) { + if (!(epmask & (1u << i))) { continue; } @@ -1380,14 +1380,11 @@ static void xhci_init_epctx(XHCIEPContext *epctx, dequeue = xhci_addr64(ctx[2] & ~0xf, ctx[3]); epctx->type = (ctx[1] >> EP_TYPE_SHIFT) & EP_TYPE_MASK; - DPRINTF("xhci: endpoint %d.%d type is %d\n", epid/2, epid%2, epctx->type); epctx->pctx = pctx; epctx->max_psize = ctx[1]>>16; epctx->max_psize *= 1+((ctx[1]>>8)&0xff); epctx->max_pstreams = (ctx[0] >> 10) & 0xf; epctx->lsa = (ctx[0] >> 15) & 1; - DPRINTF("xhci: endpoint %d.%d max transaction (burst) size is %d\n", - epid/2, epid%2, epctx->max_psize); if (epctx->max_pstreams) { xhci_alloc_streams(epctx, dequeue); } else { @@ -1418,6 +1415,9 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, slot->eps[epid-1] = epctx; xhci_init_epctx(epctx, pctx, ctx); + DPRINTF("xhci: endpoint %d.%d type is %d, max transaction (burst) " + "size is %d\n", epid/2, epid%2, epctx->type, epctx->max_psize); + epctx->mfindex_last = 0; epctx->state = EP_RUNNING; @@ -2465,7 +2465,7 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid, res = xhci_alloc_device_streams(xhci, slotid, ictl_ctx[1]); if (res != CC_SUCCESS) { for (i = 2; i <= 31; i++) { - if (ictl_ctx[1] & (1 << i)) { + if (ictl_ctx[1] & (1u << i)) { xhci_disable_ep(xhci, slotid, i); } } @@ -3644,6 +3644,43 @@ static int usb_xhci_initfn(struct PCIDevice *dev) return 0; } +static void usb_xhci_exit(PCIDevice *dev) +{ + int i; + XHCIState *xhci = XHCI(dev); + + trace_usb_xhci_exit(); + + for (i = 0; i < xhci->numslots; i++) { + xhci_disable_slot(xhci, i + 1); + } + + if (xhci->mfwrap_timer) { + timer_del(xhci->mfwrap_timer); + timer_free(xhci->mfwrap_timer); + xhci->mfwrap_timer = NULL; + } + + memory_region_del_subregion(&xhci->mem, &xhci->mem_cap); + memory_region_del_subregion(&xhci->mem, &xhci->mem_oper); + memory_region_del_subregion(&xhci->mem, &xhci->mem_runtime); + memory_region_del_subregion(&xhci->mem, &xhci->mem_doorbell); + + for (i = 0; i < xhci->numports; i++) { + XHCIPort *port = &xhci->ports[i]; + memory_region_del_subregion(&xhci->mem, &port->mem); + } + + /* destroy msix memory region */ + if (dev->msix_table && dev->msix_pba + && dev->msix_entry_used) { + memory_region_del_subregion(&xhci->mem, &dev->msix_table_mmio); + memory_region_del_subregion(&xhci->mem, &dev->msix_pba_mmio); + } + + usb_bus_release(&xhci->bus); +} + static int usb_xhci_post_load(void *opaque, int version_id) { XHCIState *xhci = opaque; @@ -3836,6 +3873,7 @@ static void xhci_class_init(ObjectClass *klass, void *data) dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_USB, dc->categories); k->init = usb_xhci_initfn; + k->exit = usb_xhci_exit; k->vendor_id = PCI_VENDOR_ID_NEC; k->device_id = PCI_DEVICE_ID_NEC_UPD720200; k->class_id = PCI_CLASS_SERIAL_USB; |