aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/9pfs/Makefile.objs14
-rw-r--r--hw/9pfs/virtio-9p-posix-acl.c6
-rw-r--r--hw/9pfs/virtio-9p-synth.c4
-rw-r--r--hw/9pfs/virtio-9p-xattr-user.c3
-rw-r--r--hw/9pfs/virtio-9p-xattr.c3
-rw-r--r--hw/9pfs/virtio-9p.c5
-rw-r--r--hw/Makefile.objs233
-rw-r--r--hw/acpi.c24
-rw-r--r--hw/acpi_piix4.c8
-rw-r--r--hw/ads7846.c7
-rw-r--r--hw/alpha_dp264.c2
-rw-r--r--hw/alpha_pci.c24
-rw-r--r--hw/alpha_sys.h2
-rw-r--r--hw/arm/Makefile.objs1
-rw-r--r--hw/armv7m_nvic.c3
-rw-r--r--hw/blizzard.c2
-rw-r--r--hw/bt-hci.c8
-rw-r--r--hw/cadence_uart.c2
-rw-r--r--hw/cirrus_vga.c13
-rw-r--r--hw/device-hotplug.c1
-rw-r--r--hw/e1000.c4
-rw-r--r--hw/eepro100.c4
-rw-r--r--hw/exynos4210_combiner.c4
-rw-r--r--hw/exynos4210_gic.c4
-rw-r--r--hw/exynos4210_mct.c2
-rw-r--r--hw/fdc.c6
-rw-r--r--hw/fifo.c78
-rw-r--r--hw/fifo.h99
-rw-r--r--hw/framebuffer.c1
-rw-r--r--hw/fw_cfg.c25
-rw-r--r--hw/hw.h2
-rw-r--r--hw/ide/Makefile.objs20
-rw-r--r--hw/ide/ahci.c2
-rw-r--r--hw/ide/atapi.c6
-rw-r--r--hw/ide/core.c33
-rw-r--r--hw/ide/macio.c1
-rw-r--r--hw/ide/pci.c4
-rw-r--r--hw/ide/qdev.c2
-rw-r--r--hw/imx_avic.c4
-rw-r--r--hw/imx_timer.c4
-rw-r--r--hw/intel-hda.c8
-rw-r--r--hw/ioh3420.c1
-rw-r--r--hw/irq.c27
-rw-r--r--hw/irq.h11
-rw-r--r--hw/isa-bus.c22
-rw-r--r--hw/isa.h2
-rw-r--r--hw/ivshmem.c17
-rw-r--r--hw/kvm/pci-assign.c12
-rw-r--r--hw/kzm.c2
-rw-r--r--hw/leon3.c2
-rw-r--r--hw/lm32_hwsetup.h2
-rw-r--r--hw/lm4549.c2
-rw-r--r--hw/m25p80.c598
-rw-r--r--hw/max111x.c7
-rw-r--r--hw/mc146818rtc.c40
-rw-r--r--hw/mc146818rtc_regs.h4
-rw-r--r--hw/megasas.c1
-rw-r--r--hw/microblaze/Makefile.objs1
-rw-r--r--hw/mips_fulong2e.c2
-rw-r--r--hw/mips_malta.c10
-rw-r--r--hw/msix.c1
-rw-r--r--hw/nand.c34
-rw-r--r--hw/nseries.c14
-rw-r--r--hw/onenand.c2
-rw-r--r--hw/pc.c62
-rw-r--r--hw/pc.h13
-rw-r--r--hw/pc_piix.c32
-rw-r--r--hw/pci.c20
-rw-r--r--hw/pci.h3
-rw-r--r--hw/petalogix_ml605_mmu.c27
-rw-r--r--hw/pflash_cfi01.c50
-rw-r--r--hw/pflash_cfi02.c38
-rw-r--r--hw/pl190.c18
-rw-r--r--hw/ppc/e500.c24
-rw-r--r--hw/ppc405_uc.c16
-rw-r--r--hw/ppc_newworld.c1
-rw-r--r--hw/ppc_oldworld.c1
-rw-r--r--hw/ppc_prep.c1
-rw-r--r--hw/pxa2xx_keypad.c1
-rw-r--r--hw/qdev.c14
-rw-r--r--hw/qdev.h1
-rw-r--r--hw/qxl.c108
-rw-r--r--hw/r2d.c2
-rw-r--r--hw/rtl8139.c6
-rw-r--r--hw/sb16.c1
-rw-r--r--hw/scsi-bus.c31
-rw-r--r--hw/scsi-disk.c71
-rw-r--r--hw/scsi-generic.c4
-rw-r--r--hw/scsi.h2
-rw-r--r--hw/sd.c16
-rw-r--r--hw/spapr.c336
-rw-r--r--hw/spapr.h14
-rw-r--r--hw/spapr_hcall.c50
-rw-r--r--hw/spapr_iommu.c24
-rw-r--r--hw/spapr_pci.c12
-rw-r--r--hw/spapr_rtas.c5
-rw-r--r--hw/spapr_vio.c37
-rw-r--r--hw/spapr_vio.h2
-rw-r--r--hw/spapr_vscsi.c2
-rw-r--r--hw/spitz.c8
-rw-r--r--hw/srp.h8
-rw-r--r--hw/ssd0323.c7
-rw-r--r--hw/ssi-sd.c7
-rw-r--r--hw/ssi.c109
-rw-r--r--hw/ssi.h42
-rw-r--r--hw/stellaris.c93
-rw-r--r--hw/sun4m.c14
-rw-r--r--hw/sun4u.c1
-rw-r--r--hw/sysbus.c2
-rw-r--r--hw/tcx.c1
-rw-r--r--hw/usb.h9
-rw-r--r--hw/usb/Makefile.objs14
-rw-r--r--hw/usb/core.c23
-rw-r--r--hw/usb/desc.c174
-rw-r--r--hw/usb/desc.h52
-rw-r--r--hw/usb/dev-audio.c2
-rw-r--r--hw/usb/dev-network.c49
-rw-r--r--hw/usb/dev-serial.c2
-rw-r--r--hw/usb/dev-storage.c46
-rw-r--r--hw/usb/dev-uas.c2
-rw-r--r--hw/usb/hcd-ehci.c431
-rw-r--r--hw/usb/hcd-ohci.c2
-rw-r--r--hw/usb/hcd-uhci.c10
-rw-r--r--hw/usb/hcd-xhci.c1566
-rw-r--r--hw/usb/host-linux.c2
-rw-r--r--hw/usb/libhw.c24
-rw-r--r--hw/usb/redirect.c905
-rw-r--r--hw/versatilepb.c3
-rw-r--r--hw/vexpress.c33
-rw-r--r--hw/vfio_pci.c1864
-rw-r--r--hw/vfio_pci_int.h114
-rw-r--r--hw/vga-isa-mm.c1
-rw-r--r--hw/vga-pci.c10
-rw-r--r--hw/vga-pci.h12
-rw-r--r--hw/vga.c5
-rw-r--r--hw/virtio-blk.c23
-rw-r--r--hw/virtio-net.c8
-rw-r--r--hw/virtio-serial-bus.c11
-rw-r--r--hw/virtio.c32
-rw-r--r--hw/virtio.h5
-rw-r--r--hw/vmware_vga.c2
-rw-r--r--hw/vmware_vga.h15
-rw-r--r--hw/xen-host-pci-device.c6
-rw-r--r--hw/xen.h1
-rw-r--r--hw/xen_domainbuild.c1
-rw-r--r--hw/xen_nic.c1
-rw-r--r--hw/xen_platform.c8
-rw-r--r--hw/xen_pt.c7
-rw-r--r--hw/xen_pt.h4
-rw-r--r--hw/xen_pt_config_init.c53
-rw-r--r--hw/xics.c114
-rw-r--r--hw/xics.h8
-rw-r--r--hw/xilinx.h16
-rw-r--r--hw/xilinx_spi.c385
-rw-r--r--hw/xilinx_spips.c354
-rw-r--r--hw/xilinx_timer.c16
-rw-r--r--hw/xilinx_zynq.c34
-rw-r--r--hw/z2.c7
158 files changed, 6963 insertions, 2332 deletions
diff --git a/hw/9pfs/Makefile.objs b/hw/9pfs/Makefile.objs
index 972df2405..1e9b595cb 100644
--- a/hw/9pfs/Makefile.objs
+++ b/hw/9pfs/Makefile.objs
@@ -1,9 +1,9 @@
-hw-obj-y = virtio-9p.o
-hw-obj-y += virtio-9p-local.o virtio-9p-xattr.o
-hw-obj-y += virtio-9p-xattr-user.o virtio-9p-posix-acl.o
-hw-obj-y += virtio-9p-coth.o cofs.o codir.o cofile.o
-hw-obj-y += coxattr.o virtio-9p-synth.o
-hw-obj-$(CONFIG_OPEN_BY_HANDLE) += virtio-9p-handle.o
-hw-obj-y += virtio-9p-proxy.o
+common-obj-y = virtio-9p.o
+common-obj-y += virtio-9p-local.o virtio-9p-xattr.o
+common-obj-y += virtio-9p-xattr-user.o virtio-9p-posix-acl.o
+common-obj-y += virtio-9p-coth.o cofs.o codir.o cofile.o
+common-obj-y += coxattr.o virtio-9p-synth.o
+common-obj-$(CONFIG_OPEN_BY_HANDLE) += virtio-9p-handle.o
+common-obj-y += virtio-9p-proxy.o
obj-y += virtio-9p-device.o
diff --git a/hw/9pfs/virtio-9p-posix-acl.c b/hw/9pfs/virtio-9p-posix-acl.c
index a1948e3af..c064017b1 100644
--- a/hw/9pfs/virtio-9p-posix-acl.c
+++ b/hw/9pfs/virtio-9p-posix-acl.c
@@ -44,7 +44,8 @@ static ssize_t mp_pacl_listxattr(FsContext *ctx, const char *path,
return -1;
}
- strncpy(value, ACL_ACCESS, len);
+ /* len includes the trailing NUL */
+ memcpy(value, ACL_ACCESS, len);
return 0;
}
@@ -95,7 +96,8 @@ static ssize_t mp_dacl_listxattr(FsContext *ctx, const char *path,
return -1;
}
- strncpy(value, ACL_DEFAULT, len);
+ /* len includes the trailing NUL */
+ memcpy(value, ACL_ACCESS, len);
return 0;
}
diff --git a/hw/9pfs/virtio-9p-synth.c b/hw/9pfs/virtio-9p-synth.c
index 92e0b09d3..e95a856d2 100644
--- a/hw/9pfs/virtio-9p-synth.c
+++ b/hw/9pfs/virtio-9p-synth.c
@@ -58,7 +58,7 @@ static V9fsSynthNode *v9fs_add_dir_node(V9fsSynthNode *parent, int mode,
node->attr->read = NULL;
}
node->private = node;
- strncpy(node->name, name, sizeof(node->name));
+ pstrcpy(node->name, sizeof(node->name), name);
QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling);
return node;
}
@@ -132,7 +132,7 @@ int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode,
node->attr->write = write;
node->attr->mode = mode;
node->private = arg;
- strncpy(node->name, name, sizeof(node->name));
+ pstrcpy(node->name, sizeof(node->name), name);
QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling);
ret = 0;
err_out:
diff --git a/hw/9pfs/virtio-9p-xattr-user.c b/hw/9pfs/virtio-9p-xattr-user.c
index 5044a3e5a..5bb602007 100644
--- a/hw/9pfs/virtio-9p-xattr-user.c
+++ b/hw/9pfs/virtio-9p-xattr-user.c
@@ -61,7 +61,8 @@ static ssize_t mp_user_listxattr(FsContext *ctx, const char *path,
return -1;
}
- strncpy(value, name, name_size);
+ /* name_size includes the trailing NUL. */
+ memcpy(value, name, name_size);
return name_size;
}
diff --git a/hw/9pfs/virtio-9p-xattr.c b/hw/9pfs/virtio-9p-xattr.c
index 7f08f6e17..a83960676 100644
--- a/hw/9pfs/virtio-9p-xattr.c
+++ b/hw/9pfs/virtio-9p-xattr.c
@@ -53,7 +53,8 @@ ssize_t pt_listxattr(FsContext *ctx, const char *path,
return -1;
}
- strncpy(value, name, name_size);
+ /* no need for strncpy: name_size is strlen(name)+1 */
+ memcpy(value, name, name_size);
return name_size;
}
diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c
index 4b5254011..8b9cdc96e 100644
--- a/hw/9pfs/virtio-9p.c
+++ b/hw/9pfs/virtio-9p.c
@@ -505,7 +505,6 @@ static void virtfs_reset(V9fsPDU *pdu)
error_report("9pfs:%s: One or more uncluncked fids "
"found during reset", __func__);
}
- return;
}
#define P9_QID_TYPE_DIR 0x80
@@ -934,7 +933,6 @@ static void v9fs_version(void *opaque)
out:
complete_pdu(s, pdu, offset);
v9fs_string_free(&version);
- return;
}
static void v9fs_attach(void *opaque)
@@ -1314,7 +1312,6 @@ out_nofid:
g_free(wnames);
g_free(qids);
}
- return;
}
static int32_t get_iounit(V9fsPDU *pdu, V9fsPath *path)
@@ -2257,7 +2254,6 @@ static void v9fs_flush(void *opaque)
free_pdu(pdu->s, cancel_pdu);
}
complete_pdu(s, pdu, 7);
- return;
}
static void v9fs_link(void *opaque)
@@ -2763,7 +2759,6 @@ out:
put_fid(pdu, fidp);
out_nofid:
complete_pdu(s, pdu, retval);
- return;
}
static void v9fs_mknod(void *opaque)
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 850b87b02..66ff86897 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -1,141 +1,142 @@
-hw-obj-y = usb/ ide/
-hw-obj-y += loader.o
-hw-obj-$(CONFIG_VIRTIO) += virtio-console.o
-hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
-hw-obj-y += fw_cfg.o
-hw-obj-$(CONFIG_PCI) += pci.o pci_bridge.o pci_bridge_dev.o
-hw-obj-$(CONFIG_PCI) += msix.o msi.o
-hw-obj-$(CONFIG_PCI) += shpc.o
-hw-obj-$(CONFIG_PCI) += slotid_cap.o
-hw-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o
-hw-obj-$(CONFIG_PCI) += ioh3420.o xio3130_upstream.o xio3130_downstream.o
-hw-obj-y += watchdog.o
-hw-obj-$(CONFIG_ISA_MMIO) += isa_mmio.o
-hw-obj-$(CONFIG_ECC) += ecc.o
-hw-obj-$(CONFIG_NAND) += nand.o
-hw-obj-$(CONFIG_PFLASH_CFI01) += pflash_cfi01.o
-hw-obj-$(CONFIG_PFLASH_CFI02) += pflash_cfi02.o
-
-hw-obj-$(CONFIG_M48T59) += m48t59.o
-hw-obj-$(CONFIG_ESCC) += escc.o
-hw-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o
-
-hw-obj-$(CONFIG_SERIAL) += serial.o
-hw-obj-$(CONFIG_PARALLEL) += parallel.o
-hw-obj-$(CONFIG_I8254) += i8254_common.o i8254.o
-hw-obj-$(CONFIG_PCSPK) += pcspk.o
-hw-obj-$(CONFIG_PCKBD) += pckbd.o
-hw-obj-$(CONFIG_FDC) += fdc.o
-# needs fixes for cpu hotplug, so moved to Makefile.target:
-# hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
-hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o
-hw-obj-$(CONFIG_DMA) += dma.o
-hw-obj-$(CONFIG_I82374) += i82374.o
-hw-obj-$(CONFIG_HPET) += hpet.o
-hw-obj-$(CONFIG_APPLESMC) += applesmc.o
-hw-obj-$(CONFIG_SMARTCARD) += ccid-card-passthru.o
-hw-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o
-hw-obj-$(CONFIG_I8259) += i8259_common.o i8259.o
+common-obj-y = usb/ ide/
+common-obj-y += loader.o
+common-obj-$(CONFIG_VIRTIO) += virtio-console.o
+common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
+common-obj-y += fw_cfg.o
+common-obj-$(CONFIG_PCI) += pci.o pci_bridge.o pci_bridge_dev.o
+common-obj-$(CONFIG_PCI) += msix.o msi.o
+common-obj-$(CONFIG_PCI) += shpc.o
+common-obj-$(CONFIG_PCI) += slotid_cap.o
+common-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o
+common-obj-$(CONFIG_PCI) += ioh3420.o xio3130_upstream.o xio3130_downstream.o
+common-obj-y += watchdog.o
+common-obj-$(CONFIG_ISA_MMIO) += isa_mmio.o
+common-obj-$(CONFIG_ECC) += ecc.o
+common-obj-$(CONFIG_NAND) += nand.o
+common-obj-$(CONFIG_PFLASH_CFI01) += pflash_cfi01.o
+common-obj-$(CONFIG_PFLASH_CFI02) += pflash_cfi02.o
+
+common-obj-$(CONFIG_M48T59) += m48t59.o
+common-obj-$(CONFIG_ESCC) += escc.o
+common-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o
+
+common-obj-$(CONFIG_SERIAL) += serial.o
+common-obj-$(CONFIG_PARALLEL) += parallel.o
+common-obj-$(CONFIG_I8254) += i8254_common.o i8254.o
+common-obj-$(CONFIG_PCSPK) += pcspk.o
+common-obj-$(CONFIG_PCKBD) += pckbd.o
+common-obj-$(CONFIG_FDC) += fdc.o
+# qemu-kvm: acpi_piix4.c uses TARGET_I386 define
+#common-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
+common-obj-$(CONFIG_APM) += pm_smbus.o apm.o
+common-obj-$(CONFIG_DMA) += dma.o
+common-obj-$(CONFIG_I82374) += i82374.o
+common-obj-$(CONFIG_HPET) += hpet.o
+common-obj-$(CONFIG_APPLESMC) += applesmc.o
+common-obj-$(CONFIG_SMARTCARD) += ccid-card-passthru.o
+common-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o
+common-obj-$(CONFIG_I8259) += i8259_common.o i8259.o
+common-obj-y += fifo.o
# PPC devices
-hw-obj-$(CONFIG_PREP_PCI) += prep_pci.o
-hw-obj-$(CONFIG_I82378) += i82378.o
+common-obj-$(CONFIG_PREP_PCI) += prep_pci.o
+common-obj-$(CONFIG_I82378) += i82378.o
# Mac shared devices
-hw-obj-$(CONFIG_MACIO) += macio.o
-hw-obj-$(CONFIG_CUDA) += cuda.o
-hw-obj-$(CONFIG_ADB) += adb.o
-hw-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o
-hw-obj-$(CONFIG_MAC_DBDMA) += mac_dbdma.o
+common-obj-$(CONFIG_MACIO) += macio.o
+common-obj-$(CONFIG_CUDA) += cuda.o
+common-obj-$(CONFIG_ADB) += adb.o
+common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o
+common-obj-$(CONFIG_MAC_DBDMA) += mac_dbdma.o
# OldWorld PowerMac
-hw-obj-$(CONFIG_HEATHROW_PIC) += heathrow_pic.o
-hw-obj-$(CONFIG_GRACKLE_PCI) += grackle_pci.o
+common-obj-$(CONFIG_HEATHROW_PIC) += heathrow_pic.o
+common-obj-$(CONFIG_GRACKLE_PCI) += grackle_pci.o
# NewWorld PowerMac
-hw-obj-$(CONFIG_UNIN_PCI) += unin_pci.o
-hw-obj-$(CONFIG_DEC_PCI) += dec_pci.o
+common-obj-$(CONFIG_UNIN_PCI) += unin_pci.o
+common-obj-$(CONFIG_DEC_PCI) += dec_pci.o
# PowerPC E500 boards
-hw-obj-$(CONFIG_PPCE500_PCI) += ppce500_pci.o
+common-obj-$(CONFIG_PPCE500_PCI) += ppce500_pci.o
# MIPS devices
-hw-obj-$(CONFIG_PIIX4) += piix4.o
-hw-obj-$(CONFIG_G364FB) += g364fb.o
-hw-obj-$(CONFIG_JAZZ_LED) += jazz_led.o
+common-obj-$(CONFIG_PIIX4) += piix4.o
+common-obj-$(CONFIG_G364FB) += g364fb.o
+common-obj-$(CONFIG_JAZZ_LED) += jazz_led.o
# Xilinx devices
-hw-obj-$(CONFIG_XILINX) += xilinx_intc.o
-hw-obj-$(CONFIG_XILINX) += xilinx_timer.o
-hw-obj-$(CONFIG_XILINX) += xilinx_uartlite.o
-hw-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o
-hw-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o
-hw-obj-$(CONFIG_XILINX_AXI) += stream.o
+common-obj-$(CONFIG_XILINX) += xilinx_intc.o
+common-obj-$(CONFIG_XILINX) += xilinx_timer.o
+common-obj-$(CONFIG_XILINX) += xilinx_uartlite.o
+common-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o
+common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o
+common-obj-$(CONFIG_XILINX_AXI) += stream.o
# PKUnity SoC devices
-hw-obj-$(CONFIG_PUV3) += puv3_intc.o
-hw-obj-$(CONFIG_PUV3) += puv3_ost.o
-hw-obj-$(CONFIG_PUV3) += puv3_gpio.o
-hw-obj-$(CONFIG_PUV3) += puv3_pm.o
-hw-obj-$(CONFIG_PUV3) += puv3_dma.o
+common-obj-$(CONFIG_PUV3) += puv3_intc.o
+common-obj-$(CONFIG_PUV3) += puv3_ost.o
+common-obj-$(CONFIG_PUV3) += puv3_gpio.o
+common-obj-$(CONFIG_PUV3) += puv3_pm.o
+common-obj-$(CONFIG_PUV3) += puv3_dma.o
# ARM devices
-hw-obj-$(CONFIG_ARM_TIMER) += arm_timer.o
-hw-obj-$(CONFIG_PL011) += pl011.o
-hw-obj-$(CONFIG_PL022) += pl022.o
-hw-obj-$(CONFIG_PL031) += pl031.o
-hw-obj-$(CONFIG_PL041) += pl041.o lm4549.o
-hw-obj-$(CONFIG_PL050) += pl050.o
-hw-obj-$(CONFIG_PL061) += pl061.o
-hw-obj-$(CONFIG_PL080) += pl080.o
-hw-obj-$(CONFIG_PL110) += pl110.o
-hw-obj-$(CONFIG_PL181) += pl181.o
-hw-obj-$(CONFIG_PL190) += pl190.o
-hw-obj-$(CONFIG_PL310) += arm_l2x0.o
-hw-obj-$(CONFIG_VERSATILE_PCI) += versatile_pci.o
-hw-obj-$(CONFIG_VERSATILE_I2C) += versatile_i2c.o
-hw-obj-$(CONFIG_CADENCE) += cadence_uart.o
-hw-obj-$(CONFIG_CADENCE) += cadence_ttc.o
-hw-obj-$(CONFIG_CADENCE) += cadence_gem.o
-hw-obj-$(CONFIG_XGMAC) += xgmac.o
+common-obj-$(CONFIG_ARM_TIMER) += arm_timer.o
+common-obj-$(CONFIG_PL011) += pl011.o
+common-obj-$(CONFIG_PL022) += pl022.o
+common-obj-$(CONFIG_PL031) += pl031.o
+common-obj-$(CONFIG_PL041) += pl041.o lm4549.o
+common-obj-$(CONFIG_PL050) += pl050.o
+common-obj-$(CONFIG_PL061) += pl061.o
+common-obj-$(CONFIG_PL080) += pl080.o
+common-obj-$(CONFIG_PL110) += pl110.o
+common-obj-$(CONFIG_PL181) += pl181.o
+common-obj-$(CONFIG_PL190) += pl190.o
+common-obj-$(CONFIG_PL310) += arm_l2x0.o
+common-obj-$(CONFIG_VERSATILE_PCI) += versatile_pci.o
+common-obj-$(CONFIG_VERSATILE_I2C) += versatile_i2c.o
+common-obj-$(CONFIG_CADENCE) += cadence_uart.o
+common-obj-$(CONFIG_CADENCE) += cadence_ttc.o
+common-obj-$(CONFIG_CADENCE) += cadence_gem.o
+common-obj-$(CONFIG_XGMAC) += xgmac.o
# PCI watchdog devices
-hw-obj-$(CONFIG_PCI) += wdt_i6300esb.o
+common-obj-$(CONFIG_PCI) += wdt_i6300esb.o
-hw-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o
+common-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o
# PCI network cards
-hw-obj-$(CONFIG_NE2000_PCI) += ne2000.o
-hw-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o
-hw-obj-$(CONFIG_PCNET_PCI) += pcnet-pci.o
-hw-obj-$(CONFIG_PCNET_COMMON) += pcnet.o
-hw-obj-$(CONFIG_E1000_PCI) += e1000.o
-hw-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o
-
-hw-obj-$(CONFIG_SMC91C111) += smc91c111.o
-hw-obj-$(CONFIG_LAN9118) += lan9118.o
-hw-obj-$(CONFIG_NE2000_ISA) += ne2000-isa.o
-hw-obj-$(CONFIG_OPENCORES_ETH) += opencores_eth.o
+common-obj-$(CONFIG_NE2000_PCI) += ne2000.o
+common-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o
+common-obj-$(CONFIG_PCNET_PCI) += pcnet-pci.o
+common-obj-$(CONFIG_PCNET_COMMON) += pcnet.o
+common-obj-$(CONFIG_E1000_PCI) += e1000.o
+common-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o
+
+common-obj-$(CONFIG_SMC91C111) += smc91c111.o
+common-obj-$(CONFIG_LAN9118) += lan9118.o
+common-obj-$(CONFIG_NE2000_ISA) += ne2000-isa.o
+common-obj-$(CONFIG_OPENCORES_ETH) += opencores_eth.o
# SCSI layer
-hw-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o
-hw-obj-$(CONFIG_MEGASAS_SCSI_PCI) += megasas.o
-hw-obj-$(CONFIG_ESP) += esp.o
-hw-obj-$(CONFIG_ESP_PCI) += esp-pci.o
+common-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o
+common-obj-$(CONFIG_MEGASAS_SCSI_PCI) += megasas.o
+common-obj-$(CONFIG_ESP) += esp.o
+common-obj-$(CONFIG_ESP_PCI) += esp-pci.o
-hw-obj-y += sysbus.o isa-bus.o
-hw-obj-y += qdev-addr.o
+common-obj-y += sysbus.o isa-bus.o
+common-obj-y += qdev-addr.o
# VGA
-hw-obj-$(CONFIG_VGA_PCI) += vga-pci.o
-hw-obj-$(CONFIG_VGA_ISA) += vga-isa.o
-hw-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o
-hw-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o
-hw-obj-$(CONFIG_VMMOUSE) += vmmouse.o
-hw-obj-$(CONFIG_VGA_CIRRUS) += cirrus_vga.o
+common-obj-$(CONFIG_VGA_PCI) += vga-pci.o
+common-obj-$(CONFIG_VGA_ISA) += vga-isa.o
+common-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o
+common-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o
+common-obj-$(CONFIG_VMMOUSE) += vmmouse.o
+common-obj-$(CONFIG_VGA_CIRRUS) += cirrus_vga.o
-hw-obj-$(CONFIG_RC4030) += rc4030.o
-hw-obj-$(CONFIG_DP8393X) += dp8393x.o
-hw-obj-$(CONFIG_DS1225Y) += ds1225y.o
-hw-obj-$(CONFIG_MIPSNET) += mipsnet.o
+common-obj-$(CONFIG_RC4030) += rc4030.o
+common-obj-$(CONFIG_DP8393X) += dp8393x.o
+common-obj-$(CONFIG_DS1225Y) += ds1225y.o
+common-obj-$(CONFIG_MIPSNET) += mipsnet.o
-hw-obj-y += null-machine.o
+common-obj-y += null-machine.o
# Sound
sound-obj-y =
@@ -149,9 +150,9 @@ sound-obj-$(CONFIG_HDA) += intel-hda.o hda-audio.o
$(obj)/adlib.o $(obj)/fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0
-hw-obj-$(CONFIG_SOUND) += $(sound-obj-y)
+common-obj-$(CONFIG_SOUND) += $(sound-obj-y)
-hw-obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/
+common-obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/
common-obj-y += usb/
common-obj-y += irq.o
@@ -174,6 +175,7 @@ common-obj-y += scsi-disk.o cdrom.o hd-geometry.o block-common.o
common-obj-y += scsi-generic.o scsi-bus.o
common-obj-y += hid.o
common-obj-$(CONFIG_SSI) += ssi.o
+common-obj-$(CONFIG_SSI_M25P80) += m25p80.o
common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
common-obj-$(CONFIG_SD) += sd.o
common-obj-y += bt.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o
@@ -199,7 +201,8 @@ obj-$(CONFIG_VGA) += vga.o
obj-$(CONFIG_SOFTMMU) += device-hotplug.o
obj-$(CONFIG_XEN) += xen_domainbuild.o xen_machine_pv.o
-# Inter-VM PCI shared memory
+# Inter-VM PCI shared memory & VFIO PCI device assignment
ifeq ($(CONFIG_PCI), y)
obj-$(CONFIG_KVM) += ivshmem.o
+obj-$(CONFIG_LINUX) += vfio_pci.o
endif
diff --git a/hw/acpi.c b/hw/acpi.c
index f7950be26..f4aca493f 100644
--- a/hw/acpi.c
+++ b/hw/acpi.c
@@ -61,18 +61,6 @@ static int acpi_checksum(const uint8_t *data, int len)
return (-sum) & 0xff;
}
-/* like strncpy() but zero-fills the tail of destination */
-static void strzcpy(char *dst, const char *src, size_t size)
-{
- size_t len = strlen(src);
- if (len >= size) {
- len = size;
- } else {
- memset(dst + len, 0, size - len);
- }
- memcpy(dst, src, len);
-}
-
/* XXX fixme: this function uses obsolete argument parsing interface */
int acpi_table_add(const char *t)
{
@@ -157,7 +145,8 @@ int acpi_table_add(const char *t)
hdr._length = cpu_to_le16(len);
if (get_param_value(buf, sizeof(buf), "sig", t)) {
- strzcpy(hdr.sig, buf, sizeof(hdr.sig));
+ /* strncpy is justified: the field need not be NUL-terminated. */
+ strncpy(hdr.sig, buf, sizeof(hdr.sig));
++changed;
}
@@ -187,12 +176,14 @@ int acpi_table_add(const char *t)
}
if (get_param_value(buf, sizeof(buf), "oem_id", t)) {
- strzcpy(hdr.oem_id, buf, sizeof(hdr.oem_id));
+ /* strncpy is justified: the field need not be NUL-terminated. */
+ strncpy(hdr.oem_id, buf, sizeof(hdr.oem_id));
++changed;
}
if (get_param_value(buf, sizeof(buf), "oem_table_id", t)) {
- strzcpy(hdr.oem_table_id, buf, sizeof(hdr.oem_table_id));
+ /* strncpy is justified: the field need not be NUL-terminated. */
+ strncpy(hdr.oem_table_id, buf, sizeof(hdr.oem_table_id));
++changed;
}
@@ -207,7 +198,8 @@ int acpi_table_add(const char *t)
}
if (get_param_value(buf, sizeof(buf), "asl_compiler_id", t)) {
- strzcpy(hdr.asl_compiler_id, buf, sizeof(hdr.asl_compiler_id));
+ /* strncpy is justified: the field need not be NUL-terminated. */
+ strncpy(hdr.asl_compiler_id, buf, sizeof(hdr.asl_compiler_id));
++changed;
}
diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c
index dad38870a..a48187fa7 100644
--- a/hw/acpi_piix4.c
+++ b/hw/acpi_piix4.c
@@ -73,6 +73,7 @@ typedef struct PIIX4PMState {
qemu_irq smi_irq;
int kvm_enabled;
Notifier machine_ready;
+ Notifier powerdown_notifier;
/* for pci hotplug */
struct gpe_regs gpe_cpu;
@@ -368,9 +369,9 @@ static void piix4_reset(void *opaque)
piix4_update_hotplug(s);
}
-static void piix4_powerdown(void *opaque, int irq, int power_failing)
+static void piix4_pm_powerdown_req(Notifier *n, void *opaque)
{
- PIIX4PMState *s = opaque;
+ PIIX4PMState *s = container_of(n, PIIX4PMState, powerdown_notifier);
assert(s != NULL);
acpi_pm1_evt_power_down(&s->ar);
@@ -427,7 +428,8 @@ static int piix4_pm_initfn(PCIDevice *dev)
acpi_pm_tmr_init(&s->ar, pm_tmr_timer);
acpi_gpe_init(&s->ar, GPE_LEN);
- qemu_system_powerdown = *qemu_allocate_irqs(piix4_powerdown, s, 1);
+ s->powerdown_notifier.notify = piix4_pm_powerdown_req;
+ qemu_register_powerdown_notifier(&s->powerdown_notifier);
pm_smbus_init(&s->dev.qdev, &s->smb);
s->machine_ready.notify = piix4_pm_machine_ready;
diff --git a/hw/ads7846.c b/hw/ads7846.c
index 41c7f101c..2ea9e55bb 100644
--- a/hw/ads7846.c
+++ b/hw/ads7846.c
@@ -119,11 +119,12 @@ static int ads7856_post_load(void *opaque, int version_id)
static const VMStateDescription vmstate_ads7846 = {
.name = "ads7846",
- .version_id = 0,
- .minimum_version_id = 0,
- .minimum_version_id_old = 0,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
.post_load = ads7856_post_load,
.fields = (VMStateField[]) {
+ VMSTATE_SSI_SLAVE(ssidev, ADS7846State),
VMSTATE_INT32_ARRAY(input, ADS7846State, 8),
VMSTATE_INT32(noise, ADS7846State),
VMSTATE_INT32(cycle, ADS7846State),
diff --git a/hw/alpha_dp264.c b/hw/alpha_dp264.c
index 9eb939f38..5ea04c75a 100644
--- a/hw/alpha_dp264.c
+++ b/hw/alpha_dp264.c
@@ -77,7 +77,7 @@ static void clipper_init(ram_addr_t ram_size,
isa_create_simple(isa_bus, "i8042");
/* VGA setup. Don't bother loading the bios. */
- alpha_pci_vga_setup(pci_bus);
+ pci_vga_init(pci_bus);
/* Serial code setup. */
for (i = 0; i < MAX_SERIAL_PORTS; ++i) {
diff --git a/hw/alpha_pci.c b/hw/alpha_pci.c
index ea546f83f..8079a46ae 100644
--- a/hw/alpha_pci.c
+++ b/hw/alpha_pci.c
@@ -10,8 +10,6 @@
#include "alpha_sys.h"
#include "qemu-log.h"
#include "sysemu.h"
-#include "vmware_vga.h"
-#include "vga-pci.h"
/* PCI IO reads/writes, to byte-word addressable memory. */
@@ -109,25 +107,3 @@ const MemoryRegionOps alpha_pci_iack_ops = {
.max_access_size = 4,
},
};
-
-void alpha_pci_vga_setup(PCIBus *pci_bus)
-{
- switch (vga_interface_type) {
-#ifdef CONFIG_SPICE
- case VGA_QXL:
- pci_create_simple(pci_bus, -1, "qxl-vga");
- return;
-#endif
- case VGA_CIRRUS:
- pci_cirrus_vga_init(pci_bus);
- return;
- case VGA_VMWARE:
- pci_vmsvga_init(pci_bus);
- return;
- }
- /* If VGA is enabled at all, and one of the above didn't work, then
- fallback to Standard VGA. */
- if (vga_interface_type != VGA_NONE) {
- pci_vga_init(pci_bus);
- }
-}
diff --git a/hw/alpha_sys.h b/hw/alpha_sys.h
index de40f8b61..7604d09c8 100644
--- a/hw/alpha_sys.h
+++ b/hw/alpha_sys.h
@@ -19,6 +19,4 @@ extern const MemoryRegionOps alpha_pci_bw_io_ops;
extern const MemoryRegionOps alpha_pci_conf1_ops;
extern const MemoryRegionOps alpha_pci_iack_ops;
-void alpha_pci_vga_setup(PCIBus *pci_bus);
-
#endif
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 2b39fb3c8..6d049e7de 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -1,6 +1,7 @@
obj-y = integratorcp.o versatilepb.o arm_pic.o
obj-y += arm_boot.o
obj-y += xilinx_zynq.o zynq_slcr.o
+obj-y += xilinx_spips.o
obj-y += arm_gic.o arm_gic_common.o
obj-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
obj-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o
diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c
index 6a0832eb3..5c0911647 100644
--- a/hw/armv7m_nvic.c
+++ b/hw/armv7m_nvic.c
@@ -489,7 +489,8 @@ static int armv7m_nvic_init(SysBusDevice *dev)
*/
memory_region_init_alias(&s->gic_iomem_alias, "nvic-gic", &s->gic.iomem,
0x100, 0xc00);
- memory_region_add_subregion_overlap(&s->container, 0x100, &s->gic.iomem, 1);
+ memory_region_add_subregion_overlap(&s->container, 0x100,
+ &s->gic_iomem_alias, 1);
/* Map the whole thing into system memory at the location required
* by the v7M architecture.
*/
diff --git a/hw/blizzard.c b/hw/blizzard.c
index d1c9d8151..06e19b364 100644
--- a/hw/blizzard.c
+++ b/hw/blizzard.c
@@ -878,8 +878,6 @@ void s1d13745_write_block(void *opaque, int dc,
len -= 2;
buf += 2;
}
-
- return;
}
static void blizzard_update_display(void *opaque)
diff --git a/hw/bt-hci.c b/hw/bt-hci.c
index a3a7fb49e..e54cfd781 100644
--- a/hw/bt-hci.c
+++ b/hw/bt-hci.c
@@ -786,7 +786,6 @@ static void bt_hci_lmp_connection_request(struct bt_link_s *link)
memcpy(&params.dev_class, &link->host->class, sizeof(params.dev_class));
params.link_type = ACL_LINK;
bt_hci_event(hci, EVT_CONN_REQUEST, &params, EVT_CONN_REQUEST_SIZE);
- return;
}
static void bt_hci_conn_accept_timeout(void *opaque)
@@ -943,7 +942,6 @@ static int bt_hci_name_req(struct bt_hci_s *hci, bdaddr_t *bdaddr)
{
struct bt_device_s *slave;
evt_remote_name_req_complete params;
- int len;
for (slave = hci->device.net->slave; slave; slave = slave->next)
if (slave->page_scan && !bacmp(&slave->bd_addr, bdaddr))
@@ -955,9 +953,7 @@ static int bt_hci_name_req(struct bt_hci_s *hci, bdaddr_t *bdaddr)
params.status = HCI_SUCCESS;
bacpy(&params.bdaddr, &slave->bd_addr);
- len = snprintf(params.name, sizeof(params.name),
- "%s", slave->lmp_name ?: "");
- memset(params.name + len, 0, sizeof(params.name) - len);
+ pstrcpy(params.name, sizeof(params.name), slave->lmp_name ?: "");
bt_hci_event(hci, EVT_REMOTE_NAME_REQ_COMPLETE,
&params, EVT_REMOTE_NAME_REQ_COMPLETE_SIZE);
@@ -1388,7 +1384,7 @@ static inline void bt_hci_event_complete_read_local_name(struct bt_hci_s *hci)
params.status = HCI_SUCCESS;
memset(params.name, 0, sizeof(params.name));
if (hci->device.lmp_name)
- strncpy(params.name, hci->device.lmp_name, sizeof(params.name));
+ pstrcpy(params.name, sizeof(params.name), hci->device.lmp_name);
bt_hci_event_complete(hci, &params, READ_LOCAL_NAME_RP_SIZE);
}
diff --git a/hw/cadence_uart.c b/hw/cadence_uart.c
index d98e53137..f8afc4ed2 100644
--- a/hw/cadence_uart.c
+++ b/hw/cadence_uart.c
@@ -404,7 +404,7 @@ static uint64_t uart_read(void *opaque, target_phys_addr_t offset,
uint32_t c = 0;
offset >>= 2;
- if (offset > R_MAX) {
+ if (offset >= R_MAX) {
return 0;
} else if (offset == R_TX_RX) {
uart_read_rx_fifo(s, &c);
diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c
index 035cc346b..df059307b 100644
--- a/hw/cirrus_vga.c
+++ b/hw/cirrus_vga.c
@@ -28,7 +28,6 @@
*/
#include "hw.h"
#include "pci.h"
-#include "vga-pci.h"
#include "console.h"
#include "vga_int.h"
#include "loader.h"
@@ -2441,6 +2440,8 @@ static uint32_t cirrus_vga_ioport_read(void *opaque, uint32_t addr)
VGACommonState *s = &c->vga;
int val, index;
+ qemu_flush_coalesced_mmio_buffer();
+
if (vga_ioport_invalid(s, addr)) {
val = 0xff;
} else {
@@ -2534,6 +2535,8 @@ static void cirrus_vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
VGACommonState *s = &c->vga;
int index;
+ qemu_flush_coalesced_mmio_buffer();
+
/* check port range access depending on color/monochrome mode */
if (vga_ioport_invalid(s, addr)) {
return;
@@ -2854,6 +2857,7 @@ static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci,
/* I/O handler for LFB */
memory_region_init_io(&s->cirrus_linear_io, &cirrus_linear_io_ops, s,
"cirrus-linear-io", VGA_RAM_SIZE);
+ memory_region_set_flush_coalesced(&s->cirrus_linear_io);
/* I/O handler for LFB */
memory_region_init_io(&s->cirrus_linear_bitblt_io,
@@ -2861,10 +2865,12 @@ static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci,
s,
"cirrus-bitblt-mmio",
0x400000);
+ memory_region_set_flush_coalesced(&s->cirrus_linear_bitblt_io);
/* I/O handler for memory-mapped I/O */
memory_region_init_io(&s->cirrus_mmio_io, &cirrus_mmio_io_ops, s,
"cirrus-mmio", CIRRUS_PNPMMIO_SIZE);
+ memory_region_set_flush_coalesced(&s->cirrus_mmio_io);
s->real_vram_size =
(s->device_id == CIRRUS_ID_CLGD5446) ? 4096 * 1024 : 2048 * 1024;
@@ -2963,11 +2969,6 @@ static int pci_cirrus_vga_initfn(PCIDevice *dev)
return 0;
}
-DeviceState *pci_cirrus_vga_init(PCIBus *bus)
-{
- return &pci_create_simple(bus, -1, "cirrus-vga")->qdev;
-}
-
static void cirrus_vga_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
diff --git a/hw/device-hotplug.c b/hw/device-hotplug.c
index 2bdc615b4..eec0fe314 100644
--- a/hw/device-hotplug.c
+++ b/hw/device-hotplug.c
@@ -89,5 +89,4 @@ err:
if (dinfo) {
drive_put_ref(dinfo);
}
- return;
}
diff --git a/hw/e1000.c b/hw/e1000.c
index ae8a6c552..ec3a7c4ec 100644
--- a/hw/e1000.c
+++ b/hw/e1000.c
@@ -295,6 +295,7 @@ set_rx_control(E1000State *s, int index, uint32_t val)
s->rxbuf_min_shift = ((val / E1000_RCTL_RDMTS_QUAT) & 3) + 1;
DBGOUT(RX, "RCTL: %d, mac_reg[RCTL] = 0x%x\n", s->mac_reg[RDT],
s->mac_reg[RCTL]);
+ qemu_flush_queued_packets(&s->nic->nc);
}
static void
@@ -926,6 +927,9 @@ set_rdt(E1000State *s, int index, uint32_t val)
{
s->check_rxov = 0;
s->mac_reg[index] = val & 0xffff;
+ if (e1000_has_rxbufs(s, 1)) {
+ qemu_flush_queued_packets(&s->nic->nc);
+ }
}
static void
diff --git a/hw/eepro100.c b/hw/eepro100.c
index 50d117e35..5b231163d 100644
--- a/hw/eepro100.c
+++ b/hw/eepro100.c
@@ -1036,6 +1036,7 @@ static void eepro100_ru_command(EEPRO100State * s, uint8_t val)
}
set_ru_state(s, ru_ready);
s->ru_offset = e100_read_reg4(s, SCBPointer);
+ qemu_flush_queued_packets(&s->nic->nc);
TRACE(OTHER, logout("val=0x%02x (rx start)\n", val));
break;
case RX_RESUME:
@@ -1770,7 +1771,8 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
if (rfd_command & COMMAND_EL) {
/* EL bit is set, so this was the last frame. */
logout("receive: Running out of frames\n");
- set_ru_state(s, ru_suspended);
+ set_ru_state(s, ru_no_resources);
+ eepro100_rnr_interrupt(s);
}
if (rfd_command & COMMAND_S) {
/* S bit is set. */
diff --git a/hw/exynos4210_combiner.c b/hw/exynos4210_combiner.c
index 80af22cc3..60b33c788 100644
--- a/hw/exynos4210_combiner.c
+++ b/hw/exynos4210_combiner.c
@@ -347,8 +347,6 @@ static void exynos4210_combiner_write(void *opaque, target_phys_addr_t offset,
TARGET_FMT_plx "\n", offset);
break;
}
-
- return;
}
/* Get combiner group and bit from irq number */
@@ -380,8 +378,6 @@ static void exynos4210_combiner_handler(void *opaque, int irq, int level)
}
exynos4210_combiner_update(s, group_n);
-
- return;
}
static void exynos4210_combiner_reset(DeviceState *d)
diff --git a/hw/exynos4210_gic.c b/hw/exynos4210_gic.c
index 7d03dd9ae..4fea09873 100644
--- a/hw/exynos4210_gic.c
+++ b/hw/exynos4210_gic.c
@@ -193,8 +193,6 @@ static void exynos4210_irq_handler(void *opaque, int irq, int level)
/* Bypass */
qemu_set_irq(s->board_irqs[irq], level);
-
- return;
}
/*
@@ -410,8 +408,6 @@ static void exynos4210_irq_gate_handler(void *opaque, int irq, int level)
}
qemu_irq_lower(s->out);
-
- return;
}
static void exynos4210_irq_gate_reset(DeviceState *d)
diff --git a/hw/exynos4210_mct.c b/hw/exynos4210_mct.c
index 7a22b1f90..6f94ce230 100644
--- a/hw/exynos4210_mct.c
+++ b/hw/exynos4210_mct.c
@@ -574,8 +574,6 @@ static void exynos4210_gfrc_event(void *opaque)
exynos4210_gfrc_set_count(&s->g_timer, distance);
exynos4210_gfrc_start(&s->g_timer);
-
- return;
}
/*
diff --git a/hw/fdc.c b/hw/fdc.c
index 08830c1ba..25a49e350 100644
--- a/hw/fdc.c
+++ b/hw/fdc.c
@@ -1286,8 +1286,6 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction)
fdctrl->msr |= FD_MSR_DIO;
/* IO based transfer: calculate len */
fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
-
- return;
}
/* Prepare a transfer of deleted data */
@@ -1994,11 +1992,11 @@ static int fdctrl_connect_drives(FDCtrl *fdctrl)
drive->fdctrl = fdctrl;
if (drive->bs) {
- if (bdrv_get_on_error(drive->bs, 0) != BLOCK_ERR_STOP_ENOSPC) {
+ if (bdrv_get_on_error(drive->bs, 0) != BLOCKDEV_ON_ERROR_ENOSPC) {
error_report("fdc doesn't support drive option werror");
return -1;
}
- if (bdrv_get_on_error(drive->bs, 1) != BLOCK_ERR_REPORT) {
+ if (bdrv_get_on_error(drive->bs, 1) != BLOCKDEV_ON_ERROR_REPORT) {
error_report("fdc doesn't support drive option rerror");
return -1;
}
diff --git a/hw/fifo.c b/hw/fifo.c
new file mode 100644
index 000000000..68a955a77
--- /dev/null
+++ b/hw/fifo.c
@@ -0,0 +1,78 @@
+/*
+ * Generic FIFO component, implemented as a circular buffer.
+ *
+ * Copyright (c) 2012 Peter A. G. Crosthwaite
+ *
+ * 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.
+ *
+ * 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 "fifo.h"
+
+void fifo8_create(Fifo8 *fifo, uint32_t capacity)
+{
+ fifo->data = g_new(uint8_t, capacity);
+ fifo->capacity = capacity;
+ fifo->head = 0;
+ fifo->num = 0;
+}
+
+void fifo8_destroy(Fifo8 *fifo)
+{
+ g_free(fifo->data);
+}
+
+void fifo8_push(Fifo8 *fifo, uint8_t data)
+{
+ if (fifo->num == fifo->capacity) {
+ abort();
+ }
+ fifo->data[(fifo->head + fifo->num) % fifo->capacity] = data;
+ fifo->num++;
+}
+
+uint8_t fifo8_pop(Fifo8 *fifo)
+{
+ uint8_t ret;
+
+ if (fifo->num == 0) {
+ abort();
+ }
+ ret = fifo->data[fifo->head++];
+ fifo->head %= fifo->capacity;
+ fifo->num--;
+ return ret;
+}
+
+void fifo8_reset(Fifo8 *fifo)
+{
+ fifo->num = 0;
+}
+
+bool fifo8_is_empty(Fifo8 *fifo)
+{
+ return (fifo->num == 0);
+}
+
+bool fifo8_is_full(Fifo8 *fifo)
+{
+ return (fifo->num == fifo->capacity);
+}
+
+const VMStateDescription vmstate_fifo8 = {
+ .name = "Fifo8",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_VBUFFER_UINT32(data, Fifo8, 1, NULL, 0, capacity),
+ VMSTATE_UINT32(head, Fifo8),
+ VMSTATE_UINT32(num, Fifo8),
+ VMSTATE_END_OF_LIST()
+ }
+};
diff --git a/hw/fifo.h b/hw/fifo.h
new file mode 100644
index 000000000..f23890abf
--- /dev/null
+++ b/hw/fifo.h
@@ -0,0 +1,99 @@
+#ifndef FIFO_H
+#define FIFO_H
+
+#include "hw.h"
+
+typedef struct {
+ /* All fields are private */
+ uint8_t *data;
+ uint32_t capacity;
+ uint32_t head;
+ uint32_t num;
+} Fifo8;
+
+/**
+ * fifo8_create:
+ * @fifo: struct Fifo8 to initialise with new FIFO
+ * @capacity: capacity of the newly created FIFO
+ *
+ * Create a FIFO of the specified size. Clients should call fifo8_destroy()
+ * when finished using the fifo. The FIFO is initially empty.
+ */
+
+void fifo8_create(Fifo8 *fifo, uint32_t capacity);
+
+/**
+ * fifo8_destroy:
+ * @fifo: FIFO to cleanup
+ *
+ * Cleanup a FIFO created with fifo8_create(). Frees memory created for FIFO
+ *storage. The FIFO is no longer usable after this has been called.
+ */
+
+void fifo8_destroy(Fifo8 *fifo);
+
+/**
+ * fifo8_push:
+ * @fifo: FIFO to push to
+ * @data: data byte to push
+ *
+ * Push a data byte to the FIFO. Behaviour is undefined if the FIFO is full.
+ * Clients are responsible for checking for fullness using fifo8_is_full().
+ */
+
+void fifo8_push(Fifo8 *fifo, uint8_t data);
+
+/**
+ * fifo8_pop:
+ * @fifo: fifo to pop from
+ *
+ * Pop a data byte from the FIFO. Behaviour is undefined if the FIFO is empty.
+ * Clients are responsible for checking for emptyness using fifo8_is_empty().
+ *
+ * Returns: The popped data byte.
+ */
+
+uint8_t fifo8_pop(Fifo8 *fifo);
+
+/**
+ * fifo8_reset:
+ * @fifo: FIFO to reset
+ *
+ * Reset a FIFO. All data is discarded and the FIFO is emptied.
+ */
+
+void fifo8_reset(Fifo8 *fifo);
+
+/**
+ * fifo8_is_empty:
+ * @fifo: FIFO to check
+ *
+ * Check if a FIFO is empty.
+ *
+ * Returns: True if the fifo is empty, false otherwise.
+ */
+
+bool fifo8_is_empty(Fifo8 *fifo);
+
+/**
+ * fifo8_is_full:
+ * @fifo: FIFO to check
+ *
+ * Check if a FIFO is full.
+ *
+ * Returns: True if the fifo is full, false otherwise.
+ */
+
+bool fifo8_is_full(Fifo8 *fifo);
+
+extern const VMStateDescription vmstate_fifo8;
+
+#define VMSTATE_FIFO8(_field, _state) { \
+ .name = (stringify(_field)), \
+ .size = sizeof(Fifo8), \
+ .vmsd = &vmstate_fifo8, \
+ .flags = VMS_STRUCT, \
+ .offset = vmstate_offset_value(_state, _field, Fifo8), \
+}
+
+#endif /* FIFO_H */
diff --git a/hw/framebuffer.c b/hw/framebuffer.c
index 85a00a579..27fa6f51f 100644
--- a/hw/framebuffer.c
+++ b/hw/framebuffer.c
@@ -107,5 +107,4 @@ void framebuffer_update_display(
DIRTY_MEMORY_VGA);
*first_row = first;
*last_row = last;
- return;
}
diff --git a/hw/fw_cfg.c b/hw/fw_cfg.c
index 7b3b5769a..dcde1a943 100644
--- a/hw/fw_cfg.c
+++ b/hw/fw_cfg.c
@@ -183,6 +183,30 @@ static void fw_cfg_bootsplash(FWCfgState *s)
}
}
+static void fw_cfg_reboot(FWCfgState *s)
+{
+ int reboot_timeout = -1;
+ char *p;
+ const char *temp;
+
+ /* get user configuration */
+ QemuOptsList *plist = qemu_find_opts("boot-opts");
+ QemuOpts *opts = QTAILQ_FIRST(&plist->head);
+ if (opts != NULL) {
+ temp = qemu_opt_get(opts, "reboot-timeout");
+ if (temp != NULL) {
+ p = (char *)temp;
+ reboot_timeout = strtol(p, (char **)&p, 10);
+ }
+ }
+ /* validate the input */
+ if (reboot_timeout > 0xffff) {
+ error_report("reboot timeout is larger than 65535, force it to 65535.");
+ reboot_timeout = 0xffff;
+ }
+ fw_cfg_add_file(s, "etc/boot-fail-wait", g_memdup(&reboot_timeout, 4), 4);
+}
+
static void fw_cfg_write(FWCfgState *s, uint8_t value)
{
int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
@@ -497,6 +521,7 @@ FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port,
fw_cfg_add_i16(s, FW_CFG_MAX_CPUS, (uint16_t)max_cpus);
fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)boot_menu);
fw_cfg_bootsplash(s);
+ fw_cfg_reboot(s);
s->machine_ready.notify = fw_cfg_machine_ready;
qemu_add_machine_init_done_notifier(&s->machine_ready);
diff --git a/hw/hw.h b/hw/hw.h
index e5cb9bf94..16101de3c 100644
--- a/hw/hw.h
+++ b/hw/hw.h
@@ -4,7 +4,7 @@
#include "qemu-common.h"
-#if defined(TARGET_PHYS_ADDR_BITS) && !defined(NEED_CPU_H)
+#if !defined(CONFIG_USER_ONLY) && !defined(NEED_CPU_H)
#include "cpu-common.h"
#endif
diff --git a/hw/ide/Makefile.objs b/hw/ide/Makefile.objs
index cf718dd01..5c8c22aad 100644
--- a/hw/ide/Makefile.objs
+++ b/hw/ide/Makefile.objs
@@ -1,10 +1,10 @@
-hw-obj-$(CONFIG_IDE_CORE) += core.o atapi.o
-hw-obj-$(CONFIG_IDE_QDEV) += qdev.o
-hw-obj-$(CONFIG_IDE_PCI) += pci.o
-hw-obj-$(CONFIG_IDE_ISA) += isa.o
-hw-obj-$(CONFIG_IDE_PIIX) += piix.o
-hw-obj-$(CONFIG_IDE_CMD646) += cmd646.o
-hw-obj-$(CONFIG_IDE_MACIO) += macio.o
-hw-obj-$(CONFIG_IDE_VIA) += via.o
-hw-obj-$(CONFIG_AHCI) += ahci.o
-hw-obj-$(CONFIG_AHCI) += ich.o
+common-obj-$(CONFIG_IDE_CORE) += core.o atapi.o
+common-obj-$(CONFIG_IDE_QDEV) += qdev.o
+common-obj-$(CONFIG_IDE_PCI) += pci.o
+common-obj-$(CONFIG_IDE_ISA) += isa.o
+common-obj-$(CONFIG_IDE_PIIX) += piix.o
+common-obj-$(CONFIG_IDE_CMD646) += cmd646.o
+common-obj-$(CONFIG_IDE_MACIO) += macio.o
+common-obj-$(CONFIG_IDE_VIA) += via.o
+common-obj-$(CONFIG_AHCI) += ahci.o
+common-obj-$(CONFIG_AHCI) += ich.o
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index 5ea3cadb0..68671bc4d 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -1175,7 +1175,6 @@ void ahci_init(AHCIState *s, DeviceState *qdev, DMAContext *dma, int ports)
ad->port_no = i;
ad->port.dma = &ad->dma;
ad->port.dma->ops = &ahci_dma_ops;
- ad->port_regs.cmd = PORT_CMD_SPIN_UP | PORT_CMD_POWER_ON;
}
}
@@ -1199,6 +1198,7 @@ void ahci_reset(AHCIState *s)
pr->irq_stat = 0;
pr->irq_mask = 0;
pr->scr_ctl = 0;
+ pr->cmd = PORT_CMD_SPIN_UP | PORT_CMD_POWER_ON;
ahci_reset_port(s, i);
}
}
diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index f7f714c72..685cbaa88 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -875,6 +875,12 @@ static void cmd_start_stop_unit(IDEState *s, uint8_t* buf)
int sense;
bool start = buf[4] & 1;
bool loej = buf[4] & 2; /* load on start, eject on !start */
+ int pwrcnd = buf[4] & 0xf0;
+
+ if (pwrcnd) {
+ /* eject/load only happens for power condition == 0 */
+ return;
+ }
if (loej) {
if (!start && !s->tray_open && s->tray_locked) {
diff --git a/hw/ide/core.c b/hw/ide/core.c
index d65ef3d58..d683a8cc8 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -53,8 +53,6 @@ static const int smart_attributes[][12] = {
{ 0x0c, 0x03, 0x00, 0x64, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* airflow-temperature-celsius */
{ 190, 0x03, 0x00, 0x45, 0x45, 0x1f, 0x00, 0x1f, 0x1f, 0x00, 0x00, 0x32},
- /* end of list */
- { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
};
static int ide_handle_rw_error(IDEState *s, int error, int op);
@@ -558,32 +556,22 @@ void ide_dma_error(IDEState *s)
static int ide_handle_rw_error(IDEState *s, int error, int op)
{
- int is_read = (op & BM_STATUS_RETRY_READ);
- BlockErrorAction action = bdrv_get_on_error(s->bs, is_read);
+ bool is_read = (op & BM_STATUS_RETRY_READ) != 0;
+ BlockErrorAction action = bdrv_get_error_action(s->bs, is_read, error);
- if (action == BLOCK_ERR_IGNORE) {
- bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_IGNORE, is_read);
- return 0;
- }
-
- if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC)
- || action == BLOCK_ERR_STOP_ANY) {
+ if (action == BDRV_ACTION_STOP) {
s->bus->dma->ops->set_unit(s->bus->dma, s->unit);
s->bus->error_status = op;
- bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_STOP, is_read);
- vm_stop(RUN_STATE_IO_ERROR);
- bdrv_iostatus_set_err(s->bs, error);
- } else {
+ } else if (action == BDRV_ACTION_REPORT) {
if (op & BM_STATUS_DMA_RETRY) {
dma_buf_commit(s);
ide_dma_error(s);
} else {
ide_rw_error(s);
}
- bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_REPORT, is_read);
}
-
- return 1;
+ bdrv_error_action(s->bs, action, is_read, error);
+ return action != BDRV_ACTION_IGNORE;
}
void ide_dma_cb(void *opaque, int ret)
@@ -1468,9 +1456,7 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val)
case SMART_READ_THRESH:
memset(s->io_buffer, 0, 0x200);
s->io_buffer[0] = 0x01; /* smart struct version */
- for (n=0; n<30; n++) {
- if (smart_attributes[n][0] == 0)
- break;
+ for (n = 0; n < ARRAY_SIZE(smart_attributes); n++) {
s->io_buffer[2+0+(n*12)] = smart_attributes[n][0];
s->io_buffer[2+1+(n*12)] = smart_attributes[n][11];
}
@@ -1484,10 +1470,7 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val)
case SMART_READ_DATA:
memset(s->io_buffer, 0, 0x200);
s->io_buffer[0] = 0x01; /* smart struct version */
- for (n=0; n<30; n++) {
- if (smart_attributes[n][0] == 0) {
- break;
- }
+ for (n = 0; n < ARRAY_SIZE(smart_attributes); n++) {
int i;
for(i = 0; i < 11; i++) {
s->io_buffer[2+i+(n*12)] = smart_attributes[n][i];
diff --git a/hw/ide/macio.c b/hw/ide/macio.c
index 848cb3142..f22872514 100644
--- a/hw/ide/macio.c
+++ b/hw/ide/macio.c
@@ -89,7 +89,6 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
done:
bdrv_acct_done(s->bs, &s->acct);
io->dma_end(opaque);
- return;
}
static void pmac_ide_transfer_cb(void *opaque, int ret)
diff --git a/hw/ide/pci.c b/hw/ide/pci.c
index 88c0942e3..644533f77 100644
--- a/hw/ide/pci.c
+++ b/hw/ide/pci.c
@@ -188,7 +188,7 @@ static void bmdma_restart_bh(void *opaque)
{
BMDMAState *bm = opaque;
IDEBus *bus = bm->bus;
- int is_read;
+ bool is_read;
int error_status;
qemu_bh_delete(bm->bh);
@@ -198,7 +198,7 @@ static void bmdma_restart_bh(void *opaque)
return;
}
- is_read = !!(bus->error_status & BM_STATUS_RETRY_READ);
+ is_read = (bus->error_status & BM_STATUS_RETRY_READ) != 0;
/* The error status must be cleared before resubmitting the request: The
* request may fail again, and this case can only be distinguished if the
diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c
index 5ea9b8f4b..f2e4ea420 100644
--- a/hw/ide/qdev.c
+++ b/hw/ide/qdev.c
@@ -60,7 +60,7 @@ static char *idebus_get_fw_dev_path(DeviceState *dev)
snprintf(path, sizeof(path), "%s@%d", qdev_fw_name(dev),
((IDEBus*)dev->parent_bus)->bus_id);
- return strdup(path);
+ return g_strdup(path);
}
static int ide_qdev_init(DeviceState *qdev)
diff --git a/hw/imx_avic.c b/hw/imx_avic.c
index 4f010e8ee..b1a8fe6d4 100644
--- a/hw/imx_avic.c
+++ b/hw/imx_avic.c
@@ -6,9 +6,9 @@
*
* Copyright (c) 2008 OKL
* Copyright (c) 2011 NICTA Pty Ltd
- * Originally Written by Hans Jiang
+ * Originally written by Hans Jiang
*
- * This code is licenced under the GPL version 2 or later. See
+ * This code is licensed under the GPL version 2 or later. See
* the COPYING file in the top-level directory.
*
* TODO: implement vectors.
diff --git a/hw/imx_timer.c b/hw/imx_timer.c
index 16215ccf0..c28c53725 100644
--- a/hw/imx_timer.c
+++ b/hw/imx_timer.c
@@ -3,10 +3,10 @@
*
* Copyright (c) 2008 OK Labs
* Copyright (c) 2011 NICTA Pty Ltd
- * Originally Written by Hans Jiang
+ * Originally written by Hans Jiang
* Updated by Peter Chubb
*
- * This code is licenced under GPL version 2 or later. See
+ * This code is licensed under GPL version 2 or later. See
* the COPYING file in the top-level directory.
*
*/
diff --git a/hw/intel-hda.c b/hw/intel-hda.c
index 127e81888..d8e1b23a6 100644
--- a/hw/intel-hda.c
+++ b/hw/intel-hda.c
@@ -210,13 +210,7 @@ static target_phys_addr_t intel_hda_addr(uint32_t lbase, uint32_t ubase)
{
target_phys_addr_t addr;
-#if TARGET_PHYS_ADDR_BITS == 32
- addr = lbase;
-#else
- addr = ubase;
- addr <<= 32;
- addr |= lbase;
-#endif
+ addr = ((uint64_t)ubase << 32) | lbase;
return addr;
}
diff --git a/hw/ioh3420.c b/hw/ioh3420.c
index 94a537c9b..4d314733b 100644
--- a/hw/ioh3420.c
+++ b/hw/ioh3420.c
@@ -125,7 +125,6 @@ static int ioh3420_initfn(PCIDevice *d)
rc = pcie_chassis_add_slot(s);
if (rc < 0) {
goto err_pcie_cap;
- return rc;
}
pcie_cap_root_init(d);
rc = pcie_aer_init(d, IOH_EP_AER_OFFSET);
diff --git a/hw/irq.c b/hw/irq.c
index d413a0b23..f4e2a7804 100644
--- a/hw/irq.c
+++ b/hw/irq.c
@@ -38,24 +38,37 @@ void qemu_set_irq(qemu_irq irq, int level)
irq->handler(irq->opaque, irq->n, level);
}
-qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n)
+qemu_irq *qemu_extend_irqs(qemu_irq *old, int n_old, qemu_irq_handler handler,
+ void *opaque, int n)
{
qemu_irq *s;
struct IRQState *p;
int i;
- s = (qemu_irq *)g_malloc0(sizeof(qemu_irq) * n);
- p = (struct IRQState *)g_malloc0(sizeof(struct IRQState) * n);
- for (i = 0; i < n; i++) {
- p->handler = handler;
- p->opaque = opaque;
- p->n = i;
+ if (!old) {
+ n_old = 0;
+ }
+ s = old ? g_renew(qemu_irq, old, n + n_old) : g_new(qemu_irq, n);
+ p = old ? g_renew(struct IRQState, s[0], n + n_old) :
+ g_new(struct IRQState, n);
+ for (i = 0; i < n + n_old; i++) {
+ if (i >= n_old) {
+ p->handler = handler;
+ p->opaque = opaque;
+ p->n = i;
+ }
s[i] = p;
p++;
}
return s;
}
+qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n)
+{
+ return qemu_extend_irqs(NULL, 0, handler, opaque, n);
+}
+
+
void qemu_free_irqs(qemu_irq *s)
{
g_free(s[0]);
diff --git a/hw/irq.h b/hw/irq.h
index 56c55f0c4..e640c105e 100644
--- a/hw/irq.h
+++ b/hw/irq.h
@@ -23,8 +23,17 @@ static inline void qemu_irq_pulse(qemu_irq irq)
qemu_set_irq(irq, 0);
}
-/* Returns an array of N IRQs. */
+/* Returns an array of N IRQs. Each IRQ is assigned the argument handler and
+ * opaque data.
+ */
qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n);
+
+/* Extends an Array of IRQs. Old IRQs have their handlers and opaque data
+ * preserved. New IRQs are assigned the argument handler and opaque data.
+ */
+qemu_irq *qemu_extend_irqs(qemu_irq *old, int n_old, qemu_irq_handler handler,
+ void *opaque, int n);
+
void qemu_free_irqs(qemu_irq *s);
/* Returns a new IRQ with opposite polarity. */
diff --git a/hw/isa-bus.c b/hw/isa-bus.c
index f9b237387..214f19457 100644
--- a/hw/isa-bus.c
+++ b/hw/isa-bus.c
@@ -19,6 +19,7 @@
#include "hw.h"
#include "monitor.h"
#include "sysbus.h"
+#include "sysemu.h"
#include "isa.h"
#include "exec-memory.h"
@@ -166,6 +167,25 @@ ISADevice *isa_create_simple(ISABus *bus, const char *name)
return dev;
}
+ISADevice *isa_vga_init(ISABus *bus)
+{
+ switch (vga_interface_type) {
+ case VGA_CIRRUS:
+ return isa_create_simple(bus, "isa-cirrus-vga");
+ case VGA_QXL:
+ fprintf(stderr, "%s: qxl: no PCI bus\n", __func__);
+ return NULL;
+ case VGA_STD:
+ return isa_create_simple(bus, "isa-vga");
+ case VGA_VMWARE:
+ fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __func__);
+ return NULL;
+ case VGA_NONE:
+ default:
+ return NULL;
+ }
+}
+
static void isabus_dev_print(Monitor *mon, DeviceState *dev, int indent)
{
ISADevice *d = ISA_DEVICE(dev);
@@ -236,7 +256,7 @@ static char *isabus_get_fw_dev_path(DeviceState *dev)
snprintf(path + off, sizeof(path) - off, "@%04x", d->ioport_id);
}
- return strdup(path);
+ return g_strdup(path);
}
MemoryRegion *isa_address_space(ISADevice *dev)
diff --git a/hw/isa.h b/hw/isa.h
index dc970527a..8fb498aa8 100644
--- a/hw/isa.h
+++ b/hw/isa.h
@@ -47,6 +47,8 @@ ISADevice *isa_create(ISABus *bus, const char *name);
ISADevice *isa_try_create(ISABus *bus, const char *name);
ISADevice *isa_create_simple(ISABus *bus, const char *name);
+ISADevice *isa_vga_init(ISABus *bus);
+
/**
* isa_register_ioport: Install an I/O port region on the ISA bus.
*
diff --git a/hw/ivshmem.c b/hw/ivshmem.c
index 62fe53ae2..5c4ccb85f 100644
--- a/hw/ivshmem.c
+++ b/hw/ivshmem.c
@@ -71,6 +71,8 @@ typedef struct IVShmemState {
MemoryRegion bar;
MemoryRegion ivshmem;
uint64_t ivshmem_size; /* size of shared memory region */
+ uint32_t ivshmem_attr;
+ uint32_t ivshmem_64bit;
int shm_fd; /* shared memory file descriptor */
Peer *peers;
@@ -147,7 +149,6 @@ static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val)
s->intrstatus = val;
ivshmem_update_irq(s, val);
- return;
}
static uint32_t ivshmem_IntrStatus_read(IVShmemState *s)
@@ -339,7 +340,7 @@ static void create_shared_memory_BAR(IVShmemState *s, int fd) {
memory_region_add_subregion(&s->bar, 0, &s->ivshmem);
/* region for shared memory */
- pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar);
+ pci_register_bar(&s->dev, 2, s->ivshmem_attr, &s->bar);
}
static void ivshmem_add_eventfd(IVShmemState *s, int posn, int i)
@@ -508,8 +509,6 @@ static void ivshmem_read(void *opaque, const uint8_t * buf, int flags)
if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
ivshmem_add_eventfd(s, incoming_posn, guest_max_eventfd);
}
-
- return;
}
/* Select the MSI-X vectors used by device.
@@ -534,7 +533,6 @@ static void ivshmem_reset(DeviceState *d)
s->intrstatus = 0;
ivshmem_use_msix(s);
- return;
}
static uint64_t ivshmem_get_size(IVShmemState * s) {
@@ -690,6 +688,11 @@ static int pci_ivshmem_init(PCIDevice *dev)
&s->ivshmem_mmio);
memory_region_init(&s->bar, "ivshmem-bar2-container", s->ivshmem_size);
+ s->ivshmem_attr = PCI_BASE_ADDRESS_SPACE_MEMORY |
+ PCI_BASE_ADDRESS_MEM_PREFETCH;
+ if (s->ivshmem_64bit) {
+ s->ivshmem_attr |= PCI_BASE_ADDRESS_MEM_TYPE_64;
+ }
if ((s->server_chr != NULL) &&
(strncmp(s->server_chr->filename, "unix:", 5) == 0)) {
@@ -715,8 +718,7 @@ static int pci_ivshmem_init(PCIDevice *dev)
/* allocate/initialize space for interrupt handling */
s->peers = g_malloc0(s->nb_peers * sizeof(Peer));
- pci_register_bar(&s->dev, 2,
- PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar);
+ pci_register_bar(&s->dev, 2, s->ivshmem_attr, &s->bar);
s->eventfd_chr = g_malloc0(s->vectors * sizeof(CharDriverState *));
@@ -786,6 +788,7 @@ static Property ivshmem_properties[] = {
DEFINE_PROP_BIT("msi", IVShmemState, features, IVSHMEM_MSI, true),
DEFINE_PROP_STRING("shm", IVShmemState, shmobj),
DEFINE_PROP_STRING("role", IVShmemState, role),
+ DEFINE_PROP_UINT32("use64", IVShmemState, ivshmem_64bit, 1),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/kvm/pci-assign.c b/hw/kvm/pci-assign.c
index 05b93d9a5..7a0998c51 100644
--- a/hw/kvm/pci-assign.c
+++ b/hw/kvm/pci-assign.c
@@ -579,15 +579,9 @@ static int get_real_device(AssignedDevice *pci_dev, uint16_t r_seg,
snprintf(name, sizeof(name), "%sconfig", dir);
if (pci_dev->configfd_name && *pci_dev->configfd_name) {
- if (qemu_isdigit(pci_dev->configfd_name[0])) {
- dev->config_fd = strtol(pci_dev->configfd_name, NULL, 0);
- } else {
- dev->config_fd = monitor_get_fd(cur_mon, pci_dev->configfd_name);
- if (dev->config_fd < 0) {
- error_report("%s: (%s) unkown", __func__,
- pci_dev->configfd_name);
- return 1;
- }
+ dev->config_fd = monitor_handle_fd_param(cur_mon, pci_dev->configfd_name);
+ if (dev->config_fd < 0) {
+ return 1;
}
} else {
dev->config_fd = open(name, O_RDWR);
diff --git a/hw/kzm.c b/hw/kzm.c
index 6a5e9dfac..68cd1b48b 100644
--- a/hw/kzm.c
+++ b/hw/kzm.c
@@ -5,7 +5,7 @@
* Written by Hans at OK-Labs
* Updated by Peter Chubb.
*
- * This code is licenced under the GPL, version 2 or later.
+ * This code is licensed under the GPL, version 2 or later.
* See the file `COPYING' in the top level directory.
*
* It (partially) emulates a Kyoto Microcomputer
diff --git a/hw/leon3.c b/hw/leon3.c
index 878d3aa55..7a9729dc2 100644
--- a/hw/leon3.c
+++ b/hw/leon3.c
@@ -210,7 +210,7 @@ static void leon3_generic_hw_init(ram_addr_t ram_size,
}
}
-QEMUMachine leon3_generic_machine = {
+static QEMUMachine leon3_generic_machine = {
.name = "leon3_generic",
.desc = "Leon-3 generic",
.init = leon3_generic_hw_init,
diff --git a/hw/lm32_hwsetup.h b/hw/lm32_hwsetup.h
index 8fc285efc..70dc61f9e 100644
--- a/hw/lm32_hwsetup.h
+++ b/hw/lm32_hwsetup.h
@@ -96,7 +96,7 @@ static inline void hwsetup_add_tag(HWSetup *hw, enum hwsetup_tag t)
static inline void hwsetup_add_str(HWSetup *hw, const char *str)
{
- strncpy(hw->ptr, str, 31); /* make sure last byte is zero */
+ pstrcpy(hw->ptr, 32, str);
hw->ptr += 32;
}
diff --git a/hw/lm4549.c b/hw/lm4549.c
index 80b3ec4a5..e0137d54b 100644
--- a/hw/lm4549.c
+++ b/hw/lm4549.c
@@ -224,7 +224,7 @@ uint32_t lm4549_write_samples(lm4549_state *s, uint32_t left, uint32_t right)
This model supports 16-bit playback.
*/
- if (s->buffer_level >= LM4549_BUFFER_SIZE) {
+ if (s->buffer_level > LM4549_BUFFER_SIZE - 2) {
DPRINTF("write_sample Buffer full\n");
return 0;
}
diff --git a/hw/m25p80.c b/hw/m25p80.c
new file mode 100644
index 000000000..9a56de802
--- /dev/null
+++ b/hw/m25p80.c
@@ -0,0 +1,598 @@
+/*
+ * ST M25P80 emulator. Emulate all SPI flash devices based on the m25p80 command
+ * set. Known devices table current as of Jun/2012 and taken from linux.
+ * See drivers/mtd/devices/m25p80.c.
+ *
+ * Copyright (C) 2011 Edgar E. Iglesias <edgar.iglesias@gmail.com>
+ * Copyright (C) 2012 Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>
+ * Copyright (C) 2012 PetaLogix
+ *
+ * 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 or
+ * (at your option) a later version of the License.
+ *
+ * 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.h"
+#include "blockdev.h"
+#include "ssi.h"
+#include "devices.h"
+
+#ifdef M25P80_ERR_DEBUG
+#define DB_PRINT(...) do { \
+ fprintf(stderr, ": %s: ", __func__); \
+ fprintf(stderr, ## __VA_ARGS__); \
+ } while (0);
+#else
+ #define DB_PRINT(...)
+#endif
+
+/* Fields for FlashPartInfo->flags */
+
+/* erase capabilities */
+#define ER_4K 1
+#define ER_32K 2
+/* set to allow the page program command to write 0s back to 1. Useful for
+ * modelling EEPROM with SPI flash command set
+ */
+#define WR_1 0x100
+
+typedef struct FlashPartInfo {
+ const char *part_name;
+ /* jedec code. (jedec >> 16) & 0xff is the 1st byte, >> 8 the 2nd etc */
+ uint32_t jedec;
+ /* extended jedec code */
+ uint16_t ext_jedec;
+ /* there is confusion between manufacturers as to what a sector is. In this
+ * device model, a "sector" is the size that is erased by the ERASE_SECTOR
+ * command (opcode 0xd8).
+ */
+ uint32_t sector_size;
+ uint32_t n_sectors;
+ uint32_t page_size;
+ uint8_t flags;
+} FlashPartInfo;
+
+/* adapted from linux */
+
+#define INFO(_part_name, _jedec, _ext_jedec, _sector_size, _n_sectors, _flags)\
+ .part_name = (_part_name),\
+ .jedec = (_jedec),\
+ .ext_jedec = (_ext_jedec),\
+ .sector_size = (_sector_size),\
+ .n_sectors = (_n_sectors),\
+ .page_size = 256,\
+ .flags = (_flags),\
+
+static const FlashPartInfo known_devices[] = {
+ /* Atmel -- some are (confusingly) marketed as "DataFlash" */
+ { INFO("at25fs010", 0x1f6601, 0, 32 << 10, 4, ER_4K) },
+ { INFO("at25fs040", 0x1f6604, 0, 64 << 10, 8, ER_4K) },
+
+ { INFO("at25df041a", 0x1f4401, 0, 64 << 10, 8, ER_4K) },
+ { INFO("at25df321a", 0x1f4701, 0, 64 << 10, 64, ER_4K) },
+ { INFO("at25df641", 0x1f4800, 0, 64 << 10, 128, ER_4K) },
+
+ { INFO("at26f004", 0x1f0400, 0, 64 << 10, 8, ER_4K) },
+ { INFO("at26df081a", 0x1f4501, 0, 64 << 10, 16, ER_4K) },
+ { INFO("at26df161a", 0x1f4601, 0, 64 << 10, 32, ER_4K) },
+ { INFO("at26df321", 0x1f4700, 0, 64 << 10, 64, ER_4K) },
+
+ /* EON -- en25xxx */
+ { INFO("en25f32", 0x1c3116, 0, 64 << 10, 64, ER_4K) },
+ { INFO("en25p32", 0x1c2016, 0, 64 << 10, 64, 0) },
+ { INFO("en25q32b", 0x1c3016, 0, 64 << 10, 64, 0) },
+ { INFO("en25p64", 0x1c2017, 0, 64 << 10, 128, 0) },
+
+ /* Intel/Numonyx -- xxxs33b */
+ { INFO("160s33b", 0x898911, 0, 64 << 10, 32, 0) },
+ { INFO("320s33b", 0x898912, 0, 64 << 10, 64, 0) },
+ { INFO("640s33b", 0x898913, 0, 64 << 10, 128, 0) },
+
+ /* Macronix */
+ { INFO("mx25l4005a", 0xc22013, 0, 64 << 10, 8, ER_4K) },
+ { INFO("mx25l8005", 0xc22014, 0, 64 << 10, 16, 0) },
+ { INFO("mx25l1606e", 0xc22015, 0, 64 << 10, 32, ER_4K) },
+ { INFO("mx25l3205d", 0xc22016, 0, 64 << 10, 64, 0) },
+ { INFO("mx25l6405d", 0xc22017, 0, 64 << 10, 128, 0) },
+ { INFO("mx25l12805d", 0xc22018, 0, 64 << 10, 256, 0) },
+ { INFO("mx25l12855e", 0xc22618, 0, 64 << 10, 256, 0) },
+ { INFO("mx25l25635e", 0xc22019, 0, 64 << 10, 512, 0) },
+ { INFO("mx25l25655e", 0xc22619, 0, 64 << 10, 512, 0) },
+
+ /* Spansion -- single (large) sector size only, at least
+ * for the chips listed here (without boot sectors).
+ */
+ { INFO("s25sl004a", 0x010212, 0, 64 << 10, 8, 0) },
+ { INFO("s25sl008a", 0x010213, 0, 64 << 10, 16, 0) },
+ { INFO("s25sl016a", 0x010214, 0, 64 << 10, 32, 0) },
+ { INFO("s25sl032a", 0x010215, 0, 64 << 10, 64, 0) },
+ { INFO("s25sl032p", 0x010215, 0x4d00, 64 << 10, 64, ER_4K) },
+ { INFO("s25sl064a", 0x010216, 0, 64 << 10, 128, 0) },
+ { INFO("s25fl256s0", 0x010219, 0x4d00, 256 << 10, 128, 0) },
+ { INFO("s25fl256s1", 0x010219, 0x4d01, 64 << 10, 512, 0) },
+ { INFO("s25fl512s", 0x010220, 0x4d00, 256 << 10, 256, 0) },
+ { INFO("s70fl01gs", 0x010221, 0x4d00, 256 << 10, 256, 0) },
+ { INFO("s25sl12800", 0x012018, 0x0300, 256 << 10, 64, 0) },
+ { INFO("s25sl12801", 0x012018, 0x0301, 64 << 10, 256, 0) },
+ { INFO("s25fl129p0", 0x012018, 0x4d00, 256 << 10, 64, 0) },
+ { INFO("s25fl129p1", 0x012018, 0x4d01, 64 << 10, 256, 0) },
+ { INFO("s25fl016k", 0xef4015, 0, 64 << 10, 32, ER_4K | ER_32K) },
+ { INFO("s25fl064k", 0xef4017, 0, 64 << 10, 128, ER_4K | ER_32K) },
+
+ /* SST -- large erase sizes are "overlays", "sectors" are 4<< 10 */
+ { INFO("sst25vf040b", 0xbf258d, 0, 64 << 10, 8, ER_4K) },
+ { INFO("sst25vf080b", 0xbf258e, 0, 64 << 10, 16, ER_4K) },
+ { INFO("sst25vf016b", 0xbf2541, 0, 64 << 10, 32, ER_4K) },
+ { INFO("sst25vf032b", 0xbf254a, 0, 64 << 10, 64, ER_4K) },
+ { INFO("sst25wf512", 0xbf2501, 0, 64 << 10, 1, ER_4K) },
+ { INFO("sst25wf010", 0xbf2502, 0, 64 << 10, 2, ER_4K) },
+ { INFO("sst25wf020", 0xbf2503, 0, 64 << 10, 4, ER_4K) },
+ { INFO("sst25wf040", 0xbf2504, 0, 64 << 10, 8, ER_4K) },
+
+ /* ST Microelectronics -- newer production may have feature updates */
+ { INFO("m25p05", 0x202010, 0, 32 << 10, 2, 0) },
+ { INFO("m25p10", 0x202011, 0, 32 << 10, 4, 0) },
+ { INFO("m25p20", 0x202012, 0, 64 << 10, 4, 0) },
+ { INFO("m25p40", 0x202013, 0, 64 << 10, 8, 0) },
+ { INFO("m25p80", 0x202014, 0, 64 << 10, 16, 0) },
+ { INFO("m25p16", 0x202015, 0, 64 << 10, 32, 0) },
+ { INFO("m25p32", 0x202016, 0, 64 << 10, 64, 0) },
+ { INFO("m25p64", 0x202017, 0, 64 << 10, 128, 0) },
+ { INFO("m25p128", 0x202018, 0, 256 << 10, 64, 0) },
+
+ { INFO("m45pe10", 0x204011, 0, 64 << 10, 2, 0) },
+ { INFO("m45pe80", 0x204014, 0, 64 << 10, 16, 0) },
+ { INFO("m45pe16", 0x204015, 0, 64 << 10, 32, 0) },
+
+ { INFO("m25pe80", 0x208014, 0, 64 << 10, 16, 0) },
+ { INFO("m25pe16", 0x208015, 0, 64 << 10, 32, ER_4K) },
+
+ { INFO("m25px32", 0x207116, 0, 64 << 10, 64, ER_4K) },
+ { INFO("m25px32-s0", 0x207316, 0, 64 << 10, 64, ER_4K) },
+ { INFO("m25px32-s1", 0x206316, 0, 64 << 10, 64, ER_4K) },
+ { INFO("m25px64", 0x207117, 0, 64 << 10, 128, 0) },
+
+ /* Winbond -- w25x "blocks" are 64k, "sectors" are 4KiB */
+ { INFO("w25x10", 0xef3011, 0, 64 << 10, 2, ER_4K) },
+ { INFO("w25x20", 0xef3012, 0, 64 << 10, 4, ER_4K) },
+ { INFO("w25x40", 0xef3013, 0, 64 << 10, 8, ER_4K) },
+ { INFO("w25x80", 0xef3014, 0, 64 << 10, 16, ER_4K) },
+ { INFO("w25x16", 0xef3015, 0, 64 << 10, 32, ER_4K) },
+ { INFO("w25x32", 0xef3016, 0, 64 << 10, 64, ER_4K) },
+ { INFO("w25q32", 0xef4016, 0, 64 << 10, 64, ER_4K) },
+ { INFO("w25x64", 0xef3017, 0, 64 << 10, 128, ER_4K) },
+ { INFO("w25q64", 0xef4017, 0, 64 << 10, 128, ER_4K) },
+
+ /* Numonyx -- n25q128 */
+ { INFO("n25q128", 0x20ba18, 0, 64 << 10, 256, 0) },
+
+ { },
+};
+
+typedef enum {
+ NOP = 0,
+ PP = 0x2,
+ READ = 0x3,
+ WRDI = 0x4,
+ RDSR = 0x5,
+ WREN = 0x6,
+ FAST_READ = 0xb,
+ ERASE_4K = 0x20,
+ ERASE_32K = 0x52,
+ ERASE_SECTOR = 0xd8,
+ JEDEC_READ = 0x9f,
+ BULK_ERASE = 0xc7,
+} FlashCMD;
+
+typedef enum {
+ STATE_IDLE,
+ STATE_PAGE_PROGRAM,
+ STATE_READ,
+ STATE_COLLECTING_DATA,
+ STATE_READING_DATA,
+} CMDState;
+
+typedef struct Flash {
+ SSISlave ssidev;
+ uint32_t r;
+
+ BlockDriverState *bdrv;
+
+ uint8_t *storage;
+ uint32_t size;
+ int page_size;
+
+ uint8_t state;
+ uint8_t data[16];
+ uint32_t len;
+ uint32_t pos;
+ uint8_t needed_bytes;
+ uint8_t cmd_in_progress;
+ uint64_t cur_addr;
+ bool write_enable;
+
+ int64_t dirty_page;
+
+ char *part_name;
+ const FlashPartInfo *pi;
+
+} Flash;
+
+static void bdrv_sync_complete(void *opaque, int ret)
+{
+ /* do nothing. Masters do not directly interact with the backing store,
+ * only the working copy so no mutexing required.
+ */
+}
+
+static void flash_sync_page(Flash *s, int page)
+{
+ if (s->bdrv) {
+ int bdrv_sector, nb_sectors;
+ QEMUIOVector iov;
+
+ bdrv_sector = (page * s->pi->page_size) / BDRV_SECTOR_SIZE;
+ nb_sectors = DIV_ROUND_UP(s->pi->page_size, BDRV_SECTOR_SIZE);
+ qemu_iovec_init(&iov, 1);
+ qemu_iovec_add(&iov, s->storage + bdrv_sector * BDRV_SECTOR_SIZE,
+ nb_sectors * BDRV_SECTOR_SIZE);
+ bdrv_aio_writev(s->bdrv, bdrv_sector, &iov, nb_sectors,
+ bdrv_sync_complete, NULL);
+ }
+}
+
+static inline void flash_sync_area(Flash *s, int64_t off, int64_t len)
+{
+ int64_t start, end, nb_sectors;
+ QEMUIOVector iov;
+
+ if (!s->bdrv) {
+ return;
+ }
+
+ assert(!(len % BDRV_SECTOR_SIZE));
+ start = off / BDRV_SECTOR_SIZE;
+ end = (off + len) / BDRV_SECTOR_SIZE;
+ nb_sectors = end - start;
+ qemu_iovec_init(&iov, 1);
+ qemu_iovec_add(&iov, s->storage + (start * BDRV_SECTOR_SIZE),
+ nb_sectors * BDRV_SECTOR_SIZE);
+ bdrv_aio_writev(s->bdrv, start, &iov, nb_sectors, bdrv_sync_complete, NULL);
+}
+
+static void flash_erase(Flash *s, int offset, FlashCMD cmd)
+{
+ uint32_t len;
+ uint8_t capa_to_assert = 0;
+
+ switch (cmd) {
+ case ERASE_4K:
+ len = 4 << 10;
+ capa_to_assert = ER_4K;
+ break;
+ case ERASE_32K:
+ len = 32 << 10;
+ capa_to_assert = ER_32K;
+ break;
+ case ERASE_SECTOR:
+ len = s->pi->sector_size;
+ break;
+ case BULK_ERASE:
+ len = s->size;
+ break;
+ default:
+ abort();
+ }
+
+ DB_PRINT("offset = %#x, len = %d\n", offset, len);
+ if ((s->pi->flags & capa_to_assert) != capa_to_assert) {
+ hw_error("m25p80: %dk erase size not supported by device\n", len);
+ }
+
+ if (!s->write_enable) {
+ DB_PRINT("erase with write protect!\n");
+ return;
+ }
+ memset(s->storage + offset, 0xff, len);
+ flash_sync_area(s, offset, len);
+}
+
+static inline void flash_sync_dirty(Flash *s, int64_t newpage)
+{
+ if (s->dirty_page >= 0 && s->dirty_page != newpage) {
+ flash_sync_page(s, s->dirty_page);
+ s->dirty_page = newpage;
+ }
+}
+
+static inline
+void flash_write8(Flash *s, uint64_t addr, uint8_t data)
+{
+ int64_t page = addr / s->pi->page_size;
+ uint8_t prev = s->storage[s->cur_addr];
+
+ if (!s->write_enable) {
+ DB_PRINT("write with write protect!\n");
+ }
+
+ if ((prev ^ data) & data) {
+ DB_PRINT("programming zero to one! addr=%lx %x -> %x\n",
+ addr, prev, data);
+ }
+
+ if (s->pi->flags & WR_1) {
+ s->storage[s->cur_addr] = data;
+ } else {
+ s->storage[s->cur_addr] &= data;
+ }
+
+ flash_sync_dirty(s, page);
+ s->dirty_page = page;
+}
+
+static void complete_collecting_data(Flash *s)
+{
+ s->cur_addr = s->data[0] << 16;
+ s->cur_addr |= s->data[1] << 8;
+ s->cur_addr |= s->data[2];
+
+ switch (s->cmd_in_progress) {
+ case PP:
+ s->state = STATE_PAGE_PROGRAM;
+ break;
+ case READ:
+ case FAST_READ:
+ s->state = STATE_READ;
+ break;
+ case ERASE_4K:
+ case ERASE_32K:
+ case ERASE_SECTOR:
+ flash_erase(s, s->cur_addr, s->cmd_in_progress);
+ break;
+ default:
+ break;
+ }
+}
+
+static void decode_new_cmd(Flash *s, uint32_t value)
+{
+ s->cmd_in_progress = value;
+ DB_PRINT("decoded new command:%x\n", value);
+
+ switch (value) {
+
+ case ERASE_4K:
+ case ERASE_32K:
+ case ERASE_SECTOR:
+ case READ:
+ case PP:
+ s->needed_bytes = 3;
+ s->pos = 0;
+ s->len = 0;
+ s->state = STATE_COLLECTING_DATA;
+ break;
+
+ case FAST_READ:
+ s->needed_bytes = 4;
+ s->pos = 0;
+ s->len = 0;
+ s->state = STATE_COLLECTING_DATA;
+ break;
+
+ case WRDI:
+ s->write_enable = false;
+ break;
+ case WREN:
+ s->write_enable = true;
+ break;
+
+ case RDSR:
+ s->data[0] = (!!s->write_enable) << 1;
+ s->pos = 0;
+ s->len = 1;
+ s->state = STATE_READING_DATA;
+ break;
+
+ case JEDEC_READ:
+ DB_PRINT("populated jedec code\n");
+ s->data[0] = (s->pi->jedec >> 16) & 0xff;
+ s->data[1] = (s->pi->jedec >> 8) & 0xff;
+ s->data[2] = s->pi->jedec & 0xff;
+ if (s->pi->ext_jedec) {
+ s->data[3] = (s->pi->ext_jedec >> 8) & 0xff;
+ s->data[4] = s->pi->ext_jedec & 0xff;
+ s->len = 5;
+ } else {
+ s->len = 3;
+ }
+ s->pos = 0;
+ s->state = STATE_READING_DATA;
+ break;
+
+ case BULK_ERASE:
+ if (s->write_enable) {
+ DB_PRINT("chip erase\n");
+ flash_erase(s, 0, BULK_ERASE);
+ } else {
+ DB_PRINT("chip erase with write protect!\n");
+ }
+ break;
+ case NOP:
+ break;
+ default:
+ DB_PRINT("Unknown cmd %x\n", value);
+ break;
+ }
+}
+
+static int m25p80_cs(SSISlave *ss, bool select)
+{
+ Flash *s = FROM_SSI_SLAVE(Flash, ss);
+
+ if (select) {
+ s->len = 0;
+ s->pos = 0;
+ s->state = STATE_IDLE;
+ flash_sync_dirty(s, -1);
+ }
+
+ DB_PRINT("%sselect\n", select ? "de" : "");
+
+ return 0;
+}
+
+static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx)
+{
+ Flash *s = FROM_SSI_SLAVE(Flash, ss);
+ uint32_t r = 0;
+
+ switch (s->state) {
+
+ case STATE_PAGE_PROGRAM:
+ DB_PRINT("page program cur_addr=%lx data=%x\n", s->cur_addr,
+ (uint8_t)tx);
+ flash_write8(s, s->cur_addr, (uint8_t)tx);
+ s->cur_addr++;
+ break;
+
+ case STATE_READ:
+ r = s->storage[s->cur_addr];
+ DB_PRINT("READ 0x%lx=%x\n", s->cur_addr, r);
+ s->cur_addr = (s->cur_addr + 1) % s->size;
+ break;
+
+ case STATE_COLLECTING_DATA:
+ s->data[s->len] = (uint8_t)tx;
+ s->len++;
+
+ if (s->len == s->needed_bytes) {
+ complete_collecting_data(s);
+ }
+ break;
+
+ case STATE_READING_DATA:
+ r = s->data[s->pos];
+ s->pos++;
+ if (s->pos == s->len) {
+ s->pos = 0;
+ s->state = STATE_IDLE;
+ }
+ break;
+
+ default:
+ case STATE_IDLE:
+ decode_new_cmd(s, (uint8_t)tx);
+ break;
+ }
+
+ return r;
+}
+
+static int m25p80_init(SSISlave *ss)
+{
+ DriveInfo *dinfo;
+ Flash *s = FROM_SSI_SLAVE(Flash, ss);
+ const FlashPartInfo *i;
+
+ if (!s->part_name) { /* default to actual m25p80 if no partname given */
+ s->part_name = (char *)"m25p80";
+ }
+
+ i = known_devices;
+ for (i = known_devices;; i++) {
+ assert(i);
+ if (!i->part_name) {
+ fprintf(stderr, "Unknown SPI flash part: \"%s\"\n", s->part_name);
+ return 1;
+ } else if (!strcmp(i->part_name, s->part_name)) {
+ s->pi = i;
+ break;
+ }
+ }
+
+ s->size = s->pi->sector_size * s->pi->n_sectors;
+ s->dirty_page = -1;
+ s->storage = qemu_blockalign(s->bdrv, s->size);
+
+ dinfo = drive_get_next(IF_MTD);
+
+ if (dinfo && dinfo->bdrv) {
+ DB_PRINT("Binding to IF_MTD drive\n");
+ s->bdrv = dinfo->bdrv;
+ /* FIXME: Move to late init */
+ if (bdrv_read(s->bdrv, 0, s->storage, DIV_ROUND_UP(s->size,
+ BDRV_SECTOR_SIZE))) {
+ fprintf(stderr, "Failed to initialize SPI flash!\n");
+ return 1;
+ }
+ } else {
+ memset(s->storage, 0xFF, s->size);
+ }
+
+ return 0;
+}
+
+static void m25p80_pre_save(void *opaque)
+{
+ flash_sync_dirty((Flash *)opaque, -1);
+}
+
+static const VMStateDescription vmstate_m25p80 = {
+ .name = "xilinx_spi",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .pre_save = m25p80_pre_save,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(state, Flash),
+ VMSTATE_UINT8_ARRAY(data, Flash, 16),
+ VMSTATE_UINT32(len, Flash),
+ VMSTATE_UINT32(pos, Flash),
+ VMSTATE_UINT8(needed_bytes, Flash),
+ VMSTATE_UINT8(cmd_in_progress, Flash),
+ VMSTATE_UINT64(cur_addr, Flash),
+ VMSTATE_BOOL(write_enable, Flash),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property m25p80_properties[] = {
+ DEFINE_PROP_STRING("partname", Flash, part_name),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void m25p80_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
+
+ k->init = m25p80_init;
+ k->transfer = m25p80_transfer8;
+ k->set_cs = m25p80_cs;
+ k->cs_polarity = SSI_CS_LOW;
+ dc->props = m25p80_properties;
+ dc->vmsd = &vmstate_m25p80;
+}
+
+static const TypeInfo m25p80_info = {
+ .name = "m25p80",
+ .parent = TYPE_SSI_SLAVE,
+ .instance_size = sizeof(Flash),
+ .class_init = m25p80_class_init,
+};
+
+static void m25p80_register_types(void)
+{
+ type_register_static(&m25p80_info);
+}
+
+type_init(m25p80_register_types)
diff --git a/hw/max111x.c b/hw/max111x.c
index 706d89f4f..67640f109 100644
--- a/hw/max111x.c
+++ b/hw/max111x.c
@@ -99,10 +99,11 @@ static uint32_t max111x_transfer(SSISlave *dev, uint32_t value)
static const VMStateDescription vmstate_max111x = {
.name = "max111x",
- .version_id = 0,
- .minimum_version_id = 0,
- .minimum_version_id_old = 0,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
+ VMSTATE_SSI_SLAVE(ssidev, MAX111xState),
VMSTATE_UINT8(tb1, MAX111xState),
VMSTATE_UINT8(rb2, MAX111xState),
VMSTATE_UINT8(rb3, MAX111xState),
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index d63554f89..332a77d4d 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -399,6 +399,10 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
s->cmos_data[s->cmos_index] = data;
check_update_timer(s);
break;
+ case RTC_IBM_PS2_CENTURY_BYTE:
+ s->cmos_index = RTC_CENTURY;
+ /* fall through */
+ case RTC_CENTURY:
case RTC_SECONDS:
case RTC_MINUTES:
case RTC_HOURS:
@@ -515,7 +519,9 @@ static void rtc_get_time(RTCState *s, struct tm *tm)
tm->tm_wday = rtc_from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]) - 1;
tm->tm_mday = rtc_from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]);
tm->tm_mon = rtc_from_bcd(s, s->cmos_data[RTC_MONTH]) - 1;
- tm->tm_year = rtc_from_bcd(s, s->cmos_data[RTC_YEAR]) + s->base_year - 1900;
+ tm->tm_year =
+ rtc_from_bcd(s, s->cmos_data[RTC_YEAR]) + s->base_year +
+ rtc_from_bcd(s, s->cmos_data[RTC_CENTURY]) * 100 - 1900;
}
static void rtc_set_time(RTCState *s)
@@ -548,10 +554,9 @@ static void rtc_set_cmos(RTCState *s, const struct tm *tm)
s->cmos_data[RTC_DAY_OF_WEEK] = rtc_to_bcd(s, tm->tm_wday + 1);
s->cmos_data[RTC_DAY_OF_MONTH] = rtc_to_bcd(s, tm->tm_mday);
s->cmos_data[RTC_MONTH] = rtc_to_bcd(s, tm->tm_mon + 1);
- year = (tm->tm_year - s->base_year) % 100;
- if (year < 0)
- year += 100;
- s->cmos_data[RTC_YEAR] = rtc_to_bcd(s, year);
+ year = tm->tm_year + 1900 - s->base_year;
+ s->cmos_data[RTC_YEAR] = rtc_to_bcd(s, year % 100);
+ s->cmos_data[RTC_CENTURY] = rtc_to_bcd(s, year / 100);
}
static void rtc_update_time(RTCState *s)
@@ -598,6 +603,10 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
return 0xff;
} else {
switch(s->cmos_index) {
+ case RTC_IBM_PS2_CENTURY_BYTE:
+ s->cmos_index = RTC_CENTURY;
+ /* fall through */
+ case RTC_CENTURY:
case RTC_SECONDS:
case RTC_MINUTES:
case RTC_HOURS:
@@ -661,15 +670,10 @@ void rtc_set_memory(ISADevice *dev, int addr, int val)
s->cmos_data[addr] = val;
}
-/* PC cmos mappings */
-#define REG_IBM_CENTURY_BYTE 0x32
-#define REG_IBM_PS2_CENTURY_BYTE 0x37
-
static void rtc_set_date_from_host(ISADevice *dev)
{
RTCState *s = DO_UPCAST(RTCState, dev, dev);
struct tm tm;
- int val;
qemu_get_timedate(&tm, 0);
@@ -679,10 +683,6 @@ static void rtc_set_date_from_host(ISADevice *dev)
/* set the CMOS date */
rtc_set_cmos(s, &tm);
-
- val = rtc_to_bcd(s, (tm.tm_year / 100) + 19);
- rtc_set_memory(dev, REG_IBM_CENTURY_BYTE, val);
- rtc_set_memory(dev, REG_IBM_PS2_CENTURY_BYTE, val);
}
static int rtc_post_load(void *opaque, int version_id)
@@ -807,6 +807,18 @@ static int rtc_initfn(ISADevice *dev)
s->cmos_data[RTC_REG_C] = 0x00;
s->cmos_data[RTC_REG_D] = 0x80;
+ /* This is for historical reasons. The default base year qdev property
+ * was set to 2000 for most machine types before the century byte was
+ * implemented.
+ *
+ * This if statement means that the century byte will be always 0
+ * (at least until 2079...) for base_year = 1980, but will be set
+ * correctly for base_year = 2000.
+ */
+ if (s->base_year == 2000) {
+ s->base_year = 0;
+ }
+
rtc_set_date_from_host(dev);
#ifdef TARGET_I386
diff --git a/hw/mc146818rtc_regs.h b/hw/mc146818rtc_regs.h
index fc10076ec..ccdee42b3 100644
--- a/hw/mc146818rtc_regs.h
+++ b/hw/mc146818rtc_regs.h
@@ -44,6 +44,10 @@
#define RTC_REG_C 12
#define RTC_REG_D 13
+/* PC cmos mappings */
+#define RTC_CENTURY 0x32
+#define RTC_IBM_PS2_CENTURY_BYTE 0x37
+
#define REG_A_UIP 0x80
#define REG_B_SET 0x80
diff --git a/hw/megasas.c b/hw/megasas.c
index c728aea69..0e57740fd 100644
--- a/hw/megasas.c
+++ b/hw/megasas.c
@@ -652,7 +652,6 @@ static void megasas_finish_dcmd(MegasasCmd *cmd, uint32_t iov_size)
}
}
cmd->iov_size = 0;
- return;
}
static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd)
diff --git a/hw/microblaze/Makefile.objs b/hw/microblaze/Makefile.objs
index 274d2c543..3028e651c 100644
--- a/hw/microblaze/Makefile.objs
+++ b/hw/microblaze/Makefile.objs
@@ -1,6 +1,7 @@
obj-y = petalogix_s3adsp1800_mmu.o
obj-y += petalogix_ml605_mmu.o
obj-y += microblaze_boot.o
+obj-y += xilinx_spi.o
obj-y += microblaze_pic_cpu.o
obj-y += xilinx_ethlite.o
diff --git a/hw/mips_fulong2e.c b/hw/mips_fulong2e.c
index 38e4b8615..d4a8672f2 100644
--- a/hw/mips_fulong2e.c
+++ b/hw/mips_fulong2e.c
@@ -392,7 +392,7 @@ static void mips_fulong2e_init(ram_addr_t ram_size, const char *boot_device,
network_init();
}
-QEMUMachine mips_fulong2e_machine = {
+static QEMUMachine mips_fulong2e_machine = {
.name = "fulong2e",
.desc = "Fulong 2e mini pc",
.init = mips_fulong2e_init,
diff --git a/hw/mips_malta.c b/hw/mips_malta.c
index ad23f26e5..632b466e3 100644
--- a/hw/mips_malta.c
+++ b/hw/mips_malta.c
@@ -33,7 +33,6 @@
#include "mips.h"
#include "mips_cpudevs.h"
#include "pci.h"
-#include "vmware_vga.h"
#include "qemu-char.h"
#include "sysemu.h"
#include "arch_init.h"
@@ -48,7 +47,6 @@
#include "blockdev.h"
#include "exec-memory.h"
#include "sysbus.h" /* SysBusDevice */
-#include "vga-pci.h"
//#define DEBUG_BOARD_INIT
@@ -987,13 +985,7 @@ void mips_malta_init (ram_addr_t ram_size,
network_init();
/* Optional PCI video card */
- if (cirrus_vga_enabled) {
- pci_cirrus_vga_init(pci_bus);
- } else if (vmsvga_enabled) {
- pci_vmsvga_init(pci_bus);
- } else if (std_vga_enabled) {
- pci_vga_init(pci_bus);
- }
+ pci_vga_init(pci_bus);
}
static int mips_malta_sysbus_device_init(SysBusDevice *sysbusdev)
diff --git a/hw/msix.c b/hw/msix.c
index d81209413..b623cb597 100644
--- a/hw/msix.c
+++ b/hw/msix.c
@@ -366,7 +366,6 @@ void msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, MemoryRegion *pba_bar)
g_free(dev->msix_entry_used);
dev->msix_entry_used = NULL;
dev->cap_present &= ~QEMU_PCI_CAP_MSIX;
- return;
}
void msix_uninit_exclusive_bar(PCIDevice *dev)
diff --git a/hw/nand.c b/hw/nand.c
index e9501ae03..01f3adaee 100644
--- a/hw/nand.c
+++ b/hw/nand.c
@@ -654,7 +654,7 @@ static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s)
sector = SECTOR(s->addr);
off = (s->addr & PAGE_MASK) + s->offset;
soff = SECTOR_OFFSET(s->addr);
- if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1) {
+ if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS) < 0) {
printf("%s: read error in sector %" PRIu64 "\n", __func__, sector);
return;
}
@@ -666,21 +666,23 @@ static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s)
MIN(OOB_SIZE, off + s->iolen - PAGE_SIZE));
}
- if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1)
+ if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS) < 0) {
printf("%s: write error in sector %" PRIu64 "\n", __func__, sector);
+ }
} else {
off = PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset;
sector = off >> 9;
soff = off & 0x1ff;
- if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1) {
+ if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) < 0) {
printf("%s: read error in sector %" PRIu64 "\n", __func__, sector);
return;
}
mem_and(iobuf + soff, s->io, s->iolen);
- if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1)
+ if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) < 0) {
printf("%s: write error in sector %" PRIu64 "\n", __func__, sector);
+ }
}
s->offset = 0;
}
@@ -704,31 +706,37 @@ static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s)
i = SECTOR(addr);
page = SECTOR(addr + (ADDR_SHIFT + s->erase_shift));
for (; i < page; i ++)
- if (bdrv_write(s->bdrv, i, iobuf, 1) == -1)
+ if (bdrv_write(s->bdrv, i, iobuf, 1) < 0) {
printf("%s: write error in sector %" PRIu64 "\n", __func__, i);
+ }
} else {
addr = PAGE_START(addr);
page = addr >> 9;
- if (bdrv_read(s->bdrv, page, iobuf, 1) == -1)
+ if (bdrv_read(s->bdrv, page, iobuf, 1) < 0) {
printf("%s: read error in sector %" PRIu64 "\n", __func__, page);
+ }
memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1);
- if (bdrv_write(s->bdrv, page, iobuf, 1) == -1)
+ if (bdrv_write(s->bdrv, page, iobuf, 1) < 0) {
printf("%s: write error in sector %" PRIu64 "\n", __func__, page);
+ }
memset(iobuf, 0xff, 0x200);
i = (addr & ~0x1ff) + 0x200;
for (addr += ((PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200;
i < addr; i += 0x200)
- if (bdrv_write(s->bdrv, i >> 9, iobuf, 1) == -1)
+ if (bdrv_write(s->bdrv, i >> 9, iobuf, 1) < 0) {
printf("%s: write error in sector %" PRIu64 "\n",
__func__, i >> 9);
+ }
page = i >> 9;
- if (bdrv_read(s->bdrv, page, iobuf, 1) == -1)
+ if (bdrv_read(s->bdrv, page, iobuf, 1) < 0) {
printf("%s: read error in sector %" PRIu64 "\n", __func__, page);
+ }
memset(iobuf, 0xff, ((addr - 1) & 0x1ff) + 1);
- if (bdrv_write(s->bdrv, page, iobuf, 1) == -1)
+ if (bdrv_write(s->bdrv, page, iobuf, 1) < 0) {
printf("%s: write error in sector %" PRIu64 "\n", __func__, page);
+ }
}
}
@@ -740,18 +748,20 @@ static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s,
if (s->bdrv) {
if (s->mem_oob) {
- if (bdrv_read(s->bdrv, SECTOR(addr), s->io, PAGE_SECTORS) == -1)
+ if (bdrv_read(s->bdrv, SECTOR(addr), s->io, PAGE_SECTORS) < 0) {
printf("%s: read error in sector %" PRIu64 "\n",
__func__, SECTOR(addr));
+ }
memcpy(s->io + SECTOR_OFFSET(s->addr) + PAGE_SIZE,
s->storage + (PAGE(s->addr) << OOB_SHIFT),
OOB_SIZE);
s->ioaddr = s->io + SECTOR_OFFSET(s->addr) + offset;
} else {
if (bdrv_read(s->bdrv, PAGE_START(addr) >> 9,
- s->io, (PAGE_SECTORS + 2)) == -1)
+ s->io, (PAGE_SECTORS + 2)) < 0) {
printf("%s: read error in sector %" PRIu64 "\n",
__func__, PAGE_START(addr) >> 9);
+ }
s->ioaddr = s->io + (PAGE_START(addr) & 0x1ff) + offset;
}
} else {
diff --git a/hw/nseries.c b/hw/nseries.c
index 4df267032..6df71ebb4 100644
--- a/hw/nseries.c
+++ b/hw/nseries.c
@@ -189,6 +189,17 @@ static void n8x0_nand_setup(struct n800_s *s)
/* XXX: in theory should also update the OOB for both pages */
}
+static qemu_irq n8x0_system_powerdown;
+
+static void n8x0_powerdown_req(Notifier *n, void *opaque)
+{
+ qemu_irq_raise(n8x0_system_powerdown);
+}
+
+static Notifier n8x0_system_powerdown_notifier = {
+ .notify = n8x0_powerdown_req
+};
+
static void n8x0_i2c_setup(struct n800_s *s)
{
DeviceState *dev;
@@ -201,7 +212,8 @@ static void n8x0_i2c_setup(struct n800_s *s)
qdev_get_gpio_in(s->mpu->ih[0],
OMAP_INT_24XX_SYS_NIRQ));
- qemu_system_powerdown = qdev_get_gpio_in(dev, 3);
+ n8x0_system_powerdown = qdev_get_gpio_in(dev, 3);
+ qemu_register_powerdown_notifier(&n8x0_system_powerdown_notifier);
/* Attach a TMP105 PM chip (A0 wired to ground) */
dev = i2c_create_slave(i2c, "tmp105", N8X0_TMP105_ADDR);
diff --git a/hw/onenand.c b/hw/onenand.c
index db6af682c..0f7b755a6 100644
--- a/hw/onenand.c
+++ b/hw/onenand.c
@@ -351,7 +351,7 @@ static inline int onenand_erase(OneNANDState *s, int sec, int num)
for (; num > 0; num--, sec++) {
if (s->bdrv_cur) {
int erasesec = s->secs_cur + (sec >> 5);
- if (bdrv_write(s->bdrv_cur, sec, blankbuf, 1)) {
+ if (bdrv_write(s->bdrv_cur, sec, blankbuf, 1) < 0) {
goto fail;
}
if (bdrv_read(s->bdrv_cur, erasesec, tmpbuf, 1) < 0) {
diff --git a/hw/pc.c b/hw/pc.c
index c32ee8e26..0e417c9cb 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -27,7 +27,6 @@
#include "fdc.h"
#include "ide.h"
#include "pci.h"
-#include "vmware_vga.h"
#include "monitor.h"
#include "fw_cfg.h"
#include "hpet_emul.h"
@@ -51,10 +50,6 @@
#include "exec-memory.h"
#include "arch_init.h"
#include "bitmap.h"
-#include "vga-pci.h"
-
-/* output Bochs bios info messages */
-//#define DEBUG_BIOS
/* debug PC/ISA interrupts */
//#define DEBUG_IRQ
@@ -534,17 +529,6 @@ static void bochs_bios_write(void *opaque, uint32_t addr, uint32_t val)
static int shutdown_index = 0;
switch(addr) {
- /* Bochs BIOS messages */
- case 0x400:
- case 0x401:
- /* used to be panic, now unused */
- break;
- case 0x402:
- case 0x403:
-#ifdef DEBUG_BIOS
- fprintf(stderr, "%c", val);
-#endif
- break;
case 0x8900:
/* same as Bochs power off */
if (val == shutdown_str[shutdown_index]) {
@@ -558,16 +542,9 @@ static void bochs_bios_write(void *opaque, uint32_t addr, uint32_t val)
}
break;
- /* LGPL'ed VGA BIOS messages */
case 0x501:
case 0x502:
exit((val << 1) | 1);
- case 0x500:
- case 0x503:
-#ifdef DEBUG_BIOS
- fprintf(stderr, "%c", val);
-#endif
- break;
}
}
@@ -596,17 +573,11 @@ static void *bochs_bios_init(void)
uint64_t *numa_fw_cfg;
int i, j;
- register_ioport_write(0x400, 1, 2, bochs_bios_write, NULL);
- register_ioport_write(0x401, 1, 2, bochs_bios_write, NULL);
- register_ioport_write(0x402, 1, 1, bochs_bios_write, NULL);
- register_ioport_write(0x403, 1, 1, bochs_bios_write, NULL);
register_ioport_write(0x8900, 1, 1, bochs_bios_write, NULL);
register_ioport_write(0x501, 1, 1, bochs_bios_write, NULL);
register_ioport_write(0x501, 1, 2, bochs_bios_write, NULL);
register_ioport_write(0x502, 1, 2, bochs_bios_write, NULL);
- register_ioport_write(0x500, 1, 1, bochs_bios_write, NULL);
- register_ioport_write(0x503, 1, 1, bochs_bios_write, NULL);
fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0);
@@ -1019,34 +990,13 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus)
{
DeviceState *dev = NULL;
- if (cirrus_vga_enabled) {
- if (pci_bus) {
- dev = pci_cirrus_vga_init(pci_bus);
- } else {
- dev = &isa_create_simple(isa_bus, "isa-cirrus-vga")->qdev;
- }
- } else if (vmsvga_enabled) {
- if (pci_bus) {
- dev = pci_vmsvga_init(pci_bus);
- } else {
- fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __FUNCTION__);
- }
-#ifdef CONFIG_SPICE
- } else if (qxl_enabled) {
- if (pci_bus) {
- dev = &pci_create_simple(pci_bus, -1, "qxl-vga")->qdev;
- } else {
- fprintf(stderr, "%s: qxl: no PCI bus\n", __FUNCTION__);
- }
-#endif
- } else if (std_vga_enabled) {
- if (pci_bus) {
- dev = pci_vga_init(pci_bus);
- } else {
- dev = isa_vga_init(isa_bus);
- }
+ if (pci_bus) {
+ PCIDevice *pcidev = pci_vga_init(pci_bus);
+ dev = pcidev ? &pcidev->qdev : NULL;
+ } else if (isa_bus) {
+ ISADevice *isadev = isa_vga_init(isa_bus);
+ dev = isadev ? &isadev->qdev : NULL;
}
-
return dev;
}
diff --git a/hw/pc.h b/hw/pc.h
index e4db0715b..9923d9602 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -176,19 +176,6 @@ enum vga_retrace_method {
extern enum vga_retrace_method vga_retrace_method;
-static inline DeviceState *isa_vga_init(ISABus *bus)
-{
- ISADevice *dev;
-
- dev = isa_try_create(bus, "isa-vga");
- if (!dev) {
- fprintf(stderr, "Warning: isa-vga not available\n");
- return NULL;
- }
- qdev_init_nofail(&dev->qdev);
- return &dev->qdev;
-}
-
int isa_vga_mm_init(target_phys_addr_t vram_base,
target_phys_addr_t ctrl_base, int it_shift,
MemoryRegion *address_space);
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index a771d7982..1fc20cda9 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -359,8 +359,8 @@ static void pc_xen_hvm_init(ram_addr_t ram_size,
#define KVM_MACHINE_OPTIONS ""
#endif
-static QEMUMachine pc_machine_v1_2 = {
- .name = "pc-1.2",
+static QEMUMachine pc_machine_v1_3 = {
+ .name = "pc-1.3",
.alias = "pc",
.desc = "Standard PC",
.init = pc_init_pci,
@@ -369,7 +369,34 @@ static QEMUMachine pc_machine_v1_2 = {
.default_machine_opts = KVM_MACHINE_OPTIONS,
};
+#define PC_COMPAT_1_2 \
+ {\
+ .driver = "nec-usb-xhci",\
+ .property = "msi",\
+ .value = "off",\
+ },{\
+ .driver = "nec-usb-xhci",\
+ .property = "msix",\
+ .value = "off",\
+ },{\
+ .driver = "ivshmem",\
+ .property = "use64",\
+ .value = "0",\
+ }
+
+static QEMUMachine pc_machine_v1_2 = {
+ .name = "pc-1.2",
+ .desc = "Standard PC",
+ .init = pc_init_pci,
+ .max_cpus = 255,
+ .compat_props = (GlobalProperty[]) {
+ PC_COMPAT_1_2,
+ { /* end of list */ }
+ },
+};
+
#define PC_COMPAT_1_1 \
+ PC_COMPAT_1_2,\
{\
.driver = "virtio-scsi-pci",\
.property = "hotplug",\
@@ -675,6 +702,7 @@ static QEMUMachine xenfv_machine = {
static void pc_machine_init(void)
{
+ qemu_register_machine(&pc_machine_v1_3);
qemu_register_machine(&pc_machine_v1_2);
qemu_register_machine(&pc_machine_v1_1);
qemu_register_machine(&pc_machine_v1_0);
diff --git a/hw/pci.c b/hw/pci.c
index f855cf3f3..2ca6ff6fe 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -1474,6 +1474,24 @@ PCIDevice *pci_nic_init_nofail(NICInfo *nd, const char *default_model,
return res;
}
+PCIDevice *pci_vga_init(PCIBus *bus)
+{
+ switch (vga_interface_type) {
+ case VGA_CIRRUS:
+ return pci_create_simple(bus, -1, "cirrus-vga");
+ case VGA_QXL:
+ return pci_create_simple(bus, -1, "qxl-vga");
+ case VGA_STD:
+ return pci_create_simple(bus, -1, "VGA");
+ case VGA_VMWARE:
+ return pci_create_simple(bus, -1, "vmware-svga");
+ case VGA_NONE:
+ default: /* Other non-PCI types. Checking for unsupported types is already
+ done in vl.c. */
+ return NULL;
+ }
+}
+
/* Whether a given bus number is in range of the secondary
* bus of the given bridge device. */
static bool pci_secondary_bus_in_range(PCIDevice *dev, int bus_num)
@@ -1962,7 +1980,7 @@ static char *pcibus_get_fw_dev_path(DeviceState *dev)
PCI_SLOT(d->devfn));
if (PCI_FUNC(d->devfn))
snprintf(path + off, sizeof(path) + off, ",%x", PCI_FUNC(d->devfn));
- return strdup(path);
+ return g_strdup(path);
}
static char *pcibus_get_dev_path(DeviceState *dev)
diff --git a/hw/pci.h b/hw/pci.h
index 4b6ab3d19..d50d26c8a 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -334,6 +334,9 @@ PCIDevice *pci_nic_init(NICInfo *nd, const char *default_model,
const char *default_devaddr);
PCIDevice *pci_nic_init_nofail(NICInfo *nd, const char *default_model,
const char *default_devaddr);
+
+PCIDevice *pci_vga_init(PCIBus *bus);
+
int pci_bus_num(PCIBus *s);
void pci_for_each_device(PCIBus *bus, int bus_num,
void (*fn)(PCIBus *bus, PCIDevice *d, void *opaque),
diff --git a/hw/petalogix_ml605_mmu.c b/hw/petalogix_ml605_mmu.c
index dced648f4..b9bfbed4c 100644
--- a/hw/petalogix_ml605_mmu.c
+++ b/hw/petalogix_ml605_mmu.c
@@ -36,6 +36,7 @@
#include "blockdev.h"
#include "pc.h"
#include "exec-memory.h"
+#include "ssi.h"
#include "microblaze_boot.h"
#include "microblaze_pic_cpu.h"
@@ -47,6 +48,8 @@
#define BINARY_DEVICE_TREE_FILE "petalogix-ml605.dtb"
+#define NUM_SPI_FLASHES 4
+
#define MEMORY_BASEADDR 0x50000000
#define FLASH_BASEADDR 0x86000000
#define INTC_BASEADDR 0x81800000
@@ -79,6 +82,7 @@ petalogix_ml605_init(ram_addr_t ram_size,
MemoryRegion *address_space_mem = get_system_memory();
DeviceState *dev, *dma, *eth0;
MicroBlazeCPU *cpu;
+ SysBusDevice *busdev;
CPUMBState *env;
DriveInfo *dinfo;
int i;
@@ -139,6 +143,29 @@ petalogix_ml605_init(ram_addr_t ram_size,
xilinx_axiethernetdma_init(dma, STREAM_SLAVE(eth0),
0x84600000, irq[1], irq[0], 100 * 1000000);
+ {
+ SSIBus *spi;
+
+ dev = qdev_create(NULL, "xlnx.xps-spi");
+ qdev_prop_set_uint8(dev, "num-ss-bits", NUM_SPI_FLASHES);
+ qdev_init_nofail(dev);
+ busdev = sysbus_from_qdev(dev);
+ sysbus_mmio_map(busdev, 0, 0x40a00000);
+ sysbus_connect_irq(busdev, 0, irq[4]);
+
+ spi = (SSIBus *)qdev_get_child_bus(dev, "spi");
+
+ for (i = 0; i < NUM_SPI_FLASHES; i++) {
+ qemu_irq cs_line;
+
+ dev = ssi_create_slave_no_init(spi, "m25p80");
+ qdev_prop_set_string(dev, "partname", "n25q128");
+ qdev_init_nofail(dev);
+ cs_line = qdev_get_gpio_in(dev, 0);
+ sysbus_connect_irq(busdev, i+1, cs_line);
+ }
+ }
+
microblaze_load_kernel(cpu, ddr_base, ram_size, BINARY_DEVICE_TREE_FILE,
machine_cpu_reset);
diff --git a/hw/pflash_cfi01.c b/hw/pflash_cfi01.c
index d1c742379..3b437da9c 100644
--- a/hw/pflash_cfi01.c
+++ b/hw/pflash_cfi01.c
@@ -41,6 +41,7 @@
#include "block.h"
#include "qemu-timer.h"
#include "exec-memory.h"
+#include "host-utils.h"
#define PFLASH_BUG(fmt, ...) \
do { \
@@ -320,7 +321,7 @@ static void pflash_write(pflash_t *pfl, target_phys_addr_t offset,
}
pfl->wcycle++;
pfl->cmd = cmd;
- return;
+ break;
case 1:
switch (pfl->cmd) {
case 0x10: /* Single Byte Program */
@@ -375,7 +376,7 @@ static void pflash_write(pflash_t *pfl, target_phys_addr_t offset,
default:
goto error_flash;
}
- return;
+ break;
case 2:
switch (pfl->cmd) {
case 0xe8: /* Block write */
@@ -406,7 +407,7 @@ static void pflash_write(pflash_t *pfl, target_phys_addr_t offset,
default:
goto error_flash;
}
- return;
+ break;
case 3: /* Confirm mode */
switch (pfl->cmd) {
case 0xe8: /* Block write */
@@ -422,7 +423,7 @@ static void pflash_write(pflash_t *pfl, target_phys_addr_t offset,
default:
goto error_flash;
}
- return;
+ break;
default:
/* Should never happen */
DPRINTF("%s: invalid write state\n", __func__);
@@ -441,7 +442,6 @@ static void pflash_write(pflash_t *pfl, target_phys_addr_t offset,
pfl->bypass = 0;
pfl->wcycle = 0;
pfl->cmd = 0;
- return;
}
@@ -543,42 +543,6 @@ static const MemoryRegionOps pflash_cfi01_ops_le = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-/* Count trailing zeroes of a 32 bits quantity */
-static int ctz32 (uint32_t n)
-{
- int ret;
-
- ret = 0;
- if (!(n & 0xFFFF)) {
- ret += 16;
- n = n >> 16;
- }
- if (!(n & 0xFF)) {
- ret += 8;
- n = n >> 8;
- }
- if (!(n & 0xF)) {
- ret += 4;
- n = n >> 4;
- }
- if (!(n & 0x3)) {
- ret += 2;
- n = n >> 2;
- }
- if (!(n & 0x1)) {
- ret++;
-#if 0 /* This is not necessary as n is never 0 */
- n = n >> 1;
-#endif
- }
-#if 0 /* This is not necessary as n is never 0 */
- if (!n)
- ret++;
-#endif
-
- return ret;
-}
-
pflash_t *pflash_cfi01_register(target_phys_addr_t base,
DeviceState *qdev, const char *name,
target_phys_addr_t size,
@@ -711,7 +675,7 @@ pflash_t *pflash_cfi01_register(target_phys_addr_t base,
pfl->cfi_table[0x33] = 'I';
pfl->cfi_table[0x34] = '1';
- pfl->cfi_table[0x35] = '1';
+ pfl->cfi_table[0x35] = '0';
pfl->cfi_table[0x36] = 0x00;
pfl->cfi_table[0x37] = 0x00;
@@ -723,6 +687,8 @@ pflash_t *pflash_cfi01_register(target_phys_addr_t base,
pfl->cfi_table[0x3b] = 0x00;
pfl->cfi_table[0x3c] = 0x00;
+ pfl->cfi_table[0x3f] = 0x01; /* Number of protection fields */
+
return pfl;
}
diff --git a/hw/pflash_cfi02.c b/hw/pflash_cfi02.c
index 3e2002e4b..39337ec30 100644
--- a/hw/pflash_cfi02.c
+++ b/hw/pflash_cfi02.c
@@ -40,6 +40,7 @@
#include "qemu-timer.h"
#include "block.h"
#include "exec-memory.h"
+#include "host-utils.h"
//#define PFLASH_DEBUG
#ifdef PFLASH_DEBUG
@@ -473,7 +474,6 @@ static void pflash_write (pflash_t *pfl, target_phys_addr_t offset,
do_bypass:
pfl->wcycle = 2;
pfl->cmd = 0;
- return;
}
@@ -575,42 +575,6 @@ static const MemoryRegionOps pflash_cfi02_ops_le = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-/* Count trailing zeroes of a 32 bits quantity */
-static int ctz32 (uint32_t n)
-{
- int ret;
-
- ret = 0;
- if (!(n & 0xFFFF)) {
- ret += 16;
- n = n >> 16;
- }
- if (!(n & 0xFF)) {
- ret += 8;
- n = n >> 8;
- }
- if (!(n & 0xF)) {
- ret += 4;
- n = n >> 4;
- }
- if (!(n & 0x3)) {
- ret += 2;
- n = n >> 2;
- }
- if (!(n & 0x1)) {
- ret++;
-#if 0 /* This is not necessary as n is never 0 */
- n = n >> 1;
-#endif
- }
-#if 0 /* This is not necessary as n is never 0 */
- if (!n)
- ret++;
-#endif
-
- return ret;
-}
-
pflash_t *pflash_cfi02_register(target_phys_addr_t base,
DeviceState *qdev, const char *name,
target_phys_addr_t size,
diff --git a/hw/pl190.c b/hw/pl190.c
index cb50afb9f..7332f4dba 100644
--- a/hw/pl190.c
+++ b/hw/pl190.c
@@ -117,12 +117,18 @@ static uint64_t pl190_read(void *opaque, target_phys_addr_t offset,
return s->protected;
case 12: /* VECTADDR */
/* Read vector address at the start of an ISR. Increases the
- current priority level to that of the current interrupt. */
- for (i = 0; i < s->priority; i++)
- {
- if ((s->level | s->soft_level) & s->prio_mask[i])
- break;
- }
+ * current priority level to that of the current interrupt.
+ *
+ * Since an enabled interrupt X at priority P causes prio_mask[Y]
+ * to have bit X set for all Y > P, this loop will stop with
+ * i == the priority of the highest priority set interrupt.
+ */
+ for (i = 0; i < s->priority; i++) {
+ if ((s->level | s->soft_level) & s->prio_mask[i + 1]) {
+ break;
+ }
+ }
+
/* Reading this value with no pending interrupts is undefined.
We return the default address. */
if (i == PL190_NUM_PRIO)
diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
index 6f0de6d95..d23f9b2f6 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -36,7 +36,7 @@
#define BINARY_DEVICE_TREE_FILE "mpc8544ds.dtb"
#define UIMAGE_LOAD_BASE 0
-#define DTC_LOAD_PAD 0x500000
+#define DTC_LOAD_PAD 0x1800000
#define DTC_PAD_MASK 0xFFFFF
#define INITRD_LOAD_PAD 0x2000000
#define INITRD_PAD_MASK 0xFFFFFF
@@ -139,12 +139,10 @@ static int ppce500_load_device_tree(CPUPPCState *env,
0x0, 0x10000,
};
QemuOpts *machine_opts;
- const char *dumpdtb = NULL;
const char *dtb_file = NULL;
machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0);
if (machine_opts) {
- dumpdtb = qemu_opt_get(machine_opts, "dumpdtb");
dtb_file = qemu_opt_get(machine_opts, "dtb");
toplevel_compat = qemu_opt_get(machine_opts, "dt_compatible");
}
@@ -334,18 +332,7 @@ static int ppce500_load_device_tree(CPUPPCState *env,
}
done:
- if (dumpdtb) {
- /* Dump the dtb to a file and quit */
- FILE *f = fopen(dumpdtb, "wb");
- size_t len;
- len = fwrite(fdt, fdt_size, 1, f);
- fclose(f);
- if (len != fdt_size) {
- exit(1);
- }
- exit(0);
- }
-
+ qemu_devtree_dumpdtb(fdt, fdt_size);
ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr);
if (ret < 0) {
goto out;
@@ -375,6 +362,10 @@ static void mmubooke_create_initial_mapping(CPUPPCState *env)
the device tree top */
dt_end = bi->dt_base + bi->dt_size;
ps = booke206_page_size_to_tlb(dt_end) + 1;
+ if (ps & 1) {
+ /* e500v2 can only do even TLB size bits */
+ ps++;
+ }
size = (ps << MAS1_TSIZE_SHIFT);
tlb->mas1 = MAS1_VALID | size;
tlb->mas2 = 0;
@@ -553,7 +544,8 @@ void ppce500_init(PPCE500Params *params)
/* Load initrd. */
if (params->initrd_filename) {
- initrd_base = (kernel_size + INITRD_LOAD_PAD) & ~INITRD_PAD_MASK;
+ initrd_base = (loadaddr + kernel_size + INITRD_LOAD_PAD) &
+ ~INITRD_PAD_MASK;
initrd_size = load_image_targphys(params->initrd_filename, initrd_base,
ram_size - initrd_base);
diff --git a/hw/ppc405_uc.c b/hw/ppc405_uc.c
index 89e5013b5..b52ab2f17 100644
--- a/hw/ppc405_uc.c
+++ b/hw/ppc405_uc.c
@@ -191,7 +191,8 @@ enum {
typedef struct ppc4xx_pob_t ppc4xx_pob_t;
struct ppc4xx_pob_t {
uint32_t bear;
- uint32_t besr[2];
+ uint32_t besr0;
+ uint32_t besr1;
};
static uint32_t dcr_read_pob (void *opaque, int dcrn)
@@ -205,8 +206,10 @@ static uint32_t dcr_read_pob (void *opaque, int dcrn)
ret = pob->bear;
break;
case POB0_BESR0:
+ ret = pob->besr0;
+ break;
case POB0_BESR1:
- ret = pob->besr[dcrn - POB0_BESR0];
+ ret = pob->besr1;
break;
default:
/* Avoid gcc warning */
@@ -227,9 +230,12 @@ static void dcr_write_pob (void *opaque, int dcrn, uint32_t val)
/* Read only */
break;
case POB0_BESR0:
+ /* Write-clear */
+ pob->besr0 &= ~val;
+ break;
case POB0_BESR1:
/* Write-clear */
- pob->besr[dcrn - POB0_BESR0] &= ~val;
+ pob->besr1 &= ~val;
break;
}
}
@@ -241,8 +247,8 @@ static void ppc4xx_pob_reset (void *opaque)
pob = opaque;
/* No error */
pob->bear = 0x00000000;
- pob->besr[0] = 0x0000000;
- pob->besr[1] = 0x0000000;
+ pob->besr0 = 0x0000000;
+ pob->besr1 = 0x0000000;
}
static void ppc4xx_pob_init(CPUPPCState *env)
diff --git a/hw/ppc_newworld.c b/hw/ppc_newworld.c
index e95cfe831..b8d3c9c98 100644
--- a/hw/ppc_newworld.c
+++ b/hw/ppc_newworld.c
@@ -67,7 +67,6 @@
#include "hw/usb.h"
#include "blockdev.h"
#include "exec-memory.h"
-#include "vga-pci.h"
#define MAX_IDE_BUS 2
#define CFG_ADDR 0xf0000510
diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c
index 1dcd8a6c3..2c4a47813 100644
--- a/hw/ppc_oldworld.c
+++ b/hw/ppc_oldworld.c
@@ -43,7 +43,6 @@
#include "kvm_ppc.h"
#include "blockdev.h"
#include "exec-memory.h"
-#include "vga-pci.h"
#define MAX_IDE_BUS 2
#define CFG_ADDR 0xf0000510
diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c
index 592b7b255..15444303e 100644
--- a/hw/ppc_prep.c
+++ b/hw/ppc_prep.c
@@ -39,7 +39,6 @@
#include "blockdev.h"
#include "arch_init.h"
#include "exec-memory.h"
-#include "vga-pci.h"
//#define HARD_DEBUG_PPC_IO
//#define DEBUG_PPC_IO
diff --git a/hw/pxa2xx_keypad.c b/hw/pxa2xx_keypad.c
index 59db02584..1a997c9c8 100644
--- a/hw/pxa2xx_keypad.c
+++ b/hw/pxa2xx_keypad.c
@@ -172,7 +172,6 @@ static void pxa27x_keyboard_event (PXA2xxKeyPadState *kp, int keycode)
kp->kpc |= KPC_MI;
qemu_irq_raise(kp->irq);
}
- return;
}
static uint64_t pxa2xx_keypad_read(void *opaque, target_phys_addr_t offset,
diff --git a/hw/qdev.c b/hw/qdev.c
index b5a52ac50..9b9aba376 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -34,7 +34,6 @@ int qdev_hotplug = 0;
static bool qdev_hot_added = false;
static bool qdev_hot_removed = false;
-/* Register a new device type. */
const VMStateDescription *qdev_get_vmsd(DeviceState *dev)
{
DeviceClass *dc = DEVICE_GET_CLASS(dev);
@@ -52,11 +51,6 @@ const char *qdev_fw_name(DeviceState *dev)
return object_get_typename(OBJECT(dev));
}
-bool qdev_exists(const char *name)
-{
- return !!object_class_by_name(name);
-}
-
static void qdev_property_add_legacy(DeviceState *dev, Property *prop,
Error **errp);
@@ -291,9 +285,9 @@ BusState *qdev_get_parent_bus(DeviceState *dev)
void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
{
- assert(dev->num_gpio_in == 0);
- dev->num_gpio_in = n;
- dev->gpio_in = qemu_allocate_irqs(handler, dev, n);
+ dev->gpio_in = qemu_extend_irqs(dev->gpio_in, dev->num_gpio_in, handler,
+ dev, n);
+ dev->num_gpio_in += n;
}
void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n)
@@ -520,7 +514,7 @@ char* qdev_get_fw_dev_path(DeviceState *dev)
path[l-1] = '\0';
- return strdup(path);
+ return g_strdup(path);
}
char *qdev_get_dev_path(DeviceState *dev)
diff --git a/hw/qdev.h b/hw/qdev.h
index d69919441..c6ac63620 100644
--- a/hw/qdev.h
+++ b/hw/qdev.h
@@ -153,7 +153,6 @@ typedef struct GlobalProperty {
DeviceState *qdev_create(BusState *bus, const char *name);
DeviceState *qdev_try_create(BusState *bus, const char *name);
-bool qdev_exists(const char *name);
int qdev_device_help(QemuOpts *opts);
DeviceState *qdev_device_add(QemuOpts *opts);
int qdev_init(DeviceState *dev) QEMU_WARN_UNUSED_RESULT;
diff --git a/hw/qxl.c b/hw/qxl.c
index 5b3f48426..33169f348 100644
--- a/hw/qxl.c
+++ b/hw/qxl.c
@@ -18,6 +18,8 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include <zlib.h>
+
#include "qemu-common.h"
#include "qemu-timer.h"
#include "qemu-queue.h"
@@ -141,6 +143,7 @@ static void qxl_ring_set_dirty(PCIQXLDevice *qxl);
void qxl_set_guest_bug(PCIQXLDevice *qxl, const char *msg, ...)
{
+ trace_qxl_set_guest_bug(qxl->id);
qxl_send_events(qxl, QXL_INTERRUPT_ERROR);
qxl->guest_bug = 1;
if (qxl->guestdebug) {
@@ -201,6 +204,7 @@ static void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id,
spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id, (uintptr_t)cookie);
} else {
qxl->ssd.worker->destroy_surface_wait(qxl->ssd.worker, id);
+ qxl_spice_destroy_surface_wait_complete(qxl, id);
}
}
@@ -597,9 +601,9 @@ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
case QXL_MODE_VGA:
ret = false;
qemu_mutex_lock(&qxl->ssd.lock);
- if (qxl->ssd.update != NULL) {
- update = qxl->ssd.update;
- qxl->ssd.update = NULL;
+ update = QTAILQ_FIRST(&qxl->ssd.updates);
+ if (update != NULL) {
+ QTAILQ_REMOVE(&qxl->ssd.updates, update, next);
*ext = update->ext;
ret = true;
}
@@ -953,6 +957,11 @@ static void interface_set_client_capabilities(QXLInstance *sin,
{
PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+ if (runstate_check(RUN_STATE_INMIGRATE) ||
+ runstate_check(RUN_STATE_POSTMIGRATE)) {
+ return;
+ }
+
qxl->shadow_rom.client_present = client_present;
memcpy(qxl->shadow_rom.client_capabilities, caps, sizeof(caps));
qxl->rom->client_present = client_present;
@@ -964,6 +973,79 @@ static void interface_set_client_capabilities(QXLInstance *sin,
#endif
+#if defined(CONFIG_QXL_CLIENT_MONITORS_CONFIG) \
+ && SPICE_SERVER_VERSION >= 0x000b05
+
+static uint32_t qxl_crc32(const uint8_t *p, unsigned len)
+{
+ /*
+ * zlib xors the seed with 0xffffffff, and xors the result
+ * again with 0xffffffff; Both are not done with linux's crc32,
+ * which we want to be compatible with, so undo that.
+ */
+ return crc32(0xffffffff, p, len) ^ 0xffffffff;
+}
+
+/* called from main context only */
+static int interface_client_monitors_config(QXLInstance *sin,
+ VDAgentMonitorsConfig *monitors_config)
+{
+ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+ QXLRom *rom = memory_region_get_ram_ptr(&qxl->rom_bar);
+ int i;
+
+ /*
+ * Older windows drivers set int_mask to 0 when their ISR is called,
+ * then later set it to ~0. So it doesn't relate to the actual interrupts
+ * handled. However, they are old, so clearly they don't support this
+ * interrupt
+ */
+ if (qxl->ram->int_mask == 0 || qxl->ram->int_mask == ~0 ||
+ !(qxl->ram->int_mask & QXL_INTERRUPT_CLIENT_MONITORS_CONFIG)) {
+ trace_qxl_client_monitors_config_unsupported_by_guest(qxl->id,
+ qxl->ram->int_mask,
+ monitors_config);
+ return 0;
+ }
+ if (!monitors_config) {
+ return 1;
+ }
+ memset(&rom->client_monitors_config, 0,
+ sizeof(rom->client_monitors_config));
+ rom->client_monitors_config.count = monitors_config->num_of_monitors;
+ /* monitors_config->flags ignored */
+ if (rom->client_monitors_config.count >=
+ ARRAY_SIZE(rom->client_monitors_config.heads)) {
+ trace_qxl_client_monitors_config_capped(qxl->id,
+ monitors_config->num_of_monitors,
+ ARRAY_SIZE(rom->client_monitors_config.heads));
+ rom->client_monitors_config.count =
+ ARRAY_SIZE(rom->client_monitors_config.heads);
+ }
+ for (i = 0 ; i < rom->client_monitors_config.count ; ++i) {
+ VDAgentMonConfig *monitor = &monitors_config->monitors[i];
+ QXLURect *rect = &rom->client_monitors_config.heads[i];
+ /* monitor->depth ignored */
+ rect->left = monitor->x;
+ rect->top = monitor->y;
+ rect->right = monitor->x + monitor->width;
+ rect->bottom = monitor->y + monitor->height;
+ }
+ rom->client_monitors_config_crc = qxl_crc32(
+ (const uint8_t *)&rom->client_monitors_config,
+ sizeof(rom->client_monitors_config));
+ trace_qxl_client_monitors_config_crc(qxl->id,
+ sizeof(rom->client_monitors_config),
+ rom->client_monitors_config_crc);
+
+ trace_qxl_interrupt_client_monitors_config(qxl->id,
+ rom->client_monitors_config.count,
+ rom->client_monitors_config.heads);
+ qxl_send_events(qxl, QXL_INTERRUPT_CLIENT_MONITORS_CONFIG);
+ return 1;
+}
+#endif
+
static const QXLInterface qxl_interface = {
.base.type = SPICE_INTERFACE_QXL,
.base.description = "qxl gpu",
@@ -988,6 +1070,10 @@ static const QXLInterface qxl_interface = {
#if SPICE_SERVER_VERSION >= 0x000b04
.set_client_capabilities = interface_set_client_capabilities,
#endif
+#if SPICE_SERVER_VERSION >= 0x000b05 && \
+ defined(CONFIG_QXL_CLIENT_MONITORS_CONFIG)
+ .client_monitors_config = interface_client_monitors_config,
+#endif
};
static void qxl_enter_vga_mode(PCIQXLDevice *d)
@@ -1402,7 +1488,7 @@ static void ioport_write(void *opaque, target_phys_addr_t addr,
break;
}
trace_qxl_io_unexpected_vga_mode(d->id,
- io_port, io_port_to_string(io_port));
+ addr, val, io_port_to_string(io_port));
/* be nice to buggy guest drivers */
if (io_port >= QXL_IO_UPDATE_AREA_ASYNC &&
io_port < QXL_IO_RANGE_SIZE) {
@@ -1470,6 +1556,13 @@ async_common:
return;
}
+ if (update.left < 0 || update.top < 0 || update.left >= update.right ||
+ update.top >= update.bottom) {
+ qxl_set_guest_bug(d, "QXL_IO_UPDATE_AREA: "
+ "invalid area(%d,%d,%d,%d)\n", update.left,
+ update.right, update.top, update.bottom);
+ break;
+ }
if (async == QXL_ASYNC) {
cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO,
QXL_IO_UPDATE_AREA_ASYNC);
@@ -1501,6 +1594,7 @@ async_common:
qxl_set_mode(d, val, 0);
break;
case QXL_IO_LOG:
+ trace_qxl_io_log(d->id, d->ram->log_buf);
if (d->guestdebug) {
fprintf(stderr, "qxl/guest-%d: %" PRId64 ": %s", d->id,
qemu_get_clock_ns(vm_clock), d->ram->log_buf);
@@ -1594,9 +1688,9 @@ cancel_async:
static uint64_t ioport_read(void *opaque, target_phys_addr_t addr,
unsigned size)
{
- PCIQXLDevice *d = opaque;
+ PCIQXLDevice *qxl = opaque;
- trace_qxl_io_read_unexpected(d->id);
+ trace_qxl_io_read_unexpected(qxl->id);
return 0xff;
}
@@ -1626,6 +1720,7 @@ static void qxl_send_events(PCIQXLDevice *d, uint32_t events)
uint32_t old_pending;
uint32_t le_events = cpu_to_le32(events);
+ trace_qxl_send_events(d->id, events);
assert(qemu_spice_display_is_running(&d->ssd));
old_pending = __sync_fetch_and_or(&d->ram->int_pending, le_events);
if ((old_pending & le_events) == le_events) {
@@ -1910,6 +2005,7 @@ static int qxl_init_common(PCIQXLDevice *qxl)
if (qxl->id == 0) {
vga_dirty_log_start(&qxl->vga);
}
+ memory_region_set_flush_coalesced(&qxl->io_bar);
pci_register_bar(&qxl->pci, QXL_IO_RANGE_INDEX,
diff --git a/hw/r2d.c b/hw/r2d.c
index 0f16e81af..1bc191ff3 100644
--- a/hw/r2d.c
+++ b/hw/r2d.c
@@ -332,6 +332,8 @@ static void r2d_init(ram_addr_t ram_size,
}
if (kernel_cmdline) {
+ /* I see no evidence that this .kernel_cmdline buffer requires
+ NUL-termination, so using strncpy should be ok. */
strncpy(boot_params.kernel_cmdline, kernel_cmdline,
sizeof(boot_params.kernel_cmdline));
}
diff --git a/hw/rtl8139.c b/hw/rtl8139.c
index 844f1b8c3..b7c82ee02 100644
--- a/hw/rtl8139.c
+++ b/hw/rtl8139.c
@@ -774,11 +774,7 @@ static void rtl8139_write_buffer(RTL8139State *s, const void *buf, int size)
#define MIN_BUF_SIZE 60
static inline dma_addr_t rtl8139_addr64(uint32_t low, uint32_t high)
{
-#if TARGET_PHYS_ADDR_BITS > 32
- return low | ((target_phys_addr_t)high << 32);
-#else
- return low;
-#endif
+ return low | ((uint64_t)high << 32);
}
/* Workaround for buggy guest driver such as linux who allocates rx
diff --git a/hw/sb16.c b/hw/sb16.c
index c81455d7f..523ab0d5f 100644
--- a/hw/sb16.c
+++ b/hw/sb16.c
@@ -822,7 +822,6 @@ static void complete (SB16State *s)
ldebug ("\n");
s->cmd = -1;
- return;
}
static void legacy_reset (SB16State *s)
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 4981a0243..dfb263121 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -801,26 +801,39 @@ static int ata_passthrough_16_xfer_size(SCSIDevice *dev, uint8_t *buf)
return xfer * unit;
}
-static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
+uint32_t scsi_data_cdb_length(uint8_t *buf)
+{
+ if ((buf[0] >> 5) == 0 && buf[4] == 0) {
+ return 256;
+ } else {
+ return scsi_cdb_length(buf);
+ }
+}
+
+uint32_t scsi_cdb_length(uint8_t *buf)
{
switch (buf[0] >> 5) {
case 0:
- cmd->xfer = buf[4];
+ return buf[4];
break;
case 1:
case 2:
- cmd->xfer = lduw_be_p(&buf[7]);
+ return lduw_be_p(&buf[7]);
break;
case 4:
- cmd->xfer = ldl_be_p(&buf[10]) & 0xffffffffULL;
+ return ldl_be_p(&buf[10]) & 0xffffffffULL;
break;
case 5:
- cmd->xfer = ldl_be_p(&buf[6]) & 0xffffffffULL;
+ return ldl_be_p(&buf[6]) & 0xffffffffULL;
break;
default:
return -1;
}
+}
+static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
+{
+ cmd->xfer = scsi_cdb_length(buf);
switch (buf[0]) {
case TEST_UNIT_READY:
case REWIND:
@@ -1710,12 +1723,8 @@ static char *scsibus_get_dev_path(DeviceState *dev)
static char *scsibus_get_fw_dev_path(DeviceState *dev)
{
SCSIDevice *d = SCSI_DEVICE(dev);
- char path[100];
-
- snprintf(path, sizeof(path), "channel@%x/%s@%x,%x", d->channel,
- qdev_fw_name(dev), d->id, d->lun);
-
- return strdup(path);
+ return g_strdup_printf("channel@%x/%s@%x,%x", d->channel,
+ qdev_fw_name(dev), d->id, d->lun);
}
SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int id, int lun)
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 1585683bc..1b0afa635 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -386,23 +386,11 @@ static void scsi_read_data(SCSIRequest *req)
*/
static int scsi_handle_rw_error(SCSIDiskReq *r, int error)
{
- int is_read = (r->req.cmd.xfer == SCSI_XFER_FROM_DEV);
+ bool is_read = (r->req.cmd.xfer == SCSI_XFER_FROM_DEV);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
- BlockErrorAction action = bdrv_get_on_error(s->qdev.conf.bs, is_read);
+ BlockErrorAction action = bdrv_get_error_action(s->qdev.conf.bs, is_read, error);
- if (action == BLOCK_ERR_IGNORE) {
- bdrv_emit_qmp_error_event(s->qdev.conf.bs, BDRV_ACTION_IGNORE, is_read);
- return 0;
- }
-
- if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC)
- || action == BLOCK_ERR_STOP_ANY) {
-
- bdrv_emit_qmp_error_event(s->qdev.conf.bs, BDRV_ACTION_STOP, is_read);
- vm_stop(RUN_STATE_IO_ERROR);
- bdrv_iostatus_set_err(s->qdev.conf.bs, error);
- scsi_req_retry(&r->req);
- } else {
+ if (action == BDRV_ACTION_REPORT) {
switch (error) {
case ENOMEDIUM:
scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
@@ -417,9 +405,12 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error)
scsi_check_condition(r, SENSE_CODE(IO_ERROR));
break;
}
- bdrv_emit_qmp_error_event(s->qdev.conf.bs, BDRV_ACTION_REPORT, is_read);
}
- return 1;
+ bdrv_error_action(s->qdev.conf.bs, action, is_read, error);
+ if (action == BDRV_ACTION_STOP) {
+ scsi_req_retry(&r->req);
+ }
+ return action != BDRV_ACTION_IGNORE;
}
static void scsi_write_complete(void * opaque, int ret)
@@ -678,7 +669,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
* is actually implemented, but we're good enough.
*/
outbuf[2] = 5;
- outbuf[3] = 2; /* Format 2 */
+ outbuf[3] = 2 | 0x10; /* Format 2, HiSup */
if (buflen > 36) {
outbuf[4] = buflen - 5; /* Additional Length = (Len - 1) - 4 */
@@ -1446,7 +1437,22 @@ invalid_param_len:
invalid_field:
scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
- return;
+}
+
+static inline bool check_lba_range(SCSIDiskState *s,
+ uint64_t sector_num, uint32_t nb_sectors)
+{
+ /*
+ * The first line tests that no overflow happens when computing the last
+ * sector. The second line tests that the last accessed sector is in
+ * range.
+ *
+ * Careful, the computations should not underflow for nb_sectors == 0,
+ * and a 0-block read to the first LBA beyond the end of device is
+ * valid.
+ */
+ return (sector_num <= sector_num + nb_sectors &&
+ sector_num + nb_sectors <= s->qdev.max_lba + 1);
}
typedef struct UnmapCBData {
@@ -1473,8 +1479,7 @@ static void scsi_unmap_complete(void *opaque, int ret)
if (data->count > 0 && !r->req.io_canceled) {
sector_num = ldq_be_p(&data->inbuf[0]);
nb_sectors = ldl_be_p(&data->inbuf[8]) & 0xffffffffULL;
- if (sector_num > sector_num + nb_sectors ||
- sector_num + nb_sectors - 1 > s->qdev.max_lba) {
+ if (!check_lba_range(s, sector_num, nb_sectors)) {
scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE));
goto done;
}
@@ -1529,7 +1534,6 @@ static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
invalid_param_len:
scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN));
- return;
}
static void scsi_disk_emulate_write_data(SCSIRequest *req)
@@ -1793,17 +1797,13 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
DPRINTF("Unmap (len %lu)\n", (long)r->req.cmd.xfer);
break;
case WRITE_SAME_10:
- nb_sectors = lduw_be_p(&req->cmd.buf[7]);
- goto write_same;
case WRITE_SAME_16:
- nb_sectors = ldl_be_p(&req->cmd.buf[10]) & 0xffffffffULL;
- write_same:
+ nb_sectors = scsi_data_cdb_length(r->req.cmd.buf);
if (bdrv_is_read_only(s->qdev.conf.bs)) {
scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED));
return 0;
}
- if (r->req.cmd.lba > r->req.cmd.lba + nb_sectors ||
- r->req.cmd.lba + nb_sectors - 1 > s->qdev.max_lba) {
+ if (!check_lba_range(s, r->req.cmd.lba, nb_sectors)) {
goto illegal_lba;
}
@@ -1858,7 +1858,7 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf)
{
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
- int32_t len;
+ uint32_t len;
uint8_t command;
command = buf[0];
@@ -1868,18 +1868,17 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf)
return 0;
}
+ len = scsi_data_cdb_length(r->req.cmd.buf);
switch (command) {
case READ_6:
case READ_10:
case READ_12:
case READ_16:
- len = r->req.cmd.xfer / s->qdev.blocksize;
- DPRINTF("Read (sector %" PRId64 ", count %d)\n", r->req.cmd.lba, len);
+ DPRINTF("Read (sector %" PRId64 ", count %u)\n", r->req.cmd.lba, len);
if (r->req.cmd.buf[1] & 0xe0) {
goto illegal_request;
}
- if (r->req.cmd.lba > r->req.cmd.lba + len ||
- r->req.cmd.lba + len - 1 > s->qdev.max_lba) {
+ if (!check_lba_range(s, r->req.cmd.lba, len)) {
goto illegal_lba;
}
r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512);
@@ -1900,15 +1899,13 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf)
case VERIFY_10:
case VERIFY_12:
case VERIFY_16:
- len = r->req.cmd.xfer / s->qdev.blocksize;
- DPRINTF("Write %s(sector %" PRId64 ", count %d)\n",
+ DPRINTF("Write %s(sector %" PRId64 ", count %u)\n",
(command & 0xe) == 0xe ? "And Verify " : "",
r->req.cmd.lba, len);
if (r->req.cmd.buf[1] & 0xe0) {
goto illegal_request;
}
- if (r->req.cmd.lba > r->req.cmd.lba + len ||
- r->req.cmd.lba + len - 1 > s->qdev.max_lba) {
+ if (!check_lba_range(s, r->req.cmd.lba, len)) {
goto illegal_lba;
}
r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512);
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index a5eb663ec..d9045341b 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -400,11 +400,11 @@ static int scsi_generic_initfn(SCSIDevice *s)
return -1;
}
- if (bdrv_get_on_error(s->conf.bs, 0) != BLOCK_ERR_STOP_ENOSPC) {
+ if (bdrv_get_on_error(s->conf.bs, 0) != BLOCKDEV_ON_ERROR_ENOSPC) {
error_report("Device doesn't support drive option werror");
return -1;
}
- if (bdrv_get_on_error(s->conf.bs, 1) != BLOCK_ERR_REPORT) {
+ if (bdrv_get_on_error(s->conf.bs, 1) != BLOCKDEV_ON_ERROR_REPORT) {
error_report("Device doesn't support drive option rerror");
return -1;
}
diff --git a/hw/scsi.h b/hw/scsi.h
index 1aeee4659..b8f73577d 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -218,6 +218,8 @@ extern const struct SCSISense sense_code_WRITE_PROTECTED;
#define SENSE_CODE(x) sense_code_ ## x
+uint32_t scsi_data_cdb_length(uint8_t *buf);
+uint32_t scsi_cdb_length(uint8_t *buf);
int scsi_sense_valid(SCSISense sense);
int scsi_build_sense(uint8_t *in_buf, int in_len,
uint8_t *buf, int len, bool fixed);
diff --git a/hw/sd.c b/hw/sd.c
index ec2640754..297580aab 100644
--- a/hw/sd.c
+++ b/hw/sd.c
@@ -1407,7 +1407,7 @@ static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len)
DPRINTF("sd_blk_read: addr = 0x%08llx, len = %d\n",
(unsigned long long) addr, len);
- if (!sd->bdrv || bdrv_read(sd->bdrv, addr >> 9, sd->buf, 1) == -1) {
+ if (!sd->bdrv || bdrv_read(sd->bdrv, addr >> 9, sd->buf, 1) < 0) {
fprintf(stderr, "sd_blk_read: read error on host side\n");
return;
}
@@ -1415,7 +1415,7 @@ static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len)
if (end > (addr & ~511) + 512) {
memcpy(sd->data, sd->buf + (addr & 511), 512 - (addr & 511));
- if (bdrv_read(sd->bdrv, end >> 9, sd->buf, 1) == -1) {
+ if (bdrv_read(sd->bdrv, end >> 9, sd->buf, 1) < 0) {
fprintf(stderr, "sd_blk_read: read error on host side\n");
return;
}
@@ -1429,29 +1429,31 @@ static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len)
uint64_t end = addr + len;
if ((addr & 511) || len < 512)
- if (!sd->bdrv || bdrv_read(sd->bdrv, addr >> 9, sd->buf, 1) == -1) {
+ if (!sd->bdrv || bdrv_read(sd->bdrv, addr >> 9, sd->buf, 1) < 0) {
fprintf(stderr, "sd_blk_write: read error on host side\n");
return;
}
if (end > (addr & ~511) + 512) {
memcpy(sd->buf + (addr & 511), sd->data, 512 - (addr & 511));
- if (bdrv_write(sd->bdrv, addr >> 9, sd->buf, 1) == -1) {
+ if (bdrv_write(sd->bdrv, addr >> 9, sd->buf, 1) < 0) {
fprintf(stderr, "sd_blk_write: write error on host side\n");
return;
}
- if (bdrv_read(sd->bdrv, end >> 9, sd->buf, 1) == -1) {
+ if (bdrv_read(sd->bdrv, end >> 9, sd->buf, 1) < 0) {
fprintf(stderr, "sd_blk_write: read error on host side\n");
return;
}
memcpy(sd->buf, sd->data + 512 - (addr & 511), end & 511);
- if (bdrv_write(sd->bdrv, end >> 9, sd->buf, 1) == -1)
+ if (bdrv_write(sd->bdrv, end >> 9, sd->buf, 1) < 0) {
fprintf(stderr, "sd_blk_write: write error on host side\n");
+ }
} else {
memcpy(sd->buf + (addr & 511), sd->data, len);
- if (!sd->bdrv || bdrv_write(sd->bdrv, addr >> 9, sd->buf, 1) == -1)
+ if (!sd->bdrv || bdrv_write(sd->bdrv, addr >> 9, sd->buf, 1) < 0) {
fprintf(stderr, "sd_blk_write: write error on host side\n");
+ }
}
}
diff --git a/hw/spapr.c b/hw/spapr.c
index c34b767c6..09b8e9922 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -46,7 +46,6 @@
#include "kvm.h"
#include "kvm_ppc.h"
#include "pci.h"
-#include "vga-pci.h"
#include "exec-memory.h"
#include "hw/usb.h"
@@ -85,9 +84,11 @@
#define PHANDLE_XICP 0x00001111
+#define HTAB_SIZE(spapr) (1ULL << ((spapr)->htab_shift))
+
sPAPREnvironment *spapr;
-int spapr_allocate_irq(int hint, enum xics_irq_type type)
+int spapr_allocate_irq(int hint, bool lsi)
{
int irq;
@@ -103,13 +104,13 @@ int spapr_allocate_irq(int hint, enum xics_irq_type type)
return 0;
}
- xics_set_irq_type(spapr->icp, irq, type);
+ xics_set_irq_type(spapr->icp, irq, lsi);
return irq;
}
/* Allocate block of consequtive IRQs, returns a number of the first */
-int spapr_allocate_irq_block(int num, enum xics_irq_type type)
+int spapr_allocate_irq_block(int num, bool lsi)
{
int first = -1;
int i;
@@ -117,7 +118,7 @@ int spapr_allocate_irq_block(int num, enum xics_irq_type type)
for (i = 0; i < num; ++i) {
int irq;
- irq = spapr_allocate_irq(0, type);
+ irq = spapr_allocate_irq(0, lsi);
if (!irq) {
return -1;
}
@@ -134,12 +135,13 @@ int spapr_allocate_irq_block(int num, enum xics_irq_type type)
return first;
}
-static int spapr_set_associativity(void *fdt, sPAPREnvironment *spapr)
+static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr)
{
int ret = 0, offset;
CPUPPCState *env;
char cpu_model[32];
int smt = kvmppc_smt_threads();
+ uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)};
assert(spapr->cpu_model);
@@ -163,8 +165,16 @@ static int spapr_set_associativity(void *fdt, sPAPREnvironment *spapr)
return offset;
}
- ret = fdt_setprop(fdt, offset, "ibm,associativity", associativity,
- sizeof(associativity));
+ if (nb_numa_nodes > 1) {
+ ret = fdt_setprop(fdt, offset, "ibm,associativity", associativity,
+ sizeof(associativity));
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ ret = fdt_setprop(fdt, offset, "ibm,pft-size",
+ pft_size_prop, sizeof(pft_size_prop));
if (ret < 0) {
return ret;
}
@@ -206,45 +216,36 @@ static size_t create_page_sizes_prop(CPUPPCState *env, uint32_t *prop,
return (p - prop) * sizeof(uint32_t);
}
+#define _FDT(exp) \
+ do { \
+ int ret = (exp); \
+ if (ret < 0) { \
+ fprintf(stderr, "qemu: error creating device tree: %s: %s\n", \
+ #exp, fdt_strerror(ret)); \
+ exit(1); \
+ } \
+ } while (0)
+
+
static void *spapr_create_fdt_skel(const char *cpu_model,
- target_phys_addr_t rma_size,
target_phys_addr_t initrd_base,
target_phys_addr_t initrd_size,
target_phys_addr_t kernel_size,
const char *boot_device,
- const char *kernel_cmdline,
- long hash_shift)
+ const char *kernel_cmdline)
{
void *fdt;
CPUPPCState *env;
- uint64_t mem_reg_property[2];
uint32_t start_prop = cpu_to_be32(initrd_base);
uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
- uint32_t pft_size_prop[] = {0, cpu_to_be32(hash_shift)};
char hypertas_prop[] = "hcall-pft\0hcall-term\0hcall-dabr\0hcall-interrupt"
"\0hcall-tce\0hcall-vio\0hcall-splpar\0hcall-bulk";
char qemu_hypertas_prop[] = "hcall-memop1";
+ uint32_t refpoints[] = {cpu_to_be32(0x4), cpu_to_be32(0x4)};
uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)};
- int i;
char *modelname;
- int smt = kvmppc_smt_threads();
+ int i, smt = kvmppc_smt_threads();
unsigned char vec5[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x80};
- uint32_t refpoints[] = {cpu_to_be32(0x4), cpu_to_be32(0x4)};
- uint32_t associativity[] = {cpu_to_be32(0x4), cpu_to_be32(0x0),
- cpu_to_be32(0x0), cpu_to_be32(0x0),
- cpu_to_be32(0x0)};
- char mem_name[32];
- target_phys_addr_t node0_size, mem_start;
-
-#define _FDT(exp) \
- do { \
- int ret = (exp); \
- if (ret < 0) { \
- fprintf(stderr, "qemu: error creating device tree: %s: %s\n", \
- #exp, fdt_strerror(ret)); \
- exit(1); \
- } \
- } while (0)
fdt = g_malloc0(FDT_MAX_SIZE);
_FDT((fdt_create(fdt, FDT_MAX_SIZE)));
@@ -289,55 +290,6 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
_FDT((fdt_end_node(fdt)));
- /* memory node(s) */
- node0_size = (nb_numa_nodes > 1) ? node_mem[0] : ram_size;
- if (rma_size > node0_size) {
- rma_size = node0_size;
- }
-
- /* RMA */
- mem_reg_property[0] = 0;
- mem_reg_property[1] = cpu_to_be64(rma_size);
- _FDT((fdt_begin_node(fdt, "memory@0")));
- _FDT((fdt_property_string(fdt, "device_type", "memory")));
- _FDT((fdt_property(fdt, "reg", mem_reg_property,
- sizeof(mem_reg_property))));
- _FDT((fdt_property(fdt, "ibm,associativity", associativity,
- sizeof(associativity))));
- _FDT((fdt_end_node(fdt)));
-
- /* RAM: Node 0 */
- if (node0_size > rma_size) {
- mem_reg_property[0] = cpu_to_be64(rma_size);
- mem_reg_property[1] = cpu_to_be64(node0_size - rma_size);
-
- sprintf(mem_name, "memory@" TARGET_FMT_lx, rma_size);
- _FDT((fdt_begin_node(fdt, mem_name)));
- _FDT((fdt_property_string(fdt, "device_type", "memory")));
- _FDT((fdt_property(fdt, "reg", mem_reg_property,
- sizeof(mem_reg_property))));
- _FDT((fdt_property(fdt, "ibm,associativity", associativity,
- sizeof(associativity))));
- _FDT((fdt_end_node(fdt)));
- }
-
- /* RAM: Node 1 and beyond */
- mem_start = node0_size;
- for (i = 1; i < nb_numa_nodes; i++) {
- mem_reg_property[0] = cpu_to_be64(mem_start);
- mem_reg_property[1] = cpu_to_be64(node_mem[i]);
- associativity[3] = associativity[4] = cpu_to_be32(i);
- sprintf(mem_name, "memory@" TARGET_FMT_lx, mem_start);
- _FDT((fdt_begin_node(fdt, mem_name)));
- _FDT((fdt_property_string(fdt, "device_type", "memory")));
- _FDT((fdt_property(fdt, "reg", mem_reg_property,
- sizeof(mem_reg_property))));
- _FDT((fdt_property(fdt, "ibm,associativity", associativity,
- sizeof(associativity))));
- _FDT((fdt_end_node(fdt)));
- mem_start += node_mem[i];
- }
-
/* cpus */
_FDT((fdt_begin_node(fdt, "cpus")));
@@ -389,8 +341,6 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
_FDT((fdt_property_cell(fdt, "timebase-frequency", tbfreq)));
_FDT((fdt_property_cell(fdt, "clock-frequency", cpufreq)));
_FDT((fdt_property_cell(fdt, "ibm,slb-size", env->slb_nr)));
- _FDT((fdt_property(fdt, "ibm,pft-size",
- pft_size_prop, sizeof(pft_size_prop))));
_FDT((fdt_property_string(fdt, "status", "okay")));
_FDT((fdt_property(fdt, "64-bit", NULL, 0)));
@@ -489,6 +439,68 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
return fdt;
}
+static int spapr_populate_memory(sPAPREnvironment *spapr, void *fdt)
+{
+ uint32_t associativity[] = {cpu_to_be32(0x4), cpu_to_be32(0x0),
+ cpu_to_be32(0x0), cpu_to_be32(0x0),
+ cpu_to_be32(0x0)};
+ char mem_name[32];
+ target_phys_addr_t node0_size, mem_start;
+ uint64_t mem_reg_property[2];
+ int i, off;
+
+ /* memory node(s) */
+ node0_size = (nb_numa_nodes > 1) ? node_mem[0] : ram_size;
+ if (spapr->rma_size > node0_size) {
+ spapr->rma_size = node0_size;
+ }
+
+ /* RMA */
+ mem_reg_property[0] = 0;
+ mem_reg_property[1] = cpu_to_be64(spapr->rma_size);
+ off = fdt_add_subnode(fdt, 0, "memory@0");
+ _FDT(off);
+ _FDT((fdt_setprop_string(fdt, off, "device_type", "memory")));
+ _FDT((fdt_setprop(fdt, off, "reg", mem_reg_property,
+ sizeof(mem_reg_property))));
+ _FDT((fdt_setprop(fdt, off, "ibm,associativity", associativity,
+ sizeof(associativity))));
+
+ /* RAM: Node 0 */
+ if (node0_size > spapr->rma_size) {
+ mem_reg_property[0] = cpu_to_be64(spapr->rma_size);
+ mem_reg_property[1] = cpu_to_be64(node0_size - spapr->rma_size);
+
+ sprintf(mem_name, "memory@" TARGET_FMT_lx, spapr->rma_size);
+ off = fdt_add_subnode(fdt, 0, mem_name);
+ _FDT(off);
+ _FDT((fdt_setprop_string(fdt, off, "device_type", "memory")));
+ _FDT((fdt_setprop(fdt, off, "reg", mem_reg_property,
+ sizeof(mem_reg_property))));
+ _FDT((fdt_setprop(fdt, off, "ibm,associativity", associativity,
+ sizeof(associativity))));
+ }
+
+ /* RAM: Node 1 and beyond */
+ mem_start = node0_size;
+ for (i = 1; i < nb_numa_nodes; i++) {
+ mem_reg_property[0] = cpu_to_be64(mem_start);
+ mem_reg_property[1] = cpu_to_be64(node_mem[i]);
+ associativity[3] = associativity[4] = cpu_to_be32(i);
+ sprintf(mem_name, "memory@" TARGET_FMT_lx, mem_start);
+ off = fdt_add_subnode(fdt, 0, mem_name);
+ _FDT(off);
+ _FDT((fdt_setprop_string(fdt, off, "device_type", "memory")));
+ _FDT((fdt_setprop(fdt, off, "reg", mem_reg_property,
+ sizeof(mem_reg_property))));
+ _FDT((fdt_setprop(fdt, off, "ibm,associativity", associativity,
+ sizeof(associativity))));
+ mem_start += node_mem[i];
+ }
+
+ return 0;
+}
+
static void spapr_finalize_fdt(sPAPREnvironment *spapr,
target_phys_addr_t fdt_addr,
target_phys_addr_t rtas_addr,
@@ -503,6 +515,12 @@ static void spapr_finalize_fdt(sPAPREnvironment *spapr,
/* open out the base tree into a temp buffer for the final tweaks */
_FDT((fdt_open_into(spapr->fdt_skel, fdt, FDT_MAX_SIZE)));
+ ret = spapr_populate_memory(spapr, fdt);
+ if (ret < 0) {
+ fprintf(stderr, "couldn't setup memory nodes in fdt\n");
+ exit(1);
+ }
+
ret = spapr_populate_vdevice(spapr->vio_bus, fdt);
if (ret < 0) {
fprintf(stderr, "couldn't setup vio devices in fdt\n");
@@ -525,11 +543,9 @@ static void spapr_finalize_fdt(sPAPREnvironment *spapr,
}
/* Advertise NUMA via ibm,associativity */
- if (nb_numa_nodes > 1) {
- ret = spapr_set_associativity(fdt, spapr);
- if (ret < 0) {
- fprintf(stderr, "Couldn't set up NUMA device tree properties\n");
- }
+ ret = spapr_fixup_cpu_dt(fdt, spapr);
+ if (ret < 0) {
+ fprintf(stderr, "Couldn't finalize CPU device tree properties\n");
}
if (!spapr->has_graphics) {
@@ -556,15 +572,49 @@ static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
static void emulate_spapr_hypercall(CPUPPCState *env)
{
- env->gpr[3] = spapr_hypercall(env, env->gpr[3], &env->gpr[4]);
+ if (msr_pr) {
+ hcall_dprintf("Hypercall made with MSR[PR]=1\n");
+ env->gpr[3] = H_PRIVILEGE;
+ } else {
+ env->gpr[3] = spapr_hypercall(env, env->gpr[3], &env->gpr[4]);
+ }
}
-static void spapr_reset(void *opaque)
+static void spapr_reset_htab(sPAPREnvironment *spapr)
{
- sPAPREnvironment *spapr = (sPAPREnvironment *)opaque;
+ long shift;
+
+ /* allocate hash page table. For now we always make this 16mb,
+ * later we should probably make it scale to the size of guest
+ * RAM */
+
+ shift = kvmppc_reset_htab(spapr->htab_shift);
- /* flush out the hash table */
- memset(spapr->htab, 0, spapr->htab_size);
+ if (shift > 0) {
+ /* Kernel handles htab, we don't need to allocate one */
+ spapr->htab_shift = shift;
+ } else {
+ if (!spapr->htab) {
+ /* Allocate an htab if we don't yet have one */
+ spapr->htab = qemu_memalign(HTAB_SIZE(spapr), HTAB_SIZE(spapr));
+ }
+
+ /* And clear it */
+ memset(spapr->htab, 0, HTAB_SIZE(spapr));
+ }
+
+ /* Update the RMA size if necessary */
+ if (spapr->vrma_adjust) {
+ spapr->rma_size = kvmppc_rma_size(ram_size, spapr->htab_shift);
+ }
+}
+
+static void ppc_spapr_reset(void)
+{
+ /* Reset the hash table & recalc the RMA */
+ spapr_reset_htab(spapr);
+
+ qemu_devices_reset();
/* Load the fdt */
spapr_finalize_fdt(spapr, spapr->fdt_addr, spapr->rtas_addr,
@@ -581,19 +631,31 @@ static void spapr_reset(void *opaque)
static void spapr_cpu_reset(void *opaque)
{
PowerPCCPU *cpu = opaque;
+ CPUPPCState *env = &cpu->env;
cpu_reset(CPU(cpu));
+
+ /* All CPUs start halted. CPU0 is unhalted from the machine level
+ * reset code and the rest are explicitly started up by the guest
+ * using an RTAS call */
+ env->halted = 1;
+
+ env->spr[SPR_HIOR] = 0;
+
+ env->external_htab = spapr->htab;
+ env->htab_base = -1;
+ env->htab_mask = HTAB_SIZE(spapr) - 1;
+ env->spr[SPR_SDR1] = (unsigned long)spapr->htab |
+ (spapr->htab_shift - 18);
}
/* Returns whether we want to use VGA or not */
static int spapr_vga_init(PCIBus *pci_bus)
{
switch (vga_interface_type) {
- case VGA_STD:
- pci_vga_init(pci_bus);
- return 1;
case VGA_NONE:
- return 0;
+ case VGA_STD:
+ return pci_vga_init(pci_bus) != NULL;
default:
fprintf(stderr, "This vga model is not supported,"
"currently it only supports -vga std\n");
@@ -616,11 +678,10 @@ static void ppc_spapr_init(ram_addr_t ram_size,
int i;
MemoryRegion *sysmem = get_system_memory();
MemoryRegion *ram = g_new(MemoryRegion, 1);
- target_phys_addr_t rma_alloc_size, rma_size;
+ target_phys_addr_t rma_alloc_size;
uint32_t initrd_base = 0;
long kernel_size = 0, initrd_size = 0;
long load_limit, rtas_limit, fw_size;
- long pteg_shift = 17;
char *filename;
msi_supported = true;
@@ -637,20 +698,46 @@ static void ppc_spapr_init(ram_addr_t ram_size,
hw_error("qemu: Unable to create RMA\n");
exit(1);
}
+
if (rma_alloc_size && (rma_alloc_size < ram_size)) {
- rma_size = rma_alloc_size;
+ spapr->rma_size = rma_alloc_size;
} else {
- rma_size = ram_size;
+ spapr->rma_size = ram_size;
+
+ /* With KVM, we don't actually know whether KVM supports an
+ * unbounded RMA (PR KVM) or is limited by the hash table size
+ * (HV KVM using VRMA), so we always assume the latter
+ *
+ * In that case, we also limit the initial allocations for RTAS
+ * etc... to 256M since we have no way to know what the VRMA size
+ * is going to be as it depends on the size of the hash table
+ * isn't determined yet.
+ */
+ if (kvm_enabled()) {
+ spapr->vrma_adjust = 1;
+ spapr->rma_size = MIN(spapr->rma_size, 0x10000000);
+ }
}
/* We place the device tree and RTAS just below either the top of the RMA,
* or just below 2GB, whichever is lowere, so that it can be
* processed with 32-bit real mode code if necessary */
- rtas_limit = MIN(rma_size, 0x80000000);
+ rtas_limit = MIN(spapr->rma_size, 0x80000000);
spapr->rtas_addr = rtas_limit - RTAS_MAX_SIZE;
spapr->fdt_addr = spapr->rtas_addr - FDT_MAX_SIZE;
load_limit = spapr->fdt_addr - FW_OVERHEAD;
+ /* We aim for a hash table of size 1/128 the size of RAM. The
+ * normal rule of thumb is 1/64 the size of RAM, but that's much
+ * more than needed for the Linux guests we support. */
+ spapr->htab_shift = 18; /* Minimum architected size */
+ while (spapr->htab_shift <= 46) {
+ if ((1ULL << (spapr->htab_shift + 7)) >= ram_size) {
+ break;
+ }
+ spapr->htab_shift++;
+ }
+
/* init CPUs */
if (cpu_model == NULL) {
cpu_model = kvm_enabled() ? "host" : "POWER7";
@@ -665,11 +752,16 @@ static void ppc_spapr_init(ram_addr_t ram_size,
/* Set time-base frequency to 512 MHz */
cpu_ppc_tb_init(env, TIMEBASE_FREQ);
- qemu_register_reset(spapr_cpu_reset, cpu);
- env->hreset_vector = 0x60;
+ /* PAPR always has exception vectors in RAM not ROM */
env->hreset_excp_prefix = 0;
- env->gpr[3] = env->cpu_index;
+
+ /* Tell KVM that we're in PAPR mode */
+ if (kvm_enabled()) {
+ kvmppc_set_papr(env);
+ }
+
+ qemu_register_reset(spapr_cpu_reset, cpu);
}
/* allocate RAM */
@@ -683,27 +775,6 @@ static void ppc_spapr_init(ram_addr_t ram_size,
memory_region_add_subregion(sysmem, nonrma_base, ram);
}
- /* allocate hash page table. For now we always make this 16mb,
- * later we should probably make it scale to the size of guest
- * RAM */
- spapr->htab_size = 1ULL << (pteg_shift + 7);
- spapr->htab = qemu_memalign(spapr->htab_size, spapr->htab_size);
-
- for (env = first_cpu; env != NULL; env = env->next_cpu) {
- env->external_htab = spapr->htab;
- env->htab_base = -1;
- env->htab_mask = spapr->htab_size - 1;
-
- /* Tell KVM that we're in PAPR mode */
- env->spr[SPR_SDR1] = (unsigned long)spapr->htab |
- ((pteg_shift + 7) - 18);
- env->spr[SPR_HIOR] = 0;
-
- if (kvm_enabled()) {
- kvmppc_set_papr(env);
- }
- }
-
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "spapr-rtas.bin");
spapr->rtas_size = load_image_targphys(filename, spapr->rtas_addr,
rtas_limit - spapr->rtas_addr);
@@ -776,7 +847,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
}
}
- if (rma_size < (MIN_RMA_SLOF << 20)) {
+ if (spapr->rma_size < (MIN_RMA_SLOF << 20)) {
fprintf(stderr, "qemu: pSeries SLOF firmware requires >= "
"%ldM guest RMA (Real Mode Area memory)\n", MIN_RMA_SLOF);
exit(1);
@@ -827,26 +898,19 @@ static void ppc_spapr_init(ram_addr_t ram_size,
spapr->entry_point = 0x100;
- /* SLOF will startup the secondary CPUs using RTAS */
- for (env = first_cpu; env != NULL; env = env->next_cpu) {
- env->halted = 1;
- }
-
/* Prepare the device tree */
- spapr->fdt_skel = spapr_create_fdt_skel(cpu_model, rma_size,
+ spapr->fdt_skel = spapr_create_fdt_skel(cpu_model,
initrd_base, initrd_size,
kernel_size,
- boot_device, kernel_cmdline,
- pteg_shift + 7);
+ boot_device, kernel_cmdline);
assert(spapr->fdt_skel != NULL);
-
- qemu_register_reset(spapr_reset, spapr);
}
static QEMUMachine spapr_machine = {
.name = "pseries",
.desc = "pSeries Logical Partition (PAPR compliant)",
.init = ppc_spapr_init,
+ .reset = ppc_spapr_reset,
.max_cpus = MAX_CPUS,
.no_parallel = 1,
.use_scsi = 1,
diff --git a/hw/spapr.h b/hw/spapr.h
index ac34a171e..e984e3fc3 100644
--- a/hw/spapr.h
+++ b/hw/spapr.h
@@ -15,7 +15,9 @@ typedef struct sPAPREnvironment {
target_phys_addr_t ram_limit;
void *htab;
- long htab_size;
+ long htab_shift;
+ target_phys_addr_t rma_size;
+ int vrma_adjust;
target_phys_addr_t fdt_addr, rtas_addr;
long rtas_size;
void *fdt_skel;
@@ -289,17 +291,17 @@ void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn);
target_ulong spapr_hypercall(CPUPPCState *env, target_ulong opcode,
target_ulong *args);
-int spapr_allocate_irq(int hint, enum xics_irq_type type);
-int spapr_allocate_irq_block(int num, enum xics_irq_type type);
+int spapr_allocate_irq(int hint, bool lsi);
+int spapr_allocate_irq_block(int num, bool lsi);
static inline int spapr_allocate_msi(int hint)
{
- return spapr_allocate_irq(hint, XICS_MSI);
+ return spapr_allocate_irq(hint, false);
}
static inline int spapr_allocate_lsi(int hint)
{
- return spapr_allocate_irq(hint, XICS_LSI);
+ return spapr_allocate_irq(hint, true);
}
static inline uint32_t rtas_ld(target_ulong phys, int n)
@@ -336,6 +338,8 @@ typedef struct sPAPRTCE {
void spapr_iommu_init(void);
DMAContext *spapr_tce_new_dma_context(uint32_t liobn, size_t window_size);
void spapr_tce_free(DMAContext *dma);
+void spapr_tce_reset(DMAContext *dma);
+void spapr_tce_set_bypass(DMAContext *dma, bool bypass);
int spapr_dma_dt(void *fdt, int node_off, const char *propname,
uint32_t liobn, uint64_t window, uint32_t size);
int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname,
diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c
index a5990a961..194d9c24b 100644
--- a/hw/spapr_hcall.c
+++ b/hw/spapr_hcall.c
@@ -1,6 +1,5 @@
#include "sysemu.h"
#include "cpu.h"
-#include "dyngen-exec.h"
#include "qemu-char.h"
#include "sysemu.h"
#include "qemu-char.h"
@@ -40,22 +39,6 @@
#define HPTE_V_1TB_SEG 0x4000000000000000ULL
#define HPTE_V_VRMA_MASK 0x4001ffffff000000ULL
-#define HPTE_V_HVLOCK 0x40ULL
-
-static inline int lock_hpte(void *hpte, target_ulong bits)
-{
- uint64_t pteh;
-
- pteh = ldq_p(hpte);
-
- /* We're protected by qemu's global lock here */
- if (pteh & bits) {
- return 0;
- }
- stq_p(hpte, pteh | HPTE_V_HVLOCK);
- return 1;
-}
-
static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r,
target_ulong pte_index)
{
@@ -152,8 +135,7 @@ static target_ulong h_enter(CPUPPCState *env, sPAPREnvironment *spapr,
if (i == 8) {
return H_PTEG_FULL;
}
- if (((ldq_p(hpte) & HPTE_V_VALID) == 0) &&
- lock_hpte(hpte, HPTE_V_HVLOCK | HPTE_V_VALID)) {
+ if ((ldq_p(hpte) & HPTE_V_VALID) == 0) {
break;
}
hpte += HASH_PTE_SIZE_64;
@@ -161,7 +143,7 @@ static target_ulong h_enter(CPUPPCState *env, sPAPREnvironment *spapr,
} else {
i = 0;
hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);
- if (!lock_hpte(hpte, HPTE_V_HVLOCK | HPTE_V_VALID)) {
+ if (ldq_p(hpte) & HPTE_V_VALID) {
return H_PTEG_FULL;
}
}
@@ -169,7 +151,6 @@ static target_ulong h_enter(CPUPPCState *env, sPAPREnvironment *spapr,
/* eieio(); FIXME: need some sort of barrier for smp? */
stq_p(hpte, pteh);
- assert(!(ldq_p(hpte) & HPTE_V_HVLOCK));
args[0] = pte_index + i;
return H_SUCCESS;
}
@@ -194,11 +175,6 @@ static target_ulong remove_hpte(CPUPPCState *env, target_ulong ptex,
}
hpte = env->external_htab + (ptex * HASH_PTE_SIZE_64);
- while (!lock_hpte(hpte, HPTE_V_HVLOCK)) {
- /* We have no real concurrency in qemu soft-emulation, so we
- * will never actually have a contested lock */
- assert(0);
- }
v = ldq_p(hpte);
r = ldq_p(hpte + (HASH_PTE_SIZE_64/2));
@@ -206,16 +182,13 @@ static target_ulong remove_hpte(CPUPPCState *env, target_ulong ptex,
if ((v & HPTE_V_VALID) == 0 ||
((flags & H_AVPN) && (v & ~0x7fULL) != avpn) ||
((flags & H_ANDCOND) && (v & avpn) != 0)) {
- stq_p(hpte, v & ~HPTE_V_HVLOCK);
- assert(!(ldq_p(hpte) & HPTE_V_HVLOCK));
return REMOVE_NOT_FOUND;
}
- *vp = v & ~HPTE_V_HVLOCK;
+ *vp = v;
*rp = r;
stq_p(hpte, 0);
rb = compute_tlbie_rb(v, r, ptex);
ppc_tlb_invalidate_one(env, rb);
- assert(!(ldq_p(hpte) & HPTE_V_HVLOCK));
return REMOVE_SUCCESS;
}
@@ -325,19 +298,12 @@ static target_ulong h_protect(CPUPPCState *env, sPAPREnvironment *spapr,
}
hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);
- while (!lock_hpte(hpte, HPTE_V_HVLOCK)) {
- /* We have no real concurrency in qemu soft-emulation, so we
- * will never actually have a contested lock */
- assert(0);
- }
v = ldq_p(hpte);
r = ldq_p(hpte + (HASH_PTE_SIZE_64/2));
if ((v & HPTE_V_VALID) == 0 ||
((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) {
- stq_p(hpte, v & ~HPTE_V_HVLOCK);
- assert(!(ldq_p(hpte) & HPTE_V_HVLOCK));
return H_NOT_FOUND;
}
@@ -351,8 +317,7 @@ static target_ulong h_protect(CPUPPCState *env, sPAPREnvironment *spapr,
ppc_tlb_invalidate_one(env, rb);
stq_p(hpte + (HASH_PTE_SIZE_64/2), r);
/* Don't need a memory barrier, due to qemu's global lock */
- stq_p(hpte, v & ~HPTE_V_HVLOCK);
- assert(!(ldq_p(hpte) & HPTE_V_HVLOCK));
+ stq_p(hpte, v);
return H_SUCCESS;
}
@@ -545,6 +510,8 @@ static target_ulong h_cede(CPUPPCState *env, sPAPREnvironment *spapr,
hreg_compute_hflags(env);
if (!cpu_has_work(env)) {
env->halted = 1;
+ env->exception_index = EXCP_HLT;
+ env->exit_request = 1;
}
return H_SUCCESS;
}
@@ -714,11 +681,6 @@ void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn)
target_ulong spapr_hypercall(CPUPPCState *env, target_ulong opcode,
target_ulong *args)
{
- if (msr_pr) {
- hcall_dprintf("Hypercall made with MSR[PR]=1\n");
- return H_PRIVILEGE;
- }
-
if ((opcode <= MAX_HCALL_OPCODE)
&& ((opcode & 0x3) == 0)) {
spapr_hcall_fn fn = papr_hypercall_table[opcode / 4];
diff --git a/hw/spapr_iommu.c b/hw/spapr_iommu.c
index 53b731773..38034c07b 100644
--- a/hw/spapr_iommu.c
+++ b/hw/spapr_iommu.c
@@ -42,6 +42,7 @@ struct sPAPRTCETable {
uint32_t liobn;
uint32_t window_size;
sPAPRTCE *table;
+ bool bypass;
int fd;
QLIST_ENTRY(sPAPRTCETable) list;
};
@@ -78,6 +79,12 @@ static int spapr_tce_translate(DMAContext *dma,
DMA_ADDR_FMT "\n", tcet->liobn, addr);
#endif
+ if (tcet->bypass) {
+ *paddr = addr;
+ *len = (target_phys_addr_t)-1;
+ return 0;
+ }
+
/* Check if we are in bound */
if (addr >= tcet->window_size) {
#ifdef DEBUG_TCE
@@ -162,6 +169,23 @@ void spapr_tce_free(DMAContext *dma)
}
}
+void spapr_tce_set_bypass(DMAContext *dma, bool bypass)
+{
+ sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, dma);
+
+ tcet->bypass = bypass;
+}
+
+void spapr_tce_reset(DMAContext *dma)
+{
+ sPAPRTCETable *tcet = DO_UPCAST(sPAPRTCETable, dma, dma);
+ size_t table_size = (tcet->window_size >> SPAPR_TCE_PAGE_SHIFT)
+ * sizeof(sPAPRTCE);
+
+ tcet->bypass = false;
+ memset(tcet->table, 0, table_size);
+}
+
static target_ulong put_tce_emu(sPAPRTCETable *tcet, target_ulong ioba,
target_ulong tce)
{
diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c
index 661c05bc3..b628f89a0 100644
--- a/hw/spapr_pci.c
+++ b/hw/spapr_pci.c
@@ -351,7 +351,7 @@ static void rtas_ibm_change_msi(sPAPREnvironment *spapr,
/* There is no cached config, allocate MSIs */
if (!phb->msi_table[ndev].nvec) {
- irq = spapr_allocate_irq_block(req_num, XICS_MSI);
+ irq = spapr_allocate_irq_block(req_num, true);
if (irq < 0) {
fprintf(stderr, "Cannot allocate MSIs for device#%d", ndev);
rtas_st(rets, 0, -1); /* Hardware error */
@@ -595,6 +595,15 @@ static int spapr_phb_init(SysBusDevice *s)
return 0;
}
+static void spapr_phb_reset(DeviceState *qdev)
+{
+ SysBusDevice *s = sysbus_from_qdev(qdev);
+ sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(s);
+
+ /* Reset the IOMMU state */
+ spapr_tce_reset(sphb->dma);
+}
+
static Property spapr_phb_properties[] = {
DEFINE_PROP_HEX64("buid", sPAPRPHBState, buid, 0),
DEFINE_PROP_STRING("busname", sPAPRPHBState, busname),
@@ -613,6 +622,7 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data)
sdc->init = spapr_phb_init;
dc->props = spapr_phb_properties;
+ dc->reset = spapr_phb_reset;
}
static const TypeInfo spapr_phb_info = {
diff --git a/hw/spapr_rtas.c b/hw/spapr_rtas.c
index ae1859515..b808f8001 100644
--- a/hw/spapr_rtas.c
+++ b/hw/spapr_rtas.c
@@ -184,6 +184,11 @@ static void rtas_start_cpu(sPAPREnvironment *spapr,
return;
}
+ /* This will make sure qemu state is up to date with kvm, and
+ * mark it dirty so our changes get flushed back before the
+ * new cpu enters */
+ kvm_cpu_synchronize_state(env);
+
env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME);
env->nip = start;
env->gpr[3] = r3;
diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c
index 7ca445216..848806d3f 100644
--- a/hw/spapr_vio.c
+++ b/hw/spapr_vio.c
@@ -316,17 +316,10 @@ int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq)
static void spapr_vio_quiesce_one(VIOsPAPRDevice *dev)
{
- VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
- uint32_t liobn = SPAPR_VIO_BASE_LIOBN | dev->reg;
-
if (dev->dma) {
- spapr_tce_free(dev->dma);
+ spapr_tce_reset(dev->dma);
}
- dev->dma = spapr_tce_new_dma_context(liobn, pc->rtce_window_size);
-
- dev->crq.qladdr = 0;
- dev->crq.qsize = 0;
- dev->crq.qnext = 0;
+ free_crq(dev);
}
static void rtas_set_tce_bypass(sPAPREnvironment *spapr, uint32_t token,
@@ -348,16 +341,14 @@ static void rtas_set_tce_bypass(sPAPREnvironment *spapr, uint32_t token,
rtas_st(rets, 0, -3);
return;
}
- if (enable) {
- spapr_tce_free(dev->dma);
- dev->dma = NULL;
- } else {
- VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
- uint32_t liobn = SPAPR_VIO_BASE_LIOBN | dev->reg;
- dev->dma = spapr_tce_new_dma_context(liobn, pc->rtce_window_size);
+ if (!dev->dma) {
+ rtas_st(rets, 0, -3);
+ return;
}
+ spapr_tce_set_bypass(dev->dma, !!enable);
+
rtas_st(rets, 0, 0);
}
@@ -409,9 +400,10 @@ static void spapr_vio_busdev_reset(DeviceState *qdev)
VIOsPAPRDevice *dev = DO_UPCAST(VIOsPAPRDevice, qdev, qdev);
VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
- if (dev->crq.qsize) {
- free_crq(dev);
- }
+ /* Shut down the request queue and TCEs if necessary */
+ spapr_vio_quiesce_one(dev);
+
+ dev->signal_state = 0;
if (pc->reset) {
pc->reset(dev);
@@ -422,7 +414,6 @@ static int spapr_vio_busdev_init(DeviceState *qdev)
{
VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev;
VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
- uint32_t liobn;
char *id;
if (dev->reg != -1) {
@@ -464,8 +455,10 @@ static int spapr_vio_busdev_init(DeviceState *qdev)
return -1;
}
- liobn = SPAPR_VIO_BASE_LIOBN | dev->reg;
- dev->dma = spapr_tce_new_dma_context(liobn, pc->rtce_window_size);
+ if (pc->rtce_window_size) {
+ uint32_t liobn = SPAPR_VIO_BASE_LIOBN | dev->reg;
+ dev->dma = spapr_tce_new_dma_context(liobn, pc->rtce_window_size);
+ }
return pc->init(dev);
}
diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h
index ea6aa43e2..cc85d2610 100644
--- a/hw/spapr_vio.h
+++ b/hw/spapr_vio.h
@@ -60,7 +60,6 @@ typedef struct VIOsPAPRDeviceClass {
struct VIOsPAPRDevice {
DeviceState qdev;
uint32_t reg;
- uint32_t flags;
uint32_t irq;
target_ulong signal_state;
VIOsPAPR_CRQ crq;
@@ -132,7 +131,6 @@ void spapr_vscsi_create(VIOsPAPRBus *bus);
VIOsPAPRDevice *spapr_vty_get_default(VIOsPAPRBus *bus);
-int spapr_tce_set_bypass(uint32_t unit, uint32_t enable);
void spapr_vio_quiesce(void);
#endif /* _HW_SPAPR_VIO_H */
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index 3cf5844e0..e3d4b237f 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -737,7 +737,7 @@ static int vscsi_send_adapter_info(VSCSIState *s, vscsi_req *req)
#endif
memset(&info, 0, sizeof(info));
strcpy(info.srp_version, SRP_VERSION);
- strncpy(info.partition_name, "qemu", sizeof("qemu"));
+ memcpy(info.partition_name, "qemu", sizeof("qemu"));
info.partition_number = cpu_to_be32(0);
info.mad_version = cpu_to_be32(1);
info.os_type = cpu_to_be32(2);
diff --git a/hw/spitz.c b/hw/spitz.c
index 20e783519..24346dcd5 100644
--- a/hw/spitz.c
+++ b/hw/spitz.c
@@ -1083,10 +1083,11 @@ static TypeInfo spitz_keyboard_info = {
static const VMStateDescription vmstate_corgi_ssp_regs = {
.name = "corgi-ssp",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
.fields = (VMStateField []) {
+ VMSTATE_SSI_SLAVE(ssidev, CorgiSSPState),
VMSTATE_UINT32_ARRAY(enable, CorgiSSPState, 3),
VMSTATE_END_OF_LIST(),
}
@@ -1115,6 +1116,7 @@ static const VMStateDescription vmstate_spitz_lcdtg_regs = {
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField []) {
+ VMSTATE_SSI_SLAVE(ssidev, SpitzLCDTG),
VMSTATE_UINT32(bl_intensity, SpitzLCDTG),
VMSTATE_UINT32(bl_power, SpitzLCDTG),
VMSTATE_END_OF_LIST(),
diff --git a/hw/srp.h b/hw/srp.h
index 3009bd56c..5e0cad5c1 100644
--- a/hw/srp.h
+++ b/hw/srp.h
@@ -177,13 +177,13 @@ struct srp_tsk_mgmt {
uint8_t reserved1[6];
uint64_t tag;
uint8_t reserved2[4];
- uint64_t lun QEMU_PACKED;
+ uint64_t lun;
uint8_t reserved3[2];
uint8_t tsk_mgmt_func;
uint8_t reserved4;
uint64_t task_tag;
uint8_t reserved5[8];
-};
+} QEMU_PACKED;
/*
* We need the packed attribute because the SRP spec only aligns the
@@ -198,14 +198,14 @@ struct srp_cmd {
uint8_t data_in_desc_cnt;
uint64_t tag;
uint8_t reserved2[4];
- uint64_t lun QEMU_PACKED;
+ uint64_t lun;
uint8_t reserved3;
uint8_t task_attr;
uint8_t reserved4;
uint8_t add_cdb_len;
uint8_t cdb[16];
uint8_t add_data[0];
-};
+} QEMU_PACKED;
enum {
SRP_RSP_FLAG_RSPVALID = 1 << 0,
diff --git a/hw/ssd0323.c b/hw/ssd0323.c
index b101c5112..9c42d648e 100644
--- a/hw/ssd0323.c
+++ b/hw/ssd0323.c
@@ -279,6 +279,7 @@ static void ssd0323_cd(void *opaque, int n, int level)
static void ssd0323_save(QEMUFile *f, void *opaque)
{
+ SSISlave *ss = SSI_SLAVE(opaque);
ssd0323_state *s = (ssd0323_state *)opaque;
int i;
@@ -296,10 +297,13 @@ static void ssd0323_save(QEMUFile *f, void *opaque)
qemu_put_be32(f, s->remap);
qemu_put_be32(f, s->mode);
qemu_put_buffer(f, s->framebuffer, sizeof(s->framebuffer));
+
+ qemu_put_be32(f, ss->cs);
}
static int ssd0323_load(QEMUFile *f, void *opaque, int version_id)
{
+ SSISlave *ss = SSI_SLAVE(opaque);
ssd0323_state *s = (ssd0323_state *)opaque;
int i;
@@ -321,6 +325,8 @@ static int ssd0323_load(QEMUFile *f, void *opaque, int version_id)
s->mode = qemu_get_be32(f);
qemu_get_buffer(f, s->framebuffer, sizeof(s->framebuffer));
+ ss->cs = qemu_get_be32(f);
+
return 0;
}
@@ -348,6 +354,7 @@ static void ssd0323_class_init(ObjectClass *klass, void *data)
k->init = ssd0323_init;
k->transfer = ssd0323_transfer;
+ k->cs_polarity = SSI_CS_HIGH;
}
static TypeInfo ssd0323_info = {
diff --git a/hw/ssi-sd.c b/hw/ssi-sd.c
index b519bdb29..c5505ee24 100644
--- a/hw/ssi-sd.c
+++ b/hw/ssi-sd.c
@@ -197,6 +197,7 @@ static uint32_t ssi_sd_transfer(SSISlave *dev, uint32_t val)
static void ssi_sd_save(QEMUFile *f, void *opaque)
{
+ SSISlave *ss = SSI_SLAVE(opaque);
ssi_sd_state *s = (ssi_sd_state *)opaque;
int i;
@@ -209,10 +210,13 @@ static void ssi_sd_save(QEMUFile *f, void *opaque)
qemu_put_be32(f, s->arglen);
qemu_put_be32(f, s->response_pos);
qemu_put_be32(f, s->stopping);
+
+ qemu_put_be32(f, ss->cs);
}
static int ssi_sd_load(QEMUFile *f, void *opaque, int version_id)
{
+ SSISlave *ss = SSI_SLAVE(opaque);
ssi_sd_state *s = (ssi_sd_state *)opaque;
int i;
@@ -229,6 +233,8 @@ static int ssi_sd_load(QEMUFile *f, void *opaque, int version_id)
s->response_pos = qemu_get_be32(f);
s->stopping = qemu_get_be32(f);
+ ss->cs = qemu_get_be32(f);
+
return 0;
}
@@ -250,6 +256,7 @@ static void ssi_sd_class_init(ObjectClass *klass, void *data)
k->init = ssi_sd_init;
k->transfer = ssi_sd_transfer;
+ k->cs_polarity = SSI_CS_LOW;
}
static TypeInfo ssi_sd_info = {
diff --git a/hw/ssi.c b/hw/ssi.c
index e5f14a0ce..2b5635715 100644
--- a/hw/ssi.c
+++ b/hw/ssi.c
@@ -2,6 +2,8 @@
* QEMU Synchronous Serial Interface support
*
* Copyright (c) 2009 CodeSourcery.
+ * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com)
+ * Copyright (c) 2012 PetaLogix Pty Ltd.
* Written by Paul Brook
*
* This code is licensed under the GNU GPL v2.
@@ -25,17 +27,40 @@ static const TypeInfo ssi_bus_info = {
.instance_size = sizeof(SSIBus),
};
+static void ssi_cs_default(void *opaque, int n, int level)
+{
+ SSISlave *s = SSI_SLAVE(opaque);
+ bool cs = !!level;
+ assert(n == 0);
+ if (s->cs != cs) {
+ SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(s);
+ if (ssc->set_cs) {
+ ssc->set_cs(s, cs);
+ }
+ }
+ s->cs = cs;
+}
+
+static uint32_t ssi_transfer_raw_default(SSISlave *dev, uint32_t val)
+{
+ SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(dev);
+
+ if ((dev->cs && ssc->cs_polarity == SSI_CS_HIGH) ||
+ (!dev->cs && ssc->cs_polarity == SSI_CS_LOW) ||
+ ssc->cs_polarity == SSI_CS_NONE) {
+ return ssc->transfer(dev, val);
+ }
+ return 0;
+}
+
static int ssi_slave_init(DeviceState *dev)
{
SSISlave *s = SSI_SLAVE(dev);
SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(s);
- SSIBus *bus;
- BusChild *kid;
- bus = FROM_QBUS(SSIBus, qdev_get_parent_bus(dev));
- kid = QTAILQ_FIRST(&bus->qbus.children);
- if (kid->child != dev || QTAILQ_NEXT(kid, sibling) != NULL) {
- hw_error("Too many devices on SSI bus");
+ if (ssc->transfer_raw == ssi_transfer_raw_default &&
+ ssc->cs_polarity != SSI_CS_NONE) {
+ qdev_init_gpio_in(&s->qdev, ssi_cs_default, 1);
}
return ssc->init(s);
@@ -43,9 +68,14 @@ static int ssi_slave_init(DeviceState *dev)
static void ssi_slave_class_init(ObjectClass *klass, void *data)
{
+ SSISlaveClass *ssc = SSI_SLAVE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
+
dc->init = ssi_slave_init;
dc->bus_type = TYPE_SSI_BUS;
+ if (!ssc->transfer_raw) {
+ ssc->transfer_raw = ssi_transfer_raw_default;
+ }
}
static TypeInfo ssi_slave_info = {
@@ -56,10 +86,15 @@ static TypeInfo ssi_slave_info = {
.abstract = true,
};
+DeviceState *ssi_create_slave_no_init(SSIBus *bus, const char *name)
+{
+ return qdev_create(&bus->qbus, name);
+}
+
DeviceState *ssi_create_slave(SSIBus *bus, const char *name)
{
- DeviceState *dev;
- dev = qdev_create(&bus->qbus, name);
+ DeviceState *dev = ssi_create_slave_no_init(bus, name);
+
qdev_init_nofail(dev);
return dev;
}
@@ -74,18 +109,29 @@ SSIBus *ssi_create_bus(DeviceState *parent, const char *name)
uint32_t ssi_transfer(SSIBus *bus, uint32_t val)
{
BusChild *kid;
- SSISlave *slave;
SSISlaveClass *ssc;
+ uint32_t r = 0;
- kid = QTAILQ_FIRST(&bus->qbus.children);
- if (!kid) {
- return 0;
+ QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
+ SSISlave *slave = SSI_SLAVE(kid->child);
+ ssc = SSI_SLAVE_GET_CLASS(slave);
+ r |= ssc->transfer_raw(slave, val);
}
- slave = SSI_SLAVE(kid->child);
- ssc = SSI_SLAVE_GET_CLASS(slave);
- return ssc->transfer(slave, val);
+
+ return r;
}
+const VMStateDescription vmstate_ssi_slave = {
+ .name = "SSISlave",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL(cs, SSISlave),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static void ssi_slave_register_types(void)
{
type_register_static(&ssi_bus_info);
@@ -93,3 +139,36 @@ static void ssi_slave_register_types(void)
}
type_init(ssi_slave_register_types)
+
+typedef struct SSIAutoConnectArg {
+ qemu_irq **cs_linep;
+ SSIBus *bus;
+} SSIAutoConnectArg;
+
+static int ssi_auto_connect_slave(Object *child, void *opaque)
+{
+ SSIAutoConnectArg *arg = opaque;
+ SSISlave *dev = (SSISlave *)object_dynamic_cast(child, TYPE_SSI_SLAVE);
+ qemu_irq cs_line;
+
+ if (!dev) {
+ return 0;
+ }
+
+ cs_line = qdev_get_gpio_in(DEVICE(dev), 0);
+ qdev_set_parent_bus(DEVICE(dev), &arg->bus->qbus);
+ **arg->cs_linep = cs_line;
+ (*arg->cs_linep)++;
+ return 0;
+}
+
+void ssi_auto_connect_slaves(DeviceState *parent, qemu_irq *cs_line,
+ SSIBus *bus)
+{
+ SSIAutoConnectArg arg = {
+ .cs_linep = &cs_line,
+ .bus = bus
+ };
+
+ object_child_foreach(OBJECT(parent), ssi_auto_connect_slave, &arg);
+}
diff --git a/hw/ssi.h b/hw/ssi.h
index 06657d7c1..a05d60beb 100644
--- a/hw/ssi.h
+++ b/hw/ssi.h
@@ -23,28 +23,70 @@ typedef struct SSISlave SSISlave;
#define SSI_SLAVE_GET_CLASS(obj) \
OBJECT_GET_CLASS(SSISlaveClass, (obj), TYPE_SSI_SLAVE)
+typedef enum {
+ SSI_CS_NONE = 0,
+ SSI_CS_LOW,
+ SSI_CS_HIGH,
+} SSICSMode;
+
/* Slave devices. */
typedef struct SSISlaveClass {
DeviceClass parent_class;
int (*init)(SSISlave *dev);
+
+ /* if you have standard or no CS behaviour, just override transfer.
+ * This is called when the device cs is active (true by default).
+ */
uint32_t (*transfer)(SSISlave *dev, uint32_t val);
+ /* called when the CS line changes. Optional, devices only need to implement
+ * this if they have side effects associated with the cs line (beyond
+ * tristating the txrx lines).
+ */
+ int (*set_cs)(SSISlave *dev, bool select);
+ /* define whether or not CS exists and is active low/high */
+ SSICSMode cs_polarity;
+
+ /* if you have non-standard CS behaviour override this to take control
+ * of the CS behaviour at the device level. transfer, set_cs, and
+ * cs_polarity are unused if this is overwritten. Transfer_raw will
+ * always be called for the device for every txrx access to the parent bus
+ */
+ uint32_t (*transfer_raw)(SSISlave *dev, uint32_t val);
} SSISlaveClass;
struct SSISlave {
DeviceState qdev;
+
+ /* Chip select state */
+ bool cs;
};
#define SSI_SLAVE_FROM_QDEV(dev) DO_UPCAST(SSISlave, qdev, dev)
#define FROM_SSI_SLAVE(type, dev) DO_UPCAST(type, ssidev, dev)
+extern const VMStateDescription vmstate_ssi_slave;
+
+#define VMSTATE_SSI_SLAVE(_field, _state) { \
+ .name = (stringify(_field)), \
+ .size = sizeof(SSISlave), \
+ .vmsd = &vmstate_ssi_slave, \
+ .flags = VMS_STRUCT, \
+ .offset = vmstate_offset_value(_state, _field, SSISlave), \
+}
+
DeviceState *ssi_create_slave(SSIBus *bus, const char *name);
+DeviceState *ssi_create_slave_no_init(SSIBus *bus, const char *name);
/* Master interface. */
SSIBus *ssi_create_bus(DeviceState *parent, const char *name);
uint32_t ssi_transfer(SSIBus *bus, uint32_t val);
+/* Automatically connect all children nodes a spi controller as slaves */
+void ssi_auto_connect_slaves(DeviceState *parent, qemu_irq *cs_lines,
+ SSIBus *bus);
+
/* max111x.c */
void max111x_set_input(DeviceState *dev, int line, uint8_t value);
diff --git a/hw/stellaris.c b/hw/stellaris.c
index 562fbbf49..353ca4c04 100644
--- a/hw/stellaris.c
+++ b/hw/stellaris.c
@@ -1154,57 +1154,6 @@ static int stellaris_adc_init(SysBusDevice *dev)
return 0;
}
-/* Some boards have both an OLED controller and SD card connected to
- the same SSI port, with the SD card chip select connected to a
- GPIO pin. Technically the OLED chip select is connected to the SSI
- Fss pin. We do not bother emulating that as both devices should
- never be selected simultaneously, and our OLED controller ignores stray
- 0xff commands that occur when deselecting the SD card. */
-
-typedef struct {
- SSISlave ssidev;
- qemu_irq irq;
- int current_dev;
- SSIBus *bus[2];
-} stellaris_ssi_bus_state;
-
-static void stellaris_ssi_bus_select(void *opaque, int irq, int level)
-{
- stellaris_ssi_bus_state *s = (stellaris_ssi_bus_state *)opaque;
-
- s->current_dev = level;
-}
-
-static uint32_t stellaris_ssi_bus_transfer(SSISlave *dev, uint32_t val)
-{
- stellaris_ssi_bus_state *s = FROM_SSI_SLAVE(stellaris_ssi_bus_state, dev);
-
- return ssi_transfer(s->bus[s->current_dev], val);
-}
-
-static const VMStateDescription vmstate_stellaris_ssi_bus = {
- .name = "stellaris_ssi_bus",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
- VMSTATE_INT32(current_dev, stellaris_ssi_bus_state),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static int stellaris_ssi_bus_init(SSISlave *dev)
-{
- stellaris_ssi_bus_state *s = FROM_SSI_SLAVE(stellaris_ssi_bus_state, dev);
-
- s->bus[0] = ssi_create_bus(&dev->qdev, "ssi0");
- s->bus[1] = ssi_create_bus(&dev->qdev, "ssi1");
- qdev_init_gpio_in(&dev->qdev, stellaris_ssi_bus_select, 1);
-
- vmstate_register(&dev->qdev, -1, &vmstate_stellaris_ssi_bus, s);
- return 0;
-}
-
/* Board init. */
static stellaris_board_info stellaris_boards[] = {
{ "LM3S811EVB",
@@ -1305,19 +1254,25 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model,
if (board->dc2 & (1 << 4)) {
dev = sysbus_create_simple("pl022", 0x40008000, pic[7]);
if (board->peripherals & BP_OLED_SSI) {
- DeviceState *mux;
void *bus;
-
+ DeviceState *sddev;
+ DeviceState *ssddev;
+
+ /* Some boards have both an OLED controller and SD card connected to
+ * the same SSI port, with the SD card chip select connected to a
+ * GPIO pin. Technically the OLED chip select is connected to the
+ * SSI Fss pin. We do not bother emulating that as both devices
+ * should never be selected simultaneously, and our OLED controller
+ * ignores stray 0xff commands that occur when deselecting the SD
+ * card.
+ */
bus = qdev_get_child_bus(dev, "ssi");
- mux = ssi_create_slave(bus, "evb6965-ssi");
- gpio_out[GPIO_D][0] = qdev_get_gpio_in(mux, 0);
-
- bus = qdev_get_child_bus(mux, "ssi0");
- ssi_create_slave(bus, "ssi-sd");
- bus = qdev_get_child_bus(mux, "ssi1");
- dev = ssi_create_slave(bus, "ssd0323");
- gpio_out[GPIO_C][7] = qdev_get_gpio_in(dev, 0);
+ sddev = ssi_create_slave(bus, "ssi-sd");
+ ssddev = ssi_create_slave(bus, "ssd0323");
+ gpio_out[GPIO_D][0] = qemu_irq_split(qdev_get_gpio_in(sddev, 0),
+ qdev_get_gpio_in(ssddev, 0));
+ gpio_out[GPIO_C][7] = qdev_get_gpio_in(ssddev, 1);
/* Make sure the select pin is high. */
qemu_irq_raise(gpio_out[GPIO_D][0]);
@@ -1394,21 +1349,6 @@ static void stellaris_machine_init(void)
machine_init(stellaris_machine_init);
-static void stellaris_ssi_bus_class_init(ObjectClass *klass, void *data)
-{
- SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
-
- k->init = stellaris_ssi_bus_init;
- k->transfer = stellaris_ssi_bus_transfer;
-}
-
-static TypeInfo stellaris_ssi_bus_info = {
- .name = "evb6965-ssi",
- .parent = TYPE_SSI_SLAVE,
- .instance_size = sizeof(stellaris_ssi_bus_state),
- .class_init = stellaris_ssi_bus_class_init,
-};
-
static void stellaris_i2c_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
@@ -1456,7 +1396,6 @@ static void stellaris_register_types(void)
type_register_static(&stellaris_i2c_info);
type_register_static(&stellaris_gptm_info);
type_register_static(&stellaris_adc_info);
- type_register_static(&stellaris_ssi_bus_info);
}
type_init(stellaris_register_types)
diff --git a/hw/sun4m.c b/hw/sun4m.c
index 0f909b5f8..c98cd5ec3 100644
--- a/hw/sun4m.c
+++ b/hw/sun4m.c
@@ -472,6 +472,17 @@ static void slavio_timer_init_all(target_phys_addr_t addr, qemu_irq master_irq,
}
}
+static qemu_irq slavio_system_powerdown;
+
+static void slavio_powerdown_req(Notifier *n, void *opaque)
+{
+ qemu_irq_raise(slavio_system_powerdown);
+}
+
+static Notifier slavio_system_powerdown_notifier = {
+ .notify = slavio_powerdown_req
+};
+
#define MISC_LEDS 0x01600000
#define MISC_CFG 0x01800000
#define MISC_DIAG 0x01a00000
@@ -514,7 +525,8 @@ static void slavio_misc_init(target_phys_addr_t base,
}
sysbus_connect_irq(s, 0, irq);
sysbus_connect_irq(s, 1, fdc_tc);
- qemu_system_powerdown = qdev_get_gpio_in(dev, 0);
+ slavio_system_powerdown = qdev_get_gpio_in(dev, 0);
+ qemu_register_powerdown_notifier(&slavio_system_powerdown_notifier);
}
static void ecc_init(target_phys_addr_t base, qemu_irq irq, uint32_t version)
diff --git a/hw/sun4u.c b/hw/sun4u.c
index 07cd04273..137a7c666 100644
--- a/hw/sun4u.c
+++ b/hw/sun4u.c
@@ -39,7 +39,6 @@
#include "elf.h"
#include "blockdev.h"
#include "exec-memory.h"
-#include "vga-pci.h"
//#define DEBUG_IRQ
//#define DEBUG_EBUS
diff --git a/hw/sysbus.c b/hw/sysbus.c
index 9d8b1eaf7..c1738403d 100644
--- a/hw/sysbus.c
+++ b/hw/sysbus.c
@@ -211,7 +211,7 @@ static char *sysbus_get_fw_dev_path(DeviceState *dev)
snprintf(path + off, sizeof(path) - off, "@i%04x", s->pio[0]);
}
- return strdup(path);
+ return g_strdup(path);
}
void sysbus_add_memory(SysBusDevice *dev, target_phys_addr_t addr,
diff --git a/hw/tcx.c b/hw/tcx.c
index 93994d644..2db2db123 100644
--- a/hw/tcx.c
+++ b/hw/tcx.c
@@ -472,7 +472,6 @@ static void tcx_dac_writel(void *opaque, target_phys_addr_t addr, uint64_t val,
default:
break;
}
- return;
}
static const MemoryRegionOps tcx_dac_ops = {
diff --git a/hw/usb.h b/hw/usb.h
index b8fceec89..48c8926a7 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -135,8 +135,15 @@
#define USB_DT_OTHER_SPEED_CONFIG 0x07
#define USB_DT_DEBUG 0x0A
#define USB_DT_INTERFACE_ASSOC 0x0B
+#define USB_DT_BOS 0x0F
+#define USB_DT_DEVICE_CAPABILITY 0x10
#define USB_DT_CS_INTERFACE 0x24
#define USB_DT_CS_ENDPOINT 0x25
+#define USB_DT_ENDPOINT_COMPANION 0x30
+
+#define USB_DEV_CAP_WIRELESS 0x01
+#define USB_DEV_CAP_USB2_EXT 0x02
+#define USB_DEV_CAP_SUPERSPEED 0x03
#define USB_ENDPOINT_XFER_CONTROL 0
#define USB_ENDPOINT_XFER_ISOC 1
@@ -377,6 +384,8 @@ void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep,
uint16_t raw);
int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep);
void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled);
+USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep,
+ uint64_t id);
void usb_attach(USBPort *port);
void usb_detach(USBPort *port);
diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs
index 4225136d0..6425c1ff7 100644
--- a/hw/usb/Makefile.objs
+++ b/hw/usb/Makefile.objs
@@ -1,11 +1,11 @@
-hw-obj-$(CONFIG_USB_UHCI) += hcd-uhci.o
-hw-obj-$(CONFIG_USB_OHCI) += hcd-ohci.o
-hw-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o
-hw-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o
-hw-obj-y += libhw.o
+common-obj-$(CONFIG_USB_UHCI) += hcd-uhci.o
+common-obj-$(CONFIG_USB_OHCI) += hcd-ohci.o
+common-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o
+common-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o
+common-obj-y += libhw.o
-hw-obj-$(CONFIG_SMARTCARD) += dev-smartcard-reader.o
-hw-obj-$(CONFIG_USB_REDIR) += redirect.o
+common-obj-$(CONFIG_SMARTCARD) += dev-smartcard-reader.o
+common-obj-$(CONFIG_USB_REDIR) += redirect.o
common-obj-y += core.o bus.o desc.o dev-hub.o
common-obj-y += host-$(HOST_USB).o dev-bluetooth.o
diff --git a/hw/usb/core.c b/hw/usb/core.c
index 2da38e7fd..b9f1f7a8b 100644
--- a/hw/usb/core.c
+++ b/hw/usb/core.c
@@ -398,9 +398,11 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
* When pipelining is enabled usb-devices must always return async,
* otherwise packets can complete out of order!
*/
- assert(!p->ep->pipeline);
- p->result = ret;
- usb_packet_set_state(p, USB_PACKET_COMPLETE);
+ assert(!p->ep->pipeline || QTAILQ_EMPTY(&p->ep->queue));
+ if (ret != USB_RET_NAK) {
+ p->result = ret;
+ usb_packet_set_state(p, USB_PACKET_COMPLETE);
+ }
}
} else {
ret = USB_RET_ASYNC;
@@ -724,3 +726,18 @@ void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled)
struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
uep->pipeline = enabled;
}
+
+USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep,
+ uint64_t id)
+{
+ struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
+ USBPacket *p;
+
+ while ((p = QTAILQ_FIRST(&uep->queue)) != NULL) {
+ if (p->id == id) {
+ return p;
+ }
+ }
+
+ return NULL;
+}
diff --git a/hw/usb/desc.c b/hw/usb/desc.c
index 0a9d3c9f6..1f12eaecb 100644
--- a/hw/usb/desc.c
+++ b/hw/usb/desc.c
@@ -76,7 +76,8 @@ int usb_desc_device_qualifier(const USBDescDevice *dev,
return bLength;
}
-int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
+int usb_desc_config(const USBDescConfig *conf, int flags,
+ uint8_t *dest, size_t len)
{
uint8_t bLength = 0x09;
uint16_t wTotalLength = 0;
@@ -99,7 +100,7 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
/* handle grouped interfaces if any */
for (i = 0; i < conf->nif_groups; i++) {
- rc = usb_desc_iface_group(&(conf->if_groups[i]),
+ rc = usb_desc_iface_group(&(conf->if_groups[i]), flags,
dest + wTotalLength,
len - wTotalLength);
if (rc < 0) {
@@ -110,7 +111,8 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
/* handle normal (ungrouped / no IAD) interfaces if any */
for (i = 0; i < conf->nif; i++) {
- rc = usb_desc_iface(conf->ifs + i, dest + wTotalLength, len - wTotalLength);
+ rc = usb_desc_iface(conf->ifs + i, flags,
+ dest + wTotalLength, len - wTotalLength);
if (rc < 0) {
return rc;
}
@@ -122,8 +124,8 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
return wTotalLength;
}
-int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
- size_t len)
+int usb_desc_iface_group(const USBDescIfaceAssoc *iad, int flags,
+ uint8_t *dest, size_t len)
{
int pos = 0;
int i = 0;
@@ -147,7 +149,7 @@ int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
/* handle associated interfaces in this group */
for (i = 0; i < iad->nif; i++) {
- int rc = usb_desc_iface(&(iad->ifs[i]), dest + pos, len - pos);
+ int rc = usb_desc_iface(&(iad->ifs[i]), flags, dest + pos, len - pos);
if (rc < 0) {
return rc;
}
@@ -157,7 +159,8 @@ int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
return pos;
}
-int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
+int usb_desc_iface(const USBDescIface *iface, int flags,
+ uint8_t *dest, size_t len)
{
uint8_t bLength = 0x09;
int i, rc, pos = 0;
@@ -188,7 +191,7 @@ int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
}
for (i = 0; i < iface->bNumEndpoints; i++) {
- rc = usb_desc_endpoint(iface->eps + i, dest + pos, len - pos);
+ rc = usb_desc_endpoint(iface->eps + i, flags, dest + pos, len - pos);
if (rc < 0) {
return rc;
}
@@ -198,13 +201,15 @@ int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
return pos;
}
-int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len)
+int usb_desc_endpoint(const USBDescEndpoint *ep, int flags,
+ uint8_t *dest, size_t len)
{
uint8_t bLength = ep->is_audio ? 0x09 : 0x07;
uint8_t extralen = ep->extra ? ep->extra[0] : 0;
+ uint8_t superlen = (flags & USB_DESC_FLAG_SUPER) ? 0x06 : 0;
USBDescriptor *d = (void *)dest;
- if (len < bLength + extralen) {
+ if (len < bLength + extralen + superlen) {
return -1;
}
@@ -224,7 +229,21 @@ int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len)
memcpy(dest + bLength, ep->extra, extralen);
}
- return bLength + extralen;
+ if (superlen) {
+ USBDescriptor *d = (void *)(dest + bLength + extralen);
+
+ d->bLength = 0x06;
+ d->bDescriptorType = USB_DT_ENDPOINT_COMPANION;
+
+ d->u.super_endpoint.bMaxBurst = ep->bMaxBurst;
+ d->u.super_endpoint.bmAttributes = ep->bmAttributes_super;
+ d->u.super_endpoint.wBytesPerInterval_lo =
+ usb_lo(ep->wBytesPerInterval);
+ d->u.super_endpoint.wBytesPerInterval_hi =
+ usb_hi(ep->wBytesPerInterval);
+ }
+
+ return bLength + extralen + superlen;
}
int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len)
@@ -239,6 +258,111 @@ int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len)
return bLength;
}
+static int usb_desc_cap_usb2_ext(const USBDesc *desc, uint8_t *dest, size_t len)
+{
+ uint8_t bLength = 0x07;
+ USBDescriptor *d = (void *)dest;
+
+ if (len < bLength) {
+ return -1;
+ }
+
+ d->bLength = bLength;
+ d->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
+ d->u.cap.bDevCapabilityType = USB_DEV_CAP_USB2_EXT;
+
+ d->u.cap.u.usb2_ext.bmAttributes_1 = (1 << 1); /* LPM */
+ d->u.cap.u.usb2_ext.bmAttributes_2 = 0;
+ d->u.cap.u.usb2_ext.bmAttributes_3 = 0;
+ d->u.cap.u.usb2_ext.bmAttributes_4 = 0;
+
+ return bLength;
+}
+
+static int usb_desc_cap_super(const USBDesc *desc, uint8_t *dest, size_t len)
+{
+ uint8_t bLength = 0x0a;
+ USBDescriptor *d = (void *)dest;
+
+ if (len < bLength) {
+ return -1;
+ }
+
+ d->bLength = bLength;
+ d->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
+ d->u.cap.bDevCapabilityType = USB_DEV_CAP_SUPERSPEED;
+
+ d->u.cap.u.super.bmAttributes = 0;
+ d->u.cap.u.super.wSpeedsSupported_lo = 0;
+ d->u.cap.u.super.wSpeedsSupported_hi = 0;
+ d->u.cap.u.super.bFunctionalitySupport = 0;
+ d->u.cap.u.super.bU1DevExitLat = 0x0a;
+ d->u.cap.u.super.wU2DevExitLat_lo = 0x20;
+ d->u.cap.u.super.wU2DevExitLat_hi = 0;
+
+ if (desc->full) {
+ d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 1);
+ d->u.cap.u.super.bFunctionalitySupport = 1;
+ }
+ if (desc->high) {
+ d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 2);
+ if (!d->u.cap.u.super.bFunctionalitySupport) {
+ d->u.cap.u.super.bFunctionalitySupport = 2;
+ }
+ }
+ if (desc->super) {
+ d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 3);
+ if (!d->u.cap.u.super.bFunctionalitySupport) {
+ d->u.cap.u.super.bFunctionalitySupport = 3;
+ }
+ }
+
+ return bLength;
+}
+
+static int usb_desc_bos(const USBDesc *desc, uint8_t *dest, size_t len)
+{
+ uint8_t bLength = 0x05;
+ uint16_t wTotalLength = 0;
+ uint8_t bNumDeviceCaps = 0;
+ USBDescriptor *d = (void *)dest;
+ int rc;
+
+ if (len < bLength) {
+ return -1;
+ }
+
+ d->bLength = bLength;
+ d->bDescriptorType = USB_DT_BOS;
+
+ wTotalLength += bLength;
+
+ if (desc->high != NULL) {
+ rc = usb_desc_cap_usb2_ext(desc, dest + wTotalLength,
+ len - wTotalLength);
+ if (rc < 0) {
+ return rc;
+ }
+ wTotalLength += rc;
+ bNumDeviceCaps++;
+ }
+
+ if (desc->super != NULL) {
+ rc = usb_desc_cap_super(desc, dest + wTotalLength,
+ len - wTotalLength);
+ if (rc < 0) {
+ return rc;
+ }
+ wTotalLength += rc;
+ bNumDeviceCaps++;
+ }
+
+ d->u.bos.wTotalLength_lo = usb_lo(wTotalLength);
+ d->u.bos.wTotalLength_hi = usb_hi(wTotalLength);
+ d->u.bos.bNumDeviceCaps = bNumDeviceCaps;
+ return wTotalLength;
+}
+
/* ------------------------------------------------------------------ */
static void usb_desc_ep_init(USBDevice *dev)
@@ -359,6 +483,9 @@ static void usb_desc_setdefaults(USBDevice *dev)
case USB_SPEED_HIGH:
dev->device = desc->high;
break;
+ case USB_SPEED_SUPER:
+ dev->device = desc->super;
+ break;
}
usb_desc_set_config(dev, 0);
}
@@ -376,6 +503,9 @@ void usb_desc_init(USBDevice *dev)
if (desc->high) {
dev->speedmask |= USB_SPEED_MASK_HIGH;
}
+ if (desc->super) {
+ dev->speedmask |= USB_SPEED_MASK_SUPER;
+ }
usb_desc_setdefaults(dev);
}
@@ -384,7 +514,9 @@ void usb_desc_attach(USBDevice *dev)
const USBDesc *desc = usb_device_get_usb_desc(dev);
assert(desc != NULL);
- if (desc->high && (dev->port->speedmask & USB_SPEED_MASK_HIGH)) {
+ if (desc->super && (dev->port->speedmask & USB_SPEED_MASK_SUPER)) {
+ dev->speed = USB_SPEED_SUPER;
+ } else if (desc->high && (dev->port->speedmask & USB_SPEED_MASK_HIGH)) {
dev->speed = USB_SPEED_HIGH;
} else if (desc->full && (dev->port->speedmask & USB_SPEED_MASK_FULL)) {
dev->speed = USB_SPEED_FULL;
@@ -501,7 +633,7 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len
uint8_t buf[256];
uint8_t type = value >> 8;
uint8_t index = value & 0xff;
- int ret = -1;
+ int flags, ret = -1;
if (dev->speed == USB_SPEED_HIGH) {
other_dev = usb_device_get_usb_desc(dev)->full;
@@ -509,6 +641,11 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len
other_dev = usb_device_get_usb_desc(dev)->high;
}
+ flags = 0;
+ if (dev->device->bcdUSB >= 0x0300) {
+ flags |= USB_DESC_FLAG_SUPER;
+ }
+
switch(type) {
case USB_DT_DEVICE:
ret = usb_desc_device(&desc->id, dev->device, buf, sizeof(buf));
@@ -516,7 +653,8 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len
break;
case USB_DT_CONFIG:
if (index < dev->device->bNumConfigurations) {
- ret = usb_desc_config(dev->device->confs + index, buf, sizeof(buf));
+ ret = usb_desc_config(dev->device->confs + index, flags,
+ buf, sizeof(buf));
}
trace_usb_desc_config(dev->addr, index, len, ret);
break;
@@ -524,7 +662,6 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len
ret = usb_desc_string(dev, index, buf, sizeof(buf));
trace_usb_desc_string(dev->addr, index, len, ret);
break;
-
case USB_DT_DEVICE_QUALIFIER:
if (other_dev != NULL) {
ret = usb_desc_device_qualifier(other_dev, buf, sizeof(buf));
@@ -533,11 +670,16 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len
break;
case USB_DT_OTHER_SPEED_CONFIG:
if (other_dev != NULL && index < other_dev->bNumConfigurations) {
- ret = usb_desc_config(other_dev->confs + index, buf, sizeof(buf));
+ ret = usb_desc_config(other_dev->confs + index, flags,
+ buf, sizeof(buf));
buf[0x01] = USB_DT_OTHER_SPEED_CONFIG;
}
trace_usb_desc_other_speed_config(dev->addr, index, len, ret);
break;
+ case USB_DT_BOS:
+ ret = usb_desc_bos(desc, buf, sizeof(buf));
+ trace_usb_desc_bos(dev->addr, len, ret);
+ break;
case USB_DT_DEBUG:
/* ignore silently */
diff --git a/hw/usb/desc.h b/hw/usb/desc.h
index 7cf544294..68bb57067 100644
--- a/hw/usb/desc.h
+++ b/hw/usb/desc.h
@@ -63,6 +63,37 @@ typedef struct USBDescriptor {
uint8_t bRefresh; /* only audio ep */
uint8_t bSynchAddress; /* only audio ep */
} endpoint;
+ struct {
+ uint8_t bMaxBurst;
+ uint8_t bmAttributes;
+ uint8_t wBytesPerInterval_lo;
+ uint8_t wBytesPerInterval_hi;
+ } super_endpoint;
+ struct {
+ uint8_t wTotalLength_lo;
+ uint8_t wTotalLength_hi;
+ uint8_t bNumDeviceCaps;
+ } bos;
+ struct {
+ uint8_t bDevCapabilityType;
+ union {
+ struct {
+ uint8_t bmAttributes_1;
+ uint8_t bmAttributes_2;
+ uint8_t bmAttributes_3;
+ uint8_t bmAttributes_4;
+ } usb2_ext;
+ struct {
+ uint8_t bmAttributes;
+ uint8_t wSpeedsSupported_lo;
+ uint8_t wSpeedsSupported_hi;
+ uint8_t bFunctionalitySupport;
+ uint8_t bU1DevExitLat;
+ uint8_t wU2DevExitLat_lo;
+ uint8_t wU2DevExitLat_hi;
+ } super;
+ } u;
+ } cap;
} u;
} QEMU_PACKED USBDescriptor;
@@ -139,6 +170,11 @@ struct USBDescEndpoint {
uint8_t is_audio; /* has bRefresh + bSynchAddress */
uint8_t *extra;
+
+ /* superspeed endpoint companion */
+ uint8_t bMaxBurst;
+ uint8_t bmAttributes_super;
+ uint16_t wBytesPerInterval;
};
struct USBDescOther {
@@ -152,19 +188,25 @@ struct USBDesc {
USBDescID id;
const USBDescDevice *full;
const USBDescDevice *high;
+ const USBDescDevice *super;
const char* const *str;
};
+#define USB_DESC_FLAG_SUPER (1 << 1)
+
/* generate usb packages from structs */
int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
uint8_t *dest, size_t len);
int usb_desc_device_qualifier(const USBDescDevice *dev,
uint8_t *dest, size_t len);
-int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len);
-int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
- size_t len);
-int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len);
-int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len);
+int usb_desc_config(const USBDescConfig *conf, int flags,
+ uint8_t *dest, size_t len);
+int usb_desc_iface_group(const USBDescIfaceAssoc *iad, int flags,
+ uint8_t *dest, size_t len);
+int usb_desc_iface(const USBDescIface *iface, int flags,
+ uint8_t *dest, size_t len);
+int usb_desc_endpoint(const USBDescEndpoint *ep, int flags,
+ uint8_t *dest, size_t len);
int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len);
/* control message emulation helpers */
diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c
index 79b75fb62..2594c7810 100644
--- a/hw/usb/dev-audio.c
+++ b/hw/usb/dev-audio.c
@@ -217,7 +217,7 @@ static const USBDescIface desc_iface[] = {
};
static const USBDescDevice desc_device = {
- .bcdUSB = 0x0200,
+ .bcdUSB = 0x0100,
.bMaxPacketSize0 = 64,
.bNumConfigurations = 1,
.confs = (USBDescConfig[]) {
diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c
index c84892c98..e4a43599b 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -1001,6 +1001,13 @@ static int rndis_keepalive_response(USBNetState *s,
return 0;
}
+/* Prepare to receive the next packet */
+static void usb_net_reset_in_buf(USBNetState *s)
+{
+ s->in_ptr = s->in_len = 0;
+ qemu_flush_queued_packets(&s->nic->nc);
+}
+
static int rndis_parse(USBNetState *s, uint8_t *data, int length)
{
uint32_t msg_type;
@@ -1025,7 +1032,8 @@ static int rndis_parse(USBNetState *s, uint8_t *data, int length)
case RNDIS_RESET_MSG:
rndis_clear_responsequeue(s);
- s->out_ptr = s->in_ptr = s->in_len = 0;
+ s->out_ptr = 0;
+ usb_net_reset_in_buf(s);
return rndis_reset_response(s, (rndis_reset_msg_type *) data);
case RNDIS_KEEPALIVE_MSG:
@@ -1135,7 +1143,7 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p)
int ret = USB_RET_NAK;
if (s->in_ptr > s->in_len) {
- s->in_ptr = s->in_len = 0;
+ usb_net_reset_in_buf(s);
ret = USB_RET_NAK;
return ret;
}
@@ -1152,7 +1160,7 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p)
if (s->in_ptr >= s->in_len &&
(is_rndis(s) || (s->in_len & (64 - 1)) || !ret)) {
/* no short packet necessary */
- s->in_ptr = s->in_len = 0;
+ usb_net_reset_in_buf(s);
}
#ifdef TRAFFIC_DEBUG
@@ -1250,20 +1258,32 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p)
static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t size)
{
USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
- struct rndis_packet_msg_type *msg;
+ uint8_t *in_buf = s->in_buf;
+ size_t total_size = size;
if (is_rndis(s)) {
- msg = (struct rndis_packet_msg_type *) s->in_buf;
if (s->rndis_state != RNDIS_DATA_INITIALIZED) {
return -1;
}
- if (size + sizeof(struct rndis_packet_msg_type) > sizeof(s->in_buf))
- return -1;
+ total_size += sizeof(struct rndis_packet_msg_type);
+ }
+ if (total_size > sizeof(s->in_buf)) {
+ return -1;
+ }
+ /* Only accept packet if input buffer is empty */
+ if (s->in_len > 0) {
+ return 0;
+ }
+
+ if (is_rndis(s)) {
+ struct rndis_packet_msg_type *msg;
+
+ msg = (struct rndis_packet_msg_type *)in_buf;
memset(msg, 0, sizeof(struct rndis_packet_msg_type));
msg->MessageType = cpu_to_le32(RNDIS_PACKET_MSG);
- msg->MessageLength = cpu_to_le32(size + sizeof(struct rndis_packet_msg_type));
- msg->DataOffset = cpu_to_le32(sizeof(struct rndis_packet_msg_type) - 8);
+ msg->MessageLength = cpu_to_le32(size + sizeof(*msg));
+ msg->DataOffset = cpu_to_le32(sizeof(*msg) - 8);
msg->DataLength = cpu_to_le32(size);
/* msg->OOBDataOffset;
* msg->OOBDataLength;
@@ -1273,14 +1293,11 @@ static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t siz
* msg->VcHandle;
* msg->Reserved;
*/
- memcpy(msg + 1, buf, size);
- s->in_len = size + sizeof(struct rndis_packet_msg_type);
- } else {
- if (size > sizeof(s->in_buf))
- return -1;
- memcpy(s->in_buf, buf, size);
- s->in_len = size;
+ in_buf += sizeof(*msg);
}
+
+ memcpy(in_buf, buf, size);
+ s->in_len = total_size;
s->in_ptr = 0;
return size;
}
diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c
index 8aa655286..69b6e48d5 100644
--- a/hw/usb/dev-serial.c
+++ b/hw/usb/dev-serial.c
@@ -113,7 +113,7 @@ enum {
static const USBDescStrings desc_strings = {
[STR_MANUFACTURER] = "QEMU",
[STR_PRODUCT_SERIAL] = "QEMU USB SERIAL",
- [STR_PRODUCT_BRAILLE] = "QEMU USB BRAILLE",
+ [STR_PRODUCT_BRAILLE] = "QEMU USB BAUM BRAILLE",
[STR_SERIALNUMBER] = "1",
};
diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
index ff48d9104..e732191a2 100644
--- a/hw/usb/dev-storage.c
+++ b/hw/usb/dev-storage.c
@@ -78,6 +78,7 @@ enum {
STR_SERIALNUMBER,
STR_CONFIG_FULL,
STR_CONFIG_HIGH,
+ STR_CONFIG_SUPER,
};
static const USBDescStrings desc_strings = {
@@ -86,6 +87,7 @@ static const USBDescStrings desc_strings = {
[STR_SERIALNUMBER] = "1",
[STR_CONFIG_FULL] = "Full speed config (usb 1.1)",
[STR_CONFIG_HIGH] = "High speed config (usb 2.0)",
+ [STR_CONFIG_SUPER] = "Super speed config (usb 3.0)",
};
static const USBDescIface desc_iface_full = {
@@ -158,6 +160,43 @@ static const USBDescDevice desc_device_high = {
},
};
+static const USBDescIface desc_iface_super = {
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_MASS_STORAGE,
+ .bInterfaceSubClass = 0x06, /* SCSI */
+ .bInterfaceProtocol = 0x50, /* Bulk */
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_IN | 0x01,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 1024,
+ .bMaxBurst = 15,
+ },{
+ .bEndpointAddress = USB_DIR_OUT | 0x02,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 1024,
+ .bMaxBurst = 15,
+ },
+ }
+};
+
+static const USBDescDevice desc_device_super = {
+ .bcdUSB = 0x0300,
+ .bMaxPacketSize0 = 9,
+ .bNumConfigurations = 1,
+ .confs = (USBDescConfig[]) {
+ {
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = STR_CONFIG_SUPER,
+ .bmAttributes = 0xc0,
+ .nif = 1,
+ .ifs = &desc_iface_super,
+ },
+ },
+};
+
static const USBDesc desc = {
.id = {
.idVendor = 0x46f4, /* CRC16() of "QEMU" */
@@ -167,9 +206,10 @@ static const USBDesc desc = {
.iProduct = STR_PRODUCT,
.iSerialNumber = STR_SERIALNUMBER,
},
- .full = &desc_device_full,
- .high = &desc_device_high,
- .str = desc_strings,
+ .full = &desc_device_full,
+ .high = &desc_device_high,
+ .super = &desc_device_super,
+ .str = desc_strings,
};
static void usb_msd_copy_data(MSDState *s, USBPacket *p)
diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c
index 5a0057a36..4389380e9 100644
--- a/hw/usb/dev-uas.c
+++ b/hw/usb/dev-uas.c
@@ -576,7 +576,6 @@ bad_target:
*/
usb_uas_queue_response(uas, req->tag, UAS_RC_INVALID_INFO_UNIT, 0);
g_free(req);
- return;
}
static void usb_uas_task(UASDevice *uas, uas_ui *ui)
@@ -640,7 +639,6 @@ bad_target:
incorrect_lun:
usb_uas_queue_response(uas, tag, UAS_RC_INCORRECT_LUN, 0);
- return;
}
static int usb_uas_handle_data(USBDevice *dev, USBPacket *p)
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 017342b56..8bdb806b9 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -2,6 +2,11 @@
* QEMU USB EHCI Emulation
*
* Copyright(c) 2008 Emutex Ltd. (address@hidden)
+ * Copyright(c) 2011-2012 Red Hat, Inc.
+ *
+ * Red Hat Authors:
+ * Gerd Hoffmann <kraxel@redhat.com>
+ * Hans de Goede <hdegoede@redhat.com>
*
* EHCI project was started by Mark Burkley, with contributions by
* Niels de Vos. David S. Ahern continued working on it. Kevin Wolf,
@@ -29,6 +34,7 @@
#include "monitor.h"
#include "trace.h"
#include "dma.h"
+#include "sysemu.h"
#define EHCI_DEBUG 0
@@ -134,6 +140,7 @@
#define NB_PORTS 6 // Number of downstream ports
#define BUFF_SIZE 5*4096 // Max bytes to transfer per transaction
#define MAX_QH 100 // Max allowable queue heads in a chain
+#define MIN_FR_PER_TICK 3 // Min frames to process when catching up
/* Internal periodic / asynchronous schedule state machine states
*/
@@ -340,6 +347,7 @@ typedef struct EHCIState EHCIState;
enum async_state {
EHCI_ASYNC_NONE = 0,
+ EHCI_ASYNC_INITIALIZED,
EHCI_ASYNC_INFLIGHT,
EHCI_ASYNC_FINISHED,
};
@@ -365,7 +373,7 @@ struct EHCIQueue {
uint32_t seen;
uint64_t ts;
int async;
- int revalidate;
+ int transact_ctr;
/* cached data from guest - needs to be flushed
* when guest removes an entry (doorbell, handshake sequence)
@@ -384,6 +392,9 @@ struct EHCIState {
USBBus bus;
qemu_irq irq;
MemoryRegion mem;
+ MemoryRegion mem_caps;
+ MemoryRegion mem_opreg;
+ MemoryRegion mem_ports;
int companion_count;
/* properties */
@@ -393,10 +404,10 @@ struct EHCIState {
* EHCI spec version 1.0 Section 2.3
* Host Controller Operational Registers
*/
+ uint8_t caps[OPREGBASE];
union {
- uint8_t mmio[MMIO_SIZE];
+ uint32_t opreg[(PORTSC_BEGIN-OPREGBASE)/sizeof(uint32_t)];
struct {
- uint8_t cap[OPREGBASE];
uint32_t usbcmd;
uint32_t usbsts;
uint32_t usbintr;
@@ -406,9 +417,9 @@ struct EHCIState {
uint32_t asynclistaddr;
uint32_t notused[9];
uint32_t configflag;
- uint32_t portsc[NB_PORTS];
};
};
+ uint32_t portsc[NB_PORTS];
/*
* Internal states, shadow registers, etc
@@ -466,25 +477,18 @@ static const char *ehci_state_names[] = {
};
static const char *ehci_mmio_names[] = {
- [CAPLENGTH] = "CAPLENGTH",
- [HCIVERSION] = "HCIVERSION",
- [HCSPARAMS] = "HCSPARAMS",
- [HCCPARAMS] = "HCCPARAMS",
[USBCMD] = "USBCMD",
[USBSTS] = "USBSTS",
[USBINTR] = "USBINTR",
[FRINDEX] = "FRINDEX",
[PERIODICLISTBASE] = "P-LIST BASE",
[ASYNCLISTADDR] = "A-LIST ADDR",
- [PORTSC_BEGIN] = "PORTSC #0",
- [PORTSC_BEGIN + 4] = "PORTSC #1",
- [PORTSC_BEGIN + 8] = "PORTSC #2",
- [PORTSC_BEGIN + 12] = "PORTSC #3",
- [PORTSC_BEGIN + 16] = "PORTSC #4",
- [PORTSC_BEGIN + 20] = "PORTSC #5",
[CONFIGFLAG] = "CONFIGFLAG",
};
+static int ehci_state_executing(EHCIQueue *q);
+static int ehci_state_writeback(EHCIQueue *q);
+
static const char *nr2str(const char **n, size_t len, uint32_t nr)
{
if (nr < len && n[nr] != NULL) {
@@ -501,7 +505,8 @@ static const char *state2str(uint32_t state)
static const char *addr2str(target_phys_addr_t addr)
{
- return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), addr);
+ return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names),
+ addr + OPREGBASE);
}
static void ehci_trace_usbsts(uint32_t mask, int state)
@@ -709,6 +714,12 @@ static void ehci_trace_sitd(EHCIState *s, target_phys_addr_t addr,
(bool)(sitd->results & SITD_RESULTS_ACTIVE));
}
+static void ehci_trace_guest_bug(EHCIState *s, const char *message)
+{
+ trace_usb_ehci_guest_bug(message);
+ fprintf(stderr, "ehci warning: %s\n", message);
+}
+
static inline bool ehci_enabled(EHCIState *s)
{
return s->usbcmd & USBCMD_RUNSTOP;
@@ -740,9 +751,25 @@ static EHCIPacket *ehci_alloc_packet(EHCIQueue *q)
static void ehci_free_packet(EHCIPacket *p)
{
+ if (p->async == EHCI_ASYNC_FINISHED) {
+ int state = ehci_get_state(p->queue->ehci, p->queue->async);
+ /* This is a normal, but rare condition (cancel racing completion) */
+ fprintf(stderr, "EHCI: Warning packet completed but not processed\n");
+ ehci_state_executing(p->queue);
+ ehci_state_writeback(p->queue);
+ ehci_set_state(p->queue->ehci, p->queue->async, state);
+ /* state_writeback recurses into us with async == EHCI_ASYNC_NONE!! */
+ return;
+ }
trace_usb_ehci_packet_action(p->queue, p, "free");
+ if (p->async == EHCI_ASYNC_INITIALIZED) {
+ usb_packet_unmap(&p->packet, &p->sgl);
+ qemu_sglist_destroy(&p->sgl);
+ }
if (p->async == EHCI_ASYNC_INFLIGHT) {
usb_cancel_packet(&p->packet);
+ usb_packet_unmap(&p->packet, &p->sgl);
+ qemu_sglist_destroy(&p->sgl);
}
QTAILQ_REMOVE(&p->queue->packets, p, next);
usb_packet_cleanup(&p->packet);
@@ -766,27 +793,45 @@ static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, uint32_t addr, int async)
return q;
}
-static void ehci_cancel_queue(EHCIQueue *q)
+static int ehci_cancel_queue(EHCIQueue *q)
{
EHCIPacket *p;
+ int packets = 0;
p = QTAILQ_FIRST(&q->packets);
if (p == NULL) {
- return;
+ return 0;
}
trace_usb_ehci_queue_action(q, "cancel");
do {
ehci_free_packet(p);
+ packets++;
} while ((p = QTAILQ_FIRST(&q->packets)) != NULL);
+ return packets;
}
-static void ehci_free_queue(EHCIQueue *q)
+static int ehci_reset_queue(EHCIQueue *q)
+{
+ int packets;
+
+ trace_usb_ehci_queue_action(q, "reset");
+ packets = ehci_cancel_queue(q);
+ q->dev = NULL;
+ q->qtdaddr = 0;
+ return packets;
+}
+
+static void ehci_free_queue(EHCIQueue *q, const char *warn)
{
EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues;
+ int cancelled;
trace_usb_ehci_queue_action(q, "free");
- ehci_cancel_queue(q);
+ cancelled = ehci_cancel_queue(q);
+ if (warn && cancelled > 0) {
+ ehci_trace_guest_bug(q->ehci, warn);
+ }
QTAILQ_REMOVE(head, q, next);
g_free(q);
}
@@ -805,20 +850,10 @@ static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr,
return NULL;
}
-static void ehci_queues_tag_unused_async(EHCIState *ehci)
-{
- EHCIQueue *q;
-
- QTAILQ_FOREACH(q, &ehci->aqueues, next) {
- if (!q->seen) {
- q->revalidate = 1;
- }
- }
-}
-
static void ehci_queues_rip_unused(EHCIState *ehci, int async)
{
EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
+ const char *warn = async ? "guest unlinked busy QH" : NULL;
uint64_t maxage = FRAME_TIMER_NS * ehci->maxframes * 4;
EHCIQueue *q, *tmp;
@@ -831,7 +866,19 @@ static void ehci_queues_rip_unused(EHCIState *ehci, int async)
if (ehci->last_run_ns < q->ts + maxage) {
continue;
}
- ehci_free_queue(q);
+ ehci_free_queue(q, warn);
+ }
+}
+
+static void ehci_queues_rip_unseen(EHCIState *ehci, int async)
+{
+ EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
+ EHCIQueue *q, *tmp;
+
+ QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
+ if (!q->seen) {
+ ehci_free_queue(q, NULL);
+ }
}
}
@@ -844,17 +891,18 @@ static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async)
if (q->dev != dev) {
continue;
}
- ehci_free_queue(q);
+ ehci_free_queue(q, NULL);
}
}
static void ehci_queues_rip_all(EHCIState *ehci, int async)
{
EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
+ const char *warn = async ? "guest stopped busy async schedule" : NULL;
EHCIQueue *q, *tmp;
QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
- ehci_free_queue(q);
+ ehci_free_queue(q, warn);
}
}
@@ -979,7 +1027,7 @@ static int ehci_register_companion(USBBus *bus, USBPort *ports[],
}
s->companion_count++;
- s->mmio[0x05] = (s->companion_count << 4) | portcount;
+ s->caps[0x05] = (s->companion_count << 4) | portcount;
return 0;
}
@@ -1024,7 +1072,8 @@ static void ehci_reset(void *opaque)
}
}
- memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE);
+ memset(&s->opreg, 0x00, sizeof(s->opreg));
+ memset(&s->portsc, 0x00, sizeof(s->portsc));
s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH;
s->usbsts = USBSTS_HALT;
@@ -1051,50 +1100,35 @@ static void ehci_reset(void *opaque)
qemu_bh_cancel(s->async_bh);
}
-static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr)
+static uint64_t ehci_caps_read(void *ptr, target_phys_addr_t addr,
+ unsigned size)
{
EHCIState *s = ptr;
- uint32_t val;
-
- val = s->mmio[addr];
-
- return val;
+ return s->caps[addr];
}
-static uint32_t ehci_mem_readw(void *ptr, target_phys_addr_t addr)
+static uint64_t ehci_opreg_read(void *ptr, target_phys_addr_t addr,
+ unsigned size)
{
EHCIState *s = ptr;
uint32_t val;
- val = s->mmio[addr] | (s->mmio[addr+1] << 8);
-
+ val = s->opreg[addr >> 2];
+ trace_usb_ehci_opreg_read(addr + OPREGBASE, addr2str(addr), val);
return val;
}
-static uint32_t ehci_mem_readl(void *ptr, target_phys_addr_t addr)
+static uint64_t ehci_port_read(void *ptr, target_phys_addr_t addr,
+ unsigned size)
{
EHCIState *s = ptr;
uint32_t val;
- val = s->mmio[addr] | (s->mmio[addr+1] << 8) |
- (s->mmio[addr+2] << 16) | (s->mmio[addr+3] << 24);
-
- trace_usb_ehci_mmio_readl(addr, addr2str(addr), val);
+ val = s->portsc[addr >> 2];
+ trace_usb_ehci_portsc_read(addr + PORTSC_BEGIN, addr >> 2, val);
return val;
}
-static void ehci_mem_writeb(void *ptr, target_phys_addr_t addr, uint32_t val)
-{
- fprintf(stderr, "EHCI doesn't handle byte writes to MMIO\n");
- exit(1);
-}
-
-static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t val)
-{
- fprintf(stderr, "EHCI doesn't handle 16-bit writes to MMIO\n");
- exit(1);
-}
-
static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner)
{
USBDevice *dev = s->ports[port].dev;
@@ -1123,11 +1157,17 @@ static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner)
}
}
-static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
+static void ehci_port_write(void *ptr, target_phys_addr_t addr,
+ uint64_t val, unsigned size)
{
+ EHCIState *s = ptr;
+ int port = addr >> 2;
uint32_t *portsc = &s->portsc[port];
+ uint32_t old = *portsc;
USBDevice *dev = s->ports[port].dev;
+ trace_usb_ehci_portsc_write(addr + PORTSC_BEGIN, addr >> 2, val);
+
/* Clear rwc bits */
*portsc &= ~(val & PORTSC_RWC_MASK);
/* The guest may clear, but not set the PED bit */
@@ -1159,39 +1199,20 @@ static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
*portsc &= ~PORTSC_RO_MASK;
*portsc |= val;
+ trace_usb_ehci_portsc_change(addr + PORTSC_BEGIN, addr >> 2, *portsc, old);
}
-static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
+static void ehci_opreg_write(void *ptr, target_phys_addr_t addr,
+ uint64_t val, unsigned size)
{
EHCIState *s = ptr;
- uint32_t *mmio = (uint32_t *)(&s->mmio[addr]);
+ uint32_t *mmio = s->opreg + (addr >> 2);
uint32_t old = *mmio;
int i;
- trace_usb_ehci_mmio_writel(addr, addr2str(addr), val);
-
- /* Only aligned reads are allowed on OHCI */
- if (addr & 3) {
- fprintf(stderr, "usb-ehci: Mis-aligned write to addr 0x"
- TARGET_FMT_plx "\n", addr);
- return;
- }
-
- if (addr >= PORTSC && addr < PORTSC + 4 * NB_PORTS) {
- handle_port_status_write(s, (addr-PORTSC)/4, val);
- trace_usb_ehci_mmio_change(addr, addr2str(addr), *mmio, old);
- return;
- }
-
- if (addr < OPREGBASE) {
- fprintf(stderr, "usb-ehci: write attempt to read-only register"
- TARGET_FMT_plx "\n", addr);
- return;
- }
-
+ trace_usb_ehci_opreg_write(addr + OPREGBASE, addr2str(addr), val);
- /* Do any register specific pre-write processing here. */
- switch(addr) {
+ switch (addr + OPREGBASE) {
case USBCMD:
if (val & USBCMD_HCRESET) {
ehci_reset(s);
@@ -1202,7 +1223,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
/* not supporting dynamic frame list size at the moment */
if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) {
fprintf(stderr, "attempt to set frame list size -- value %d\n",
- val & USBCMD_FLS);
+ (int)val & USBCMD_FLS);
val &= ~USBCMD_FLS;
}
@@ -1213,6 +1234,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
*/
s->async_stepdown = 0;
qemu_bh_schedule(s->async_bh);
+ trace_usb_ehci_doorbell_ring();
}
if (((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & val) !=
@@ -1268,7 +1290,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
}
*mmio = val;
- trace_usb_ehci_mmio_change(addr, addr2str(addr), *mmio, old);
+ trace_usb_ehci_opreg_change(addr + OPREGBASE, addr2str(addr), *mmio, old);
}
@@ -1450,8 +1472,8 @@ static void ehci_execute_complete(EHCIQueue *q)
assert(p != NULL);
assert(p->qtdaddr == q->qtdaddr);
- assert(p->async != EHCI_ASYNC_INFLIGHT);
- p->async = EHCI_ASYNC_NONE;
+ assert(p->async == EHCI_ASYNC_INITIALIZED ||
+ p->async == EHCI_ASYNC_FINISHED);
DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n",
q->qhaddr, q->qh.next, q->qtdaddr, q->usb_status);
@@ -1481,10 +1503,6 @@ static void ehci_execute_complete(EHCIQueue *q)
assert(0);
break;
}
- } else if ((p->usb_status > p->tbytes) && (p->pid == USB_TOKEN_IN)) {
- p->usb_status = USB_RET_BABBLE;
- q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE);
- ehci_raise_irq(q->ehci, USBSTS_ERRINT);
} else {
// TODO check 4.12 for splits
@@ -1500,6 +1518,7 @@ static void ehci_execute_complete(EHCIQueue *q)
ehci_finish_transfer(q, p->usb_status);
usb_packet_unmap(&p->packet, &p->sgl);
qemu_sglist_destroy(&p->sgl);
+ p->async = EHCI_ASYNC_NONE;
q->qh.token ^= QTD_TOKEN_DTOGGLE;
q->qh.token &= ~QTD_TOKEN_ACTIVE;
@@ -1517,6 +1536,9 @@ static int ehci_execute(EHCIPacket *p, const char *action)
int ret;
int endp;
+ assert(p->async == EHCI_ASYNC_NONE ||
+ p->async == EHCI_ASYNC_INITIALIZED);
+
if (!(p->qtd.token & QTD_TOKEN_ACTIVE)) {
fprintf(stderr, "Attempting to execute inactive qtd\n");
return USB_RET_PROCERR;
@@ -1524,7 +1546,8 @@ static int ehci_execute(EHCIPacket *p, const char *action)
p->tbytes = (p->qtd.token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH;
if (p->tbytes > BUFF_SIZE) {
- fprintf(stderr, "Request for more bytes than allowed\n");
+ ehci_trace_guest_bug(p->queue->ehci,
+ "guest requested more bytes than allowed");
return USB_RET_PROCERR;
}
@@ -1544,15 +1567,18 @@ static int ehci_execute(EHCIPacket *p, const char *action)
break;
}
- if (ehci_init_transfer(p) != 0) {
- return USB_RET_PROCERR;
- }
-
endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP);
ep = usb_ep_get(p->queue->dev, p->pid, endp);
- usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr);
- usb_packet_map(&p->packet, &p->sgl);
+ if (p->async == EHCI_ASYNC_NONE) {
+ if (ehci_init_transfer(p) != 0) {
+ return USB_RET_PROCERR;
+ }
+
+ usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr);
+ usb_packet_map(&p->packet, &p->sgl);
+ p->async = EHCI_ASYNC_INITIALIZED;
+ }
trace_usb_ehci_packet_action(p->queue, p, action);
ret = usb_handle_packet(p->queue->dev, &p->packet);
@@ -1771,7 +1797,7 @@ out:
static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
{
EHCIPacket *p;
- uint32_t entry, devaddr;
+ uint32_t entry, devaddr, endp;
EHCIQueue *q;
EHCIqh qh;
@@ -1792,26 +1818,31 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
get_dwords(ehci, NLPTR_GET(q->qhaddr),
(uint32_t *) &qh, sizeof(EHCIqh) >> 2);
- if (q->revalidate && (q->qh.epchar != qh.epchar ||
- q->qh.epcap != qh.epcap ||
- q->qh.current_qtd != qh.current_qtd)) {
- ehci_free_queue(q);
- q = ehci_alloc_queue(ehci, entry, async);
- q->seen++;
+ ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &qh);
+
+ /*
+ * The overlay area of the qh should never be changed by the guest,
+ * except when idle, in which case the reset is a nop.
+ */
+ devaddr = get_field(qh.epchar, QH_EPCHAR_DEVADDR);
+ endp = get_field(qh.epchar, QH_EPCHAR_EP);
+ if ((devaddr != get_field(q->qh.epchar, QH_EPCHAR_DEVADDR)) ||
+ (endp != get_field(q->qh.epchar, QH_EPCHAR_EP)) ||
+ (memcmp(&qh.current_qtd, &q->qh.current_qtd,
+ 9 * sizeof(uint32_t)) != 0) ||
+ (q->dev != NULL && q->dev->addr != devaddr)) {
+ if (ehci_reset_queue(q) > 0) {
+ ehci_trace_guest_bug(ehci, "guest updated active QH");
+ }
p = NULL;
}
q->qh = qh;
- q->revalidate = 0;
- ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &q->qh);
- devaddr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR);
- if (q->dev != NULL && q->dev->addr != devaddr) {
- if (!QTAILQ_EMPTY(&q->packets)) {
- /* should not happen (guest bug) */
- ehci_cancel_queue(q);
- }
- q->dev = NULL;
+ q->transact_ctr = get_field(q->qh.epcap, QH_EPCAP_MULT);
+ if (q->transact_ctr == 0) { /* Guest bug in some versions of windows */
+ q->transact_ctr = 4;
}
+
if (q->dev == NULL) {
q->dev = ehci_find_device(q->ehci, devaddr);
}
@@ -1969,8 +2000,8 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
(!NLPTR_TBIT(p->qtd.next) && (p->qtd.next != qtd.next)) ||
(!NLPTR_TBIT(p->qtd.altnext) && (p->qtd.altnext != qtd.altnext)) ||
p->qtd.bufptr[0] != qtd.bufptr[0]) {
- /* guest bug: guest updated active QH or qTD underneath us */
ehci_cancel_queue(q);
+ ehci_trace_guest_bug(q->ehci, "guest updated active QH or qTD");
p = NULL;
} else {
p->qtd = qtd;
@@ -1989,15 +2020,19 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
} else if (p != NULL) {
switch (p->async) {
case EHCI_ASYNC_NONE:
- /* Previously nacked packet (likely interrupt ep) */
- ehci_set_state(q->ehci, q->async, EST_EXECUTE);
- break;
+ case EHCI_ASYNC_INITIALIZED:
+ /* Not yet executed (MULT), or previously nacked (int) packet */
+ ehci_set_state(q->ehci, q->async, EST_EXECUTE);
+ break;
case EHCI_ASYNC_INFLIGHT:
- /* Unfinyshed async handled packet, go horizontal */
+ /* Unfinished async handled packet, go horizontal */
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
break;
case EHCI_ASYNC_FINISHED:
- /* Should never happen, as this case is caught by fetchqh */
+ /*
+ * We get here when advqueue moves to a packet which is already
+ * finished, which can happen with packets queued up by fill_queue
+ */
ehci_set_state(q->ehci, q->async, EST_EXECUTING);
break;
}
@@ -2028,7 +2063,7 @@ static int ehci_state_horizqh(EHCIQueue *q)
return again;
}
-static void ehci_fill_queue(EHCIPacket *p)
+static int ehci_fill_queue(EHCIPacket *p)
{
EHCIQueue *q = p->queue;
EHCIqtd qtd = p->qtd;
@@ -2052,9 +2087,13 @@ static void ehci_fill_queue(EHCIPacket *p)
p->qtdaddr = qtdaddr;
p->qtd = qtd;
p->usb_status = ehci_execute(p, "queue");
+ if (p->usb_status == USB_RET_PROCERR) {
+ break;
+ }
assert(p->usb_status == USB_RET_ASYNC);
p->async = EHCI_ASYNC_INFLIGHT;
}
+ return p->usb_status;
}
static int ehci_state_execute(EHCIQueue *q)
@@ -2071,15 +2110,12 @@ static int ehci_state_execute(EHCIQueue *q)
// TODO verify enough time remains in the uframe as in 4.4.1.1
// TODO write back ptr to async list when done or out of time
- // TODO Windows does not seem to ever set the MULT field
- if (!q->async) {
- int transactCtr = get_field(q->qh.epcap, QH_EPCAP_MULT);
- if (!transactCtr) {
- ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
- again = 1;
- goto out;
- }
+ /* 4.10.3, bottom of page 82, go horizontal on transaction counter == 0 */
+ if (!q->async && q->transact_ctr == 0) {
+ ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
+ again = 1;
+ goto out;
}
if (q->async) {
@@ -2096,8 +2132,11 @@ static int ehci_state_execute(EHCIQueue *q)
trace_usb_ehci_packet_action(p->queue, p, "async");
p->async = EHCI_ASYNC_INFLIGHT;
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
- again = 1;
- ehci_fill_queue(p);
+ if (q->async) {
+ again = (ehci_fill_queue(p) == USB_RET_PROCERR) ? -1 : 1;
+ } else {
+ again = 1;
+ }
goto out;
}
@@ -2117,13 +2156,9 @@ static int ehci_state_executing(EHCIQueue *q)
ehci_execute_complete(q);
- // 4.10.3
- if (!q->async) {
- int transactCtr = get_field(q->qh.epcap, QH_EPCAP_MULT);
- transactCtr--;
- set_field(&q->qh.epcap, transactCtr, QH_EPCAP_MULT);
- // 4.10.3, bottom of page 82, should exit this state when transaction
- // counter decrements to 0
+ /* 4.10.3 */
+ if (!q->async && q->transact_ctr > 0) {
+ q->transact_ctr--;
}
/* 4.10.5 */
@@ -2310,8 +2345,8 @@ static void ehci_advance_async_state(EHCIState *ehci)
*/
if (ehci->usbcmd & USBCMD_IAAD) {
/* Remove all unseen qhs from the async qhs queue */
- ehci_queues_tag_unused_async(ehci);
- DPRINTF("ASYNC: doorbell request acknowledged\n");
+ ehci_queues_rip_unseen(ehci, async);
+ trace_usb_ehci_doorbell_ack();
ehci->usbcmd &= ~USBCMD_IAAD;
ehci_raise_irq(ehci, USBSTS_IAA);
}
@@ -2392,7 +2427,7 @@ static void ehci_update_frindex(EHCIState *ehci, int frames)
if (ehci->frindex == 0x00004000) {
ehci_raise_irq(ehci, USBSTS_FLR);
ehci->frindex = 0;
- if (ehci->usbsts_frindex > 0x00004000) {
+ if (ehci->usbsts_frindex >= 0x00004000) {
ehci->usbsts_frindex -= 0x00004000;
} else {
ehci->usbsts_frindex = 0;
@@ -2427,6 +2462,19 @@ static void ehci_frame_timer(void *opaque)
}
for (i = 0; i < frames; i++) {
+ /*
+ * If we're running behind schedule, we should not catch up
+ * too fast, as that will make some guests unhappy:
+ * 1) We must process a minimum of MIN_FR_PER_TICK frames,
+ * otherwise we will never catch up
+ * 2) Process frames until the guest has requested an irq (IOC)
+ */
+ if (i >= MIN_FR_PER_TICK) {
+ ehci_commit_irq(ehci);
+ if ((ehci->usbsts & USBINTR_MASK) & ehci->usbintr) {
+ break;
+ }
+ }
ehci_update_frindex(ehci, 1);
ehci_advance_periodic_state(ehci);
ehci->last_run_ns += FRAME_TIMER_NS;
@@ -2466,11 +2514,28 @@ static void ehci_async_bh(void *opaque)
ehci_advance_async_state(ehci);
}
-static const MemoryRegionOps ehci_mem_ops = {
- .old_mmio = {
- .read = { ehci_mem_readb, ehci_mem_readw, ehci_mem_readl },
- .write = { ehci_mem_writeb, ehci_mem_writew, ehci_mem_writel },
- },
+static const MemoryRegionOps ehci_mmio_caps_ops = {
+ .read = ehci_caps_read,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .impl.min_access_size = 1,
+ .impl.max_access_size = 1,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static const MemoryRegionOps ehci_mmio_opreg_ops = {
+ .read = ehci_opreg_read,
+ .write = ehci_opreg_write,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static const MemoryRegionOps ehci_mmio_port_ops = {
+ .read = ehci_port_read,
+ .write = ehci_port_write,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
.endianness = DEVICE_LITTLE_ENDIAN,
};
@@ -2508,6 +2573,32 @@ static int usb_ehci_post_load(void *opaque, int version_id)
return 0;
}
+static void usb_ehci_vm_state_change(void *opaque, int running, RunState state)
+{
+ EHCIState *ehci = opaque;
+
+ /*
+ * We don't migrate the EHCIQueue-s, instead we rebuild them for the
+ * schedule in guest memory. We must do the rebuilt ASAP, so that
+ * USB-devices which have async handled packages have a packet in the
+ * ep queue to match the completion with.
+ */
+ if (state == RUN_STATE_RUNNING) {
+ ehci_advance_async_state(ehci);
+ }
+
+ /*
+ * The schedule rebuilt from guest memory could cause the migration dest
+ * to miss a QH unlink, and fail to cancel packets, since the unlinked QH
+ * will never have existed on the destination. Therefor we must flush the
+ * async schedule on savevm to catch any not yet noticed unlinks.
+ */
+ if (state == RUN_STATE_SAVE_VM) {
+ ehci_advance_async_state(ehci);
+ ehci_queues_rip_unseen(ehci, 1);
+ }
+}
+
static const VMStateDescription vmstate_ehci = {
.name = "ehci",
.version_id = 2,
@@ -2627,19 +2718,19 @@ static int usb_ehci_initfn(PCIDevice *dev)
pci_conf[0x6e] = 0x00;
pci_conf[0x6f] = 0xc0; // USBLEFCTLSTS
- // 2.2 host controller interface version
- s->mmio[0x00] = (uint8_t) OPREGBASE;
- s->mmio[0x01] = 0x00;
- s->mmio[0x02] = 0x00;
- s->mmio[0x03] = 0x01; // HC version
- s->mmio[0x04] = NB_PORTS; // Number of downstream ports
- s->mmio[0x05] = 0x00; // No companion ports at present
- s->mmio[0x06] = 0x00;
- s->mmio[0x07] = 0x00;
- s->mmio[0x08] = 0x80; // We can cache whole frame, not 64-bit capable
- s->mmio[0x09] = 0x68; // EECP
- s->mmio[0x0a] = 0x00;
- s->mmio[0x0b] = 0x00;
+ /* 2.2 host controller interface version */
+ s->caps[0x00] = (uint8_t) OPREGBASE;
+ s->caps[0x01] = 0x00;
+ s->caps[0x02] = 0x00;
+ s->caps[0x03] = 0x01; /* HC version */
+ s->caps[0x04] = NB_PORTS; /* Number of downstream ports */
+ s->caps[0x05] = 0x00; /* No companion ports at present */
+ s->caps[0x06] = 0x00;
+ s->caps[0x07] = 0x00;
+ s->caps[0x08] = 0x80; /* We can cache whole frame, no 64-bit */
+ s->caps[0x09] = 0x68; /* EECP */
+ s->caps[0x0a] = 0x00;
+ s->caps[0x0b] = 0x00;
s->irq = s->dev.irq[3];
@@ -2657,8 +2748,20 @@ static int usb_ehci_initfn(PCIDevice *dev)
usb_packet_init(&s->ipacket);
qemu_register_reset(ehci_reset, s);
+ qemu_add_vm_change_state_handler(usb_ehci_vm_state_change, s);
+
+ memory_region_init(&s->mem, "ehci", MMIO_SIZE);
+ memory_region_init_io(&s->mem_caps, &ehci_mmio_caps_ops, s,
+ "capabilities", OPREGBASE);
+ memory_region_init_io(&s->mem_opreg, &ehci_mmio_opreg_ops, s,
+ "operational", PORTSC_BEGIN - OPREGBASE);
+ memory_region_init_io(&s->mem_ports, &ehci_mmio_port_ops, s,
+ "ports", PORTSC_END - PORTSC_BEGIN);
+
+ memory_region_add_subregion(&s->mem, 0, &s->mem_caps);
+ memory_region_add_subregion(&s->mem, OPREGBASE, &s->mem_opreg);
+ memory_region_add_subregion(&s->mem, PORTSC_BEGIN, &s->mem_ports);
- memory_region_init_io(&s->mem, &ehci_mem_ops, s, "ehci", MMIO_SIZE);
pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem);
return 0;
diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c
index c36184ae4..59c7055cb 100644
--- a/hw/usb/hcd-ohci.c
+++ b/hw/usb/hcd-ohci.c
@@ -1470,8 +1470,6 @@ static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val)
if (old_state != port->ctrl)
ohci_set_interrupt(ohci, OHCI_INTR_RHSC);
-
- return;
}
static uint64_t ohci_mem_read(void *opaque,
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index b0db92145..cdc8bc3fb 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -729,11 +729,6 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
*int_mask |= 0x01;
if (pid == USB_TOKEN_IN) {
- if (len > max_len) {
- ret = USB_RET_BABBLE;
- goto out;
- }
-
if ((td->ctrl & TD_CTRL_SPD) && len < max_len) {
*int_mask |= 0x02;
/* short packet: do not update QH */
@@ -1005,6 +1000,9 @@ static void uhci_fill_queue(UHCIState *s, UHCI_TD *td)
}
assert(ret == TD_RESULT_ASYNC_START);
assert(int_mask == 0);
+ if (ptd.ctrl & TD_CTRL_SPD) {
+ break;
+ }
plink = ptd.link;
}
}
@@ -1102,7 +1100,7 @@ static void uhci_process_frame(UHCIState *s)
case TD_RESULT_ASYNC_START:
trace_usb_uhci_td_async(curr_qh & ~0xf, link & ~0xf);
- if (is_valid(td.link)) {
+ if (is_valid(td.link) && !(td.ctrl & TD_CTRL_SPD)) {
uhci_fill_queue(s, &td);
}
link = curr_qh ? qh.link : td.link;
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index 3eb27fadb..3a41b067c 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -23,6 +23,7 @@
#include "hw/usb.h"
#include "hw/pci.h"
#include "hw/msi.h"
+#include "hw/msix.h"
#include "trace.h"
//#define DEBUG_XHCI
@@ -36,17 +37,14 @@
#define FIXME() do { fprintf(stderr, "FIXME %s:%d\n", \
__func__, __LINE__); abort(); } while (0)
-#define MAXSLOTS 8
-#define MAXINTRS 1
+#define MAXPORTS_2 15
+#define MAXPORTS_3 15
-#define USB2_PORTS 4
-#define USB3_PORTS 4
-
-#define MAXPORTS (USB2_PORTS+USB3_PORTS)
+#define MAXPORTS (MAXPORTS_2+MAXPORTS_3)
+#define MAXSLOTS 64
+#define MAXINTRS 16
#define TD_QUEUE 24
-#define BG_XFERS 8
-#define BG_PKTS 8
/* Very pessimistic, let's hope it's enough for all cases */
#define EV_QUEUE (((3*TD_QUEUE)+16)*MAXSLOTS)
@@ -55,24 +53,28 @@
#define ER_FULL_HACK
#define LEN_CAP 0x40
-#define OFF_OPER LEN_CAP
#define LEN_OPER (0x400 + 0x10 * MAXPORTS)
-#define OFF_RUNTIME ((OFF_OPER + LEN_OPER + 0x20) & ~0x1f)
-#define LEN_RUNTIME (0x20 + MAXINTRS * 0x20)
-#define OFF_DOORBELL (OFF_RUNTIME + LEN_RUNTIME)
+#define LEN_RUNTIME ((MAXINTRS + 1) * 0x20)
#define LEN_DOORBELL ((MAXSLOTS + 1) * 0x20)
+#define OFF_OPER LEN_CAP
+#define OFF_RUNTIME 0x1000
+#define OFF_DOORBELL 0x2000
+#define OFF_MSIX_TABLE 0x3000
+#define OFF_MSIX_PBA 0x3800
/* must be power of 2 */
-#define LEN_REGS 0x2000
+#define LEN_REGS 0x4000
+#if (OFF_OPER + LEN_OPER) > OFF_RUNTIME
+#error Increase OFF_RUNTIME
+#endif
+#if (OFF_RUNTIME + LEN_RUNTIME) > OFF_DOORBELL
+#error Increase OFF_DOORBELL
+#endif
#if (OFF_DOORBELL + LEN_DOORBELL) > LEN_REGS
# error Increase LEN_REGS
#endif
-#if MAXINTRS > 1
-# error TODO: only one interrupter supported
-#endif
-
/* bit definitions */
#define USBCMD_RS (1<<0)
#define USBCMD_HCRST (1<<1)
@@ -258,6 +260,10 @@ typedef enum TRBCCode {
#define TRB_LK_TC (1<<1)
+#define TRB_INTR_SHIFT 22
+#define TRB_INTR_MASK 0x3ff
+#define TRB_INTR(t) (((t).status >> TRB_INTR_SHIFT) & TRB_INTR_MASK)
+
#define EP_TYPE_MASK 0x7
#define EP_TYPE_SHIFT 3
@@ -279,6 +285,8 @@ typedef enum TRBCCode {
#define SLOT_CONTEXT_ENTRIES_MASK 0x1f
#define SLOT_CONTEXT_ENTRIES_SHIFT 27
+typedef struct XHCIState XHCIState;
+
typedef enum EPType {
ET_INVALID = 0,
ET_ISO_OUT,
@@ -297,64 +305,67 @@ typedef struct XHCIRing {
} XHCIRing;
typedef struct XHCIPort {
- USBPort port;
+ XHCIState *xhci;
uint32_t portsc;
+ uint32_t portnr;
+ USBPort *uport;
+ uint32_t speedmask;
+ char name[16];
+ MemoryRegion mem;
} XHCIPort;
-struct XHCIState;
-typedef struct XHCIState XHCIState;
-
typedef struct XHCITransfer {
XHCIState *xhci;
USBPacket packet;
+ QEMUSGList sgl;
bool running_async;
bool running_retry;
bool cancelled;
bool complete;
- bool backgrounded;
unsigned int iso_pkts;
unsigned int slotid;
unsigned int epid;
bool in_xfer;
bool iso_xfer;
- bool bg_xfer;
unsigned int trb_count;
unsigned int trb_alloced;
XHCITRB *trbs;
- unsigned int data_length;
- unsigned int data_alloced;
- uint8_t *data;
-
TRBCCode status;
unsigned int pkts;
unsigned int pktsize;
unsigned int cur_pkt;
+
+ uint64_t mfindex_kick;
} XHCITransfer;
typedef struct XHCIEPContext {
+ XHCIState *xhci;
+ unsigned int slotid;
+ unsigned int epid;
+
XHCIRing ring;
unsigned int next_xfer;
unsigned int comp_xfer;
XHCITransfer transfers[TD_QUEUE];
XHCITransfer *retry;
- bool bg_running;
- bool bg_updating;
- unsigned int next_bg;
- XHCITransfer bg_transfers[BG_XFERS];
EPType type;
dma_addr_t pctx;
unsigned int max_psize;
- bool has_bg;
uint32_t state;
+
+ /* iso xfer scheduling */
+ unsigned int interval;
+ int64_t mfindex_last;
+ QEMUTimer *kick_timer;
} XHCIEPContext;
typedef struct XHCISlot {
bool enabled;
dma_addr_t ctx;
- unsigned int port;
+ USBPort *uport;
unsigned int devaddr;
XHCIEPContext * eps[31];
} XHCISlot;
@@ -369,15 +380,44 @@ typedef struct XHCIEvent {
uint8_t epid;
} XHCIEvent;
+typedef struct XHCIInterrupter {
+ uint32_t iman;
+ uint32_t imod;
+ uint32_t erstsz;
+ uint32_t erstba_low;
+ uint32_t erstba_high;
+ uint32_t erdp_low;
+ uint32_t erdp_high;
+
+ bool msix_used, er_pcs, er_full;
+
+ dma_addr_t er_start;
+ uint32_t er_size;
+ unsigned int er_ep_idx;
+
+ XHCIEvent ev_buffer[EV_QUEUE];
+ unsigned int ev_buffer_put;
+ unsigned int ev_buffer_get;
+
+} XHCIInterrupter;
+
struct XHCIState {
PCIDevice pci_dev;
USBBus bus;
qemu_irq irq;
MemoryRegion mem;
+ MemoryRegion mem_cap;
+ MemoryRegion mem_oper;
+ MemoryRegion mem_runtime;
+ MemoryRegion mem_doorbell;
const char *name;
- uint32_t msi;
unsigned int devaddr;
+ /* properties */
+ uint32_t numports_2;
+ uint32_t numports_3;
+ uint32_t flags;
+
/* Operational Registers */
uint32_t usbcmd;
uint32_t usbsts;
@@ -388,29 +428,15 @@ struct XHCIState {
uint32_t dcbaap_high;
uint32_t config;
+ USBPort uports[MAX(MAXPORTS_2, MAXPORTS_3)];
XHCIPort ports[MAXPORTS];
XHCISlot slots[MAXSLOTS];
+ uint32_t numports;
/* Runtime Registers */
- uint32_t mfindex;
- /* note: we only support one interrupter */
- uint32_t iman;
- uint32_t imod;
- uint32_t erstsz;
- uint32_t erstba_low;
- uint32_t erstba_high;
- uint32_t erdp_low;
- uint32_t erdp_high;
-
- dma_addr_t er_start;
- uint32_t er_size;
- bool er_pcs;
- unsigned int er_ep_idx;
- bool er_full;
-
- XHCIEvent ev_buffer[EV_QUEUE];
- unsigned int ev_buffer_put;
- unsigned int ev_buffer_get;
+ int64_t mfindex_start;
+ QEMUTimer *mfwrap_timer;
+ XHCIInterrupter intr[MAXINTRS];
XHCIRing cmd_ring;
};
@@ -422,6 +448,16 @@ typedef struct XHCIEvRingSeg {
uint32_t rsvd;
} XHCIEvRingSeg;
+enum xhci_flags {
+ XHCI_FLAG_USE_MSI = 1,
+ XHCI_FLAG_USE_MSI_X,
+};
+
+static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
+ unsigned int epid);
+static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v);
+static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v);
+
static const char *TRBType_names[] = {
[TRB_RESERVED] = "TRB_RESERVED",
[TR_NORMAL] = "TR_NORMAL",
@@ -460,6 +496,45 @@ static const char *TRBType_names[] = {
[CR_VENDOR_NEC_CHALLENGE_RESPONSE] = "CR_VENDOR_NEC_CHALLENGE_RESPONSE",
};
+static const char *TRBCCode_names[] = {
+ [CC_INVALID] = "CC_INVALID",
+ [CC_SUCCESS] = "CC_SUCCESS",
+ [CC_DATA_BUFFER_ERROR] = "CC_DATA_BUFFER_ERROR",
+ [CC_BABBLE_DETECTED] = "CC_BABBLE_DETECTED",
+ [CC_USB_TRANSACTION_ERROR] = "CC_USB_TRANSACTION_ERROR",
+ [CC_TRB_ERROR] = "CC_TRB_ERROR",
+ [CC_STALL_ERROR] = "CC_STALL_ERROR",
+ [CC_RESOURCE_ERROR] = "CC_RESOURCE_ERROR",
+ [CC_BANDWIDTH_ERROR] = "CC_BANDWIDTH_ERROR",
+ [CC_NO_SLOTS_ERROR] = "CC_NO_SLOTS_ERROR",
+ [CC_INVALID_STREAM_TYPE_ERROR] = "CC_INVALID_STREAM_TYPE_ERROR",
+ [CC_SLOT_NOT_ENABLED_ERROR] = "CC_SLOT_NOT_ENABLED_ERROR",
+ [CC_EP_NOT_ENABLED_ERROR] = "CC_EP_NOT_ENABLED_ERROR",
+ [CC_SHORT_PACKET] = "CC_SHORT_PACKET",
+ [CC_RING_UNDERRUN] = "CC_RING_UNDERRUN",
+ [CC_RING_OVERRUN] = "CC_RING_OVERRUN",
+ [CC_VF_ER_FULL] = "CC_VF_ER_FULL",
+ [CC_PARAMETER_ERROR] = "CC_PARAMETER_ERROR",
+ [CC_BANDWIDTH_OVERRUN] = "CC_BANDWIDTH_OVERRUN",
+ [CC_CONTEXT_STATE_ERROR] = "CC_CONTEXT_STATE_ERROR",
+ [CC_NO_PING_RESPONSE_ERROR] = "CC_NO_PING_RESPONSE_ERROR",
+ [CC_EVENT_RING_FULL_ERROR] = "CC_EVENT_RING_FULL_ERROR",
+ [CC_INCOMPATIBLE_DEVICE_ERROR] = "CC_INCOMPATIBLE_DEVICE_ERROR",
+ [CC_MISSED_SERVICE_ERROR] = "CC_MISSED_SERVICE_ERROR",
+ [CC_COMMAND_RING_STOPPED] = "CC_COMMAND_RING_STOPPED",
+ [CC_COMMAND_ABORTED] = "CC_COMMAND_ABORTED",
+ [CC_STOPPED] = "CC_STOPPED",
+ [CC_STOPPED_LENGTH_INVALID] = "CC_STOPPED_LENGTH_INVALID",
+ [CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR]
+ = "CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR",
+ [CC_ISOCH_BUFFER_OVERRUN] = "CC_ISOCH_BUFFER_OVERRUN",
+ [CC_EVENT_LOST_ERROR] = "CC_EVENT_LOST_ERROR",
+ [CC_UNDEFINED_ERROR] = "CC_UNDEFINED_ERROR",
+ [CC_INVALID_STREAM_ID_ERROR] = "CC_INVALID_STREAM_ID_ERROR",
+ [CC_SECONDARY_BANDWIDTH_ERROR] = "CC_SECONDARY_BANDWIDTH_ERROR",
+ [CC_SPLIT_TRANSACTION_ERROR] = "CC_SPLIT_TRANSACTION_ERROR",
+};
+
static const char *lookup_name(uint32_t index, const char **list, uint32_t llen)
{
if (index >= llen || list[index] == NULL) {
@@ -474,8 +549,42 @@ static const char *trb_name(XHCITRB *trb)
ARRAY_SIZE(TRBType_names));
}
-static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
- unsigned int epid);
+static const char *event_name(XHCIEvent *event)
+{
+ return lookup_name(event->ccode, TRBCCode_names,
+ ARRAY_SIZE(TRBCCode_names));
+}
+
+static uint64_t xhci_mfindex_get(XHCIState *xhci)
+{
+ int64_t now = qemu_get_clock_ns(vm_clock);
+ return (now - xhci->mfindex_start) / 125000;
+}
+
+static void xhci_mfwrap_update(XHCIState *xhci)
+{
+ const uint32_t bits = USBCMD_RS | USBCMD_EWE;
+ uint32_t mfindex, left;
+ int64_t now;
+
+ if ((xhci->usbcmd & bits) == bits) {
+ now = qemu_get_clock_ns(vm_clock);
+ mfindex = ((now - xhci->mfindex_start) / 125000) & 0x3fff;
+ left = 0x4000 - mfindex;
+ qemu_mod_timer(xhci->mfwrap_timer, now + left * 125000);
+ } else {
+ qemu_del_timer(xhci->mfwrap_timer);
+ }
+}
+
+static void xhci_mfwrap_timer(void *opaque)
+{
+ XHCIState *xhci = opaque;
+ XHCIEvent wrap = { ER_MFINDEX_WRAP, CC_SUCCESS };
+
+ xhci_event(xhci, &wrap, 0);
+ xhci_mfwrap_update(xhci);
+}
static inline dma_addr_t xhci_addr64(uint32_t low, uint32_t high)
{
@@ -495,29 +604,106 @@ static inline dma_addr_t xhci_mask64(uint64_t addr)
}
}
-static void xhci_irq_update(XHCIState *xhci)
+static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport)
+{
+ int index;
+
+ if (!uport->dev) {
+ return NULL;
+ }
+ switch (uport->dev->speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ case USB_SPEED_HIGH:
+ index = uport->index;
+ break;
+ case USB_SPEED_SUPER:
+ index = uport->index + xhci->numports_2;
+ break;
+ default:
+ return NULL;
+ }
+ return &xhci->ports[index];
+}
+
+static void xhci_intx_update(XHCIState *xhci)
{
int level = 0;
- if (xhci->iman & IMAN_IP && xhci->iman & IMAN_IE &&
+ if (msix_enabled(&xhci->pci_dev) ||
+ msi_enabled(&xhci->pci_dev)) {
+ return;
+ }
+
+ if (xhci->intr[0].iman & IMAN_IP &&
+ xhci->intr[0].iman & IMAN_IE &&
xhci->usbcmd & USBCMD_INTE) {
level = 1;
}
- if (xhci->msi && msi_enabled(&xhci->pci_dev)) {
- if (level) {
- trace_usb_xhci_irq_msi(0);
- msi_notify(&xhci->pci_dev, 0);
- }
+ trace_usb_xhci_irq_intx(level);
+ qemu_set_irq(xhci->irq, level);
+}
+
+static void xhci_msix_update(XHCIState *xhci, int v)
+{
+ bool enabled;
+
+ if (!msix_enabled(&xhci->pci_dev)) {
+ return;
+ }
+
+ enabled = xhci->intr[v].iman & IMAN_IE;
+ if (enabled == xhci->intr[v].msix_used) {
+ return;
+ }
+
+ if (enabled) {
+ trace_usb_xhci_irq_msix_use(v);
+ msix_vector_use(&xhci->pci_dev, v);
+ xhci->intr[v].msix_used = true;
} else {
- trace_usb_xhci_irq_intx(level);
- qemu_set_irq(xhci->irq, level);
+ trace_usb_xhci_irq_msix_unuse(v);
+ msix_vector_unuse(&xhci->pci_dev, v);
+ xhci->intr[v].msix_used = false;
+ }
+}
+
+static void xhci_intr_raise(XHCIState *xhci, int v)
+{
+ xhci->intr[v].erdp_low |= ERDP_EHB;
+ xhci->intr[v].iman |= IMAN_IP;
+ xhci->usbsts |= USBSTS_EINT;
+
+ if (!(xhci->intr[v].iman & IMAN_IE)) {
+ return;
+ }
+
+ if (!(xhci->usbcmd & USBCMD_INTE)) {
+ return;
+ }
+
+ if (msix_enabled(&xhci->pci_dev)) {
+ trace_usb_xhci_irq_msix(v);
+ msix_notify(&xhci->pci_dev, v);
+ return;
+ }
+
+ if (msi_enabled(&xhci->pci_dev)) {
+ trace_usb_xhci_irq_msi(v);
+ msi_notify(&xhci->pci_dev, v);
+ return;
+ }
+
+ if (v == 0) {
+ trace_usb_xhci_irq_intx(1);
+ qemu_set_irq(xhci->irq, 1);
}
}
static inline int xhci_running(XHCIState *xhci)
{
- return !(xhci->usbsts & USBSTS_HCH) && !xhci->er_full;
+ return !(xhci->usbsts & USBSTS_HCH) && !xhci->intr[0].er_full;
}
static void xhci_die(XHCIState *xhci)
@@ -526,8 +712,9 @@ static void xhci_die(XHCIState *xhci)
fprintf(stderr, "xhci: asserted controller error\n");
}
-static void xhci_write_event(XHCIState *xhci, XHCIEvent *event)
+static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v)
{
+ XHCIInterrupter *intr = &xhci->intr[v];
XHCITRB ev_trb;
dma_addr_t addr;
@@ -535,26 +722,28 @@ static void xhci_write_event(XHCIState *xhci, XHCIEvent *event)
ev_trb.status = cpu_to_le32(event->length | (event->ccode << 24));
ev_trb.control = (event->slotid << 24) | (event->epid << 16) |
event->flags | (event->type << TRB_TYPE_SHIFT);
- if (xhci->er_pcs) {
+ if (intr->er_pcs) {
ev_trb.control |= TRB_C;
}
ev_trb.control = cpu_to_le32(ev_trb.control);
- trace_usb_xhci_queue_event(xhci->er_ep_idx, trb_name(&ev_trb),
- ev_trb.parameter, ev_trb.status, ev_trb.control);
+ trace_usb_xhci_queue_event(v, intr->er_ep_idx, trb_name(&ev_trb),
+ event_name(event), ev_trb.parameter,
+ ev_trb.status, ev_trb.control);
- addr = xhci->er_start + TRB_SIZE*xhci->er_ep_idx;
+ addr = intr->er_start + TRB_SIZE*intr->er_ep_idx;
pci_dma_write(&xhci->pci_dev, addr, &ev_trb, TRB_SIZE);
- xhci->er_ep_idx++;
- if (xhci->er_ep_idx >= xhci->er_size) {
- xhci->er_ep_idx = 0;
- xhci->er_pcs = !xhci->er_pcs;
+ intr->er_ep_idx++;
+ if (intr->er_ep_idx >= intr->er_size) {
+ intr->er_ep_idx = 0;
+ intr->er_pcs = !intr->er_pcs;
}
}
-static void xhci_events_update(XHCIState *xhci)
+static void xhci_events_update(XHCIState *xhci, int v)
{
+ XHCIInterrupter *intr = &xhci->intr[v];
dma_addr_t erdp;
unsigned int dp_idx;
bool do_irq = 0;
@@ -563,122 +752,121 @@ static void xhci_events_update(XHCIState *xhci)
return;
}
- erdp = xhci_addr64(xhci->erdp_low, xhci->erdp_high);
- if (erdp < xhci->er_start ||
- erdp >= (xhci->er_start + TRB_SIZE*xhci->er_size)) {
+ erdp = xhci_addr64(intr->erdp_low, intr->erdp_high);
+ if (erdp < intr->er_start ||
+ erdp >= (intr->er_start + TRB_SIZE*intr->er_size)) {
fprintf(stderr, "xhci: ERDP out of bounds: "DMA_ADDR_FMT"\n", erdp);
- fprintf(stderr, "xhci: ER at "DMA_ADDR_FMT" len %d\n",
- xhci->er_start, xhci->er_size);
+ fprintf(stderr, "xhci: ER[%d] at "DMA_ADDR_FMT" len %d\n",
+ v, intr->er_start, intr->er_size);
xhci_die(xhci);
return;
}
- dp_idx = (erdp - xhci->er_start) / TRB_SIZE;
- assert(dp_idx < xhci->er_size);
+ dp_idx = (erdp - intr->er_start) / TRB_SIZE;
+ assert(dp_idx < intr->er_size);
/* NEC didn't read section 4.9.4 of the spec (v1.0 p139 top Note) and thus
* deadlocks when the ER is full. Hack it by holding off events until
* the driver decides to free at least half of the ring */
- if (xhci->er_full) {
- int er_free = dp_idx - xhci->er_ep_idx;
+ if (intr->er_full) {
+ int er_free = dp_idx - intr->er_ep_idx;
if (er_free <= 0) {
- er_free += xhci->er_size;
+ er_free += intr->er_size;
}
- if (er_free < (xhci->er_size/2)) {
+ if (er_free < (intr->er_size/2)) {
DPRINTF("xhci_events_update(): event ring still "
"more than half full (hack)\n");
return;
}
}
- while (xhci->ev_buffer_put != xhci->ev_buffer_get) {
- assert(xhci->er_full);
- if (((xhci->er_ep_idx+1) % xhci->er_size) == dp_idx) {
+ while (intr->ev_buffer_put != intr->ev_buffer_get) {
+ assert(intr->er_full);
+ if (((intr->er_ep_idx+1) % intr->er_size) == dp_idx) {
DPRINTF("xhci_events_update(): event ring full again\n");
#ifndef ER_FULL_HACK
XHCIEvent full = {ER_HOST_CONTROLLER, CC_EVENT_RING_FULL_ERROR};
- xhci_write_event(xhci, &full);
+ xhci_write_event(xhci, &full, v);
#endif
do_irq = 1;
break;
}
- XHCIEvent *event = &xhci->ev_buffer[xhci->ev_buffer_get];
- xhci_write_event(xhci, event);
- xhci->ev_buffer_get++;
+ XHCIEvent *event = &intr->ev_buffer[intr->ev_buffer_get];
+ xhci_write_event(xhci, event, v);
+ intr->ev_buffer_get++;
do_irq = 1;
- if (xhci->ev_buffer_get == EV_QUEUE) {
- xhci->ev_buffer_get = 0;
+ if (intr->ev_buffer_get == EV_QUEUE) {
+ intr->ev_buffer_get = 0;
}
}
if (do_irq) {
- xhci->erdp_low |= ERDP_EHB;
- xhci->iman |= IMAN_IP;
- xhci->usbsts |= USBSTS_EINT;
- xhci_irq_update(xhci);
+ xhci_intr_raise(xhci, v);
}
- if (xhci->er_full && xhci->ev_buffer_put == xhci->ev_buffer_get) {
+ if (intr->er_full && intr->ev_buffer_put == intr->ev_buffer_get) {
DPRINTF("xhci_events_update(): event ring no longer full\n");
- xhci->er_full = 0;
+ intr->er_full = 0;
}
- return;
}
-static void xhci_event(XHCIState *xhci, XHCIEvent *event)
+static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v)
{
+ XHCIInterrupter *intr;
dma_addr_t erdp;
unsigned int dp_idx;
- if (xhci->er_full) {
+ if (v >= MAXINTRS) {
+ DPRINTF("intr nr out of range (%d >= %d)\n", v, MAXINTRS);
+ return;
+ }
+ intr = &xhci->intr[v];
+
+ if (intr->er_full) {
DPRINTF("xhci_event(): ER full, queueing\n");
- if (((xhci->ev_buffer_put+1) % EV_QUEUE) == xhci->ev_buffer_get) {
+ if (((intr->ev_buffer_put+1) % EV_QUEUE) == intr->ev_buffer_get) {
fprintf(stderr, "xhci: event queue full, dropping event!\n");
return;
}
- xhci->ev_buffer[xhci->ev_buffer_put++] = *event;
- if (xhci->ev_buffer_put == EV_QUEUE) {
- xhci->ev_buffer_put = 0;
+ intr->ev_buffer[intr->ev_buffer_put++] = *event;
+ if (intr->ev_buffer_put == EV_QUEUE) {
+ intr->ev_buffer_put = 0;
}
return;
}
- erdp = xhci_addr64(xhci->erdp_low, xhci->erdp_high);
- if (erdp < xhci->er_start ||
- erdp >= (xhci->er_start + TRB_SIZE*xhci->er_size)) {
+ erdp = xhci_addr64(intr->erdp_low, intr->erdp_high);
+ if (erdp < intr->er_start ||
+ erdp >= (intr->er_start + TRB_SIZE*intr->er_size)) {
fprintf(stderr, "xhci: ERDP out of bounds: "DMA_ADDR_FMT"\n", erdp);
- fprintf(stderr, "xhci: ER at "DMA_ADDR_FMT" len %d\n",
- xhci->er_start, xhci->er_size);
+ fprintf(stderr, "xhci: ER[%d] at "DMA_ADDR_FMT" len %d\n",
+ v, intr->er_start, intr->er_size);
xhci_die(xhci);
return;
}
- dp_idx = (erdp - xhci->er_start) / TRB_SIZE;
- assert(dp_idx < xhci->er_size);
+ dp_idx = (erdp - intr->er_start) / TRB_SIZE;
+ assert(dp_idx < intr->er_size);
- if ((xhci->er_ep_idx+1) % xhci->er_size == dp_idx) {
+ if ((intr->er_ep_idx+1) % intr->er_size == dp_idx) {
DPRINTF("xhci_event(): ER full, queueing\n");
#ifndef ER_FULL_HACK
XHCIEvent full = {ER_HOST_CONTROLLER, CC_EVENT_RING_FULL_ERROR};
xhci_write_event(xhci, &full);
#endif
- xhci->er_full = 1;
- if (((xhci->ev_buffer_put+1) % EV_QUEUE) == xhci->ev_buffer_get) {
+ intr->er_full = 1;
+ if (((intr->ev_buffer_put+1) % EV_QUEUE) == intr->ev_buffer_get) {
fprintf(stderr, "xhci: event queue full, dropping event!\n");
return;
}
- xhci->ev_buffer[xhci->ev_buffer_put++] = *event;
- if (xhci->ev_buffer_put == EV_QUEUE) {
- xhci->ev_buffer_put = 0;
+ intr->ev_buffer[intr->ev_buffer_put++] = *event;
+ if (intr->ev_buffer_put == EV_QUEUE) {
+ intr->ev_buffer_put = 0;
}
} else {
- xhci_write_event(xhci, event);
+ xhci_write_event(xhci, event, v);
}
- xhci->erdp_low |= ERDP_EHB;
- xhci->iman |= IMAN_IP;
- xhci->usbsts |= USBSTS_EINT;
-
- xhci_irq_update(xhci);
+ xhci_intr_raise(xhci, v);
}
static void xhci_ring_init(XHCIState *xhci, XHCIRing *ring,
@@ -770,17 +958,18 @@ static int xhci_ring_chain_length(XHCIState *xhci, const XHCIRing *ring)
}
}
-static void xhci_er_reset(XHCIState *xhci)
+static void xhci_er_reset(XHCIState *xhci, int v)
{
+ XHCIInterrupter *intr = &xhci->intr[v];
XHCIEvRingSeg seg;
/* cache the (sole) event ring segment location */
- if (xhci->erstsz != 1) {
- fprintf(stderr, "xhci: invalid value for ERSTSZ: %d\n", xhci->erstsz);
+ if (intr->erstsz != 1) {
+ fprintf(stderr, "xhci: invalid value for ERSTSZ: %d\n", intr->erstsz);
xhci_die(xhci);
return;
}
- dma_addr_t erstba = xhci_addr64(xhci->erstba_low, xhci->erstba_high);
+ dma_addr_t erstba = xhci_addr64(intr->erstba_low, intr->erstba_high);
pci_dma_read(&xhci->pci_dev, erstba, &seg, sizeof(seg));
le32_to_cpus(&seg.addr_low);
le32_to_cpus(&seg.addr_high);
@@ -790,21 +979,22 @@ static void xhci_er_reset(XHCIState *xhci)
xhci_die(xhci);
return;
}
- xhci->er_start = xhci_addr64(seg.addr_low, seg.addr_high);
- xhci->er_size = seg.size;
+ intr->er_start = xhci_addr64(seg.addr_low, seg.addr_high);
+ intr->er_size = seg.size;
- xhci->er_ep_idx = 0;
- xhci->er_pcs = 1;
- xhci->er_full = 0;
+ intr->er_ep_idx = 0;
+ intr->er_pcs = 1;
+ intr->er_full = 0;
- DPRINTF("xhci: event ring:" DMA_ADDR_FMT " [%d]\n",
- xhci->er_start, xhci->er_size);
+ DPRINTF("xhci: event ring[%d]:" DMA_ADDR_FMT " [%d]\n",
+ v, intr->er_start, intr->er_size);
}
static void xhci_run(XHCIState *xhci)
{
trace_usb_xhci_run();
xhci->usbsts &= ~USBSTS_HCH;
+ xhci->mfindex_start = qemu_get_clock_ns(vm_clock);
}
static void xhci_stop(XHCIState *xhci)
@@ -833,6 +1023,12 @@ static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx,
epctx->state = state;
}
+static void xhci_ep_kick_timer(void *opaque)
+{
+ XHCIEPContext *epctx = opaque;
+ xhci_kick_ep(epctx->xhci, epctx->slotid, epctx->epid);
+}
+
static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
unsigned int epid, dma_addr_t pctx,
uint32_t *ctx)
@@ -854,6 +1050,9 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
epctx = g_malloc(sizeof(XHCIEPContext));
memset(epctx, 0, sizeof(XHCIEPContext));
+ epctx->xhci = xhci;
+ epctx->slotid = slotid;
+ epctx->epid = epid;
slot->eps[epid-1] = epctx;
@@ -866,16 +1065,16 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
epctx->pctx = pctx;
epctx->max_psize = ctx[1]>>16;
epctx->max_psize *= 1+((ctx[1]>>8)&0xff);
- epctx->has_bg = false;
- if (epctx->type == ET_ISO_IN) {
- epctx->has_bg = true;
- }
DPRINTF("xhci: endpoint %d.%d max transaction (burst) size is %d\n",
epid/2, epid%2, epctx->max_psize);
for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) {
usb_packet_init(&epctx->transfers[i].packet);
}
+ epctx->interval = 1 << (ctx[0] >> 16) & 0xff;
+ epctx->mfindex_last = 0;
+ epctx->kick_timer = qemu_new_timer_ns(vm_clock, xhci_ep_kick_timer, epctx);
+
epctx->state = EP_RUNNING;
ctx[0] &= ~EP_STATE_MASK;
ctx[0] |= EP_RUNNING;
@@ -915,42 +1114,16 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid,
if (t->running_retry) {
t->running_retry = 0;
epctx->retry = NULL;
- }
- if (t->backgrounded) {
- t->backgrounded = 0;
+ qemu_del_timer(epctx->kick_timer);
}
if (t->trbs) {
g_free(t->trbs);
}
- if (t->data) {
- g_free(t->data);
- }
t->trbs = NULL;
- t->data = NULL;
t->trb_count = t->trb_alloced = 0;
- t->data_length = t->data_alloced = 0;
xferi = (xferi + 1) % TD_QUEUE;
}
- if (epctx->has_bg) {
- xferi = epctx->next_bg;
- for (i = 0; i < BG_XFERS; i++) {
- XHCITransfer *t = &epctx->bg_transfers[xferi];
- if (t->running_async) {
- usb_cancel_packet(&t->packet);
- t->running_async = 0;
- t->cancelled = 1;
- DPRINTF("xhci: cancelling bg transfer %d, waiting for it to complete...\n", i);
- killed++;
- }
- if (t->data) {
- g_free(t->data);
- }
-
- t->data = NULL;
- xferi = (xferi + 1) % BG_XFERS;
- }
- }
return killed;
}
@@ -977,6 +1150,7 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
xhci_set_ep_state(xhci, epctx, EP_DISABLED);
+ qemu_free_timer(epctx->kick_timer);
g_free(epctx);
slot->eps[epid-1] = NULL;
@@ -1057,7 +1231,7 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid,
ep |= 0x80;
}
- dev = xhci->ports[xhci->slots[slotid-1].port-1].port.dev;
+ dev = xhci->slots[slotid-1].uport->dev;
if (!dev) {
return CC_USB_TRANSACTION_ERROR;
}
@@ -1081,7 +1255,7 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid,
return CC_TRB_ERROR;
}
- DPRINTF("xhci_set_ep_dequeue(%d, %d, %016"PRIx64")\n", slotid, epid, pdequeue);
+ trace_usb_xhci_ep_set_dequeue(slotid, epid, pdequeue);
dequeue = xhci_mask64(pdequeue);
slot = &xhci->slots[slotid-1];
@@ -1107,24 +1281,13 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid,
return CC_SUCCESS;
}
-static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data,
- unsigned int length, bool in_xfer, bool out_xfer,
- bool report)
+static int xhci_xfer_map(XHCITransfer *xfer)
{
- int i;
- uint32_t edtla = 0;
- unsigned int transferred = 0;
- unsigned int left = length;
- bool reported = 0;
- bool shortpkt = 0;
- XHCIEvent event = {ER_TRANSFER, CC_SUCCESS};
+ int in_xfer = (xfer->packet.pid == USB_TOKEN_IN);
XHCIState *xhci = xfer->xhci;
+ int i;
- DPRINTF("xhci_xfer_data(len=%d, in_xfer=%d, out_xfer=%d, report=%d)\n",
- length, in_xfer, out_xfer, report);
-
- assert(!(in_xfer && out_xfer));
-
+ pci_dma_sglist_init(&xfer->sgl, &xhci->pci_dev, xfer->trb_count);
for (i = 0; i < xfer->trb_count; i++) {
XHCITRB *trb = &xfer->trbs[i];
dma_addr_t addr;
@@ -1134,54 +1297,70 @@ static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data,
case TR_DATA:
if ((!(trb->control & TRB_TR_DIR)) != (!in_xfer)) {
fprintf(stderr, "xhci: data direction mismatch for TR_DATA\n");
- xhci_die(xhci);
- return transferred;
+ goto err;
}
/* fallthrough */
case TR_NORMAL:
case TR_ISOCH:
addr = xhci_mask64(trb->parameter);
chunk = trb->status & 0x1ffff;
+ if (trb->control & TRB_TR_IDT) {
+ if (chunk > 8 || in_xfer) {
+ fprintf(stderr, "xhci: invalid immediate data TRB\n");
+ goto err;
+ }
+ qemu_sglist_add(&xfer->sgl, trb->addr, chunk);
+ } else {
+ qemu_sglist_add(&xfer->sgl, addr, chunk);
+ }
+ break;
+ }
+ }
+
+ usb_packet_map(&xfer->packet, &xfer->sgl);
+ return 0;
+
+err:
+ qemu_sglist_destroy(&xfer->sgl);
+ xhci_die(xhci);
+ return -1;
+}
+
+static void xhci_xfer_unmap(XHCITransfer *xfer)
+{
+ usb_packet_unmap(&xfer->packet, &xfer->sgl);
+ qemu_sglist_destroy(&xfer->sgl);
+}
+
+static void xhci_xfer_report(XHCITransfer *xfer)
+{
+ uint32_t edtla = 0;
+ unsigned int left;
+ bool reported = 0;
+ bool shortpkt = 0;
+ XHCIEvent event = {ER_TRANSFER, CC_SUCCESS};
+ XHCIState *xhci = xfer->xhci;
+ int i;
+
+ left = xfer->packet.result < 0 ? 0 : xfer->packet.result;
+
+ for (i = 0; i < xfer->trb_count; i++) {
+ XHCITRB *trb = &xfer->trbs[i];
+ unsigned int chunk = 0;
+
+ switch (TRB_TYPE(*trb)) {
+ case TR_DATA:
+ case TR_NORMAL:
+ case TR_ISOCH:
+ chunk = trb->status & 0x1ffff;
if (chunk > left) {
chunk = left;
- shortpkt = 1;
- }
- if (in_xfer || out_xfer) {
- if (trb->control & TRB_TR_IDT) {
- uint64_t idata;
- if (chunk > 8 || in_xfer) {
- fprintf(stderr, "xhci: invalid immediate data TRB\n");
- xhci_die(xhci);
- return transferred;
- }
- idata = le64_to_cpu(trb->parameter);
- memcpy(data, &idata, chunk);
- } else {
- DPRINTF("xhci_xfer_data: r/w(%d) %d bytes at "
- DMA_ADDR_FMT "\n", in_xfer, chunk, addr);
- if (in_xfer) {
- pci_dma_write(&xhci->pci_dev, addr, data, chunk);
- } else {
- pci_dma_read(&xhci->pci_dev, addr, data, chunk);
- }
-#ifdef DEBUG_DATA
- unsigned int count = chunk;
- int i;
- if (count > 16) {
- count = 16;
- }
- DPRINTF(" ::");
- for (i = 0; i < count; i++) {
- DPRINTF(" %02x", data[i]);
- }
- DPRINTF("\n");
-#endif
+ if (xfer->status == CC_SUCCESS) {
+ shortpkt = 1;
}
}
left -= chunk;
- data += chunk;
edtla += chunk;
- transferred += chunk;
break;
case TR_STATUS:
reported = 0;
@@ -1189,8 +1368,9 @@ static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data,
break;
}
- if (report && !reported && (trb->control & TRB_TR_IOC ||
- (shortpkt && (trb->control & TRB_TR_ISP)))) {
+ if (!reported && ((trb->control & TRB_TR_IOC) ||
+ (shortpkt && (trb->control & TRB_TR_ISP)) ||
+ (xfer->status != CC_SUCCESS))) {
event.slotid = xfer->slotid;
event.epid = xfer->epid;
event.length = (trb->status & 0x1ffff) - chunk;
@@ -1208,11 +1388,13 @@ static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data,
DPRINTF("xhci_xfer_data: EDTLA=%d\n", event.length);
edtla = 0;
}
- xhci_event(xhci, &event);
+ xhci_event(xhci, &event, TRB_INTR(*trb));
reported = 1;
+ if (xfer->status != CC_SUCCESS) {
+ return;
+ }
}
}
- return transferred;
}
static void xhci_stall_ep(XHCITransfer *xfer)
@@ -1231,169 +1413,30 @@ static void xhci_stall_ep(XHCITransfer *xfer)
static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer,
XHCIEPContext *epctx);
-static void xhci_bg_update(XHCIState *xhci, XHCIEPContext *epctx)
-{
- if (epctx->bg_updating) {
- return;
- }
- DPRINTF("xhci_bg_update(%p, %p)\n", xhci, epctx);
- assert(epctx->has_bg);
- DPRINTF("xhci: fg=%d bg=%d\n", epctx->comp_xfer, epctx->next_bg);
- epctx->bg_updating = 1;
- while (epctx->transfers[epctx->comp_xfer].backgrounded &&
- epctx->bg_transfers[epctx->next_bg].complete) {
- XHCITransfer *fg = &epctx->transfers[epctx->comp_xfer];
- XHCITransfer *bg = &epctx->bg_transfers[epctx->next_bg];
-#if 0
- DPRINTF("xhci: completing fg %d from bg %d.%d (stat: %d)\n",
- epctx->comp_xfer, epctx->next_bg, bg->cur_pkt,
- bg->usbxfer->iso_packet_desc[bg->cur_pkt].status
- );
-#endif
- assert(epctx->type == ET_ISO_IN);
- assert(bg->iso_xfer);
- assert(bg->in_xfer);
- uint8_t *p = bg->data + bg->cur_pkt * bg->pktsize;
-#if 0
- int len = bg->usbxfer->iso_packet_desc[bg->cur_pkt].actual_length;
- fg->status = libusb_to_ccode(bg->usbxfer->iso_packet_desc[bg->cur_pkt].status);
-#else
- int len = 0;
- FIXME();
-#endif
- fg->complete = 1;
- fg->backgrounded = 0;
-
- if (fg->status == CC_STALL_ERROR) {
- xhci_stall_ep(fg);
- }
-
- xhci_xfer_data(fg, p, len, 1, 0, 1);
-
- epctx->comp_xfer++;
- if (epctx->comp_xfer == TD_QUEUE) {
- epctx->comp_xfer = 0;
- }
- DPRINTF("next fg xfer: %d\n", epctx->comp_xfer);
- bg->cur_pkt++;
- if (bg->cur_pkt == bg->pkts) {
- bg->complete = 0;
- if (xhci_submit(xhci, bg, epctx) < 0) {
- fprintf(stderr, "xhci: bg resubmit failed\n");
- }
- epctx->next_bg++;
- if (epctx->next_bg == BG_XFERS) {
- epctx->next_bg = 0;
- }
- DPRINTF("next bg xfer: %d\n", epctx->next_bg);
-
- xhci_kick_ep(xhci, fg->slotid, fg->epid);
- }
- }
- epctx->bg_updating = 0;
-}
-
-#if 0
-static void xhci_xfer_cb(struct libusb_transfer *transfer)
+static int xhci_setup_packet(XHCITransfer *xfer)
{
- XHCIState *xhci;
- XHCITransfer *xfer;
-
- xfer = (XHCITransfer *)transfer->user_data;
- xhci = xfer->xhci;
-
- DPRINTF("xhci_xfer_cb(slot=%d, ep=%d, status=%d)\n", xfer->slotid,
- xfer->epid, transfer->status);
-
- assert(xfer->slotid >= 1 && xfer->slotid <= MAXSLOTS);
- assert(xfer->epid >= 1 && xfer->epid <= 31);
-
- if (xfer->cancelled) {
- DPRINTF("xhci: transfer cancelled, not reporting anything\n");
- xfer->running = 0;
- return;
- }
-
- XHCIEPContext *epctx;
- XHCISlot *slot;
- slot = &xhci->slots[xfer->slotid-1];
- assert(slot->eps[xfer->epid-1]);
- epctx = slot->eps[xfer->epid-1];
-
- if (xfer->bg_xfer) {
- DPRINTF("xhci: background transfer, updating\n");
- xfer->complete = 1;
- xfer->running = 0;
- xhci_bg_update(xhci, epctx);
- return;
- }
-
- if (xfer->iso_xfer) {
- transfer->status = transfer->iso_packet_desc[0].status;
- transfer->actual_length = transfer->iso_packet_desc[0].actual_length;
- }
-
- xfer->status = libusb_to_ccode(transfer->status);
-
- xfer->complete = 1;
- xfer->running = 0;
-
- if (transfer->status == LIBUSB_TRANSFER_STALL)
- xhci_stall_ep(xhci, epctx, xfer);
+ XHCIState *xhci = xfer->xhci;
+ USBDevice *dev;
+ USBEndpoint *ep;
+ int dir;
- DPRINTF("xhci: transfer actual length = %d\n", transfer->actual_length);
+ dir = xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT;
- if (xfer->in_xfer) {
- if (xfer->epid == 1) {
- xhci_xfer_data(xhci, xfer, xfer->data + 8,
- transfer->actual_length, 1, 0, 1);
- } else {
- xhci_xfer_data(xhci, xfer, xfer->data,
- transfer->actual_length, 1, 0, 1);
- }
+ if (xfer->packet.ep) {
+ ep = xfer->packet.ep;
+ dev = ep->dev;
} else {
- xhci_xfer_data(xhci, xfer, NULL, transfer->actual_length, 0, 0, 1);
- }
-
- xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
-}
-
-static int xhci_hle_control(XHCIState *xhci, XHCITransfer *xfer,
- uint8_t bmRequestType, uint8_t bRequest,
- uint16_t wValue, uint16_t wIndex, uint16_t wLength)
-{
- uint16_t type_req = (bmRequestType << 8) | bRequest;
-
- switch (type_req) {
- case 0x0000 | USB_REQ_SET_CONFIGURATION:
- DPRINTF("xhci: HLE switch configuration\n");
- return xhci_switch_config(xhci, xfer->slotid, wValue) == 0;
- case 0x0100 | USB_REQ_SET_INTERFACE:
- DPRINTF("xhci: HLE set interface altsetting\n");
- return xhci_set_iface_alt(xhci, xfer->slotid, wIndex, wValue) == 0;
- case 0x0200 | USB_REQ_CLEAR_FEATURE:
- if (wValue == 0) { // endpoint halt
- DPRINTF("xhci: HLE clear halt\n");
- return xhci_clear_halt(xhci, xfer->slotid, wIndex);
- }
- case 0x0000 | USB_REQ_SET_ADDRESS:
- fprintf(stderr, "xhci: warn: illegal SET_ADDRESS request\n");
- return 0;
- default:
- return 0;
+ if (!xhci->slots[xfer->slotid-1].uport) {
+ fprintf(stderr, "xhci: slot %d has no device\n",
+ xfer->slotid);
+ return -1;
+ }
+ dev = xhci->slots[xfer->slotid-1].uport->dev;
+ ep = usb_ep_get(dev, dir, xfer->epid >> 1);
}
-}
-#endif
-
-static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev)
-{
- USBEndpoint *ep;
- int dir;
- dir = xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT;
- ep = usb_ep_get(dev, dir, xfer->epid >> 1);
usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr);
- usb_packet_addbuf(&xfer->packet, xfer->data, xfer->data_length);
+ xhci_xfer_map(xfer);
DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n",
xfer->packet.pid, dev->addr, ep->nr);
return 0;
@@ -1419,12 +1462,13 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret)
xfer->running_async = 0;
xfer->running_retry = 0;
xfer->complete = 1;
+ xhci_xfer_unmap(xfer);
}
if (ret >= 0) {
- xfer->status = CC_SUCCESS;
- xhci_xfer_data(xfer, xfer->data, ret, xfer->in_xfer, 0, 1);
trace_usb_xhci_xfer_success(xfer, ret);
+ xfer->status = CC_SUCCESS;
+ xhci_xfer_report(xfer);
return 0;
}
@@ -1433,12 +1477,12 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret)
switch (ret) {
case USB_RET_NODEV:
xfer->status = CC_USB_TRANSACTION_ERROR;
- xhci_xfer_data(xfer, xfer->data, 0, xfer->in_xfer, 0, 1);
+ xhci_xfer_report(xfer);
xhci_stall_ep(xfer);
break;
case USB_RET_STALL:
xfer->status = CC_STALL_ERROR;
- xhci_xfer_data(xfer, xfer->data, 0, xfer->in_xfer, 0, 1);
+ xhci_xfer_report(xfer);
xhci_stall_ep(xfer);
break;
default:
@@ -1448,28 +1492,16 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret)
return 0;
}
-static USBDevice *xhci_find_device(XHCIPort *port, uint8_t addr)
-{
- if (!(port->portsc & PORTSC_PED)) {
- return NULL;
- }
- return usb_find_device(&port->port, addr);
-}
-
static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
{
XHCITRB *trb_setup, *trb_status;
uint8_t bmRequestType;
- uint16_t wLength;
- XHCIPort *port;
- USBDevice *dev;
int ret;
trb_setup = &xfer->trbs[0];
trb_status = &xfer->trbs[xfer->trb_count-1];
- trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid,
- trb_setup->parameter >> 48);
+ trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid);
/* at most one Event Data TRB allowed after STATUS */
if (TRB_TYPE(*trb_status) == TR_EVDATA && xfer->trb_count > 2) {
@@ -1498,38 +1530,16 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
}
bmRequestType = trb_setup->parameter;
- wLength = trb_setup->parameter >> 48;
-
- if (xfer->data && xfer->data_alloced < wLength) {
- xfer->data_alloced = 0;
- g_free(xfer->data);
- xfer->data = NULL;
- }
- if (!xfer->data) {
- DPRINTF("xhci: alloc %d bytes data\n", wLength);
- xfer->data = g_malloc(wLength+1);
- xfer->data_alloced = wLength;
- }
- xfer->data_length = wLength;
-
- port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1];
- dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr);
- if (!dev) {
- fprintf(stderr, "xhci: slot %d port %d has no device\n", xfer->slotid,
- xhci->slots[xfer->slotid-1].port);
- return -1;
- }
xfer->in_xfer = bmRequestType & USB_DIR_IN;
xfer->iso_xfer = false;
- xhci_setup_packet(xfer, dev);
- xfer->packet.parameter = trb_setup->parameter;
- if (!xfer->in_xfer) {
- xhci_xfer_data(xfer, xfer->data, wLength, 0, 1, 0);
+ if (xhci_setup_packet(xfer) < 0) {
+ return -1;
}
+ xfer->packet.parameter = trb_setup->parameter;
- ret = usb_handle_packet(dev, &xfer->packet);
+ ret = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
xhci_complete_packet(xfer, ret);
if (!xfer->running_async && !xfer->running_retry) {
@@ -1538,53 +1548,70 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
return 0;
}
-static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx)
+static void xhci_calc_iso_kick(XHCIState *xhci, XHCITransfer *xfer,
+ XHCIEPContext *epctx, uint64_t mfindex)
{
- XHCIPort *port;
- USBDevice *dev;
- int ret;
-
- DPRINTF("xhci_submit(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid);
-
- xfer->in_xfer = epctx->type>>2;
-
- if (xfer->data && xfer->data_alloced < xfer->data_length) {
- xfer->data_alloced = 0;
- g_free(xfer->data);
- xfer->data = NULL;
- }
- if (!xfer->data && xfer->data_length) {
- DPRINTF("xhci: alloc %d bytes data\n", xfer->data_length);
- xfer->data = g_malloc(xfer->data_length);
- xfer->data_alloced = xfer->data_length;
- }
- if (epctx->type == ET_ISO_IN || epctx->type == ET_ISO_OUT) {
- if (!xfer->bg_xfer) {
- xfer->pkts = 1;
+ if (xfer->trbs[0].control & TRB_TR_SIA) {
+ uint64_t asap = ((mfindex + epctx->interval - 1) &
+ ~(epctx->interval-1));
+ if (asap >= epctx->mfindex_last &&
+ asap <= epctx->mfindex_last + epctx->interval * 4) {
+ xfer->mfindex_kick = epctx->mfindex_last + epctx->interval;
+ } else {
+ xfer->mfindex_kick = asap;
}
} else {
- xfer->pkts = 0;
+ xfer->mfindex_kick = (xfer->trbs[0].control >> TRB_TR_FRAMEID_SHIFT)
+ & TRB_TR_FRAMEID_MASK;
+ xfer->mfindex_kick |= mfindex & ~0x3fff;
+ if (xfer->mfindex_kick < mfindex) {
+ xfer->mfindex_kick += 0x4000;
+ }
}
+}
- port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1];
- dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr);
- if (!dev) {
- fprintf(stderr, "xhci: slot %d port %d has no device\n", xfer->slotid,
- xhci->slots[xfer->slotid-1].port);
- return -1;
+static void xhci_check_iso_kick(XHCIState *xhci, XHCITransfer *xfer,
+ XHCIEPContext *epctx, uint64_t mfindex)
+{
+ if (xfer->mfindex_kick > mfindex) {
+ qemu_mod_timer(epctx->kick_timer, qemu_get_clock_ns(vm_clock) +
+ (xfer->mfindex_kick - mfindex) * 125000);
+ xfer->running_retry = 1;
+ } else {
+ epctx->mfindex_last = xfer->mfindex_kick;
+ qemu_del_timer(epctx->kick_timer);
+ xfer->running_retry = 0;
}
+}
+
+
+static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx)
+{
+ uint64_t mfindex;
+ int ret;
+
+ DPRINTF("xhci_submit(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid);
- xhci_setup_packet(xfer, dev);
+ xfer->in_xfer = epctx->type>>2;
switch(epctx->type) {
case ET_INTR_OUT:
case ET_INTR_IN:
case ET_BULK_OUT:
case ET_BULK_IN:
+ xfer->pkts = 0;
+ xfer->iso_xfer = false;
break;
case ET_ISO_OUT:
case ET_ISO_IN:
- FIXME();
+ xfer->pkts = 1;
+ xfer->iso_xfer = true;
+ mfindex = xhci_mfindex_get(xhci);
+ xhci_calc_iso_kick(xhci, xfer, epctx, mfindex);
+ xhci_check_iso_kick(xhci, xfer, epctx, mfindex);
+ if (xfer->running_retry) {
+ return -1;
+ }
break;
default:
fprintf(stderr, "xhci: unknown or unhandled EP "
@@ -1593,10 +1620,10 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx
return -1;
}
- if (!xfer->in_xfer) {
- xhci_xfer_data(xfer, xfer->data, xfer->data_length, 0, 1, 0);
+ if (xhci_setup_packet(xfer) < 0) {
+ return -1;
}
- ret = usb_handle_packet(dev, &xfer->packet);
+ ret = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
xhci_complete_packet(xfer, ret);
if (!xfer->running_async && !xfer->running_retry) {
@@ -1607,50 +1634,14 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx
static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx)
{
- int i;
- unsigned int length = 0;
- XHCITRB *trb;
-
- for (i = 0; i < xfer->trb_count; i++) {
- trb = &xfer->trbs[i];
- if (TRB_TYPE(*trb) == TR_NORMAL || TRB_TYPE(*trb) == TR_ISOCH) {
- length += trb->status & 0x1ffff;
- }
- }
-
- trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, length);
-
- if (!epctx->has_bg) {
- xfer->data_length = length;
- xfer->backgrounded = 0;
- return xhci_submit(xhci, xfer, epctx);
- } else {
- if (!epctx->bg_running) {
- for (i = 0; i < BG_XFERS; i++) {
- XHCITransfer *t = &epctx->bg_transfers[i];
- t->xhci = xhci;
- t->epid = xfer->epid;
- t->slotid = xfer->slotid;
- t->pkts = BG_PKTS;
- t->pktsize = epctx->max_psize;
- t->data_length = t->pkts * t->pktsize;
- t->bg_xfer = 1;
- if (xhci_submit(xhci, t, epctx) < 0) {
- fprintf(stderr, "xhci: bg submit failed\n");
- return -1;
- }
- }
- epctx->bg_running = 1;
- }
- xfer->backgrounded = 1;
- xhci_bg_update(xhci, epctx);
- return 0;
- }
+ trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid);
+ return xhci_submit(xhci, xfer, epctx);
}
static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid)
{
XHCIEPContext *epctx;
+ uint64_t mfindex;
int length;
int i;
@@ -1670,18 +1661,35 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
}
if (epctx->retry) {
- /* retry nak'ed transfer */
XHCITransfer *xfer = epctx->retry;
int result;
trace_usb_xhci_xfer_retry(xfer);
assert(xfer->running_retry);
- xhci_setup_packet(xfer, xfer->packet.ep->dev);
- result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
- if (result == USB_RET_NAK) {
- return;
+ if (xfer->iso_xfer) {
+ /* retry delayed iso transfer */
+ mfindex = xhci_mfindex_get(xhci);
+ xhci_check_iso_kick(xhci, xfer, epctx, mfindex);
+ if (xfer->running_retry) {
+ return;
+ }
+ if (xhci_setup_packet(xfer) < 0) {
+ return;
+ }
+ result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
+ assert(result != USB_RET_NAK);
+ xhci_complete_packet(xfer, result);
+ } else {
+ /* retry nak'ed transfer */
+ if (xhci_setup_packet(xfer) < 0) {
+ return;
+ }
+ result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
+ if (result == USB_RET_NAK) {
+ return;
+ }
+ xhci_complete_packet(xfer, result);
}
- xhci_complete_packet(xfer, result);
assert(!xfer->running_retry);
epctx->retry = NULL;
}
@@ -1695,7 +1703,7 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
while (1) {
XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer];
- if (xfer->running_async || xfer->running_retry || xfer->backgrounded) {
+ if (xfer->running_async || xfer->running_retry) {
break;
}
length = xhci_ring_chain_length(xhci, &epctx->ring);
@@ -1733,7 +1741,9 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
if (xhci_fire_transfer(xhci, xfer, epctx) >= 0) {
epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
} else {
- fprintf(stderr, "xhci: error firing data transfer\n");
+ if (!xfer->iso_xfer) {
+ fprintf(stderr, "xhci: error firing data transfer\n");
+ }
}
}
@@ -1753,7 +1763,7 @@ static TRBCCode xhci_enable_slot(XHCIState *xhci, unsigned int slotid)
trace_usb_xhci_slot_enable(slotid);
assert(slotid >= 1 && slotid <= MAXSLOTS);
xhci->slots[slotid-1].enabled = 1;
- xhci->slots[slotid-1].port = 0;
+ xhci->slots[slotid-1].uport = NULL;
memset(xhci->slots[slotid-1].eps, 0, sizeof(XHCIEPContext*)*31);
return CC_SUCCESS;
@@ -1776,17 +1786,42 @@ static TRBCCode xhci_disable_slot(XHCIState *xhci, unsigned int slotid)
return CC_SUCCESS;
}
+static USBPort *xhci_lookup_uport(XHCIState *xhci, uint32_t *slot_ctx)
+{
+ USBPort *uport;
+ char path[32];
+ int i, pos, port;
+
+ port = (slot_ctx[1]>>16) & 0xFF;
+ port = xhci->ports[port-1].uport->index+1;
+ pos = snprintf(path, sizeof(path), "%d", port);
+ for (i = 0; i < 5; i++) {
+ port = (slot_ctx[0] >> 4*i) & 0x0f;
+ if (!port) {
+ break;
+ }
+ pos += snprintf(path + pos, sizeof(path) - pos, ".%d", port);
+ }
+
+ QTAILQ_FOREACH(uport, &xhci->bus.used, next) {
+ if (strcmp(uport->path, path) == 0) {
+ return uport;
+ }
+ }
+ return NULL;
+}
+
static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
uint64_t pictx, bool bsr)
{
XHCISlot *slot;
+ USBPort *uport;
USBDevice *dev;
dma_addr_t ictx, octx, dcbaap;
uint64_t poctx;
uint32_t ictl_ctx[2];
uint32_t slot_ctx[4];
uint32_t ep0_ctx[5];
- unsigned int port;
int i;
TRBCCode res;
@@ -1818,27 +1853,28 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
DPRINTF("xhci: input ep0 context: %08x %08x %08x %08x %08x\n",
ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]);
- port = (slot_ctx[1]>>16) & 0xFF;
- dev = xhci->ports[port-1].port.dev;
-
- if (port < 1 || port > MAXPORTS) {
- fprintf(stderr, "xhci: bad port %d\n", port);
+ uport = xhci_lookup_uport(xhci, slot_ctx);
+ if (uport == NULL) {
+ fprintf(stderr, "xhci: port not found\n");
return CC_TRB_ERROR;
- } else if (!dev) {
- fprintf(stderr, "xhci: port %d not connected\n", port);
+ }
+
+ dev = uport->dev;
+ if (!dev) {
+ fprintf(stderr, "xhci: port %s not connected\n", uport->path);
return CC_USB_TRANSACTION_ERROR;
}
for (i = 0; i < MAXSLOTS; i++) {
- if (xhci->slots[i].port == port) {
- fprintf(stderr, "xhci: port %d already assigned to slot %d\n",
- port, i+1);
+ if (xhci->slots[i].uport == uport) {
+ fprintf(stderr, "xhci: port %s already assigned to slot %d\n",
+ uport->path, i+1);
return CC_TRB_ERROR;
}
}
slot = &xhci->slots[slotid-1];
- slot->port = port;
+ slot->uport = uport;
slot->ctx = octx;
if (bsr) {
@@ -2070,7 +2106,7 @@ static unsigned int xhci_get_slot(XHCIState *xhci, XHCIEvent *event, XHCITRB *tr
static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx)
{
dma_addr_t ctx;
- uint8_t bw_ctx[MAXPORTS+1];
+ uint8_t bw_ctx[xhci->numports+1];
DPRINTF("xhci_get_port_bandwidth()\n");
@@ -2080,7 +2116,7 @@ static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx)
/* TODO: actually implement real values here */
bw_ctx[0] = 0;
- memset(&bw_ctx[1], 80, MAXPORTS); /* 80% */
+ memset(&bw_ctx[1], 80, xhci->numports); /* 80% */
pci_dma_write(&xhci->pci_dev, ctx, bw_ctx, sizeof(bw_ctx));
return CC_SUCCESS;
@@ -2244,18 +2280,17 @@ static void xhci_process_commands(XHCIState *xhci)
break;
}
event.slotid = slotid;
- xhci_event(xhci, &event);
+ xhci_event(xhci, &event, 0);
}
}
static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach)
{
- int nr = port->port.index + 1;
-
port->portsc = PORTSC_PP;
- if (port->port.dev && port->port.dev->attached && !is_detach) {
+ if (port->uport->dev && port->uport->dev->attached && !is_detach &&
+ (1 << port->uport->dev->speed) & port->speedmask) {
port->portsc |= PORTSC_CCS;
- switch (port->port.dev->speed) {
+ switch (port->uport->dev->speed) {
case USB_SPEED_LOW:
port->portsc |= PORTSC_SPEED_LOW;
break;
@@ -2265,14 +2300,18 @@ static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach)
case USB_SPEED_HIGH:
port->portsc |= PORTSC_SPEED_HIGH;
break;
+ case USB_SPEED_SUPER:
+ port->portsc |= PORTSC_SPEED_SUPER;
+ break;
}
}
if (xhci_running(xhci)) {
port->portsc |= PORTSC_CSC;
- XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, nr << 24};
- xhci_event(xhci, &ev);
- DPRINTF("xhci: port change event for port %d\n", nr);
+ XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS,
+ port->portnr << 24};
+ xhci_event(xhci, &ev, 0);
+ DPRINTF("xhci: port change event for port %d\n", port->portnr);
}
}
@@ -2300,28 +2339,34 @@ static void xhci_reset(DeviceState *dev)
xhci_disable_slot(xhci, i+1);
}
- for (i = 0; i < MAXPORTS; i++) {
+ for (i = 0; i < xhci->numports; i++) {
xhci_update_port(xhci, xhci->ports + i, 0);
}
- xhci->mfindex = 0;
- xhci->iman = 0;
- xhci->imod = 0;
- xhci->erstsz = 0;
- xhci->erstba_low = 0;
- xhci->erstba_high = 0;
- xhci->erdp_low = 0;
- xhci->erdp_high = 0;
+ for (i = 0; i < MAXINTRS; i++) {
+ xhci->intr[i].iman = 0;
+ xhci->intr[i].imod = 0;
+ xhci->intr[i].erstsz = 0;
+ xhci->intr[i].erstba_low = 0;
+ xhci->intr[i].erstba_high = 0;
+ xhci->intr[i].erdp_low = 0;
+ xhci->intr[i].erdp_high = 0;
+ xhci->intr[i].msix_used = 0;
- xhci->er_ep_idx = 0;
- xhci->er_pcs = 1;
- xhci->er_full = 0;
- xhci->ev_buffer_put = 0;
- xhci->ev_buffer_get = 0;
+ xhci->intr[i].er_ep_idx = 0;
+ xhci->intr[i].er_pcs = 1;
+ xhci->intr[i].er_full = 0;
+ xhci->intr[i].ev_buffer_put = 0;
+ xhci->intr[i].ev_buffer_get = 0;
+ }
+
+ xhci->mfindex_start = qemu_get_clock_ns(vm_clock);
+ xhci_mfwrap_update(xhci);
}
-static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg)
+static uint64_t xhci_cap_read(void *ptr, target_phys_addr_t reg, unsigned size)
{
+ XHCIState *xhci = ptr;
uint32_t ret;
switch (reg) {
@@ -2329,7 +2374,8 @@ static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg)
ret = 0x01000000 | LEN_CAP;
break;
case 0x04: /* HCSPARAMS 1 */
- ret = (MAXPORTS<<24) | (MAXINTRS<<8) | MAXSLOTS;
+ ret = ((xhci->numports_2+xhci->numports_3)<<24)
+ | (MAXINTRS<<8) | MAXSLOTS;
break;
case 0x08: /* HCSPARAMS 2 */
ret = 0x0000000f;
@@ -2359,7 +2405,7 @@ static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg)
ret = 0x20425455; /* "USB " */
break;
case 0x28: /* Supported Protocol:08 */
- ret = 0x00000001 | (USB2_PORTS<<8);
+ ret = 0x00000001 | (xhci->numports_2<<8);
break;
case 0x2c: /* Supported Protocol:0c */
ret = 0x00000000; /* reserved */
@@ -2371,13 +2417,13 @@ static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg)
ret = 0x20425455; /* "USB " */
break;
case 0x38: /* Supported Protocol:08 */
- ret = 0x00000000 | (USB2_PORTS+1) | (USB3_PORTS<<8);
+ ret = 0x00000000 | (xhci->numports_2+1) | (xhci->numports_3<<8);
break;
case 0x3c: /* Supported Protocol:0c */
ret = 0x00000000; /* reserved */
break;
default:
- fprintf(stderr, "xhci_cap_read: reg %d unimplemented\n", reg);
+ fprintf(stderr, "xhci_cap_read: reg %d unimplemented\n", (int)reg);
ret = 0;
}
@@ -2385,20 +2431,14 @@ static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg)
return ret;
}
-static uint32_t xhci_port_read(XHCIState *xhci, uint32_t reg)
+static uint64_t xhci_port_read(void *ptr, target_phys_addr_t reg, unsigned size)
{
- uint32_t port = reg >> 4;
+ XHCIPort *port = ptr;
uint32_t ret;
- if (port >= MAXPORTS) {
- fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port);
- ret = 0;
- goto out;
- }
-
- switch (reg & 0xf) {
+ switch (reg) {
case 0x00: /* PORTSC */
- ret = xhci->ports[port].portsc;
+ ret = port->portsc;
break;
case 0x04: /* PORTPMSC */
case 0x08: /* PORTLI */
@@ -2407,30 +2447,25 @@ static uint32_t xhci_port_read(XHCIState *xhci, uint32_t reg)
case 0x0c: /* reserved */
default:
fprintf(stderr, "xhci_port_read (port %d): reg 0x%x unimplemented\n",
- port, reg);
+ port->portnr, (uint32_t)reg);
ret = 0;
}
-out:
- trace_usb_xhci_port_read(port, reg & 0x0f, ret);
+ trace_usb_xhci_port_read(port->portnr, reg, ret);
return ret;
}
-static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+static void xhci_port_write(void *ptr, target_phys_addr_t reg,
+ uint64_t val, unsigned size)
{
- uint32_t port = reg >> 4;
+ XHCIPort *port = ptr;
uint32_t portsc;
- trace_usb_xhci_port_write(port, reg & 0x0f, val);
+ trace_usb_xhci_port_write(port->portnr, reg, val);
- if (port >= MAXPORTS) {
- fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port);
- return;
- }
-
- switch (reg & 0xf) {
+ switch (reg) {
case 0x00: /* PORTSC */
- portsc = xhci->ports[port].portsc;
+ portsc = port->portsc;
/* write-1-to-clear bits*/
portsc &= ~(val & (PORTSC_CSC|PORTSC_PEC|PORTSC_WRC|PORTSC_OCC|
PORTSC_PRC|PORTSC_PLC|PORTSC_CEC));
@@ -2445,27 +2480,24 @@ static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val)
/* write-1-to-start bits */
if (val & PORTSC_PR) {
DPRINTF("xhci: port %d reset\n", port);
- usb_device_reset(xhci->ports[port].port.dev);
+ usb_device_reset(port->uport->dev);
portsc |= PORTSC_PRC | PORTSC_PED;
}
- xhci->ports[port].portsc = portsc;
+ port->portsc = portsc;
break;
case 0x04: /* PORTPMSC */
case 0x08: /* PORTLI */
default:
fprintf(stderr, "xhci_port_write (port %d): reg 0x%x unimplemented\n",
- port, reg);
+ port->portnr, (uint32_t)reg);
}
}
-static uint32_t xhci_oper_read(XHCIState *xhci, uint32_t reg)
+static uint64_t xhci_oper_read(void *ptr, target_phys_addr_t reg, unsigned size)
{
+ XHCIState *xhci = ptr;
uint32_t ret;
- if (reg >= 0x400) {
- return xhci_port_read(xhci, reg - 0x400);
- }
-
switch (reg) {
case 0x00: /* USBCMD */
ret = xhci->usbcmd;
@@ -2495,7 +2527,7 @@ static uint32_t xhci_oper_read(XHCIState *xhci, uint32_t reg)
ret = xhci->config;
break;
default:
- fprintf(stderr, "xhci_oper_read: reg 0x%x unimplemented\n", reg);
+ fprintf(stderr, "xhci_oper_read: reg 0x%x unimplemented\n", (int)reg);
ret = 0;
}
@@ -2503,12 +2535,10 @@ static uint32_t xhci_oper_read(XHCIState *xhci, uint32_t reg)
return ret;
}
-static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+static void xhci_oper_write(void *ptr, target_phys_addr_t reg,
+ uint64_t val, unsigned size)
{
- if (reg >= 0x400) {
- xhci_port_write(xhci, reg - 0x400, val);
- return;
- }
+ XHCIState *xhci = ptr;
trace_usb_xhci_oper_write(reg, val);
@@ -2520,16 +2550,17 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val)
xhci_stop(xhci);
}
xhci->usbcmd = val & 0xc0f;
+ xhci_mfwrap_update(xhci);
if (val & USBCMD_HCRST) {
xhci_reset(&xhci->pci_dev.qdev);
}
- xhci_irq_update(xhci);
+ xhci_intx_update(xhci);
break;
case 0x04: /* USBSTS */
/* these bits are write-1-to-clear */
xhci->usbsts &= ~(val & (USBSTS_HSE|USBSTS_EINT|USBSTS_PCD|USBSTS_SRE));
- xhci_irq_update(xhci);
+ xhci_intx_update(xhci);
break;
case 0x14: /* DNCTRL */
@@ -2543,7 +2574,7 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val)
if (xhci->crcr_low & (CRCR_CA|CRCR_CS) && (xhci->crcr_low & CRCR_CRR)) {
XHCIEvent event = {ER_COMMAND_COMPLETE, CC_COMMAND_RING_STOPPED};
xhci->crcr_low &= ~CRCR_CRR;
- xhci_event(xhci, &event);
+ xhci_event(xhci, &event, 0);
DPRINTF("xhci: command ring stopped (CRCR=%08x)\n", xhci->crcr_low);
} else {
dma_addr_t base = xhci_addr64(xhci->crcr_low & ~0x3f, val);
@@ -2561,101 +2592,127 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val)
xhci->config = val & 0xff;
break;
default:
- fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg);
+ fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", (int)reg);
}
}
-static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg)
+static uint64_t xhci_runtime_read(void *ptr, target_phys_addr_t reg,
+ unsigned size)
{
- uint32_t ret;
+ XHCIState *xhci = ptr;
+ uint32_t ret = 0;
- switch (reg) {
- case 0x00: /* MFINDEX */
- fprintf(stderr, "xhci_runtime_read: MFINDEX not yet implemented\n");
- ret = xhci->mfindex;
- break;
- case 0x20: /* IMAN */
- ret = xhci->iman;
- break;
- case 0x24: /* IMOD */
- ret = xhci->imod;
- break;
- case 0x28: /* ERSTSZ */
- ret = xhci->erstsz;
- break;
- case 0x30: /* ERSTBA low */
- ret = xhci->erstba_low;
- break;
- case 0x34: /* ERSTBA high */
- ret = xhci->erstba_high;
- break;
- case 0x38: /* ERDP low */
- ret = xhci->erdp_low;
- break;
- case 0x3c: /* ERDP high */
- ret = xhci->erdp_high;
- break;
- default:
- fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n", reg);
- ret = 0;
+ if (reg < 0x20) {
+ switch (reg) {
+ case 0x00: /* MFINDEX */
+ ret = xhci_mfindex_get(xhci) & 0x3fff;
+ break;
+ default:
+ fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n",
+ (int)reg);
+ break;
+ }
+ } else {
+ int v = (reg - 0x20) / 0x20;
+ XHCIInterrupter *intr = &xhci->intr[v];
+ switch (reg & 0x1f) {
+ case 0x00: /* IMAN */
+ ret = intr->iman;
+ break;
+ case 0x04: /* IMOD */
+ ret = intr->imod;
+ break;
+ case 0x08: /* ERSTSZ */
+ ret = intr->erstsz;
+ break;
+ case 0x10: /* ERSTBA low */
+ ret = intr->erstba_low;
+ break;
+ case 0x14: /* ERSTBA high */
+ ret = intr->erstba_high;
+ break;
+ case 0x18: /* ERDP low */
+ ret = intr->erdp_low;
+ break;
+ case 0x1c: /* ERDP high */
+ ret = intr->erdp_high;
+ break;
+ }
}
trace_usb_xhci_runtime_read(reg, ret);
return ret;
}
-static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+static void xhci_runtime_write(void *ptr, target_phys_addr_t reg,
+ uint64_t val, unsigned size)
{
- trace_usb_xhci_runtime_read(reg, val);
+ XHCIState *xhci = ptr;
+ int v = (reg - 0x20) / 0x20;
+ XHCIInterrupter *intr = &xhci->intr[v];
+ trace_usb_xhci_runtime_write(reg, val);
- switch (reg) {
- case 0x20: /* IMAN */
+ if (reg < 0x20) {
+ fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", (int)reg);
+ return;
+ }
+
+ switch (reg & 0x1f) {
+ case 0x00: /* IMAN */
if (val & IMAN_IP) {
- xhci->iman &= ~IMAN_IP;
+ intr->iman &= ~IMAN_IP;
}
- xhci->iman &= ~IMAN_IE;
- xhci->iman |= val & IMAN_IE;
- xhci_irq_update(xhci);
+ intr->iman &= ~IMAN_IE;
+ intr->iman |= val & IMAN_IE;
+ if (v == 0) {
+ xhci_intx_update(xhci);
+ }
+ xhci_msix_update(xhci, v);
break;
- case 0x24: /* IMOD */
- xhci->imod = val;
+ case 0x04: /* IMOD */
+ intr->imod = val;
break;
- case 0x28: /* ERSTSZ */
- xhci->erstsz = val & 0xffff;
+ case 0x08: /* ERSTSZ */
+ intr->erstsz = val & 0xffff;
break;
- case 0x30: /* ERSTBA low */
+ case 0x10: /* ERSTBA low */
/* XXX NEC driver bug: it doesn't align this to 64 bytes
- xhci->erstba_low = val & 0xffffffc0; */
- xhci->erstba_low = val & 0xfffffff0;
+ intr->erstba_low = val & 0xffffffc0; */
+ intr->erstba_low = val & 0xfffffff0;
break;
- case 0x34: /* ERSTBA high */
- xhci->erstba_high = val;
- xhci_er_reset(xhci);
+ case 0x14: /* ERSTBA high */
+ intr->erstba_high = val;
+ xhci_er_reset(xhci, v);
break;
- case 0x38: /* ERDP low */
+ case 0x18: /* ERDP low */
if (val & ERDP_EHB) {
- xhci->erdp_low &= ~ERDP_EHB;
+ intr->erdp_low &= ~ERDP_EHB;
}
- xhci->erdp_low = (val & ~ERDP_EHB) | (xhci->erdp_low & ERDP_EHB);
+ intr->erdp_low = (val & ~ERDP_EHB) | (intr->erdp_low & ERDP_EHB);
break;
- case 0x3c: /* ERDP high */
- xhci->erdp_high = val;
- xhci_events_update(xhci);
+ case 0x1c: /* ERDP high */
+ intr->erdp_high = val;
+ xhci_events_update(xhci, v);
break;
default:
- fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg);
+ fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n",
+ (int)reg);
}
}
-static uint32_t xhci_doorbell_read(XHCIState *xhci, uint32_t reg)
+static uint64_t xhci_doorbell_read(void *ptr, target_phys_addr_t reg,
+ unsigned size)
{
/* doorbells always read as 0 */
trace_usb_xhci_doorbell_read(reg, 0);
return 0;
}
-static void xhci_doorbell_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+static void xhci_doorbell_write(void *ptr, target_phys_addr_t reg,
+ uint64_t val, unsigned size)
{
+ XHCIState *xhci = ptr;
+
trace_usb_xhci_doorbell_write(reg, val);
if (!xhci_running(xhci)) {
@@ -2669,69 +2726,57 @@ static void xhci_doorbell_write(XHCIState *xhci, uint32_t reg, uint32_t val)
if (val == 0) {
xhci_process_commands(xhci);
} else {
- fprintf(stderr, "xhci: bad doorbell 0 write: 0x%x\n", val);
+ fprintf(stderr, "xhci: bad doorbell 0 write: 0x%x\n",
+ (uint32_t)val);
}
} else {
if (reg > MAXSLOTS) {
- fprintf(stderr, "xhci: bad doorbell %d\n", reg);
+ fprintf(stderr, "xhci: bad doorbell %d\n", (int)reg);
} else if (val > 31) {
- fprintf(stderr, "xhci: bad doorbell %d write: 0x%x\n", reg, val);
+ fprintf(stderr, "xhci: bad doorbell %d write: 0x%x\n",
+ (int)reg, (uint32_t)val);
} else {
xhci_kick_ep(xhci, reg, val);
}
}
}
-static uint64_t xhci_mem_read(void *ptr, target_phys_addr_t addr,
- unsigned size)
-{
- XHCIState *xhci = ptr;
-
- /* Only aligned reads are allowed on xHCI */
- if (addr & 3) {
- fprintf(stderr, "xhci_mem_read: Mis-aligned read\n");
- return 0;
- }
-
- if (addr < LEN_CAP) {
- return xhci_cap_read(xhci, addr);
- } else if (addr >= OFF_OPER && addr < (OFF_OPER + LEN_OPER)) {
- return xhci_oper_read(xhci, addr - OFF_OPER);
- } else if (addr >= OFF_RUNTIME && addr < (OFF_RUNTIME + LEN_RUNTIME)) {
- return xhci_runtime_read(xhci, addr - OFF_RUNTIME);
- } else if (addr >= OFF_DOORBELL && addr < (OFF_DOORBELL + LEN_DOORBELL)) {
- return xhci_doorbell_read(xhci, addr - OFF_DOORBELL);
- } else {
- fprintf(stderr, "xhci_mem_read: Bad offset %x\n", (int)addr);
- return 0;
- }
-}
+static const MemoryRegionOps xhci_cap_ops = {
+ .read = xhci_cap_read,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .impl.min_access_size = 4,
+ .impl.max_access_size = 4,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
-static void xhci_mem_write(void *ptr, target_phys_addr_t addr,
- uint64_t val, unsigned size)
-{
- XHCIState *xhci = ptr;
+static const MemoryRegionOps xhci_oper_ops = {
+ .read = xhci_oper_read,
+ .write = xhci_oper_write,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
- /* Only aligned writes are allowed on xHCI */
- if (addr & 3) {
- fprintf(stderr, "xhci_mem_write: Mis-aligned write\n");
- return;
- }
+static const MemoryRegionOps xhci_port_ops = {
+ .read = xhci_port_read,
+ .write = xhci_port_write,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
- if (addr >= OFF_OPER && addr < (OFF_OPER + LEN_OPER)) {
- xhci_oper_write(xhci, addr - OFF_OPER, val);
- } else if (addr >= OFF_RUNTIME && addr < (OFF_RUNTIME + LEN_RUNTIME)) {
- xhci_runtime_write(xhci, addr - OFF_RUNTIME, val);
- } else if (addr >= OFF_DOORBELL && addr < (OFF_DOORBELL + LEN_DOORBELL)) {
- xhci_doorbell_write(xhci, addr - OFF_DOORBELL, val);
- } else {
- fprintf(stderr, "xhci_mem_write: Bad offset %x\n", (int)addr);
- }
-}
+static const MemoryRegionOps xhci_runtime_ops = {
+ .read = xhci_runtime_read,
+ .write = xhci_runtime_write,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
-static const MemoryRegionOps xhci_mem_ops = {
- .read = xhci_mem_read,
- .write = xhci_mem_write,
+static const MemoryRegionOps xhci_doorbell_ops = {
+ .read = xhci_doorbell_read,
+ .write = xhci_doorbell_write,
.valid.min_access_size = 4,
.valid.max_access_size = 4,
.endianness = DEVICE_LITTLE_ENDIAN,
@@ -2740,7 +2785,7 @@ static const MemoryRegionOps xhci_mem_ops = {
static void xhci_attach(USBPort *usbport)
{
XHCIState *xhci = usbport->opaque;
- XHCIPort *port = &xhci->ports[usbport->index];
+ XHCIPort *port = xhci_lookup_port(xhci, usbport);
xhci_update_port(xhci, port, 0);
}
@@ -2748,7 +2793,7 @@ static void xhci_attach(USBPort *usbport)
static void xhci_detach(USBPort *usbport)
{
XHCIState *xhci = usbport->opaque;
- XHCIPort *port = &xhci->ports[usbport->index];
+ XHCIPort *port = xhci_lookup_port(xhci, usbport);
xhci_update_port(xhci, port, 1);
}
@@ -2756,9 +2801,9 @@ static void xhci_detach(USBPort *usbport)
static void xhci_wakeup(USBPort *usbport)
{
XHCIState *xhci = usbport->opaque;
- XHCIPort *port = &xhci->ports[usbport->index];
- int nr = port->port.index + 1;
- XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, nr << 24};
+ XHCIPort *port = xhci_lookup_port(xhci, usbport);
+ XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS,
+ port->portnr << 24};
uint32_t pls;
pls = (port->portsc >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK;
@@ -2770,7 +2815,7 @@ static void xhci_wakeup(USBPort *usbport)
return;
}
port->portsc |= PORTSC_PLC;
- xhci_event(xhci, &ev);
+ xhci_event(xhci, &ev, 0);
}
static void xhci_complete(USBPort *port, USBPacket *packet)
@@ -2781,12 +2826,20 @@ static void xhci_complete(USBPort *port, USBPacket *packet)
xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid);
}
-static void xhci_child_detach(USBPort *port, USBDevice *child)
+static void xhci_child_detach(USBPort *uport, USBDevice *child)
{
- FIXME();
+ USBBus *bus = usb_bus_from_device(child);
+ XHCIState *xhci = container_of(bus, XHCIState, bus);
+ int i;
+
+ for (i = 0; i < MAXSLOTS; i++) {
+ if (xhci->slots[i].uport == uport) {
+ xhci->slots[i].uport = NULL;
+ }
+ }
}
-static USBPortOps xhci_port_ops = {
+static USBPortOps xhci_uport_ops = {
.attach = xhci_attach,
.detach = xhci_detach,
.wakeup = xhci_wakeup,
@@ -2840,28 +2893,51 @@ static USBBusOps xhci_bus_ops = {
static void usb_xhci_init(XHCIState *xhci, DeviceState *dev)
{
- int i;
+ XHCIPort *port;
+ int i, usbports, speedmask;
xhci->usbsts = USBSTS_HCH;
+ if (xhci->numports_2 > MAXPORTS_2) {
+ xhci->numports_2 = MAXPORTS_2;
+ }
+ if (xhci->numports_3 > MAXPORTS_3) {
+ xhci->numports_3 = MAXPORTS_3;
+ }
+ usbports = MAX(xhci->numports_2, xhci->numports_3);
+ xhci->numports = xhci->numports_2 + xhci->numports_3;
+
usb_bus_new(&xhci->bus, &xhci_bus_ops, &xhci->pci_dev.qdev);
- for (i = 0; i < MAXPORTS; i++) {
- memset(&xhci->ports[i], 0, sizeof(xhci->ports[i]));
- usb_register_port(&xhci->bus, &xhci->ports[i].port, xhci, i,
- &xhci_port_ops,
- USB_SPEED_MASK_LOW |
- USB_SPEED_MASK_FULL |
- USB_SPEED_MASK_HIGH);
- }
- for (i = 0; i < MAXSLOTS; i++) {
- xhci->slots[i].enabled = 0;
+ for (i = 0; i < usbports; i++) {
+ speedmask = 0;
+ if (i < xhci->numports_2) {
+ port = &xhci->ports[i];
+ port->portnr = i + 1;
+ port->uport = &xhci->uports[i];
+ port->speedmask =
+ USB_SPEED_MASK_LOW |
+ USB_SPEED_MASK_FULL |
+ USB_SPEED_MASK_HIGH;
+ snprintf(port->name, sizeof(port->name), "usb2 port #%d", i+1);
+ speedmask |= port->speedmask;
+ }
+ if (i < xhci->numports_3) {
+ port = &xhci->ports[i + xhci->numports_2];
+ port->portnr = i + 1 + xhci->numports_2;
+ port->uport = &xhci->uports[i];
+ port->speedmask = USB_SPEED_MASK_SUPER;
+ snprintf(port->name, sizeof(port->name), "usb3 port #%d", i+1);
+ speedmask |= port->speedmask;
+ }
+ usb_register_port(&xhci->bus, &xhci->uports[i], xhci, i,
+ &xhci_uport_ops, speedmask);
}
}
static int usb_xhci_initfn(struct PCIDevice *dev)
{
- int ret;
+ int i, ret;
XHCIState *xhci = DO_UPCAST(XHCIState, pci_dev, dev);
@@ -2872,10 +2948,34 @@ static int usb_xhci_initfn(struct PCIDevice *dev)
usb_xhci_init(xhci, &dev->qdev);
+ xhci->mfwrap_timer = qemu_new_timer_ns(vm_clock, xhci_mfwrap_timer, xhci);
+
xhci->irq = xhci->pci_dev.irq[0];
- memory_region_init_io(&xhci->mem, &xhci_mem_ops, xhci,
- "xhci", LEN_REGS);
+ memory_region_init(&xhci->mem, "xhci", LEN_REGS);
+ memory_region_init_io(&xhci->mem_cap, &xhci_cap_ops, xhci,
+ "capabilities", LEN_CAP);
+ memory_region_init_io(&xhci->mem_oper, &xhci_oper_ops, xhci,
+ "operational", 0x400);
+ memory_region_init_io(&xhci->mem_runtime, &xhci_runtime_ops, xhci,
+ "runtime", LEN_RUNTIME);
+ memory_region_init_io(&xhci->mem_doorbell, &xhci_doorbell_ops, xhci,
+ "doorbell", LEN_DOORBELL);
+
+ memory_region_add_subregion(&xhci->mem, 0, &xhci->mem_cap);
+ memory_region_add_subregion(&xhci->mem, OFF_OPER, &xhci->mem_oper);
+ memory_region_add_subregion(&xhci->mem, OFF_RUNTIME, &xhci->mem_runtime);
+ memory_region_add_subregion(&xhci->mem, OFF_DOORBELL, &xhci->mem_doorbell);
+
+ for (i = 0; i < xhci->numports; i++) {
+ XHCIPort *port = &xhci->ports[i];
+ uint32_t offset = OFF_OPER + 0x400 + 0x10 * i;
+ port->xhci = xhci;
+ memory_region_init_io(&port->mem, &xhci_port_ops, port,
+ port->name, 0x10);
+ memory_region_add_subregion(&xhci->mem, offset, &port->mem);
+ }
+
pci_register_bar(&xhci->pci_dev, 0,
PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64,
&xhci->mem);
@@ -2883,32 +2983,29 @@ static int usb_xhci_initfn(struct PCIDevice *dev)
ret = pcie_cap_init(&xhci->pci_dev, 0xa0, PCI_EXP_TYPE_ENDPOINT, 0);
assert(ret >= 0);
- if (xhci->msi) {
- ret = msi_init(&xhci->pci_dev, 0x70, 1, true, false);
- assert(ret >= 0);
+ if (xhci->flags & (1 << XHCI_FLAG_USE_MSI)) {
+ msi_init(&xhci->pci_dev, 0x70, MAXINTRS, true, false);
+ }
+ if (xhci->flags & (1 << XHCI_FLAG_USE_MSI_X)) {
+ msix_init(&xhci->pci_dev, MAXINTRS,
+ &xhci->mem, 0, OFF_MSIX_TABLE,
+ &xhci->mem, 0, OFF_MSIX_PBA,
+ 0x90);
}
return 0;
}
-static void xhci_write_config(PCIDevice *dev, uint32_t addr, uint32_t val,
- int len)
-{
- XHCIState *xhci = DO_UPCAST(XHCIState, pci_dev, dev);
-
- pci_default_write_config(dev, addr, val, len);
- if (xhci->msi) {
- msi_write_config(dev, addr, val, len);
- }
-}
-
static const VMStateDescription vmstate_xhci = {
.name = "xhci",
.unmigratable = 1,
};
static Property xhci_properties[] = {
- DEFINE_PROP_UINT32("msi", XHCIState, msi, 0),
+ DEFINE_PROP_BIT("msi", XHCIState, flags, XHCI_FLAG_USE_MSI, true),
+ DEFINE_PROP_BIT("msix", XHCIState, flags, XHCI_FLAG_USE_MSI_X, true),
+ DEFINE_PROP_UINT32("p2", XHCIState, numports_2, 4),
+ DEFINE_PROP_UINT32("p3", XHCIState, numports_3, 4),
DEFINE_PROP_END_OF_LIST(),
};
@@ -2926,7 +3023,6 @@ static void xhci_class_init(ObjectClass *klass, void *data)
k->class_id = PCI_CLASS_SERIAL_USB;
k->revision = 0x03;
k->is_express = 1;
- k->config_write = xhci_write_config;
}
static TypeInfo xhci_info = {
diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c
index 8df92074d..44f1a64b3 100644
--- a/hw/usb/host-linux.c
+++ b/hw/usb/host-linux.c
@@ -1045,7 +1045,6 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
/* Note request is (bRequestType << 8) | bRequest */
trace_usb_host_req_control(s->bus_num, s->addr, p, request, value, index);
- assert(p->result == 0);
switch (request) {
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
@@ -1074,6 +1073,7 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
}
/* The rest are asynchronous */
+ assert(p && p->result == 0);
if (length > sizeof(dev->data_buf)) {
fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n",
diff --git a/hw/usb/libhw.c b/hw/usb/libhw.c
index c0de30ea8..703e2d213 100644
--- a/hw/usb/libhw.c
+++ b/hw/usb/libhw.c
@@ -28,19 +28,25 @@ int usb_packet_map(USBPacket *p, QEMUSGList *sgl)
{
DMADirection dir = (p->pid == USB_TOKEN_IN) ?
DMA_DIRECTION_FROM_DEVICE : DMA_DIRECTION_TO_DEVICE;
- dma_addr_t len;
void *mem;
int i;
for (i = 0; i < sgl->nsg; i++) {
- len = sgl->sg[i].len;
- mem = dma_memory_map(sgl->dma, sgl->sg[i].base, &len, dir);
- if (!mem) {
- goto err;
- }
- qemu_iovec_add(&p->iov, mem, len);
- if (len != sgl->sg[i].len) {
- goto err;
+ dma_addr_t base = sgl->sg[i].base;
+ dma_addr_t len = sgl->sg[i].len;
+
+ while (len) {
+ dma_addr_t xlen = len;
+ mem = dma_memory_map(sgl->dma, sgl->sg[i].base, &xlen, dir);
+ if (!mem) {
+ goto err;
+ }
+ if (xlen > len) {
+ xlen = len;
+ }
+ qemu_iovec_add(&p->iov, mem, xlen);
+ len -= xlen;
+ base += xlen;
}
}
return 0;
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index 10b4fbb3a..b10241a13 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -1,7 +1,7 @@
/*
* USB redirector usb-guest
*
- * Copyright (c) 2011 Red Hat, Inc.
+ * Copyright (c) 2011-2012 Red Hat, Inc.
*
* Red Hat Authors:
* Hans de Goede <hdegoede@redhat.com>
@@ -43,7 +43,6 @@
#define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f))
#define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f))
-typedef struct AsyncURB AsyncURB;
typedef struct USBRedirDevice USBRedirDevice;
/* Struct to hold buffered packets (iso or int input packets) */
@@ -58,6 +57,7 @@ struct endp_data {
uint8_t type;
uint8_t interval;
uint8_t interface; /* bInterfaceNumber this ep belongs to */
+ uint16_t max_packet_size; /* In bytes, not wMaxPacketSize format !! */
uint8_t iso_started;
uint8_t iso_error; /* For reporting iso errors to the HC */
uint8_t interrupt_started;
@@ -65,8 +65,20 @@ struct endp_data {
uint8_t bufpq_prefilled;
uint8_t bufpq_dropping_packets;
QTAILQ_HEAD(, buf_packet) bufpq;
- int bufpq_size;
- int bufpq_target_size;
+ int32_t bufpq_size;
+ int32_t bufpq_target_size;
+};
+
+struct PacketIdQueueEntry {
+ uint64_t id;
+ QTAILQ_ENTRY(PacketIdQueueEntry)next;
+};
+
+struct PacketIdQueue {
+ USBRedirDevice *dev;
+ const char *name;
+ QTAILQ_HEAD(, PacketIdQueueEntry) head;
+ int size;
};
struct USBRedirDevice {
@@ -79,15 +91,15 @@ struct USBRedirDevice {
/* Data passed from chardev the fd_read cb to the usbredirparser read cb */
const uint8_t *read_buf;
int read_buf_size;
- /* For async handling of open/close */
- QEMUBH *open_close_bh;
+ /* For async handling of close */
+ QEMUBH *chardev_close_bh;
/* To delay the usb attach in case of quick chardev close + open */
QEMUTimer *attach_timer;
int64_t next_attach_time;
struct usbredirparser *parser;
struct endp_data endpoint[MAX_ENDPOINTS];
- uint32_t packet_id;
- QTAILQ_HEAD(, AsyncURB) asyncq;
+ struct PacketIdQueue cancelled;
+ struct PacketIdQueue already_in_flight;
/* Data for device filtering */
struct usb_redir_device_connect_header device_info;
struct usb_redir_interface_info_header interface_info;
@@ -95,19 +107,6 @@ struct USBRedirDevice {
int filter_rules_count;
};
-struct AsyncURB {
- USBRedirDevice *dev;
- USBPacket *packet;
- uint32_t packet_id;
- int get;
- union {
- struct usb_redir_control_packet_header control_packet;
- struct usb_redir_bulk_packet_header bulk_packet;
- struct usb_redir_interrupt_packet_header interrupt_packet;
- };
- QTAILQ_ENTRY(AsyncURB)next;
-};
-
static void usbredir_hello(void *priv, struct usb_redir_hello_header *h);
static void usbredir_device_connect(void *priv,
struct usb_redir_device_connect_header *device_connect);
@@ -116,33 +115,35 @@ static void usbredir_interface_info(void *priv,
struct usb_redir_interface_info_header *interface_info);
static void usbredir_ep_info(void *priv,
struct usb_redir_ep_info_header *ep_info);
-static void usbredir_configuration_status(void *priv, uint32_t id,
+static void usbredir_configuration_status(void *priv, uint64_t id,
struct usb_redir_configuration_status_header *configuration_status);
-static void usbredir_alt_setting_status(void *priv, uint32_t id,
+static void usbredir_alt_setting_status(void *priv, uint64_t id,
struct usb_redir_alt_setting_status_header *alt_setting_status);
-static void usbredir_iso_stream_status(void *priv, uint32_t id,
+static void usbredir_iso_stream_status(void *priv, uint64_t id,
struct usb_redir_iso_stream_status_header *iso_stream_status);
-static void usbredir_interrupt_receiving_status(void *priv, uint32_t id,
+static void usbredir_interrupt_receiving_status(void *priv, uint64_t id,
struct usb_redir_interrupt_receiving_status_header
*interrupt_receiving_status);
-static void usbredir_bulk_streams_status(void *priv, uint32_t id,
+static void usbredir_bulk_streams_status(void *priv, uint64_t id,
struct usb_redir_bulk_streams_status_header *bulk_streams_status);
-static void usbredir_control_packet(void *priv, uint32_t id,
+static void usbredir_control_packet(void *priv, uint64_t id,
struct usb_redir_control_packet_header *control_packet,
uint8_t *data, int data_len);
-static void usbredir_bulk_packet(void *priv, uint32_t id,
+static void usbredir_bulk_packet(void *priv, uint64_t id,
struct usb_redir_bulk_packet_header *bulk_packet,
uint8_t *data, int data_len);
-static void usbredir_iso_packet(void *priv, uint32_t id,
+static void usbredir_iso_packet(void *priv, uint64_t id,
struct usb_redir_iso_packet_header *iso_packet,
uint8_t *data, int data_len);
-static void usbredir_interrupt_packet(void *priv, uint32_t id,
+static void usbredir_interrupt_packet(void *priv, uint64_t id,
struct usb_redir_interrupt_packet_header *interrupt_header,
uint8_t *data, int data_len);
static int usbredir_handle_status(USBRedirDevice *dev,
int status, int actual_len);
+#define VERSION "qemu usb-redir guest " QEMU_VERSION
+
/*
* Logging stuff
*/
@@ -241,62 +242,133 @@ static int usbredir_write(void *priv, uint8_t *data, int count)
return 0;
}
+ /* Don't send new data to the chardev until our state is fully synced */
+ if (!runstate_check(RUN_STATE_RUNNING)) {
+ return 0;
+ }
+
return qemu_chr_fe_write(dev->cs, data, count);
}
/*
- * Async and buffered packets helpers
+ * Cancelled and buffered packets helpers
*/
-static AsyncURB *async_alloc(USBRedirDevice *dev, USBPacket *p)
+static void packet_id_queue_init(struct PacketIdQueue *q,
+ USBRedirDevice *dev, const char *name)
+{
+ q->dev = dev;
+ q->name = name;
+ QTAILQ_INIT(&q->head);
+ q->size = 0;
+}
+
+static void packet_id_queue_add(struct PacketIdQueue *q, uint64_t id)
{
- AsyncURB *aurb = (AsyncURB *) g_malloc0(sizeof(AsyncURB));
- aurb->dev = dev;
- aurb->packet = p;
- aurb->packet_id = dev->packet_id;
- QTAILQ_INSERT_TAIL(&dev->asyncq, aurb, next);
- dev->packet_id++;
+ USBRedirDevice *dev = q->dev;
+ struct PacketIdQueueEntry *e;
- return aurb;
+ DPRINTF("adding packet id %"PRIu64" to %s queue\n", id, q->name);
+
+ e = g_malloc0(sizeof(struct PacketIdQueueEntry));
+ e->id = id;
+ QTAILQ_INSERT_TAIL(&q->head, e, next);
+ q->size++;
}
-static void async_free(USBRedirDevice *dev, AsyncURB *aurb)
+static int packet_id_queue_remove(struct PacketIdQueue *q, uint64_t id)
{
- QTAILQ_REMOVE(&dev->asyncq, aurb, next);
- g_free(aurb);
+ USBRedirDevice *dev = q->dev;
+ struct PacketIdQueueEntry *e;
+
+ QTAILQ_FOREACH(e, &q->head, next) {
+ if (e->id == id) {
+ DPRINTF("removing packet id %"PRIu64" from %s queue\n",
+ id, q->name);
+ QTAILQ_REMOVE(&q->head, e, next);
+ q->size--;
+ g_free(e);
+ return 1;
+ }
+ }
+ return 0;
}
-static AsyncURB *async_find(USBRedirDevice *dev, uint32_t packet_id)
+static void packet_id_queue_empty(struct PacketIdQueue *q)
{
- AsyncURB *aurb;
+ USBRedirDevice *dev = q->dev;
+ struct PacketIdQueueEntry *e, *next_e;
- QTAILQ_FOREACH(aurb, &dev->asyncq, next) {
- if (aurb->packet_id == packet_id) {
- return aurb;
- }
+ DPRINTF("removing %d packet-ids from %s queue\n", q->size, q->name);
+
+ QTAILQ_FOREACH_SAFE(e, &q->head, next, next_e) {
+ QTAILQ_REMOVE(&q->head, e, next);
+ g_free(e);
}
- DPRINTF("could not find async urb for packet_id %u\n", packet_id);
- return NULL;
+ q->size = 0;
}
static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p)
{
USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
- AsyncURB *aurb;
- QTAILQ_FOREACH(aurb, &dev->asyncq, next) {
- if (p != aurb->packet) {
- continue;
- }
+ packet_id_queue_add(&dev->cancelled, p->id);
+ usbredirparser_send_cancel_data_packet(dev->parser, p->id);
+ usbredirparser_do_write(dev->parser);
+}
- DPRINTF("async cancel id %u\n", aurb->packet_id);
- usbredirparser_send_cancel_data_packet(dev->parser, aurb->packet_id);
- usbredirparser_do_write(dev->parser);
+static int usbredir_is_cancelled(USBRedirDevice *dev, uint64_t id)
+{
+ if (!dev->dev.attached) {
+ return 1; /* Treat everything as cancelled after a disconnect */
+ }
+ return packet_id_queue_remove(&dev->cancelled, id);
+}
- /* Mark it as dead */
- aurb->packet = NULL;
- break;
+static void usbredir_fill_already_in_flight_from_ep(USBRedirDevice *dev,
+ struct USBEndpoint *ep)
+{
+ static USBPacket *p;
+
+ QTAILQ_FOREACH(p, &ep->queue, queue) {
+ packet_id_queue_add(&dev->already_in_flight, p->id);
+ }
+}
+
+static void usbredir_fill_already_in_flight(USBRedirDevice *dev)
+{
+ int ep;
+ struct USBDevice *udev = &dev->dev;
+
+ usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_ctl);
+
+ for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
+ usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_in[ep]);
+ usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_out[ep]);
+ }
+}
+
+static int usbredir_already_in_flight(USBRedirDevice *dev, uint64_t id)
+{
+ return packet_id_queue_remove(&dev->already_in_flight, id);
+}
+
+static USBPacket *usbredir_find_packet_by_id(USBRedirDevice *dev,
+ uint8_t ep, uint64_t id)
+{
+ USBPacket *p;
+
+ if (usbredir_is_cancelled(dev, id)) {
+ return NULL;
+ }
+
+ p = usb_ep_find_packet_by_id(&dev->dev,
+ (ep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT,
+ ep & 0x0f, id);
+ if (p == NULL) {
+ ERROR("could not find packet with id %"PRIu64"\n", id);
}
+ return p;
}
static void bufp_alloc(USBRedirDevice *dev,
@@ -492,25 +564,26 @@ static void usbredir_stop_iso_stream(USBRedirDevice *dev, uint8_t ep)
static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
uint8_t ep)
{
- AsyncURB *aurb = async_alloc(dev, p);
struct usb_redir_bulk_packet_header bulk_packet;
- DPRINTF("bulk-out ep %02X len %zd id %u\n", ep,
- p->iov.size, aurb->packet_id);
+ DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, p->iov.size, p->id);
+
+ if (usbredir_already_in_flight(dev, p->id)) {
+ return USB_RET_ASYNC;
+ }
bulk_packet.endpoint = ep;
bulk_packet.length = p->iov.size;
bulk_packet.stream_id = 0;
- aurb->bulk_packet = bulk_packet;
if (ep & USB_DIR_IN) {
- usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id,
+ usbredirparser_send_bulk_packet(dev->parser, p->id,
&bulk_packet, NULL, 0);
} else {
uint8_t buf[p->iov.size];
usb_packet_copy(p, buf, p->iov.size);
usbredir_log_data(dev, "bulk data out:", buf, p->iov.size);
- usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id,
+ usbredirparser_send_bulk_packet(dev->parser, p->id,
&bulk_packet, buf, p->iov.size);
}
usbredirparser_do_write(dev->parser);
@@ -573,20 +646,22 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
return len;
} else {
/* Output interrupt endpoint, normal async operation */
- AsyncURB *aurb = async_alloc(dev, p);
struct usb_redir_interrupt_packet_header interrupt_packet;
uint8_t buf[p->iov.size];
- DPRINTF("interrupt-out ep %02X len %zd id %u\n", ep, p->iov.size,
- aurb->packet_id);
+ DPRINTF("interrupt-out ep %02X len %zd id %"PRIu64"\n", ep,
+ p->iov.size, p->id);
+
+ if (usbredir_already_in_flight(dev, p->id)) {
+ return USB_RET_ASYNC;
+ }
interrupt_packet.endpoint = ep;
interrupt_packet.length = p->iov.size;
- aurb->interrupt_packet = interrupt_packet;
usb_packet_copy(p, buf, p->iov.size);
usbredir_log_data(dev, "interrupt data out:", buf, p->iov.size);
- usbredirparser_send_interrupt_packet(dev->parser, aurb->packet_id,
+ usbredirparser_send_interrupt_packet(dev->parser, p->id,
&interrupt_packet, buf, p->iov.size);
usbredirparser_do_write(dev->parser);
return USB_RET_ASYNC;
@@ -640,10 +715,9 @@ static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p,
int config)
{
struct usb_redir_set_configuration_header set_config;
- AsyncURB *aurb = async_alloc(dev, p);
int i;
- DPRINTF("set config %d id %u\n", config, aurb->packet_id);
+ DPRINTF("set config %d id %"PRIu64"\n", config, p->id);
for (i = 0; i < MAX_ENDPOINTS; i++) {
switch (dev->endpoint[i].type) {
@@ -660,20 +734,16 @@ static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p,
}
set_config.configuration = config;
- usbredirparser_send_set_configuration(dev->parser, aurb->packet_id,
- &set_config);
+ usbredirparser_send_set_configuration(dev->parser, p->id, &set_config);
usbredirparser_do_write(dev->parser);
return USB_RET_ASYNC;
}
static int usbredir_get_config(USBRedirDevice *dev, USBPacket *p)
{
- AsyncURB *aurb = async_alloc(dev, p);
-
- DPRINTF("get config id %u\n", aurb->packet_id);
+ DPRINTF("get config id %"PRIu64"\n", p->id);
- aurb->get = 1;
- usbredirparser_send_get_configuration(dev->parser, aurb->packet_id);
+ usbredirparser_send_get_configuration(dev->parser, p->id);
usbredirparser_do_write(dev->parser);
return USB_RET_ASYNC;
}
@@ -682,11 +752,9 @@ static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p,
int interface, int alt)
{
struct usb_redir_set_alt_setting_header set_alt;
- AsyncURB *aurb = async_alloc(dev, p);
int i;
- DPRINTF("set interface %d alt %d id %u\n", interface, alt,
- aurb->packet_id);
+ DPRINTF("set interface %d alt %d id %"PRIu64"\n", interface, alt, p->id);
for (i = 0; i < MAX_ENDPOINTS; i++) {
if (dev->endpoint[i].interface == interface) {
@@ -706,8 +774,7 @@ static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p,
set_alt.interface = interface;
set_alt.alt = alt;
- usbredirparser_send_set_alt_setting(dev->parser, aurb->packet_id,
- &set_alt);
+ usbredirparser_send_set_alt_setting(dev->parser, p->id, &set_alt);
usbredirparser_do_write(dev->parser);
return USB_RET_ASYNC;
}
@@ -716,14 +783,11 @@ static int usbredir_get_interface(USBRedirDevice *dev, USBPacket *p,
int interface)
{
struct usb_redir_get_alt_setting_header get_alt;
- AsyncURB *aurb = async_alloc(dev, p);
- DPRINTF("get interface %d id %u\n", interface, aurb->packet_id);
+ DPRINTF("get interface %d id %"PRIu64"\n", interface, p->id);
get_alt.interface = interface;
- aurb->get = 1;
- usbredirparser_send_get_alt_setting(dev->parser, aurb->packet_id,
- &get_alt);
+ usbredirparser_send_get_alt_setting(dev->parser, p->id, &get_alt);
usbredirparser_do_write(dev->parser);
return USB_RET_ASYNC;
}
@@ -733,7 +797,10 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
{
USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
struct usb_redir_control_packet_header control_packet;
- AsyncURB *aurb;
+
+ if (usbredir_already_in_flight(dev, p->id)) {
+ return USB_RET_ASYNC;
+ }
/* Special cases for certain standard device requests */
switch (request) {
@@ -751,13 +818,10 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
return usbredir_get_interface(dev, p, index);
}
- /* "Normal" ctrl requests */
- aurb = async_alloc(dev, p);
-
- /* Note request is (bRequestType << 8) | bRequest */
- DPRINTF("ctrl-out type 0x%x req 0x%x val 0x%x index %d len %d id %u\n",
- request >> 8, request & 0xff, value, index, length,
- aurb->packet_id);
+ /* Normal ctrl requests, note request is (bRequestType << 8) | bRequest */
+ DPRINTF(
+ "ctrl-out type 0x%x req 0x%x val 0x%x index %d len %d id %"PRIu64"\n",
+ request >> 8, request & 0xff, value, index, length, p->id);
control_packet.request = request & 0xFF;
control_packet.requesttype = request >> 8;
@@ -765,14 +829,13 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
control_packet.value = value;
control_packet.index = index;
control_packet.length = length;
- aurb->control_packet = control_packet;
if (control_packet.requesttype & USB_DIR_IN) {
- usbredirparser_send_control_packet(dev->parser, aurb->packet_id,
+ usbredirparser_send_control_packet(dev->parser, p->id,
&control_packet, NULL, 0);
} else {
usbredir_log_data(dev, "ctrl data out:", data, length);
- usbredirparser_send_control_packet(dev->parser, aurb->packet_id,
+ usbredirparser_send_control_packet(dev->parser, p->id,
&control_packet, data, length);
}
usbredirparser_do_write(dev->parser);
@@ -784,53 +847,73 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
* from within the USBDevice data / control packet callbacks and doing a
* usb_detach from within these callbacks is not a good idea.
*
- * So we use a bh handler to take care of close events. We also handle
- * open events from this callback to make sure that a close directly followed
- * by an open gets handled in the right order.
+ * So we use a bh handler to take care of close events.
*/
-static void usbredir_open_close_bh(void *opaque)
+static void usbredir_chardev_close_bh(void *opaque)
{
USBRedirDevice *dev = opaque;
- uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, };
- char version[32];
-
- strcpy(version, "qemu usb-redir guest ");
- pstrcat(version, sizeof(version), qemu_get_version());
usbredir_device_disconnect(dev);
if (dev->parser) {
+ DPRINTF("destroying usbredirparser\n");
usbredirparser_destroy(dev->parser);
dev->parser = NULL;
}
+}
- if (dev->cs->opened) {
- dev->parser = qemu_oom_check(usbredirparser_create());
- dev->parser->priv = dev;
- dev->parser->log_func = usbredir_log;
- dev->parser->read_func = usbredir_read;
- dev->parser->write_func = usbredir_write;
- dev->parser->hello_func = usbredir_hello;
- dev->parser->device_connect_func = usbredir_device_connect;
- dev->parser->device_disconnect_func = usbredir_device_disconnect;
- dev->parser->interface_info_func = usbredir_interface_info;
- dev->parser->ep_info_func = usbredir_ep_info;
- dev->parser->configuration_status_func = usbredir_configuration_status;
- dev->parser->alt_setting_status_func = usbredir_alt_setting_status;
- dev->parser->iso_stream_status_func = usbredir_iso_stream_status;
- dev->parser->interrupt_receiving_status_func =
- usbredir_interrupt_receiving_status;
- dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status;
- dev->parser->control_packet_func = usbredir_control_packet;
- dev->parser->bulk_packet_func = usbredir_bulk_packet;
- dev->parser->iso_packet_func = usbredir_iso_packet;
- dev->parser->interrupt_packet_func = usbredir_interrupt_packet;
- dev->read_buf = NULL;
- dev->read_buf_size = 0;
+static void usbredir_chardev_open(USBRedirDevice *dev)
+{
+ uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, };
+ int flags = 0;
+
+ /* Make sure any pending closes are handled (no-op if none pending) */
+ usbredir_chardev_close_bh(dev);
+ qemu_bh_cancel(dev->chardev_close_bh);
+
+ DPRINTF("creating usbredirparser\n");
+
+ dev->parser = qemu_oom_check(usbredirparser_create());
+ dev->parser->priv = dev;
+ dev->parser->log_func = usbredir_log;
+ dev->parser->read_func = usbredir_read;
+ dev->parser->write_func = usbredir_write;
+ dev->parser->hello_func = usbredir_hello;
+ dev->parser->device_connect_func = usbredir_device_connect;
+ dev->parser->device_disconnect_func = usbredir_device_disconnect;
+ dev->parser->interface_info_func = usbredir_interface_info;
+ dev->parser->ep_info_func = usbredir_ep_info;
+ dev->parser->configuration_status_func = usbredir_configuration_status;
+ dev->parser->alt_setting_status_func = usbredir_alt_setting_status;
+ dev->parser->iso_stream_status_func = usbredir_iso_stream_status;
+ dev->parser->interrupt_receiving_status_func =
+ usbredir_interrupt_receiving_status;
+ dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status;
+ dev->parser->control_packet_func = usbredir_control_packet;
+ dev->parser->bulk_packet_func = usbredir_bulk_packet;
+ dev->parser->iso_packet_func = usbredir_iso_packet;
+ dev->parser->interrupt_packet_func = usbredir_interrupt_packet;
+ dev->read_buf = NULL;
+ dev->read_buf_size = 0;
+
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version);
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_ep_info_max_packet_size);
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids);
+
+ if (runstate_check(RUN_STATE_INMIGRATE)) {
+ flags |= usbredirparser_fl_no_hello;
+ }
+ usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE,
+ flags);
+ usbredirparser_do_write(dev->parser);
+}
- usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version);
- usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
- usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, 0);
+static void usbredir_reject_device(USBRedirDevice *dev)
+{
+ usbredir_device_disconnect(dev);
+ if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) {
+ usbredirparser_send_filter_reject(dev->parser);
usbredirparser_do_write(dev->parser);
}
}
@@ -839,12 +922,19 @@ static void usbredir_do_attach(void *opaque)
{
USBRedirDevice *dev = opaque;
+ /* In order to work properly with XHCI controllers we need these caps */
+ if ((dev->dev.port->speedmask & USB_SPEED_MASK_SUPER) && !(
+ usbredirparser_peer_has_cap(dev->parser,
+ usb_redir_cap_ep_info_max_packet_size) &&
+ usbredirparser_peer_has_cap(dev->parser,
+ usb_redir_cap_64bits_ids))) {
+ ERROR("usb-redir-host lacks capabilities needed for use with XHCI\n");
+ usbredir_reject_device(dev);
+ return;
+ }
+
if (usb_device_attach(&dev->dev) != 0) {
- usbredir_device_disconnect(dev);
- if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) {
- usbredirparser_send_filter_reject(dev->parser);
- usbredirparser_do_write(dev->parser);
- }
+ usbredir_reject_device(dev);
}
}
@@ -856,13 +946,18 @@ static int usbredir_chardev_can_read(void *opaque)
{
USBRedirDevice *dev = opaque;
- if (dev->parser) {
- /* usbredir_parser_do_read will consume *all* data we give it */
- return 1024 * 1024;
- } else {
- /* usbredir_open_close_bh hasn't handled the open event yet */
+ if (!dev->parser) {
+ WARNING("chardev_can_read called on non open chardev!\n");
+ return 0;
+ }
+
+ /* Don't read new data from the chardev until our state is fully synced */
+ if (!runstate_check(RUN_STATE_RUNNING)) {
return 0;
}
+
+ /* usbredir_parser_do_read will consume *all* data we give it */
+ return 1024 * 1024;
}
static void usbredir_chardev_read(void *opaque, const uint8_t *buf, int size)
@@ -886,8 +981,12 @@ static void usbredir_chardev_event(void *opaque, int event)
switch (event) {
case CHR_EVENT_OPENED:
+ DPRINTF("chardev open\n");
+ usbredir_chardev_open(dev);
+ break;
case CHR_EVENT_CLOSED:
- qemu_bh_schedule(dev->open_close_bh);
+ DPRINTF("chardev close\n");
+ qemu_bh_schedule(dev->chardev_close_bh);
break;
}
}
@@ -896,6 +995,15 @@ static void usbredir_chardev_event(void *opaque, int event)
* init + destroy
*/
+static void usbredir_vm_state_change(void *priv, int running, RunState state)
+{
+ USBRedirDevice *dev = priv;
+
+ if (state == RUN_STATE_RUNNING && dev->parser != NULL) {
+ usbredirparser_do_write(dev->parser); /* Flush any pending writes */
+ }
+}
+
static int usbredir_initfn(USBDevice *udev)
{
USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
@@ -917,10 +1025,11 @@ static int usbredir_initfn(USBDevice *udev)
}
}
- dev->open_close_bh = qemu_bh_new(usbredir_open_close_bh, dev);
+ dev->chardev_close_bh = qemu_bh_new(usbredir_chardev_close_bh, dev);
dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev);
- QTAILQ_INIT(&dev->asyncq);
+ packet_id_queue_init(&dev->cancelled, dev, "cancelled");
+ packet_id_queue_init(&dev->already_in_flight, dev, "already-in-flight");
for (i = 0; i < MAX_ENDPOINTS; i++) {
QTAILQ_INIT(&dev->endpoint[i].bufpq);
}
@@ -933,18 +1042,17 @@ static int usbredir_initfn(USBDevice *udev)
qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read,
usbredir_chardev_read, usbredir_chardev_event, dev);
+ qemu_add_vm_change_state_handler(usbredir_vm_state_change, dev);
add_boot_device_path(dev->bootindex, &udev->qdev, NULL);
return 0;
}
static void usbredir_cleanup_device_queues(USBRedirDevice *dev)
{
- AsyncURB *aurb, *next_aurb;
int i;
- QTAILQ_FOREACH_SAFE(aurb, &dev->asyncq, next, next_aurb) {
- async_free(dev, aurb);
- }
+ packet_id_queue_empty(&dev->cancelled);
+ packet_id_queue_empty(&dev->already_in_flight);
for (i = 0; i < MAX_ENDPOINTS; i++) {
usbredir_free_bufpq(dev, I2EP(i));
}
@@ -957,7 +1065,7 @@ static void usbredir_handle_destroy(USBDevice *udev)
qemu_chr_fe_close(dev->cs);
qemu_chr_delete(dev->cs);
/* Note must be done after qemu_chr_close, as that causes a close event */
- qemu_bh_delete(dev->open_close_bh);
+ qemu_bh_delete(dev->chardev_close_bh);
qemu_del_timer(dev->attach_timer);
qemu_free_timer(dev->attach_timer);
@@ -1007,11 +1115,7 @@ static int usbredir_check_filter(USBRedirDevice *dev)
return 0;
error:
- usbredir_device_disconnect(dev);
- if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) {
- usbredirparser_send_filter_reject(dev->parser);
- usbredirparser_do_write(dev->parser);
- }
+ usbredir_reject_device(dev);
return -1;
}
@@ -1028,11 +1132,14 @@ static int usbredir_handle_status(USBRedirDevice *dev,
case usb_redir_stall:
return USB_RET_STALL;
case usb_redir_cancelled:
- WARNING("returning cancelled packet to HC?\n");
- return USB_RET_NAK;
+ /*
+ * When the usbredir-host unredirects a device, it will report a status
+ * of cancelled for all pending packets, followed by a disconnect msg.
+ */
+ return USB_RET_IOERROR;
case usb_redir_inval:
WARNING("got invalid param error from usb-host?\n");
- return USB_RET_NAK;
+ return USB_RET_IOERROR;
case usb_redir_babble:
return USB_RET_BABBLE;
case usb_redir_ioerror:
@@ -1124,6 +1231,7 @@ static void usbredir_device_disconnect(void *priv)
qemu_del_timer(dev->attach_timer);
if (dev->dev.attached) {
+ DPRINTF("detaching device\n");
usb_device_detach(&dev->dev);
/*
* Delay next usb device attach to give the guest a chance to see
@@ -1199,70 +1307,68 @@ static void usbredir_ep_info(void *priv,
i & 0x0f);
usb_ep->type = dev->endpoint[i].type;
usb_ep->ifnum = dev->endpoint[i].interface;
+ if (usbredirparser_peer_has_cap(dev->parser,
+ usb_redir_cap_ep_info_max_packet_size)) {
+ dev->endpoint[i].max_packet_size =
+ usb_ep->max_packet_size = ep_info->max_packet_size[i];
+ }
+ if (ep_info->type[i] == usb_redir_type_bulk) {
+ usb_ep->pipeline = true;
+ }
}
}
-static void usbredir_configuration_status(void *priv, uint32_t id,
+static void usbredir_configuration_status(void *priv, uint64_t id,
struct usb_redir_configuration_status_header *config_status)
{
USBRedirDevice *dev = priv;
- AsyncURB *aurb;
+ USBPacket *p;
int len = 0;
- DPRINTF("set config status %d config %d id %u\n", config_status->status,
- config_status->configuration, id);
+ DPRINTF("set config status %d config %d id %"PRIu64"\n",
+ config_status->status, config_status->configuration, id);
- aurb = async_find(dev, id);
- if (!aurb) {
- return;
- }
- if (aurb->packet) {
- if (aurb->get) {
+ p = usbredir_find_packet_by_id(dev, 0, id);
+ if (p) {
+ if (dev->dev.setup_buf[0] & USB_DIR_IN) {
dev->dev.data_buf[0] = config_status->configuration;
len = 1;
}
- aurb->packet->result =
- usbredir_handle_status(dev, config_status->status, len);
- usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
+ p->result = usbredir_handle_status(dev, config_status->status, len);
+ usb_generic_async_ctrl_complete(&dev->dev, p);
}
- async_free(dev, aurb);
}
-static void usbredir_alt_setting_status(void *priv, uint32_t id,
+static void usbredir_alt_setting_status(void *priv, uint64_t id,
struct usb_redir_alt_setting_status_header *alt_setting_status)
{
USBRedirDevice *dev = priv;
- AsyncURB *aurb;
+ USBPacket *p;
int len = 0;
- DPRINTF("alt status %d intf %d alt %d id: %u\n",
- alt_setting_status->status,
- alt_setting_status->interface,
+ DPRINTF("alt status %d intf %d alt %d id: %"PRIu64"\n",
+ alt_setting_status->status, alt_setting_status->interface,
alt_setting_status->alt, id);
- aurb = async_find(dev, id);
- if (!aurb) {
- return;
- }
- if (aurb->packet) {
- if (aurb->get) {
+ p = usbredir_find_packet_by_id(dev, 0, id);
+ if (p) {
+ if (dev->dev.setup_buf[0] & USB_DIR_IN) {
dev->dev.data_buf[0] = alt_setting_status->alt;
len = 1;
}
- aurb->packet->result =
+ p->result =
usbredir_handle_status(dev, alt_setting_status->status, len);
- usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
+ usb_generic_async_ctrl_complete(&dev->dev, p);
}
- async_free(dev, aurb);
}
-static void usbredir_iso_stream_status(void *priv, uint32_t id,
+static void usbredir_iso_stream_status(void *priv, uint64_t id,
struct usb_redir_iso_stream_status_header *iso_stream_status)
{
USBRedirDevice *dev = priv;
uint8_t ep = iso_stream_status->endpoint;
- DPRINTF("iso status %d ep %02X id %u\n", iso_stream_status->status,
+ DPRINTF("iso status %d ep %02X id %"PRIu64"\n", iso_stream_status->status,
ep, id);
if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].iso_started) {
@@ -1276,14 +1382,14 @@ static void usbredir_iso_stream_status(void *priv, uint32_t id,
}
}
-static void usbredir_interrupt_receiving_status(void *priv, uint32_t id,
+static void usbredir_interrupt_receiving_status(void *priv, uint64_t id,
struct usb_redir_interrupt_receiving_status_header
*interrupt_receiving_status)
{
USBRedirDevice *dev = priv;
uint8_t ep = interrupt_receiving_status->endpoint;
- DPRINTF("interrupt recv status %d ep %02X id %u\n",
+ DPRINTF("interrupt recv status %d ep %02X id %"PRIu64"\n",
interrupt_receiving_status->status, ep, id);
if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].interrupt_started) {
@@ -1298,37 +1404,24 @@ static void usbredir_interrupt_receiving_status(void *priv, uint32_t id,
}
}
-static void usbredir_bulk_streams_status(void *priv, uint32_t id,
+static void usbredir_bulk_streams_status(void *priv, uint64_t id,
struct usb_redir_bulk_streams_status_header *bulk_streams_status)
{
}
-static void usbredir_control_packet(void *priv, uint32_t id,
+static void usbredir_control_packet(void *priv, uint64_t id,
struct usb_redir_control_packet_header *control_packet,
uint8_t *data, int data_len)
{
USBRedirDevice *dev = priv;
+ USBPacket *p;
int len = control_packet->length;
- AsyncURB *aurb;
- DPRINTF("ctrl-in status %d len %d id %u\n", control_packet->status,
+ DPRINTF("ctrl-in status %d len %d id %"PRIu64"\n", control_packet->status,
len, id);
- aurb = async_find(dev, id);
- if (!aurb) {
- free(data);
- return;
- }
-
- aurb->control_packet.status = control_packet->status;
- aurb->control_packet.length = control_packet->length;
- if (memcmp(&aurb->control_packet, control_packet,
- sizeof(*control_packet))) {
- ERROR("return control packet mismatch, please report this!\n");
- len = USB_RET_NAK;
- }
-
- if (aurb->packet) {
+ p = usbredir_find_packet_by_id(dev, 0, id);
+ if (p) {
len = usbredir_handle_status(dev, control_packet->status, len);
if (len > 0) {
usbredir_log_data(dev, "ctrl data in:", data, data_len);
@@ -1340,65 +1433,52 @@ static void usbredir_control_packet(void *priv, uint32_t id,
len = USB_RET_STALL;
}
}
- aurb->packet->result = len;
- usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
+ p->result = len;
+ usb_generic_async_ctrl_complete(&dev->dev, p);
}
- async_free(dev, aurb);
free(data);
}
-static void usbredir_bulk_packet(void *priv, uint32_t id,
+static void usbredir_bulk_packet(void *priv, uint64_t id,
struct usb_redir_bulk_packet_header *bulk_packet,
uint8_t *data, int data_len)
{
USBRedirDevice *dev = priv;
uint8_t ep = bulk_packet->endpoint;
int len = bulk_packet->length;
- AsyncURB *aurb;
+ USBPacket *p;
- DPRINTF("bulk-in status %d ep %02X len %d id %u\n", bulk_packet->status,
- ep, len, id);
+ DPRINTF("bulk-in status %d ep %02X len %d id %"PRIu64"\n",
+ bulk_packet->status, ep, len, id);
- aurb = async_find(dev, id);
- if (!aurb) {
- free(data);
- return;
- }
-
- if (aurb->bulk_packet.endpoint != bulk_packet->endpoint ||
- aurb->bulk_packet.stream_id != bulk_packet->stream_id) {
- ERROR("return bulk packet mismatch, please report this!\n");
- len = USB_RET_NAK;
- }
-
- if (aurb->packet) {
+ p = usbredir_find_packet_by_id(dev, ep, id);
+ if (p) {
len = usbredir_handle_status(dev, bulk_packet->status, len);
if (len > 0) {
usbredir_log_data(dev, "bulk data in:", data, data_len);
- if (data_len <= aurb->packet->iov.size) {
- usb_packet_copy(aurb->packet, data, data_len);
+ if (data_len <= p->iov.size) {
+ usb_packet_copy(p, data, data_len);
} else {
- ERROR("bulk buffer too small (%d > %zd)\n", data_len,
- aurb->packet->iov.size);
- len = USB_RET_STALL;
+ ERROR("bulk got more data then requested (%d > %zd)\n",
+ data_len, p->iov.size);
+ len = USB_RET_BABBLE;
}
}
- aurb->packet->result = len;
- usb_packet_complete(&dev->dev, aurb->packet);
+ p->result = len;
+ usb_packet_complete(&dev->dev, p);
}
- async_free(dev, aurb);
free(data);
}
-static void usbredir_iso_packet(void *priv, uint32_t id,
+static void usbredir_iso_packet(void *priv, uint64_t id,
struct usb_redir_iso_packet_header *iso_packet,
uint8_t *data, int data_len)
{
USBRedirDevice *dev = priv;
uint8_t ep = iso_packet->endpoint;
- DPRINTF2("iso-in status %d ep %02X len %d id %u\n", iso_packet->status, ep,
- data_len, id);
+ DPRINTF2("iso-in status %d ep %02X len %d id %"PRIu64"\n",
+ iso_packet->status, ep, data_len, id);
if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_ISOC) {
ERROR("received iso packet for non iso endpoint %02X\n", ep);
@@ -1416,14 +1496,14 @@ static void usbredir_iso_packet(void *priv, uint32_t id,
bufp_alloc(dev, data, data_len, iso_packet->status, ep);
}
-static void usbredir_interrupt_packet(void *priv, uint32_t id,
+static void usbredir_interrupt_packet(void *priv, uint64_t id,
struct usb_redir_interrupt_packet_header *interrupt_packet,
uint8_t *data, int data_len)
{
USBRedirDevice *dev = priv;
uint8_t ep = interrupt_packet->endpoint;
- DPRINTF("interrupt-in status %d ep %02X len %d id %u\n",
+ DPRINTF("interrupt-in status %d ep %02X len %d id %"PRIu64"\n",
interrupt_packet->status, ep, data_len, id);
if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_INT) {
@@ -1444,25 +1524,331 @@ static void usbredir_interrupt_packet(void *priv, uint32_t id,
} else {
int len = interrupt_packet->length;
- AsyncURB *aurb = async_find(dev, id);
- if (!aurb) {
- return;
+ USBPacket *p = usbredir_find_packet_by_id(dev, ep, id);
+ if (p) {
+ p->result = usbredir_handle_status(dev,
+ interrupt_packet->status, len);
+ usb_packet_complete(&dev->dev, p);
}
+ }
+}
- if (aurb->interrupt_packet.endpoint != interrupt_packet->endpoint) {
- ERROR("return int packet mismatch, please report this!\n");
- len = USB_RET_NAK;
- }
+/*
+ * Migration code
+ */
- if (aurb->packet) {
- aurb->packet->result = usbredir_handle_status(dev,
- interrupt_packet->status, len);
- usb_packet_complete(&dev->dev, aurb->packet);
+static void usbredir_pre_save(void *priv)
+{
+ USBRedirDevice *dev = priv;
+
+ usbredir_fill_already_in_flight(dev);
+}
+
+static int usbredir_post_load(void *priv, int version_id)
+{
+ USBRedirDevice *dev = priv;
+ struct USBEndpoint *usb_ep;
+ int i;
+
+ switch (dev->device_info.speed) {
+ case usb_redir_speed_low:
+ dev->dev.speed = USB_SPEED_LOW;
+ break;
+ case usb_redir_speed_full:
+ dev->dev.speed = USB_SPEED_FULL;
+ break;
+ case usb_redir_speed_high:
+ dev->dev.speed = USB_SPEED_HIGH;
+ break;
+ case usb_redir_speed_super:
+ dev->dev.speed = USB_SPEED_SUPER;
+ break;
+ default:
+ dev->dev.speed = USB_SPEED_FULL;
+ }
+ dev->dev.speedmask = (1 << dev->dev.speed);
+
+ for (i = 0; i < MAX_ENDPOINTS; i++) {
+ usb_ep = usb_ep_get(&dev->dev,
+ (i & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT,
+ i & 0x0f);
+ usb_ep->type = dev->endpoint[i].type;
+ usb_ep->ifnum = dev->endpoint[i].interface;
+ usb_ep->max_packet_size = dev->endpoint[i].max_packet_size;
+ if (dev->endpoint[i].type == usb_redir_type_bulk) {
+ usb_ep->pipeline = true;
}
- async_free(dev, aurb);
}
+ return 0;
+}
+
+/* For usbredirparser migration */
+static void usbredir_put_parser(QEMUFile *f, void *priv, size_t unused)
+{
+ USBRedirDevice *dev = priv;
+ uint8_t *data;
+ int len;
+
+ if (dev->parser == NULL) {
+ qemu_put_be32(f, 0);
+ return;
+ }
+
+ usbredirparser_serialize(dev->parser, &data, &len);
+ qemu_oom_check(data);
+
+ qemu_put_be32(f, len);
+ qemu_put_buffer(f, data, len);
+
+ free(data);
+}
+
+static int usbredir_get_parser(QEMUFile *f, void *priv, size_t unused)
+{
+ USBRedirDevice *dev = priv;
+ uint8_t *data;
+ int len, ret;
+
+ len = qemu_get_be32(f);
+ if (len == 0) {
+ return 0;
+ }
+
+ /*
+ * Our chardev should be open already at this point, otherwise
+ * the usbredir channel will be broken (ie spice without seamless)
+ */
+ if (dev->parser == NULL) {
+ ERROR("get_parser called with closed chardev, failing migration\n");
+ return -1;
+ }
+
+ data = g_malloc(len);
+ qemu_get_buffer(f, data, len);
+
+ ret = usbredirparser_unserialize(dev->parser, data, len);
+
+ g_free(data);
+
+ return ret;
+}
+
+static const VMStateInfo usbredir_parser_vmstate_info = {
+ .name = "usb-redir-parser",
+ .put = usbredir_put_parser,
+ .get = usbredir_get_parser,
+};
+
+
+/* For buffered packets (iso/irq) queue migration */
+static void usbredir_put_bufpq(QEMUFile *f, void *priv, size_t unused)
+{
+ struct endp_data *endp = priv;
+ struct buf_packet *bufp;
+ int remain = endp->bufpq_size;
+
+ qemu_put_be32(f, endp->bufpq_size);
+ QTAILQ_FOREACH(bufp, &endp->bufpq, next) {
+ qemu_put_be32(f, bufp->len);
+ qemu_put_be32(f, bufp->status);
+ qemu_put_buffer(f, bufp->data, bufp->len);
+ remain--;
+ }
+ assert(remain == 0);
+}
+
+static int usbredir_get_bufpq(QEMUFile *f, void *priv, size_t unused)
+{
+ struct endp_data *endp = priv;
+ struct buf_packet *bufp;
+ int i;
+
+ endp->bufpq_size = qemu_get_be32(f);
+ for (i = 0; i < endp->bufpq_size; i++) {
+ bufp = g_malloc(sizeof(struct buf_packet));
+ bufp->len = qemu_get_be32(f);
+ bufp->status = qemu_get_be32(f);
+ bufp->data = qemu_oom_check(malloc(bufp->len)); /* regular malloc! */
+ qemu_get_buffer(f, bufp->data, bufp->len);
+ QTAILQ_INSERT_TAIL(&endp->bufpq, bufp, next);
+ }
+ return 0;
+}
+
+static const VMStateInfo usbredir_ep_bufpq_vmstate_info = {
+ .name = "usb-redir-bufpq",
+ .put = usbredir_put_bufpq,
+ .get = usbredir_get_bufpq,
+};
+
+
+/* For endp_data migration */
+static const VMStateDescription usbredir_ep_vmstate = {
+ .name = "usb-redir-ep",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(type, struct endp_data),
+ VMSTATE_UINT8(interval, struct endp_data),
+ VMSTATE_UINT8(interface, struct endp_data),
+ VMSTATE_UINT16(max_packet_size, struct endp_data),
+ VMSTATE_UINT8(iso_started, struct endp_data),
+ VMSTATE_UINT8(iso_error, struct endp_data),
+ VMSTATE_UINT8(interrupt_started, struct endp_data),
+ VMSTATE_UINT8(interrupt_error, struct endp_data),
+ VMSTATE_UINT8(bufpq_prefilled, struct endp_data),
+ VMSTATE_UINT8(bufpq_dropping_packets, struct endp_data),
+ {
+ .name = "bufpq",
+ .version_id = 0,
+ .field_exists = NULL,
+ .size = 0,
+ .info = &usbredir_ep_bufpq_vmstate_info,
+ .flags = VMS_SINGLE,
+ .offset = 0,
+ },
+ VMSTATE_INT32(bufpq_target_size, struct endp_data),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+
+/* For PacketIdQueue migration */
+static void usbredir_put_packet_id_q(QEMUFile *f, void *priv, size_t unused)
+{
+ struct PacketIdQueue *q = priv;
+ USBRedirDevice *dev = q->dev;
+ struct PacketIdQueueEntry *e;
+ int remain = q->size;
+
+ DPRINTF("put_packet_id_q %s size %d\n", q->name, q->size);
+ qemu_put_be32(f, q->size);
+ QTAILQ_FOREACH(e, &q->head, next) {
+ qemu_put_be64(f, e->id);
+ remain--;
+ }
+ assert(remain == 0);
}
+static int usbredir_get_packet_id_q(QEMUFile *f, void *priv, size_t unused)
+{
+ struct PacketIdQueue *q = priv;
+ USBRedirDevice *dev = q->dev;
+ int i, size;
+ uint64_t id;
+
+ size = qemu_get_be32(f);
+ DPRINTF("get_packet_id_q %s size %d\n", q->name, size);
+ for (i = 0; i < size; i++) {
+ id = qemu_get_be64(f);
+ packet_id_queue_add(q, id);
+ }
+ assert(q->size == size);
+ return 0;
+}
+
+static const VMStateInfo usbredir_ep_packet_id_q_vmstate_info = {
+ .name = "usb-redir-packet-id-q",
+ .put = usbredir_put_packet_id_q,
+ .get = usbredir_get_packet_id_q,
+};
+
+static const VMStateDescription usbredir_ep_packet_id_queue_vmstate = {
+ .name = "usb-redir-packet-id-queue",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ {
+ .name = "queue",
+ .version_id = 0,
+ .field_exists = NULL,
+ .size = 0,
+ .info = &usbredir_ep_packet_id_q_vmstate_info,
+ .flags = VMS_SINGLE,
+ .offset = 0,
+ },
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+
+/* For usb_redir_device_connect_header migration */
+static const VMStateDescription usbredir_device_info_vmstate = {
+ .name = "usb-redir-device-info",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(speed, struct usb_redir_device_connect_header),
+ VMSTATE_UINT8(device_class, struct usb_redir_device_connect_header),
+ VMSTATE_UINT8(device_subclass, struct usb_redir_device_connect_header),
+ VMSTATE_UINT8(device_protocol, struct usb_redir_device_connect_header),
+ VMSTATE_UINT16(vendor_id, struct usb_redir_device_connect_header),
+ VMSTATE_UINT16(product_id, struct usb_redir_device_connect_header),
+ VMSTATE_UINT16(device_version_bcd,
+ struct usb_redir_device_connect_header),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+
+/* For usb_redir_interface_info_header migration */
+static const VMStateDescription usbredir_interface_info_vmstate = {
+ .name = "usb-redir-interface-info",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(interface_count,
+ struct usb_redir_interface_info_header),
+ VMSTATE_UINT8_ARRAY(interface,
+ struct usb_redir_interface_info_header, 32),
+ VMSTATE_UINT8_ARRAY(interface_class,
+ struct usb_redir_interface_info_header, 32),
+ VMSTATE_UINT8_ARRAY(interface_subclass,
+ struct usb_redir_interface_info_header, 32),
+ VMSTATE_UINT8_ARRAY(interface_protocol,
+ struct usb_redir_interface_info_header, 32),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+
+/* And finally the USBRedirDevice vmstate itself */
+static const VMStateDescription usbredir_vmstate = {
+ .name = "usb-redir",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .pre_save = usbredir_pre_save,
+ .post_load = usbredir_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_USB_DEVICE(dev, USBRedirDevice),
+ VMSTATE_TIMER(attach_timer, USBRedirDevice),
+ {
+ .name = "parser",
+ .version_id = 0,
+ .field_exists = NULL,
+ .size = 0,
+ .info = &usbredir_parser_vmstate_info,
+ .flags = VMS_SINGLE,
+ .offset = 0,
+ },
+ VMSTATE_STRUCT_ARRAY(endpoint, USBRedirDevice, MAX_ENDPOINTS, 1,
+ usbredir_ep_vmstate, struct endp_data),
+ VMSTATE_STRUCT(cancelled, USBRedirDevice, 1,
+ usbredir_ep_packet_id_queue_vmstate,
+ struct PacketIdQueue),
+ VMSTATE_STRUCT(already_in_flight, USBRedirDevice, 1,
+ usbredir_ep_packet_id_queue_vmstate,
+ struct PacketIdQueue),
+ VMSTATE_STRUCT(device_info, USBRedirDevice, 1,
+ usbredir_device_info_vmstate,
+ struct usb_redir_device_connect_header),
+ VMSTATE_STRUCT(interface_info, USBRedirDevice, 1,
+ usbredir_interface_info_vmstate,
+ struct usb_redir_interface_info_header),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static Property usbredir_properties[] = {
DEFINE_PROP_CHR("chardev", USBRedirDevice, cs),
DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0),
@@ -1483,6 +1869,7 @@ static void usbredir_class_initfn(ObjectClass *klass, void *data)
uc->handle_reset = usbredir_handle_reset;
uc->handle_data = usbredir_handle_data;
uc->handle_control = usbredir_handle_control;
+ dc->vmsd = &usbredir_vmstate;
dc->props = usbredir_properties;
}
diff --git a/hw/versatilepb.c b/hw/versatilepb.c
index 7a9203471..b3f807714 100644
--- a/hw/versatilepb.c
+++ b/hw/versatilepb.c
@@ -211,7 +211,8 @@ static void versatile_init(ram_addr_t ram_size,
cpu_pic = arm_pic_init_cpu(cpu);
dev = sysbus_create_varargs("pl190", 0x10140000,
- cpu_pic[0], cpu_pic[1], NULL);
+ cpu_pic[ARM_PIC_CPU_IRQ],
+ cpu_pic[ARM_PIC_CPU_FIQ], NULL);
for (n = 0; n < 32; n++) {
pic[n] = qdev_get_gpio_in(dev, n);
}
diff --git a/hw/vexpress.c b/hw/vexpress.c
index b6158447d..3596d1e33 100644
--- a/hw/vexpress.c
+++ b/hw/vexpress.c
@@ -29,8 +29,12 @@
#include "sysemu.h"
#include "boards.h"
#include "exec-memory.h"
+#include "blockdev.h"
+#include "flash.h"
#define VEXPRESS_BOARD_ID 0x8e0
+#define VEXPRESS_FLASH_SIZE (64 * 1024 * 1024)
+#define VEXPRESS_FLASH_SECT_SIZE (256 * 1024)
static struct arm_boot_info vexpress_binfo;
@@ -62,7 +66,6 @@ enum {
VE_COMPACTFLASH,
VE_CLCD,
VE_NORFLASH0,
- VE_NORFLASH0ALIAS,
VE_NORFLASH1,
VE_SRAM,
VE_VIDEORAM,
@@ -104,9 +107,8 @@ static target_phys_addr_t motherboard_legacy_map[] = {
};
static target_phys_addr_t motherboard_aseries_map[] = {
- /* CS0: 0x00000000 .. 0x0c000000 */
- [VE_NORFLASH0] = 0x00000000,
- [VE_NORFLASH0ALIAS] = 0x08000000,
+ /* CS0: 0x08000000 .. 0x0c000000 */
+ [VE_NORFLASH0] = 0x08000000,
/* CS4: 0x0c000000 .. 0x10000000 */
[VE_NORFLASH1] = 0x0c000000,
/* CS5: 0x10000000 .. 0x14000000 */
@@ -357,6 +359,7 @@ static void vexpress_common_init(const VEDBoardInfo *daughterboard,
qemu_irq pic[64];
uint32_t proc_id;
uint32_t sys_id;
+ DriveInfo *dinfo;
ram_addr_t vram_size, sram_size;
MemoryRegion *sysmem = get_system_memory();
MemoryRegion *vram = g_new(MemoryRegion, 1);
@@ -412,9 +415,25 @@ static void vexpress_common_init(const VEDBoardInfo *daughterboard,
sysbus_create_simple("pl111", map[VE_CLCD], pic[14]);
- /* VE_NORFLASH0: not modelled */
- /* VE_NORFLASH0ALIAS: not modelled */
- /* VE_NORFLASH1: not modelled */
+ dinfo = drive_get_next(IF_PFLASH);
+ if (!pflash_cfi01_register(map[VE_NORFLASH0], NULL, "vexpress.flash0",
+ VEXPRESS_FLASH_SIZE, dinfo ? dinfo->bdrv : NULL,
+ VEXPRESS_FLASH_SECT_SIZE,
+ VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE, 4,
+ 0x00, 0x89, 0x00, 0x18, 0)) {
+ fprintf(stderr, "vexpress: error registering flash 0.\n");
+ exit(1);
+ }
+
+ dinfo = drive_get_next(IF_PFLASH);
+ if (!pflash_cfi01_register(map[VE_NORFLASH1], NULL, "vexpress.flash1",
+ VEXPRESS_FLASH_SIZE, dinfo ? dinfo->bdrv : NULL,
+ VEXPRESS_FLASH_SECT_SIZE,
+ VEXPRESS_FLASH_SIZE / VEXPRESS_FLASH_SECT_SIZE, 4,
+ 0x00, 0x89, 0x00, 0x18, 0)) {
+ fprintf(stderr, "vexpress: error registering flash 1.\n");
+ exit(1);
+ }
sram_size = 0x2000000;
memory_region_init_ram(sram, "vexpress.sram", sram_size);
diff --git a/hw/vfio_pci.c b/hw/vfio_pci.c
new file mode 100644
index 000000000..a1eeced8f
--- /dev/null
+++ b/hw/vfio_pci.c
@@ -0,0 +1,1864 @@
+/*
+ * vfio based device assignment support
+ *
+ * Copyright Red Hat, Inc. 2012
+ *
+ * Authors:
+ * Alex Williamson <alex.williamson@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ * Based on qemu-kvm device-assignment:
+ * Adapted for KVM by Qumranet.
+ * Copyright (c) 2007, Neocleus, Alex Novik (alex@neocleus.com)
+ * Copyright (c) 2007, Neocleus, Guy Zana (guy@neocleus.com)
+ * Copyright (C) 2008, Qumranet, Amit Shah (amit.shah@qumranet.com)
+ * Copyright (C) 2008, Red Hat, Amit Shah (amit.shah@redhat.com)
+ * Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com)
+ */
+
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <linux/vfio.h>
+
+#include "config.h"
+#include "event_notifier.h"
+#include "exec-memory.h"
+#include "kvm.h"
+#include "memory.h"
+#include "msi.h"
+#include "msix.h"
+#include "qemu-error.h"
+#include "range.h"
+#include "vfio_pci_int.h"
+
+/* #define DEBUG_VFIO */
+#ifdef DEBUG_VFIO
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stderr, "vfio: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+ do { } while (0)
+#endif
+
+#define MSIX_CAP_LENGTH 12
+
+static QLIST_HEAD(, VFIOContainer)
+ container_list = QLIST_HEAD_INITIALIZER(container_list);
+
+static QLIST_HEAD(, VFIOGroup)
+ group_list = QLIST_HEAD_INITIALIZER(group_list);
+
+static void vfio_disable_interrupts(VFIODevice *vdev);
+static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len);
+static void vfio_mmap_set_enabled(VFIODevice *vdev, bool enabled);
+
+/*
+ * Common VFIO interrupt disable
+ */
+static void vfio_disable_irqindex(VFIODevice *vdev, int index)
+{
+ struct vfio_irq_set irq_set = {
+ .argsz = sizeof(irq_set),
+ .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER,
+ .index = index,
+ .start = 0,
+ .count = 0,
+ };
+
+ ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set);
+
+ vdev->interrupt = VFIO_INT_NONE;
+}
+
+/*
+ * INTx
+ */
+static void vfio_unmask_intx(VFIODevice *vdev)
+{
+ struct vfio_irq_set irq_set = {
+ .argsz = sizeof(irq_set),
+ .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_UNMASK,
+ .index = VFIO_PCI_INTX_IRQ_INDEX,
+ .start = 0,
+ .count = 1,
+ };
+
+ ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set);
+}
+
+static void vfio_intx_interrupt(void *opaque)
+{
+ VFIODevice *vdev = opaque;
+
+ if (!event_notifier_test_and_clear(&vdev->intx.interrupt)) {
+ return;
+ }
+
+ DPRINTF("%s(%04x:%02x:%02x.%x) Pin %c\n", __func__, vdev->host.domain,
+ vdev->host.bus, vdev->host.slot, vdev->host.function,
+ 'A' + vdev->intx.pin);
+
+ vdev->intx.pending = true;
+ qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 1);
+}
+
+static void vfio_eoi(VFIODevice *vdev)
+{
+ if (!vdev->intx.pending) {
+ return;
+ }
+
+ DPRINTF("%s(%04x:%02x:%02x.%x) EOI\n", __func__, vdev->host.domain,
+ vdev->host.bus, vdev->host.slot, vdev->host.function);
+
+ vdev->intx.pending = false;
+ qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 0);
+ vfio_unmask_intx(vdev);
+}
+
+typedef struct QEMU_PACKED VFIOIRQSetFD {
+ struct vfio_irq_set irq_set;
+ int32_t fd;
+} VFIOIRQSetFD;
+
+static int vfio_enable_intx(VFIODevice *vdev)
+{
+ VFIOIRQSetFD irq_set_fd = {
+ .irq_set = {
+ .argsz = sizeof(irq_set_fd),
+ .flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER,
+ .index = VFIO_PCI_INTX_IRQ_INDEX,
+ .start = 0,
+ .count = 1,
+ },
+ };
+ uint8_t pin = vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1);
+ int ret;
+
+ if (vdev->intx.disabled || !pin) {
+ return 0;
+ }
+
+ vfio_disable_interrupts(vdev);
+
+ vdev->intx.pin = pin - 1; /* Pin A (1) -> irq[0] */
+ ret = event_notifier_init(&vdev->intx.interrupt, 0);
+ if (ret) {
+ error_report("vfio: Error: event_notifier_init failed\n");
+ return ret;
+ }
+
+ irq_set_fd.fd = event_notifier_get_fd(&vdev->intx.interrupt);
+ qemu_set_fd_handler(irq_set_fd.fd, vfio_intx_interrupt, NULL, vdev);
+
+ if (ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set_fd)) {
+ error_report("vfio: Error: Failed to setup INTx fd: %m\n");
+ return -errno;
+ }
+
+ /*
+ * Disable mmaps so we can trap on BAR accesses. We interpret any
+ * access as a response to an interrupt and unmask the physical
+ * device. The device will re-assert if the interrupt is still
+ * pending. We'll likely retrigger on the host multiple times per
+ * guest interrupt, but without EOI notification it's better than
+ * nothing. Acceleration paths through KVM will avoid this.
+ */
+ vfio_mmap_set_enabled(vdev, false);
+
+ vdev->interrupt = VFIO_INT_INTx;
+
+ DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain,
+ vdev->host.bus, vdev->host.slot, vdev->host.function);
+
+ return 0;
+}
+
+static void vfio_disable_intx(VFIODevice *vdev)
+{
+ int fd;
+
+ vfio_disable_irqindex(vdev, VFIO_PCI_INTX_IRQ_INDEX);
+ vdev->intx.pending = false;
+ qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 0);
+ vfio_mmap_set_enabled(vdev, true);
+
+ fd = event_notifier_get_fd(&vdev->intx.interrupt);
+ qemu_set_fd_handler(fd, NULL, NULL, vdev);
+ event_notifier_cleanup(&vdev->intx.interrupt);
+
+ vdev->interrupt = VFIO_INT_NONE;
+
+ DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain,
+ vdev->host.bus, vdev->host.slot, vdev->host.function);
+}
+
+/*
+ * MSI/X
+ */
+static void vfio_msi_interrupt(void *opaque)
+{
+ VFIOMSIVector *vector = opaque;
+ VFIODevice *vdev = vector->vdev;
+ int nr = vector - vdev->msi_vectors;
+
+ if (!event_notifier_test_and_clear(&vector->interrupt)) {
+ return;
+ }
+
+ DPRINTF("%s(%04x:%02x:%02x.%x) vector %d\n", __func__,
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function, nr);
+
+ if (vdev->interrupt == VFIO_INT_MSIX) {
+ msix_notify(&vdev->pdev, nr);
+ } else if (vdev->interrupt == VFIO_INT_MSI) {
+ msi_notify(&vdev->pdev, nr);
+ } else {
+ error_report("vfio: MSI interrupt receieved, but not enabled?\n");
+ }
+}
+
+static int vfio_enable_vectors(VFIODevice *vdev, bool msix)
+{
+ struct vfio_irq_set *irq_set;
+ int ret = 0, i, argsz;
+ int32_t *fds;
+
+ argsz = sizeof(*irq_set) + (vdev->nr_vectors * sizeof(*fds));
+
+ irq_set = g_malloc0(argsz);
+ irq_set->argsz = argsz;
+ irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
+ irq_set->index = msix ? VFIO_PCI_MSIX_IRQ_INDEX : VFIO_PCI_MSI_IRQ_INDEX;
+ irq_set->start = 0;
+ irq_set->count = vdev->nr_vectors;
+ fds = (int32_t *)&irq_set->data;
+
+ for (i = 0; i < vdev->nr_vectors; i++) {
+ if (!vdev->msi_vectors[i].use) {
+ fds[i] = -1;
+ continue;
+ }
+
+ fds[i] = event_notifier_get_fd(&vdev->msi_vectors[i].interrupt);
+ }
+
+ ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set);
+
+ g_free(irq_set);
+
+ if (!ret) {
+ vdev->interrupt = msix ? VFIO_INT_MSIX : VFIO_INT_MSI;
+ }
+
+ return ret;
+}
+
+static int vfio_msix_vector_use(PCIDevice *pdev,
+ unsigned int nr, MSIMessage msg)
+{
+ VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev);
+ VFIOMSIVector *vector;
+ int ret;
+
+ DPRINTF("%s(%04x:%02x:%02x.%x) vector %d used\n", __func__,
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function, nr);
+
+ if (vdev->interrupt != VFIO_INT_MSIX) {
+ vfio_disable_interrupts(vdev);
+ }
+
+ if (!vdev->msi_vectors) {
+ vdev->msi_vectors = g_malloc0(vdev->msix->entries *
+ sizeof(VFIOMSIVector));
+ }
+
+ vector = &vdev->msi_vectors[nr];
+ vector->vdev = vdev;
+ vector->use = true;
+
+ msix_vector_use(pdev, nr);
+
+ if (event_notifier_init(&vector->interrupt, 0)) {
+ error_report("vfio: Error: event_notifier_init failed\n");
+ }
+
+ /*
+ * Attempt to enable route through KVM irqchip,
+ * default to userspace handling if unavailable.
+ */
+ vector->virq = kvm_irqchip_add_msi_route(kvm_state, msg);
+ if (vector->virq < 0 ||
+ kvm_irqchip_add_irqfd_notifier(kvm_state, &vector->interrupt,
+ vector->virq) < 0) {
+ if (vector->virq >= 0) {
+ kvm_irqchip_release_virq(kvm_state, vector->virq);
+ vector->virq = -1;
+ }
+ qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt),
+ vfio_msi_interrupt, NULL, vector);
+ }
+
+ /*
+ * We don't want to have the host allocate all possible MSI vectors
+ * for a device if they're not in use, so we shutdown and incrementally
+ * increase them as needed.
+ */
+ if (vdev->nr_vectors < nr + 1) {
+ int i;
+
+ vfio_disable_irqindex(vdev, VFIO_PCI_MSIX_IRQ_INDEX);
+ vdev->nr_vectors = nr + 1;
+ ret = vfio_enable_vectors(vdev, true);
+ if (ret) {
+ error_report("vfio: failed to enable vectors, %d\n", ret);
+ }
+
+ /* We don't know if we've missed interrupts in the interim... */
+ for (i = 0; i < vdev->msix->entries; i++) {
+ if (vdev->msi_vectors[i].use) {
+ msix_notify(&vdev->pdev, i);
+ }
+ }
+ } else {
+ VFIOIRQSetFD irq_set_fd = {
+ .irq_set = {
+ .argsz = sizeof(irq_set_fd),
+ .flags = VFIO_IRQ_SET_DATA_EVENTFD |
+ VFIO_IRQ_SET_ACTION_TRIGGER,
+ .index = VFIO_PCI_MSIX_IRQ_INDEX,
+ .start = nr,
+ .count = 1,
+ },
+ .fd = event_notifier_get_fd(&vector->interrupt),
+ };
+ ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set_fd);
+ if (ret) {
+ error_report("vfio: failed to modify vector, %d\n", ret);
+ }
+
+ /*
+ * If we were connected to the hardware PBA we could skip this,
+ * until then, a spurious interrupt is better than starvation.
+ */
+ msix_notify(&vdev->pdev, nr);
+ }
+
+ return 0;
+}
+
+static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr)
+{
+ VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev);
+ VFIOMSIVector *vector = &vdev->msi_vectors[nr];
+ VFIOIRQSetFD irq_set_fd = {
+ .irq_set = {
+ .argsz = sizeof(irq_set_fd),
+ .flags = VFIO_IRQ_SET_DATA_EVENTFD |
+ VFIO_IRQ_SET_ACTION_TRIGGER,
+ .index = VFIO_PCI_MSIX_IRQ_INDEX,
+ .start = nr,
+ .count = 1,
+ },
+ .fd = -1,
+ };
+
+ DPRINTF("%s(%04x:%02x:%02x.%x) vector %d released\n", __func__,
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function, nr);
+
+ /*
+ * XXX What's the right thing to do here? This turns off the interrupt
+ * completely, but do we really just want to switch the interrupt to
+ * bouncing through userspace and let msix.c drop it? Not sure.
+ */
+ msix_vector_unuse(pdev, nr);
+ ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set_fd);
+
+ if (vector->virq < 0) {
+ qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt),
+ NULL, NULL, NULL);
+ } else {
+ kvm_irqchip_remove_irqfd_notifier(kvm_state, &vector->interrupt,
+ vector->virq);
+ kvm_irqchip_release_virq(kvm_state, vector->virq);
+ vector->virq = -1;
+ }
+
+ event_notifier_cleanup(&vector->interrupt);
+ vector->use = false;
+}
+
+/* TODO This should move to msi.c */
+static MSIMessage msi_get_msg(PCIDevice *pdev, unsigned int vector)
+{
+ uint16_t flags = pci_get_word(pdev->config + pdev->msi_cap + PCI_MSI_FLAGS);
+ bool msi64bit = flags & PCI_MSI_FLAGS_64BIT;
+ MSIMessage msg;
+
+ if (msi64bit) {
+ msg.address = pci_get_quad(pdev->config +
+ pdev->msi_cap + PCI_MSI_ADDRESS_LO);
+ } else {
+ msg.address = pci_get_long(pdev->config +
+ pdev->msi_cap + PCI_MSI_ADDRESS_LO);
+ }
+
+ msg.data = pci_get_word(pdev->config + pdev->msi_cap +
+ (msi64bit ? PCI_MSI_DATA_64 : PCI_MSI_DATA_32));
+ msg.data += vector;
+
+ return msg;
+}
+
+/* So should this */
+static void msi_set_qsize(PCIDevice *pdev, uint8_t size)
+{
+ uint8_t *config = pdev->config + pdev->msi_cap;
+ uint16_t flags;
+
+ flags = pci_get_word(config + PCI_MSI_FLAGS);
+ flags = le16_to_cpu(flags);
+ flags &= ~PCI_MSI_FLAGS_QSIZE;
+ flags |= (size & 0x7) << 4;
+ flags = cpu_to_le16(flags);
+ pci_set_word(config + PCI_MSI_FLAGS, flags);
+}
+
+static void vfio_enable_msi(VFIODevice *vdev)
+{
+ int ret, i;
+
+ vfio_disable_interrupts(vdev);
+
+ vdev->nr_vectors = msi_nr_vectors_allocated(&vdev->pdev);
+retry:
+ vdev->msi_vectors = g_malloc0(vdev->nr_vectors * sizeof(VFIOMSIVector));
+
+ for (i = 0; i < vdev->nr_vectors; i++) {
+ MSIMessage msg;
+ VFIOMSIVector *vector = &vdev->msi_vectors[i];
+
+ vector->vdev = vdev;
+ vector->use = true;
+
+ if (event_notifier_init(&vector->interrupt, 0)) {
+ error_report("vfio: Error: event_notifier_init failed\n");
+ }
+
+ msg = msi_get_msg(&vdev->pdev, i);
+
+ /*
+ * Attempt to enable route through KVM irqchip,
+ * default to userspace handling if unavailable.
+ */
+ vector->virq = kvm_irqchip_add_msi_route(kvm_state, msg);
+ if (vector->virq < 0 ||
+ kvm_irqchip_add_irqfd_notifier(kvm_state, &vector->interrupt,
+ vector->virq) < 0) {
+ qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt),
+ vfio_msi_interrupt, NULL, vector);
+ }
+ }
+
+ ret = vfio_enable_vectors(vdev, false);
+ if (ret) {
+ if (ret < 0) {
+ error_report("vfio: Error: Failed to setup MSI fds: %m\n");
+ } else if (ret != vdev->nr_vectors) {
+ error_report("vfio: Error: Failed to enable %d "
+ "MSI vectors, retry with %d\n", vdev->nr_vectors, ret);
+ }
+
+ for (i = 0; i < vdev->nr_vectors; i++) {
+ VFIOMSIVector *vector = &vdev->msi_vectors[i];
+ if (vector->virq >= 0) {
+ kvm_irqchip_remove_irqfd_notifier(kvm_state, &vector->interrupt,
+ vector->virq);
+ kvm_irqchip_release_virq(kvm_state, vector->virq);
+ vector->virq = -1;
+ } else {
+ qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt),
+ NULL, NULL, NULL);
+ }
+ event_notifier_cleanup(&vector->interrupt);
+ }
+
+ g_free(vdev->msi_vectors);
+
+ if (ret > 0 && ret != vdev->nr_vectors) {
+ vdev->nr_vectors = ret;
+ goto retry;
+ }
+ vdev->nr_vectors = 0;
+
+ return;
+ }
+
+ msi_set_qsize(&vdev->pdev, vdev->nr_vectors);
+
+ DPRINTF("%s(%04x:%02x:%02x.%x) Enabled %d MSI vectors\n", __func__,
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function, vdev->nr_vectors);
+}
+
+static void vfio_disable_msi_x(VFIODevice *vdev, bool msix)
+{
+ int i;
+
+ vfio_disable_irqindex(vdev, msix ? VFIO_PCI_MSIX_IRQ_INDEX :
+ VFIO_PCI_MSI_IRQ_INDEX);
+
+ for (i = 0; i < vdev->nr_vectors; i++) {
+ VFIOMSIVector *vector = &vdev->msi_vectors[i];
+
+ if (!vector->use) {
+ continue;
+ }
+
+ if (vector->virq >= 0) {
+ kvm_irqchip_remove_irqfd_notifier(kvm_state,
+ &vector->interrupt, vector->virq);
+ kvm_irqchip_release_virq(kvm_state, vector->virq);
+ vector->virq = -1;
+ } else {
+ qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt),
+ NULL, NULL, NULL);
+ }
+
+ if (msix) {
+ msix_vector_unuse(&vdev->pdev, i);
+ }
+
+ event_notifier_cleanup(&vector->interrupt);
+ }
+
+ g_free(vdev->msi_vectors);
+ vdev->msi_vectors = NULL;
+ vdev->nr_vectors = 0;
+
+ if (!msix) {
+ msi_set_qsize(&vdev->pdev, 0); /* Actually still means 1 vector */
+ }
+
+ DPRINTF("%s(%04x:%02x:%02x.%x, msi%s)\n", __func__,
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function, msix ? "x" : "");
+
+ vfio_enable_intx(vdev);
+}
+
+/*
+ * IO Port/MMIO - Beware of the endians, VFIO is always little endian
+ */
+static void vfio_bar_write(void *opaque, target_phys_addr_t addr,
+ uint64_t data, unsigned size)
+{
+ VFIOBAR *bar = opaque;
+ union {
+ uint8_t byte;
+ uint16_t word;
+ uint32_t dword;
+ uint64_t qword;
+ } buf;
+
+ switch (size) {
+ case 1:
+ buf.byte = data;
+ break;
+ case 2:
+ buf.word = cpu_to_le16(data);
+ break;
+ case 4:
+ buf.dword = cpu_to_le32(data);
+ break;
+ default:
+ hw_error("vfio: unsupported write size, %d bytes\n", size);
+ break;
+ }
+
+ if (pwrite(bar->fd, &buf, size, bar->fd_offset + addr) != size) {
+ error_report("%s(,0x%"TARGET_PRIxPHYS", 0x%"PRIx64", %d) failed: %m\n",
+ __func__, addr, data, size);
+ }
+
+ DPRINTF("%s(BAR%d+0x%"TARGET_PRIxPHYS", 0x%"PRIx64", %d)\n",
+ __func__, bar->nr, addr, data, size);
+
+ /*
+ * A read or write to a BAR always signals an INTx EOI. This will
+ * do nothing if not pending (including not in INTx mode). We assume
+ * that a BAR access is in response to an interrupt and that BAR
+ * accesses will service the interrupt. Unfortunately, we don't know
+ * which access will service the interrupt, so we're potentially
+ * getting quite a few host interrupts per guest interrupt.
+ */
+ vfio_eoi(DO_UPCAST(VFIODevice, bars[bar->nr], bar));
+}
+
+static uint64_t vfio_bar_read(void *opaque,
+ target_phys_addr_t addr, unsigned size)
+{
+ VFIOBAR *bar = opaque;
+ union {
+ uint8_t byte;
+ uint16_t word;
+ uint32_t dword;
+ uint64_t qword;
+ } buf;
+ uint64_t data = 0;
+
+ if (pread(bar->fd, &buf, size, bar->fd_offset + addr) != size) {
+ error_report("%s(,0x%"TARGET_PRIxPHYS", %d) failed: %m\n",
+ __func__, addr, size);
+ return (uint64_t)-1;
+ }
+
+ switch (size) {
+ case 1:
+ data = buf.byte;
+ break;
+ case 2:
+ data = le16_to_cpu(buf.word);
+ break;
+ case 4:
+ data = le32_to_cpu(buf.dword);
+ break;
+ default:
+ hw_error("vfio: unsupported read size, %d bytes\n", size);
+ break;
+ }
+
+ DPRINTF("%s(BAR%d+0x%"TARGET_PRIxPHYS", %d) = 0x%"PRIx64"\n",
+ __func__, bar->nr, addr, size, data);
+
+ /* Same as write above */
+ vfio_eoi(DO_UPCAST(VFIODevice, bars[bar->nr], bar));
+
+ return data;
+}
+
+static const MemoryRegionOps vfio_bar_ops = {
+ .read = vfio_bar_read,
+ .write = vfio_bar_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+/*
+ * PCI config space
+ */
+static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len)
+{
+ VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev);
+ uint32_t val = 0;
+
+ /*
+ * We only need QEMU PCI config support for the ROM BAR, the MSI and MSIX
+ * capabilities, and the multifunction bit below. We let VFIO handle
+ * virtualizing everything else. Performance is not a concern here.
+ */
+ if (ranges_overlap(addr, len, PCI_ROM_ADDRESS, 4) ||
+ (pdev->cap_present & QEMU_PCI_CAP_MSIX &&
+ ranges_overlap(addr, len, pdev->msix_cap, MSIX_CAP_LENGTH)) ||
+ (pdev->cap_present & QEMU_PCI_CAP_MSI &&
+ ranges_overlap(addr, len, pdev->msi_cap, vdev->msi_cap_size))) {
+
+ val = pci_default_read_config(pdev, addr, len);
+ } else {
+ if (pread(vdev->fd, &val, len, vdev->config_offset + addr) != len) {
+ error_report("%s(%04x:%02x:%02x.%x, 0x%x, 0x%x) failed: %m\n",
+ __func__, vdev->host.domain, vdev->host.bus,
+ vdev->host.slot, vdev->host.function, addr, len);
+ return -errno;
+ }
+ val = le32_to_cpu(val);
+ }
+
+ /* Multifunction bit is virualized in QEMU */
+ if (unlikely(ranges_overlap(addr, len, PCI_HEADER_TYPE, 1))) {
+ uint32_t mask = PCI_HEADER_TYPE_MULTI_FUNCTION;
+
+ if (len == 4) {
+ mask <<= 16;
+ }
+
+ if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) {
+ val |= mask;
+ } else {
+ val &= ~mask;
+ }
+ }
+
+ DPRINTF("%s(%04x:%02x:%02x.%x, @0x%x, len=0x%x) %x\n", __func__,
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function, addr, len, val);
+
+ return val;
+}
+
+static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr,
+ uint32_t val, int len)
+{
+ VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev);
+ uint32_t val_le = cpu_to_le32(val);
+
+ DPRINTF("%s(%04x:%02x:%02x.%x, @0x%x, 0x%x, len=0x%x)\n", __func__,
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function, addr, val, len);
+
+ /* Write everything to VFIO, let it filter out what we can't write */
+ if (pwrite(vdev->fd, &val_le, len, vdev->config_offset + addr) != len) {
+ error_report("%s(%04x:%02x:%02x.%x, 0x%x, 0x%x, 0x%x) failed: %m\n",
+ __func__, vdev->host.domain, vdev->host.bus,
+ vdev->host.slot, vdev->host.function, addr, val, len);
+ }
+
+ /* Write standard header bits to emulation */
+ if (addr < PCI_CONFIG_HEADER_SIZE) {
+ pci_default_write_config(pdev, addr, val, len);
+ return;
+ }
+
+ /* MSI/MSI-X Enabling/Disabling */
+ if (pdev->cap_present & QEMU_PCI_CAP_MSI &&
+ ranges_overlap(addr, len, pdev->msi_cap, vdev->msi_cap_size)) {
+ int is_enabled, was_enabled = msi_enabled(pdev);
+
+ pci_default_write_config(pdev, addr, val, len);
+
+ is_enabled = msi_enabled(pdev);
+
+ if (!was_enabled && is_enabled) {
+ vfio_enable_msi(vdev);
+ } else if (was_enabled && !is_enabled) {
+ vfio_disable_msi_x(vdev, false);
+ }
+ }
+
+ if (pdev->cap_present & QEMU_PCI_CAP_MSIX &&
+ ranges_overlap(addr, len, pdev->msix_cap, MSIX_CAP_LENGTH)) {
+ int is_enabled, was_enabled = msix_enabled(pdev);
+
+ pci_default_write_config(pdev, addr, val, len);
+
+ is_enabled = msix_enabled(pdev);
+
+ if (!was_enabled && is_enabled) {
+ /* vfio_msix_vector_use handles this automatically */
+ } else if (was_enabled && !is_enabled) {
+ vfio_disable_msi_x(vdev, true);
+ }
+ }
+}
+
+/*
+ * DMA - Mapping and unmapping for the "type1" IOMMU interface used on x86
+ */
+static int vfio_dma_map(VFIOContainer *container, target_phys_addr_t iova,
+ ram_addr_t size, void *vaddr, bool readonly)
+{
+ struct vfio_iommu_type1_dma_map map = {
+ .argsz = sizeof(map),
+ .flags = VFIO_DMA_MAP_FLAG_READ,
+ .vaddr = (__u64)(intptr_t)vaddr,
+ .iova = iova,
+ .size = size,
+ };
+
+ if (!readonly) {
+ map.flags |= VFIO_DMA_MAP_FLAG_WRITE;
+ }
+
+ if (ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map)) {
+ DPRINTF("VFIO_MAP_DMA: %d\n", -errno);
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int vfio_dma_unmap(VFIOContainer *container,
+ target_phys_addr_t iova, ram_addr_t size)
+{
+ struct vfio_iommu_type1_dma_unmap unmap = {
+ .argsz = sizeof(unmap),
+ .flags = 0,
+ .iova = iova,
+ .size = size,
+ };
+
+ if (ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, &unmap)) {
+ DPRINTF("VFIO_UNMAP_DMA: %d\n", -errno);
+ return -errno;
+ }
+
+ return 0;
+}
+
+static void vfio_listener_dummy1(MemoryListener *listener)
+{
+ /* We don't do batching (begin/commit) or care about logging */
+}
+
+static void vfio_listener_dummy2(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+ /* We don't do logging or care about nops */
+}
+
+static void vfio_listener_dummy3(MemoryListener *listener,
+ MemoryRegionSection *section,
+ bool match_data, uint64_t data,
+ EventNotifier *e)
+{
+ /* We don't care about eventfds */
+}
+
+static bool vfio_listener_skipped_section(MemoryRegionSection *section)
+{
+ return !memory_region_is_ram(section->mr);
+}
+
+static void vfio_listener_region_add(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+ VFIOContainer *container = container_of(listener, VFIOContainer,
+ iommu_data.listener);
+ target_phys_addr_t iova, end;
+ void *vaddr;
+ int ret;
+
+ if (vfio_listener_skipped_section(section)) {
+ DPRINTF("vfio: SKIPPING region_add %"TARGET_PRIxPHYS" - %"PRIx64"\n",
+ section->offset_within_address_space,
+ section->offset_within_address_space + section->size - 1);
+ return;
+ }
+
+ if (unlikely((section->offset_within_address_space & ~TARGET_PAGE_MASK) !=
+ (section->offset_within_region & ~TARGET_PAGE_MASK))) {
+ error_report("%s received unaligned region\n", __func__);
+ return;
+ }
+
+ iova = TARGET_PAGE_ALIGN(section->offset_within_address_space);
+ end = (section->offset_within_address_space + section->size) &
+ TARGET_PAGE_MASK;
+
+ if (iova >= end) {
+ return;
+ }
+
+ vaddr = memory_region_get_ram_ptr(section->mr) +
+ section->offset_within_region +
+ (iova - section->offset_within_address_space);
+
+ DPRINTF("vfio: region_add %"TARGET_PRIxPHYS" - %"TARGET_PRIxPHYS" [%p]\n",
+ iova, end - 1, vaddr);
+
+ ret = vfio_dma_map(container, iova, end - iova, vaddr, section->readonly);
+ if (ret) {
+ error_report("vfio_dma_map(%p, 0x%"TARGET_PRIxPHYS", "
+ "0x%"TARGET_PRIxPHYS", %p) = %d (%m)\n",
+ container, iova, end - iova, vaddr, ret);
+ }
+}
+
+static void vfio_listener_region_del(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+ VFIOContainer *container = container_of(listener, VFIOContainer,
+ iommu_data.listener);
+ target_phys_addr_t iova, end;
+ int ret;
+
+ if (vfio_listener_skipped_section(section)) {
+ DPRINTF("vfio: SKIPPING region_del %"TARGET_PRIxPHYS" - %"PRIx64"\n",
+ section->offset_within_address_space,
+ section->offset_within_address_space + section->size - 1);
+ return;
+ }
+
+ if (unlikely((section->offset_within_address_space & ~TARGET_PAGE_MASK) !=
+ (section->offset_within_region & ~TARGET_PAGE_MASK))) {
+ error_report("%s received unaligned region\n", __func__);
+ return;
+ }
+
+ iova = TARGET_PAGE_ALIGN(section->offset_within_address_space);
+ end = (section->offset_within_address_space + section->size) &
+ TARGET_PAGE_MASK;
+
+ if (iova >= end) {
+ return;
+ }
+
+ DPRINTF("vfio: region_del %"TARGET_PRIxPHYS" - %"TARGET_PRIxPHYS"\n",
+ iova, end - 1);
+
+ ret = vfio_dma_unmap(container, iova, end - iova);
+ if (ret) {
+ error_report("vfio_dma_unmap(%p, 0x%"TARGET_PRIxPHYS", "
+ "0x%"TARGET_PRIxPHYS") = %d (%m)\n",
+ container, iova, end - iova, ret);
+ }
+}
+
+static MemoryListener vfio_memory_listener = {
+ .begin = vfio_listener_dummy1,
+ .commit = vfio_listener_dummy1,
+ .region_add = vfio_listener_region_add,
+ .region_del = vfio_listener_region_del,
+ .region_nop = vfio_listener_dummy2,
+ .log_start = vfio_listener_dummy2,
+ .log_stop = vfio_listener_dummy2,
+ .log_sync = vfio_listener_dummy2,
+ .log_global_start = vfio_listener_dummy1,
+ .log_global_stop = vfio_listener_dummy1,
+ .eventfd_add = vfio_listener_dummy3,
+ .eventfd_del = vfio_listener_dummy3,
+};
+
+static void vfio_listener_release(VFIOContainer *container)
+{
+ memory_listener_unregister(&container->iommu_data.listener);
+}
+
+/*
+ * Interrupt setup
+ */
+static void vfio_disable_interrupts(VFIODevice *vdev)
+{
+ switch (vdev->interrupt) {
+ case VFIO_INT_INTx:
+ vfio_disable_intx(vdev);
+ break;
+ case VFIO_INT_MSI:
+ vfio_disable_msi_x(vdev, false);
+ break;
+ case VFIO_INT_MSIX:
+ vfio_disable_msi_x(vdev, true);
+ break;
+ }
+}
+
+static int vfio_setup_msi(VFIODevice *vdev, int pos)
+{
+ uint16_t ctrl;
+ bool msi_64bit, msi_maskbit;
+ int ret, entries;
+
+ /*
+ * TODO: don't peek into msi_supported, let msi_init fail and
+ * check for ENOTSUP
+ */
+ if (!msi_supported) {
+ return 0;
+ }
+
+ if (pread(vdev->fd, &ctrl, sizeof(ctrl),
+ vdev->config_offset + pos + PCI_CAP_FLAGS) != sizeof(ctrl)) {
+ return -errno;
+ }
+ ctrl = le16_to_cpu(ctrl);
+
+ msi_64bit = !!(ctrl & PCI_MSI_FLAGS_64BIT);
+ msi_maskbit = !!(ctrl & PCI_MSI_FLAGS_MASKBIT);
+ entries = 1 << ((ctrl & PCI_MSI_FLAGS_QMASK) >> 1);
+
+ DPRINTF("%04x:%02x:%02x.%x PCI MSI CAP @0x%x\n", vdev->host.domain,
+ vdev->host.bus, vdev->host.slot, vdev->host.function, pos);
+
+ ret = msi_init(&vdev->pdev, pos, entries, msi_64bit, msi_maskbit);
+ if (ret < 0) {
+ error_report("vfio: msi_init failed\n");
+ return ret;
+ }
+ vdev->msi_cap_size = 0xa + (msi_maskbit ? 0xa : 0) + (msi_64bit ? 0x4 : 0);
+
+ return 0;
+}
+
+/*
+ * We don't have any control over how pci_add_capability() inserts
+ * capabilities into the chain. In order to setup MSI-X we need a
+ * MemoryRegion for the BAR. In order to setup the BAR and not
+ * attempt to mmap the MSI-X table area, which VFIO won't allow, we
+ * need to first look for where the MSI-X table lives. So we
+ * unfortunately split MSI-X setup across two functions.
+ */
+static int vfio_early_setup_msix(VFIODevice *vdev)
+{
+ uint8_t pos;
+ uint16_t ctrl;
+ uint32_t table, pba;
+
+ pos = pci_find_capability(&vdev->pdev, PCI_CAP_ID_MSIX);
+ if (!pos) {
+ return 0;
+ }
+
+ if (pread(vdev->fd, &ctrl, sizeof(ctrl),
+ vdev->config_offset + pos + PCI_CAP_FLAGS) != sizeof(ctrl)) {
+ return -errno;
+ }
+
+ if (pread(vdev->fd, &table, sizeof(table),
+ vdev->config_offset + pos + PCI_MSIX_TABLE) != sizeof(table)) {
+ return -errno;
+ }
+
+ if (pread(vdev->fd, &pba, sizeof(pba),
+ vdev->config_offset + pos + PCI_MSIX_PBA) != sizeof(pba)) {
+ return -errno;
+ }
+
+ ctrl = le16_to_cpu(ctrl);
+ table = le32_to_cpu(table);
+ pba = le32_to_cpu(pba);
+
+ vdev->msix = g_malloc0(sizeof(*(vdev->msix)));
+ vdev->msix->table_bar = table & PCI_MSIX_FLAGS_BIRMASK;
+ vdev->msix->table_offset = table & ~PCI_MSIX_FLAGS_BIRMASK;
+ vdev->msix->pba_bar = pba & PCI_MSIX_FLAGS_BIRMASK;
+ vdev->msix->pba_offset = pba & ~PCI_MSIX_FLAGS_BIRMASK;
+ vdev->msix->entries = (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1;
+
+ DPRINTF("%04x:%02x:%02x.%x "
+ "PCI MSI-X CAP @0x%x, BAR %d, offset 0x%x, entries %d\n",
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function, pos, vdev->msix->table_bar,
+ vdev->msix->table_offset, vdev->msix->entries);
+
+ return 0;
+}
+
+static int vfio_setup_msix(VFIODevice *vdev, int pos)
+{
+ int ret;
+
+ /*
+ * TODO: don't peek into msi_supported, let msix_init fail and
+ * check for ENOTSUP
+ */
+ if (!msi_supported) {
+ return 0;
+ }
+
+ ret = msix_init(&vdev->pdev, vdev->msix->entries,
+ &vdev->bars[vdev->msix->table_bar].mem,
+ vdev->msix->table_bar, vdev->msix->table_offset,
+ &vdev->bars[vdev->msix->pba_bar].mem,
+ vdev->msix->pba_bar, vdev->msix->pba_offset, pos);
+ if (ret < 0) {
+ error_report("vfio: msix_init failed\n");
+ return ret;
+ }
+
+ ret = msix_set_vector_notifiers(&vdev->pdev, vfio_msix_vector_use,
+ vfio_msix_vector_release);
+ if (ret) {
+ error_report("vfio: msix_set_vector_notifiers failed %d\n", ret);
+ msix_uninit(&vdev->pdev, &vdev->bars[vdev->msix->table_bar].mem,
+ &vdev->bars[vdev->msix->pba_bar].mem);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void vfio_teardown_msi(VFIODevice *vdev)
+{
+ msi_uninit(&vdev->pdev);
+
+ if (vdev->msix) {
+ /* FIXME: Why can't unset just silently do nothing?? */
+ if (vdev->pdev.msix_vector_use_notifier &&
+ vdev->pdev.msix_vector_release_notifier) {
+ msix_unset_vector_notifiers(&vdev->pdev);
+ }
+
+ msix_uninit(&vdev->pdev, &vdev->bars[vdev->msix->table_bar].mem,
+ &vdev->bars[vdev->msix->pba_bar].mem);
+ }
+}
+
+/*
+ * Resource setup
+ */
+static void vfio_mmap_set_enabled(VFIODevice *vdev, bool enabled)
+{
+ int i;
+
+ for (i = 0; i < PCI_ROM_SLOT; i++) {
+ VFIOBAR *bar = &vdev->bars[i];
+
+ if (!bar->size) {
+ continue;
+ }
+
+ memory_region_set_enabled(&bar->mmap_mem, enabled);
+ if (vdev->msix && vdev->msix->table_bar == i) {
+ memory_region_set_enabled(&vdev->msix->mmap_mem, enabled);
+ }
+ }
+}
+
+static void vfio_unmap_bar(VFIODevice *vdev, int nr)
+{
+ VFIOBAR *bar = &vdev->bars[nr];
+
+ if (!bar->size) {
+ return;
+ }
+
+ memory_region_del_subregion(&bar->mem, &bar->mmap_mem);
+ munmap(bar->mmap, memory_region_size(&bar->mmap_mem));
+
+ if (vdev->msix && vdev->msix->table_bar == nr) {
+ memory_region_del_subregion(&bar->mem, &vdev->msix->mmap_mem);
+ munmap(vdev->msix->mmap, memory_region_size(&vdev->msix->mmap_mem));
+ }
+
+ memory_region_destroy(&bar->mem);
+}
+
+static int vfio_mmap_bar(VFIOBAR *bar, MemoryRegion *mem, MemoryRegion *submem,
+ void **map, size_t size, off_t offset,
+ const char *name)
+{
+ int ret = 0;
+
+ if (size && bar->flags & VFIO_REGION_INFO_FLAG_MMAP) {
+ int prot = 0;
+
+ if (bar->flags & VFIO_REGION_INFO_FLAG_READ) {
+ prot |= PROT_READ;
+ }
+
+ if (bar->flags & VFIO_REGION_INFO_FLAG_WRITE) {
+ prot |= PROT_WRITE;
+ }
+
+ *map = mmap(NULL, size, prot, MAP_SHARED,
+ bar->fd, bar->fd_offset + offset);
+ if (*map == MAP_FAILED) {
+ *map = NULL;
+ ret = -errno;
+ goto empty_region;
+ }
+
+ memory_region_init_ram_ptr(submem, name, size, *map);
+ } else {
+empty_region:
+ /* Create a zero sized sub-region to make cleanup easy. */
+ memory_region_init(submem, name, 0);
+ }
+
+ memory_region_add_subregion(mem, offset, submem);
+
+ return ret;
+}
+
+static void vfio_map_bar(VFIODevice *vdev, int nr)
+{
+ VFIOBAR *bar = &vdev->bars[nr];
+ unsigned size = bar->size;
+ char name[64];
+ uint32_t pci_bar;
+ uint8_t type;
+ int ret;
+
+ /* Skip both unimplemented BARs and the upper half of 64bit BARS. */
+ if (!size) {
+ return;
+ }
+
+ snprintf(name, sizeof(name), "VFIO %04x:%02x:%02x.%x BAR %d",
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function, nr);
+
+ /* Determine what type of BAR this is for registration */
+ ret = pread(vdev->fd, &pci_bar, sizeof(pci_bar),
+ vdev->config_offset + PCI_BASE_ADDRESS_0 + (4 * nr));
+ if (ret != sizeof(pci_bar)) {
+ error_report("vfio: Failed to read BAR %d (%m)\n", nr);
+ return;
+ }
+
+ pci_bar = le32_to_cpu(pci_bar);
+ type = pci_bar & (pci_bar & PCI_BASE_ADDRESS_SPACE_IO ?
+ ~PCI_BASE_ADDRESS_IO_MASK : ~PCI_BASE_ADDRESS_MEM_MASK);
+
+ /* A "slow" read/write mapping underlies all BARs */
+ memory_region_init_io(&bar->mem, &vfio_bar_ops, bar, name, size);
+ pci_register_bar(&vdev->pdev, nr, type, &bar->mem);
+
+ /*
+ * We can't mmap areas overlapping the MSIX vector table, so we
+ * potentially insert a direct-mapped subregion before and after it.
+ */
+ if (vdev->msix && vdev->msix->table_bar == nr) {
+ size = vdev->msix->table_offset & TARGET_PAGE_MASK;
+ }
+
+ strncat(name, " mmap", sizeof(name) - strlen(name) - 1);
+ if (vfio_mmap_bar(bar, &bar->mem,
+ &bar->mmap_mem, &bar->mmap, size, 0, name)) {
+ error_report("%s unsupported. Performance may be slow\n", name);
+ }
+
+ if (vdev->msix && vdev->msix->table_bar == nr) {
+ unsigned start;
+
+ start = TARGET_PAGE_ALIGN(vdev->msix->table_offset +
+ (vdev->msix->entries * PCI_MSIX_ENTRY_SIZE));
+
+ size = start < bar->size ? bar->size - start : 0;
+ strncat(name, " msix-hi", sizeof(name) - strlen(name) - 1);
+ /* VFIOMSIXInfo contains another MemoryRegion for this mapping */
+ if (vfio_mmap_bar(bar, &bar->mem, &vdev->msix->mmap_mem,
+ &vdev->msix->mmap, size, start, name)) {
+ error_report("%s unsupported. Performance may be slow\n", name);
+ }
+ }
+}
+
+static void vfio_map_bars(VFIODevice *vdev)
+{
+ int i;
+
+ for (i = 0; i < PCI_ROM_SLOT; i++) {
+ vfio_map_bar(vdev, i);
+ }
+}
+
+static void vfio_unmap_bars(VFIODevice *vdev)
+{
+ int i;
+
+ for (i = 0; i < PCI_ROM_SLOT; i++) {
+ vfio_unmap_bar(vdev, i);
+ }
+}
+
+/*
+ * General setup
+ */
+static uint8_t vfio_std_cap_max_size(PCIDevice *pdev, uint8_t pos)
+{
+ uint8_t tmp, next = 0xff;
+
+ for (tmp = pdev->config[PCI_CAPABILITY_LIST]; tmp;
+ tmp = pdev->config[tmp + 1]) {
+ if (tmp > pos && tmp < next) {
+ next = tmp;
+ }
+ }
+
+ return next - pos;
+}
+
+static int vfio_add_std_cap(VFIODevice *vdev, uint8_t pos)
+{
+ PCIDevice *pdev = &vdev->pdev;
+ uint8_t cap_id, next, size;
+ int ret;
+
+ cap_id = pdev->config[pos];
+ next = pdev->config[pos + 1];
+
+ /*
+ * If it becomes important to configure capabilities to their actual
+ * size, use this as the default when it's something we don't recognize.
+ * Since QEMU doesn't actually handle many of the config accesses,
+ * exact size doesn't seem worthwhile.
+ */
+ size = vfio_std_cap_max_size(pdev, pos);
+
+ /*
+ * pci_add_capability always inserts the new capability at the head
+ * of the chain. Therefore to end up with a chain that matches the
+ * physical device, we insert from the end by making this recursive.
+ * This is also why we pre-caclulate size above as cached config space
+ * will be changed as we unwind the stack.
+ */
+ if (next) {
+ ret = vfio_add_std_cap(vdev, next);
+ if (ret) {
+ return ret;
+ }
+ } else {
+ pdev->config[PCI_CAPABILITY_LIST] = 0; /* Begin the rebuild */
+ }
+
+ switch (cap_id) {
+ case PCI_CAP_ID_MSI:
+ ret = vfio_setup_msi(vdev, pos);
+ break;
+ case PCI_CAP_ID_MSIX:
+ ret = vfio_setup_msix(vdev, pos);
+ break;
+ default:
+ ret = pci_add_capability(pdev, cap_id, pos, size);
+ break;
+ }
+
+ if (ret < 0) {
+ error_report("vfio: %04x:%02x:%02x.%x Error adding PCI capability "
+ "0x%x[0x%x]@0x%x: %d\n", vdev->host.domain,
+ vdev->host.bus, vdev->host.slot, vdev->host.function,
+ cap_id, size, pos, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int vfio_add_capabilities(VFIODevice *vdev)
+{
+ PCIDevice *pdev = &vdev->pdev;
+
+ if (!(pdev->config[PCI_STATUS] & PCI_STATUS_CAP_LIST) ||
+ !pdev->config[PCI_CAPABILITY_LIST]) {
+ return 0; /* Nothing to add */
+ }
+
+ return vfio_add_std_cap(vdev, pdev->config[PCI_CAPABILITY_LIST]);
+}
+
+static int vfio_load_rom(VFIODevice *vdev)
+{
+ uint64_t size = vdev->rom_size;
+ char name[32];
+ off_t off = 0, voff = vdev->rom_offset;
+ ssize_t bytes;
+ void *ptr;
+
+ /* If loading ROM from file, pci handles it */
+ if (vdev->pdev.romfile || !vdev->pdev.rom_bar || !size) {
+ return 0;
+ }
+
+ DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain,
+ vdev->host.bus, vdev->host.slot, vdev->host.function);
+
+ snprintf(name, sizeof(name), "vfio[%04x:%02x:%02x.%x].rom",
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function);
+ memory_region_init_ram(&vdev->pdev.rom, name, size);
+ ptr = memory_region_get_ram_ptr(&vdev->pdev.rom);
+ memset(ptr, 0xff, size);
+
+ while (size) {
+ bytes = pread(vdev->fd, ptr + off, size, voff + off);
+ if (bytes == 0) {
+ break; /* expect that we could get back less than the ROM BAR */
+ } else if (bytes > 0) {
+ off += bytes;
+ size -= bytes;
+ } else {
+ if (errno == EINTR || errno == EAGAIN) {
+ continue;
+ }
+ error_report("vfio: Error reading device ROM: %m\n");
+ memory_region_destroy(&vdev->pdev.rom);
+ return -errno;
+ }
+ }
+
+ pci_register_bar(&vdev->pdev, PCI_ROM_SLOT, 0, &vdev->pdev.rom);
+ vdev->pdev.has_rom = true;
+ return 0;
+}
+
+static int vfio_connect_container(VFIOGroup *group)
+{
+ VFIOContainer *container;
+ int ret, fd;
+
+ if (group->container) {
+ return 0;
+ }
+
+ QLIST_FOREACH(container, &container_list, next) {
+ if (!ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) {
+ group->container = container;
+ QLIST_INSERT_HEAD(&container->group_list, group, container_next);
+ return 0;
+ }
+ }
+
+ fd = qemu_open("/dev/vfio/vfio", O_RDWR);
+ if (fd < 0) {
+ error_report("vfio: failed to open /dev/vfio/vfio: %m\n");
+ return -errno;
+ }
+
+ ret = ioctl(fd, VFIO_GET_API_VERSION);
+ if (ret != VFIO_API_VERSION) {
+ error_report("vfio: supported vfio version: %d, "
+ "reported version: %d\n", VFIO_API_VERSION, ret);
+ close(fd);
+ return -EINVAL;
+ }
+
+ container = g_malloc0(sizeof(*container));
+ container->fd = fd;
+
+ if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU)) {
+ ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd);
+ if (ret) {
+ error_report("vfio: failed to set group container: %m\n");
+ g_free(container);
+ close(fd);
+ return -errno;
+ }
+
+ ret = ioctl(fd, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU);
+ if (ret) {
+ error_report("vfio: failed to set iommu for container: %m\n");
+ g_free(container);
+ close(fd);
+ return -errno;
+ }
+
+ container->iommu_data.listener = vfio_memory_listener;
+ container->iommu_data.release = vfio_listener_release;
+
+ memory_listener_register(&container->iommu_data.listener,
+ get_system_memory());
+ } else {
+ error_report("vfio: No available IOMMU models\n");
+ g_free(container);
+ close(fd);
+ return -EINVAL;
+ }
+
+ QLIST_INIT(&container->group_list);
+ QLIST_INSERT_HEAD(&container_list, container, next);
+
+ group->container = container;
+ QLIST_INSERT_HEAD(&container->group_list, group, container_next);
+
+ return 0;
+}
+
+static void vfio_disconnect_container(VFIOGroup *group)
+{
+ VFIOContainer *container = group->container;
+
+ if (ioctl(group->fd, VFIO_GROUP_UNSET_CONTAINER, &container->fd)) {
+ error_report("vfio: error disconnecting group %d from container\n",
+ group->groupid);
+ }
+
+ QLIST_REMOVE(group, container_next);
+ group->container = NULL;
+
+ if (QLIST_EMPTY(&container->group_list)) {
+ if (container->iommu_data.release) {
+ container->iommu_data.release(container);
+ }
+ QLIST_REMOVE(container, next);
+ DPRINTF("vfio_disconnect_container: close container->fd\n");
+ close(container->fd);
+ g_free(container);
+ }
+}
+
+static VFIOGroup *vfio_get_group(int groupid)
+{
+ VFIOGroup *group;
+ char path[32];
+ struct vfio_group_status status = { .argsz = sizeof(status) };
+
+ QLIST_FOREACH(group, &group_list, next) {
+ if (group->groupid == groupid) {
+ return group;
+ }
+ }
+
+ group = g_malloc0(sizeof(*group));
+
+ snprintf(path, sizeof(path), "/dev/vfio/%d", groupid);
+ group->fd = qemu_open(path, O_RDWR);
+ if (group->fd < 0) {
+ error_report("vfio: error opening %s: %m\n", path);
+ g_free(group);
+ return NULL;
+ }
+
+ if (ioctl(group->fd, VFIO_GROUP_GET_STATUS, &status)) {
+ error_report("vfio: error getting group status: %m\n");
+ close(group->fd);
+ g_free(group);
+ return NULL;
+ }
+
+ if (!(status.flags & VFIO_GROUP_FLAGS_VIABLE)) {
+ error_report("vfio: error, group %d is not viable, please ensure "
+ "all devices within the iommu_group are bound to their "
+ "vfio bus driver.\n", groupid);
+ close(group->fd);
+ g_free(group);
+ return NULL;
+ }
+
+ group->groupid = groupid;
+ QLIST_INIT(&group->device_list);
+
+ if (vfio_connect_container(group)) {
+ error_report("vfio: failed to setup container for group %d\n", groupid);
+ close(group->fd);
+ g_free(group);
+ return NULL;
+ }
+
+ QLIST_INSERT_HEAD(&group_list, group, next);
+
+ return group;
+}
+
+static void vfio_put_group(VFIOGroup *group)
+{
+ if (!QLIST_EMPTY(&group->device_list)) {
+ return;
+ }
+
+ vfio_disconnect_container(group);
+ QLIST_REMOVE(group, next);
+ DPRINTF("vfio_put_group: close group->fd\n");
+ close(group->fd);
+ g_free(group);
+}
+
+static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev)
+{
+ struct vfio_device_info dev_info = { .argsz = sizeof(dev_info) };
+ struct vfio_region_info reg_info = { .argsz = sizeof(reg_info) };
+ int ret, i;
+
+ ret = ioctl(group->fd, VFIO_GROUP_GET_DEVICE_FD, name);
+ if (ret < 0) {
+ error_report("vfio: error getting device %s from group %d: %m\n",
+ name, group->groupid);
+ error_report("Verify all devices in group %d are bound to vfio-pci "
+ "or pci-stub and not already in use\n", group->groupid);
+ return ret;
+ }
+
+ vdev->fd = ret;
+ vdev->group = group;
+ QLIST_INSERT_HEAD(&group->device_list, vdev, next);
+
+ /* Sanity check device */
+ ret = ioctl(vdev->fd, VFIO_DEVICE_GET_INFO, &dev_info);
+ if (ret) {
+ error_report("vfio: error getting device info: %m\n");
+ goto error;
+ }
+
+ DPRINTF("Device %s flags: %u, regions: %u, irgs: %u\n", name,
+ dev_info.flags, dev_info.num_regions, dev_info.num_irqs);
+
+ if (!(dev_info.flags & VFIO_DEVICE_FLAGS_PCI)) {
+ error_report("vfio: Um, this isn't a PCI device\n");
+ goto error;
+ }
+
+ vdev->reset_works = !!(dev_info.flags & VFIO_DEVICE_FLAGS_RESET);
+ if (!vdev->reset_works) {
+ error_report("Warning, device %s does not support reset\n", name);
+ }
+
+ if (dev_info.num_regions != VFIO_PCI_NUM_REGIONS) {
+ error_report("vfio: unexpected number of io regions %u\n",
+ dev_info.num_regions);
+ goto error;
+ }
+
+ if (dev_info.num_irqs != VFIO_PCI_NUM_IRQS) {
+ error_report("vfio: unexpected number of irqs %u\n", dev_info.num_irqs);
+ goto error;
+ }
+
+ for (i = VFIO_PCI_BAR0_REGION_INDEX; i < VFIO_PCI_ROM_REGION_INDEX; i++) {
+ reg_info.index = i;
+
+ ret = ioctl(vdev->fd, VFIO_DEVICE_GET_REGION_INFO, &reg_info);
+ if (ret) {
+ error_report("vfio: Error getting region %d info: %m\n", i);
+ goto error;
+ }
+
+ DPRINTF("Device %s region %d:\n", name, i);
+ DPRINTF(" size: 0x%lx, offset: 0x%lx, flags: 0x%lx\n",
+ (unsigned long)reg_info.size, (unsigned long)reg_info.offset,
+ (unsigned long)reg_info.flags);
+
+ vdev->bars[i].flags = reg_info.flags;
+ vdev->bars[i].size = reg_info.size;
+ vdev->bars[i].fd_offset = reg_info.offset;
+ vdev->bars[i].fd = vdev->fd;
+ vdev->bars[i].nr = i;
+ }
+
+ reg_info.index = VFIO_PCI_ROM_REGION_INDEX;
+
+ ret = ioctl(vdev->fd, VFIO_DEVICE_GET_REGION_INFO, &reg_info);
+ if (ret) {
+ error_report("vfio: Error getting ROM info: %m\n");
+ goto error;
+ }
+
+ DPRINTF("Device %s ROM:\n", name);
+ DPRINTF(" size: 0x%lx, offset: 0x%lx, flags: 0x%lx\n",
+ (unsigned long)reg_info.size, (unsigned long)reg_info.offset,
+ (unsigned long)reg_info.flags);
+
+ vdev->rom_size = reg_info.size;
+ vdev->rom_offset = reg_info.offset;
+
+ reg_info.index = VFIO_PCI_CONFIG_REGION_INDEX;
+
+ ret = ioctl(vdev->fd, VFIO_DEVICE_GET_REGION_INFO, &reg_info);
+ if (ret) {
+ error_report("vfio: Error getting config info: %m\n");
+ goto error;
+ }
+
+ DPRINTF("Device %s config:\n", name);
+ DPRINTF(" size: 0x%lx, offset: 0x%lx, flags: 0x%lx\n",
+ (unsigned long)reg_info.size, (unsigned long)reg_info.offset,
+ (unsigned long)reg_info.flags);
+
+ vdev->config_size = reg_info.size;
+ vdev->config_offset = reg_info.offset;
+
+error:
+ if (ret) {
+ QLIST_REMOVE(vdev, next);
+ vdev->group = NULL;
+ close(vdev->fd);
+ }
+ return ret;
+}
+
+static void vfio_put_device(VFIODevice *vdev)
+{
+ QLIST_REMOVE(vdev, next);
+ vdev->group = NULL;
+ DPRINTF("vfio_put_device: close vdev->fd\n");
+ close(vdev->fd);
+ if (vdev->msix) {
+ g_free(vdev->msix);
+ vdev->msix = NULL;
+ }
+}
+
+static int vfio_initfn(PCIDevice *pdev)
+{
+ VFIODevice *pvdev, *vdev = DO_UPCAST(VFIODevice, pdev, pdev);
+ VFIOGroup *group;
+ char path[PATH_MAX], iommu_group_path[PATH_MAX], *group_name;
+ ssize_t len;
+ struct stat st;
+ int groupid;
+ int ret;
+
+ /* Check that the host device exists */
+ snprintf(path, sizeof(path),
+ "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/",
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function);
+ if (stat(path, &st) < 0) {
+ error_report("vfio: error: no such host device: %s\n", path);
+ return -errno;
+ }
+
+ strncat(path, "iommu_group", sizeof(path) - strlen(path) - 1);
+
+ len = readlink(path, iommu_group_path, PATH_MAX);
+ if (len <= 0) {
+ error_report("vfio: error no iommu_group for device\n");
+ return -errno;
+ }
+
+ iommu_group_path[len] = 0;
+ group_name = basename(iommu_group_path);
+
+ if (sscanf(group_name, "%d", &groupid) != 1) {
+ error_report("vfio: error reading %s: %m\n", path);
+ return -errno;
+ }
+
+ DPRINTF("%s(%04x:%02x:%02x.%x) group %d\n", __func__, vdev->host.domain,
+ vdev->host.bus, vdev->host.slot, vdev->host.function, groupid);
+
+ group = vfio_get_group(groupid);
+ if (!group) {
+ error_report("vfio: failed to get group %d\n", groupid);
+ return -ENOENT;
+ }
+
+ snprintf(path, sizeof(path), "%04x:%02x:%02x.%01x",
+ vdev->host.domain, vdev->host.bus, vdev->host.slot,
+ vdev->host.function);
+
+ QLIST_FOREACH(pvdev, &group->device_list, next) {
+ if (pvdev->host.domain == vdev->host.domain &&
+ pvdev->host.bus == vdev->host.bus &&
+ pvdev->host.slot == vdev->host.slot &&
+ pvdev->host.function == vdev->host.function) {
+
+ error_report("vfio: error: device %s is already attached\n", path);
+ vfio_put_group(group);
+ return -EBUSY;
+ }
+ }
+
+ ret = vfio_get_device(group, path, vdev);
+ if (ret) {
+ error_report("vfio: failed to get device %s\n", path);
+ vfio_put_group(group);
+ return ret;
+ }
+
+ /* Get a copy of config space */
+ ret = pread(vdev->fd, vdev->pdev.config,
+ MIN(pci_config_size(&vdev->pdev), vdev->config_size),
+ vdev->config_offset);
+ if (ret < (int)MIN(pci_config_size(&vdev->pdev), vdev->config_size)) {
+ ret = ret < 0 ? -errno : -EFAULT;
+ error_report("vfio: Failed to read device config space\n");
+ goto out_put;
+ }
+
+ /*
+ * Clear host resource mapping info. If we choose not to register a
+ * BAR, such as might be the case with the option ROM, we can get
+ * confusing, unwritable, residual addresses from the host here.
+ */
+ memset(&vdev->pdev.config[PCI_BASE_ADDRESS_0], 0, 24);
+ memset(&vdev->pdev.config[PCI_ROM_ADDRESS], 0, 4);
+
+ vfio_load_rom(vdev);
+
+ ret = vfio_early_setup_msix(vdev);
+ if (ret) {
+ goto out_put;
+ }
+
+ vfio_map_bars(vdev);
+
+ ret = vfio_add_capabilities(vdev);
+ if (ret) {
+ goto out_teardown;
+ }
+
+ if (vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1)) {
+ if (vdev->intx.intx && strcmp(vdev->intx.intx, "off")) {
+ error_report("vfio: Unknown option x-intx=%s, "
+ "valid options: \"off\".\n", vdev->intx.intx);
+ ret = -EINVAL;
+ goto out_teardown;
+ }
+
+ if (vdev->intx.intx && !strcmp(vdev->intx.intx, "off")) {
+ vdev->intx.disabled = true;
+ }
+
+ ret = vfio_enable_intx(vdev);
+ if (ret) {
+ goto out_teardown;
+ }
+ }
+
+ return 0;
+
+out_teardown:
+ pci_device_set_intx_routing_notifier(&vdev->pdev, NULL);
+ vfio_teardown_msi(vdev);
+ vfio_unmap_bars(vdev);
+out_put:
+ vfio_put_device(vdev);
+ vfio_put_group(group);
+ return ret;
+}
+
+static void vfio_exitfn(PCIDevice *pdev)
+{
+ VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev);
+ VFIOGroup *group = vdev->group;
+
+ pci_device_set_intx_routing_notifier(&vdev->pdev, NULL);
+ vfio_disable_interrupts(vdev);
+ vfio_teardown_msi(vdev);
+ vfio_unmap_bars(vdev);
+ vfio_put_device(vdev);
+ vfio_put_group(group);
+}
+
+static void vfio_pci_reset(DeviceState *dev)
+{
+ PCIDevice *pdev = DO_UPCAST(PCIDevice, qdev, dev);
+ VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev);
+
+ if (!vdev->reset_works) {
+ return;
+ }
+
+ if (ioctl(vdev->fd, VFIO_DEVICE_RESET)) {
+ error_report("vfio: Error unable to reset physical device "
+ "(%04x:%02x:%02x.%x): %m\n", vdev->host.domain,
+ vdev->host.bus, vdev->host.slot, vdev->host.function);
+ }
+}
+
+static Property vfio_pci_dev_properties[] = {
+ DEFINE_PROP_PCI_HOST_DEVADDR("host", VFIODevice, host),
+ DEFINE_PROP_STRING("x-intx", VFIODevice, intx.intx),
+ /*
+ * TODO - support passed fds... is this necessary?
+ * DEFINE_PROP_STRING("vfiofd", VFIODevice, vfiofd_name),
+ * DEFINE_PROP_STRING("vfiogroupfd, VFIODevice, vfiogroupfd_name),
+ */
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+
+static void vfio_pci_dev_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *pdc = PCI_DEVICE_CLASS(klass);
+
+ dc->reset = vfio_pci_reset;
+ dc->props = vfio_pci_dev_properties;
+ pdc->init = vfio_initfn;
+ pdc->exit = vfio_exitfn;
+ pdc->config_read = vfio_pci_read_config;
+ pdc->config_write = vfio_pci_write_config;
+}
+
+static const TypeInfo vfio_pci_dev_info = {
+ .name = "vfio-pci",
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(VFIODevice),
+ .class_init = vfio_pci_dev_class_init,
+};
+
+static void register_vfio_pci_dev_type(void)
+{
+ type_register_static(&vfio_pci_dev_info);
+}
+
+type_init(register_vfio_pci_dev_type)
diff --git a/hw/vfio_pci_int.h b/hw/vfio_pci_int.h
new file mode 100644
index 000000000..3812d8d7f
--- /dev/null
+++ b/hw/vfio_pci_int.h
@@ -0,0 +1,114 @@
+/*
+ * vfio based device assignment support
+ *
+ * Copyright Red Hat, Inc. 2012
+ *
+ * Authors:
+ * Alex Williamson <alex.williamson@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef HW_VFIO_PCI_INT_H
+#define HW_VFIO_PCI_INT_H
+
+#include "qemu-common.h"
+#include "qemu-queue.h"
+#include "pci.h"
+#include "event_notifier.h"
+
+typedef struct VFIOBAR {
+ off_t fd_offset; /* offset of BAR within device fd */
+ int fd; /* device fd, allows us to pass VFIOBAR as opaque data */
+ MemoryRegion mem; /* slow, read/write access */
+ MemoryRegion mmap_mem; /* direct mapped access */
+ void *mmap;
+ size_t size;
+ uint32_t flags; /* VFIO region flags (rd/wr/mmap) */
+ uint8_t nr; /* cache the BAR number for debug */
+} VFIOBAR;
+
+typedef struct VFIOINTx {
+ bool pending; /* interrupt pending */
+ bool kvm_accel; /* set when QEMU bypass through KVM enabled */
+ uint8_t pin; /* which pin to pull for qemu_set_irq */
+ EventNotifier interrupt; /* eventfd triggered on interrupt */
+ EventNotifier unmask; /* eventfd for unmask on QEMU bypass */
+ PCIINTxRoute route; /* routing info for QEMU bypass */
+ bool disabled;
+ char *intx;
+} VFIOINTx;
+
+struct VFIODevice;
+
+typedef struct VFIOMSIVector {
+ EventNotifier interrupt; /* eventfd triggered on interrupt */
+ struct VFIODevice *vdev; /* back pointer to device */
+ int virq; /* KVM irqchip route for QEMU bypass */
+ bool use;
+} VFIOMSIVector;
+
+enum {
+ VFIO_INT_NONE = 0,
+ VFIO_INT_INTx = 1,
+ VFIO_INT_MSI = 2,
+ VFIO_INT_MSIX = 3,
+};
+
+struct VFIOGroup;
+
+typedef struct VFIOContainer {
+ int fd; /* /dev/vfio/vfio, empowered by the attached groups */
+ struct {
+ /* enable abstraction to support various iommu backends */
+ union {
+ MemoryListener listener; /* Used by type1 iommu */
+ };
+ void (*release)(struct VFIOContainer *);
+ } iommu_data;
+ QLIST_HEAD(, VFIOGroup) group_list;
+ QLIST_ENTRY(VFIOContainer) next;
+} VFIOContainer;
+
+/* Cache of MSI-X setup plus extra mmap and memory region for split BAR map */
+typedef struct VFIOMSIXInfo {
+ uint8_t table_bar;
+ uint8_t pba_bar;
+ uint16_t entries;
+ uint32_t table_offset;
+ uint32_t pba_offset;
+ MemoryRegion mmap_mem;
+ void *mmap;
+} VFIOMSIXInfo;
+
+typedef struct VFIODevice {
+ PCIDevice pdev;
+ int fd;
+ VFIOINTx intx;
+ unsigned int config_size;
+ off_t config_offset; /* Offset of config space region within device fd */
+ unsigned int rom_size;
+ off_t rom_offset; /* Offset of ROM region within device fd */
+ int msi_cap_size;
+ VFIOMSIVector *msi_vectors;
+ VFIOMSIXInfo *msix;
+ int nr_vectors; /* Number of MSI/MSIX vectors currently in use */
+ int interrupt; /* Current interrupt type */
+ VFIOBAR bars[PCI_NUM_REGIONS - 1]; /* No ROM */
+ PCIHostDeviceAddress host;
+ QLIST_ENTRY(VFIODevice) next;
+ struct VFIOGroup *group;
+ bool reset_works;
+} VFIODevice;
+
+typedef struct VFIOGroup {
+ int fd;
+ int groupid;
+ VFIOContainer *container;
+ QLIST_HEAD(, VFIODevice) device_list;
+ QLIST_ENTRY(VFIOGroup) next;
+ QLIST_ENTRY(VFIOGroup) container_next;
+} VFIOGroup;
+
+#endif /* HW_VFIO_PCI_INT_H */
diff --git a/hw/vga-isa-mm.c b/hw/vga-isa-mm.c
index 44ae7d92c..306e6ba44 100644
--- a/hw/vga-isa-mm.c
+++ b/hw/vga-isa-mm.c
@@ -107,6 +107,7 @@ static void vga_mm_init(ISAVGAMMState *s, target_phys_addr_t vram_base,
s_ioport_ctrl = g_malloc(sizeof(*s_ioport_ctrl));
memory_region_init_io(s_ioport_ctrl, &vga_mm_ctrl_ops, s,
"vga-mm-ctrl", 0x100000);
+ memory_region_set_flush_coalesced(s_ioport_ctrl);
vga_io_memory = g_malloc(sizeof(*vga_io_memory));
/* XXX: endianness? */
diff --git a/hw/vga-pci.c b/hw/vga-pci.c
index 9abbada8f..996d47f23 100644
--- a/hw/vga-pci.c
+++ b/hw/vga-pci.c
@@ -24,7 +24,6 @@
#include "hw.h"
#include "console.h"
#include "pci.h"
-#include "vga-pci.h"
#include "vga_int.h"
#include "pixel_ops.h"
#include "qemu-timer.h"
@@ -47,7 +46,7 @@ static const VMStateDescription vmstate_vga_pci = {
}
};
-static int pci_vga_initfn(PCIDevice *dev)
+static int pci_std_vga_initfn(PCIDevice *dev)
{
PCIVGAState *d = DO_UPCAST(PCIVGAState, dev, dev);
VGACommonState *s = &d->vga;
@@ -70,11 +69,6 @@ static int pci_vga_initfn(PCIDevice *dev)
return 0;
}
-DeviceState *pci_vga_init(PCIBus *bus)
-{
- return &pci_create_simple(bus, -1, "VGA")->qdev;
-}
-
static Property vga_pci_properties[] = {
DEFINE_PROP_UINT32("vgamem_mb", PCIVGAState, vga.vram_size_mb, 16),
DEFINE_PROP_END_OF_LIST(),
@@ -86,7 +80,7 @@ static void vga_class_init(ObjectClass *klass, void *data)
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
k->no_hotplug = 1;
- k->init = pci_vga_initfn;
+ k->init = pci_std_vga_initfn;
k->romfile = "vgabios-stdvga.bin";
k->vendor_id = PCI_VENDOR_ID_QEMU;
k->device_id = PCI_DEVICE_ID_QEMU_VGA;
diff --git a/hw/vga-pci.h b/hw/vga-pci.h
deleted file mode 100644
index 49abf1309..000000000
--- a/hw/vga-pci.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef VGA_PCI_H
-#define VGA_PCI_H
-
-#include "qemu-common.h"
-
-/* vga-pci.c */
-DeviceState *pci_vga_init(PCIBus *bus);
-
-/* cirrus_vga.c */
-DeviceState *pci_cirrus_vga_init(PCIBus *bus);
-
-#endif
diff --git a/hw/vga.c b/hw/vga.c
index 80299ea1d..afaef0d71 100644
--- a/hw/vga.c
+++ b/hw/vga.c
@@ -361,6 +361,8 @@ uint32_t vga_ioport_read(void *opaque, uint32_t addr)
VGACommonState *s = opaque;
int val, index;
+ qemu_flush_coalesced_mmio_buffer();
+
if (vga_ioport_invalid(s, addr)) {
val = 0xff;
} else {
@@ -453,6 +455,8 @@ void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
VGACommonState *s = opaque;
int index;
+ qemu_flush_coalesced_mmio_buffer();
+
/* check port range access depending on color/monochrome mode */
if (vga_ioport_invalid(s, addr)) {
return;
@@ -2338,6 +2342,7 @@ MemoryRegion *vga_init_io(VGACommonState *s,
vga_mem = g_malloc(sizeof(*vga_mem));
memory_region_init_io(vga_mem, &vga_mem_ops, s,
"vga-lowmem", 0x20000);
+ memory_region_set_flush_coalesced(vga_mem);
return vga_mem;
}
diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c
index 6f6d172fd..e25cc9647 100644
--- a/hw/virtio-blk.c
+++ b/hw/virtio-blk.c
@@ -64,31 +64,22 @@ static void virtio_blk_req_complete(VirtIOBlockReq *req, int status)
}
static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
- int is_read)
+ bool is_read)
{
- BlockErrorAction action = bdrv_get_on_error(req->dev->bs, is_read);
+ BlockErrorAction action = bdrv_get_error_action(req->dev->bs, is_read, error);
VirtIOBlock *s = req->dev;
- if (action == BLOCK_ERR_IGNORE) {
- bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_IGNORE, is_read);
- return 0;
- }
-
- if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC)
- || action == BLOCK_ERR_STOP_ANY) {
+ if (action == BDRV_ACTION_STOP) {
req->next = s->rq;
s->rq = req;
- bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_STOP, is_read);
- vm_stop(RUN_STATE_IO_ERROR);
- bdrv_iostatus_set_err(s->bs, error);
- } else {
+ } else if (action == BDRV_ACTION_REPORT) {
virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
bdrv_acct_done(s->bs, &req->acct);
g_free(req);
- bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_REPORT, is_read);
}
- return 1;
+ bdrv_error_action(s->bs, action, is_read, error);
+ return action != BDRV_ACTION_IGNORE;
}
static void virtio_blk_rw_complete(void *opaque, int ret)
@@ -98,7 +89,7 @@ static void virtio_blk_rw_complete(void *opaque, int ret)
trace_virtio_blk_rw_complete(req, ret);
if (ret) {
- int is_read = !(ldl_p(&req->out->type) & VIRTIO_BLK_T_OUT);
+ bool is_read = !(ldl_p(&req->out->type) & VIRTIO_BLK_T_OUT);
if (virtio_blk_handle_rw_error(req, -ret, is_read))
return;
}
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index b1998b27d..247d7bef5 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -447,10 +447,6 @@ static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)
VirtIONet *n = to_virtio_net(vdev);
qemu_flush_queued_packets(&n->nic->nc);
-
- /* We now have RX buffers, signal to the IO thread to break out of the
- * select to re-poll the tap file descriptor */
- qemu_notify_event();
}
static int virtio_net_can_receive(NetClientState *nc)
@@ -694,7 +690,7 @@ static void virtio_net_tx_complete(NetClientState *nc, ssize_t len)
{
VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque;
- virtqueue_push(n->tx_vq, &n->async_tx.elem, n->async_tx.len);
+ virtqueue_push(n->tx_vq, &n->async_tx.elem, 0);
virtio_notify(&n->vdev, n->tx_vq);
n->async_tx.elem.out_num = n->async_tx.len = 0;
@@ -758,7 +754,7 @@ static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)
len += ret;
- virtqueue_push(vq, &elem, len);
+ virtqueue_push(vq, &elem, 0);
virtio_notify(&n->vdev, vq);
if (++num_packets >= n->tx_burst) {
diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c
index 82073f5dc..d20bd8bf7 100644
--- a/hw/virtio-serial-bus.c
+++ b/hw/virtio-serial-bus.c
@@ -287,6 +287,7 @@ ssize_t virtio_serial_write(VirtIOSerialPort *port, const uint8_t *buf,
size_t virtio_serial_guest_ready(VirtIOSerialPort *port)
{
VirtQueue *vq = port->ivq;
+ unsigned int bytes;
if (!virtio_queue_ready(vq) ||
!(port->vser->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) ||
@@ -296,14 +297,8 @@ size_t virtio_serial_guest_ready(VirtIOSerialPort *port)
if (use_multiport(port->vser) && !port->guest_connected) {
return 0;
}
-
- if (virtqueue_avail_bytes(vq, 4096, 0)) {
- return 4096;
- }
- if (virtqueue_avail_bytes(vq, 1, 0)) {
- return 1;
- }
- return 0;
+ virtqueue_get_avail_bytes(vq, &bytes, NULL);
+ return bytes;
}
static void flush_queued_data_bh(void *opaque)
diff --git a/hw/virtio.c b/hw/virtio.c
index 209c76375..6821092df 100644
--- a/hw/virtio.c
+++ b/hw/virtio.c
@@ -241,7 +241,7 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
elem->in_sg[i].iov_len,
1, size);
- offset += elem->in_sg[i].iov_len;
+ offset += size;
}
for (i = 0; i < elem->out_num; i++)
@@ -335,10 +335,11 @@ static unsigned virtqueue_next_desc(target_phys_addr_t desc_pa,
return next;
}
-int virtqueue_avail_bytes(VirtQueue *vq, int in_bytes, int out_bytes)
+void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
+ unsigned int *out_bytes)
{
unsigned int idx;
- int total_bufs, in_total, out_total;
+ unsigned int total_bufs, in_total, out_total;
idx = vq->last_avail_idx;
@@ -380,13 +381,9 @@ int virtqueue_avail_bytes(VirtQueue *vq, int in_bytes, int out_bytes)
}
if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_WRITE) {
- if (in_bytes > 0 &&
- (in_total += vring_desc_len(desc_pa, i)) >= in_bytes)
- return 1;
+ in_total += vring_desc_len(desc_pa, i);
} else {
- if (out_bytes > 0 &&
- (out_total += vring_desc_len(desc_pa, i)) >= out_bytes)
- return 1;
+ out_total += vring_desc_len(desc_pa, i);
}
} while ((i = virtqueue_next_desc(desc_pa, i, max)) != max);
@@ -395,7 +392,24 @@ int virtqueue_avail_bytes(VirtQueue *vq, int in_bytes, int out_bytes)
else
total_bufs++;
}
+ if (in_bytes) {
+ *in_bytes = in_total;
+ }
+ if (out_bytes) {
+ *out_bytes = out_total;
+ }
+}
+int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes,
+ unsigned int out_bytes)
+{
+ unsigned int in_total, out_total;
+
+ virtqueue_get_avail_bytes(vq, &in_total, &out_total);
+ if ((in_bytes && in_bytes < in_total)
+ || (out_bytes && out_bytes < out_total)) {
+ return 1;
+ }
return 0;
}
diff --git a/hw/virtio.h b/hw/virtio.h
index 7a4f56452..80de3757e 100644
--- a/hw/virtio.h
+++ b/hw/virtio.h
@@ -147,7 +147,10 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
void virtqueue_map_sg(struct iovec *sg, target_phys_addr_t *addr,
size_t num_sg, int is_write);
int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem);
-int virtqueue_avail_bytes(VirtQueue *vq, int in_bytes, int out_bytes);
+int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes,
+ unsigned int out_bytes);
+void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
+ unsigned int *out_bytes);
void virtio_notify(VirtIODevice *vdev, VirtQueue *vq);
diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c
index b68e88367..6f7074e55 100644
--- a/hw/vmware_vga.c
+++ b/hw/vmware_vga.c
@@ -25,7 +25,6 @@
#include "loader.h"
#include "console.h"
#include "pci.h"
-#include "vmware_vga.h"
#undef VERBOSE
#define HW_RECT_ACCEL
@@ -1186,6 +1185,7 @@ static int pci_vmsvga_initfn(PCIDevice *dev)
memory_region_init_io(&s->io_bar, &vmsvga_io_ops, &s->chip,
"vmsvga-io", 0x10);
+ memory_region_set_flush_coalesced(&s->io_bar);
pci_register_bar(&s->card, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar);
vmsvga_init(&s->chip, pci_address_space(dev),
diff --git a/hw/vmware_vga.h b/hw/vmware_vga.h
deleted file mode 100644
index 000fbddc0..000000000
--- a/hw/vmware_vga.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef QEMU_VMWARE_VGA_H
-#define QEMU_VMWARE_VGA_H
-
-#include "qemu-common.h"
-
-/* vmware_vga.c */
-static inline DeviceState *pci_vmsvga_init(PCIBus *bus)
-{
- PCIDevice *dev;
-
- dev = pci_create_simple(bus, -1, "vmware-svga");
- return &dev->qdev;
-}
-
-#endif
diff --git a/hw/xen-host-pci-device.c b/hw/xen-host-pci-device.c
index e7ff680ef..743b37b99 100644
--- a/hw/xen-host-pci-device.c
+++ b/hw/xen-host-pci-device.c
@@ -47,13 +47,13 @@ static int xen_host_pci_sysfs_path(const XenHostPCIDevice *d,
}
-/* This size should be enough to read the first 7 lines of a ressource file */
-#define XEN_HOST_PCI_RESSOURCE_BUFFER_SIZE 400
+/* This size should be enough to read the first 7 lines of a resource file */
+#define XEN_HOST_PCI_RESOURCE_BUFFER_SIZE 400
static int xen_host_pci_get_resource(XenHostPCIDevice *d)
{
int i, rc, fd;
char path[PATH_MAX];
- char buf[XEN_HOST_PCI_RESSOURCE_BUFFER_SIZE];
+ char buf[XEN_HOST_PCI_RESOURCE_BUFFER_SIZE];
unsigned long long start, end, flags, size;
char *endptr, *s;
uint8_t type;
diff --git a/hw/xen.h b/hw/xen.h
index e5926b7b8..d14e92d5a 100644
--- a/hw/xen.h
+++ b/hw/xen.h
@@ -48,6 +48,7 @@ void xenstore_store_pv_console_info(int i, struct CharDriverState *chr);
struct MemoryRegion;
void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size,
struct MemoryRegion *mr);
+void xen_modified_memory(ram_addr_t start, ram_addr_t length);
#endif
struct MemoryRegion;
diff --git a/hw/xen_domainbuild.c b/hw/xen_domainbuild.c
index a6a12e593..db1497469 100644
--- a/hw/xen_domainbuild.c
+++ b/hw/xen_domainbuild.c
@@ -153,7 +153,6 @@ static void xen_domain_poll(void *opaque)
quit:
qemu_system_shutdown_request();
- return;
}
static int xen_domain_watcher(void)
diff --git a/hw/xen_nic.c b/hw/xen_nic.c
index 8b79bfb73..cf7d5591b 100644
--- a/hw/xen_nic.c
+++ b/hw/xen_nic.c
@@ -415,6 +415,7 @@ static void net_event(struct XenDevice *xendev)
{
struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
net_tx_packets(netdev);
+ qemu_flush_queued_packets(&netdev->nic->nc);
}
static int net_free(struct XenDevice *xendev)
diff --git a/hw/xen_platform.c b/hw/xen_platform.c
index 0d6c2ff8c..956dbfeae 100644
--- a/hw/xen_platform.c
+++ b/hw/xen_platform.c
@@ -85,8 +85,10 @@ static void log_writeb(PCIXenPlatformState *s, char val)
static void unplug_nic(PCIBus *b, PCIDevice *d, void *o)
{
+ /* We have to ignore passthrough devices */
if (pci_get_word(d->config + PCI_CLASS_DEVICE) ==
- PCI_CLASS_NETWORK_ETHERNET) {
+ PCI_CLASS_NETWORK_ETHERNET
+ && strcmp(d->name, "xen-pci-passthrough") != 0) {
qdev_free(&d->qdev);
}
}
@@ -98,8 +100,10 @@ static void pci_unplug_nics(PCIBus *bus)
static void unplug_disks(PCIBus *b, PCIDevice *d, void *o)
{
+ /* We have to ignore passthrough devices */
if (pci_get_word(d->config + PCI_CLASS_DEVICE) ==
- PCI_CLASS_STORAGE_IDE) {
+ PCI_CLASS_STORAGE_IDE
+ && strcmp(d->name, "xen-pci-passthrough") != 0) {
qdev_unplug(&(d->qdev), NULL);
}
}
diff --git a/hw/xen_pt.c b/hw/xen_pt.c
index 307119a12..838bcea4d 100644
--- a/hw/xen_pt.c
+++ b/hw/xen_pt.c
@@ -410,14 +410,17 @@ static int xen_pt_register_regions(XenPCIPassthroughState *s)
if (r->type & XEN_HOST_PCI_REGION_TYPE_PREFETCH) {
type |= PCI_BASE_ADDRESS_MEM_PREFETCH;
}
+ if (r->type & XEN_HOST_PCI_REGION_TYPE_MEM_64) {
+ type |= PCI_BASE_ADDRESS_MEM_TYPE_64;
+ }
}
memory_region_init_io(&s->bar[i], &ops, &s->dev,
"xen-pci-pt-bar", r->size);
pci_register_bar(&s->dev, i, type, &s->bar[i]);
- XEN_PT_LOG(&s->dev, "IO region %i registered (size=0x%08"PRIx64
- " base_addr=0x%08"PRIx64" type: %#x)\n",
+ XEN_PT_LOG(&s->dev, "IO region %i registered (size=0x%lx"PRIx64
+ " base_addr=0x%lx"PRIx64" type: %#x)\n",
i, r->size, r->base_addr, type);
}
diff --git a/hw/xen_pt.h b/hw/xen_pt.h
index 41904ece9..112477a88 100644
--- a/hw/xen_pt.h
+++ b/hw/xen_pt.h
@@ -96,7 +96,7 @@ typedef struct XenPTRegion {
* - do NOT use ALL F for init_val, otherwise the tbl will not be registered.
*/
-/* emulated register infomation */
+/* emulated register information */
struct XenPTRegInfo {
uint32_t offset;
uint32_t size;
@@ -140,7 +140,7 @@ typedef int (*xen_pt_reg_size_init_fn)
(XenPCIPassthroughState *, const XenPTRegGroupInfo *,
uint32_t base_offset, uint8_t *size);
-/* emulated register group infomation */
+/* emulated register group information */
struct XenPTRegGroupInfo {
uint8_t grp_id;
XenPTRegisterGroupType grp_type;
diff --git a/hw/xen_pt_config_init.c b/hw/xen_pt_config_init.c
index 00eb3d997..0a5f82cb8 100644
--- a/hw/xen_pt_config_init.c
+++ b/hw/xen_pt_config_init.c
@@ -342,6 +342,23 @@ static int xen_pt_cmd_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
#define XEN_PT_BAR_IO_RO_MASK 0x00000003 /* BAR ReadOnly mask(I/O) */
#define XEN_PT_BAR_IO_EMU_MASK 0xFFFFFFFC /* BAR emul mask(I/O) */
+static bool is_64bit_bar(PCIIORegion *r)
+{
+ return !!(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64);
+}
+
+static uint64_t xen_pt_get_bar_size(PCIIORegion *r)
+{
+ if (is_64bit_bar(r)) {
+ uint64_t size64;
+ size64 = (r + 1)->size;
+ size64 <<= 32;
+ size64 += r->size;
+ return size64;
+ }
+ return r->size;
+}
+
static XenPTBarFlag xen_pt_bar_reg_parse(XenPCIPassthroughState *s,
XenPTRegInfo *reg)
{
@@ -366,7 +383,7 @@ static XenPTBarFlag xen_pt_bar_reg_parse(XenPCIPassthroughState *s,
/* check unused BAR */
r = &d->io_regions[index];
- if (r->size == 0) {
+ if (!xen_pt_get_bar_size(r)) {
return XEN_PT_BAR_FLAG_UNUSED;
}
@@ -481,7 +498,12 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
switch (s->bases[index].bar_flag) {
case XEN_PT_BAR_FLAG_MEM:
bar_emu_mask = XEN_PT_BAR_MEM_EMU_MASK;
- bar_ro_mask = XEN_PT_BAR_MEM_RO_MASK | (r_size - 1);
+ if (!r_size) {
+ /* low 32 bits mask for 64 bit bars */
+ bar_ro_mask = XEN_PT_BAR_ALLF;
+ } else {
+ bar_ro_mask = XEN_PT_BAR_MEM_RO_MASK | (r_size - 1);
+ }
break;
case XEN_PT_BAR_FLAG_IO:
bar_emu_mask = XEN_PT_BAR_IO_EMU_MASK;
@@ -489,7 +511,7 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
break;
case XEN_PT_BAR_FLAG_UPPER:
bar_emu_mask = XEN_PT_BAR_ALLF;
- bar_ro_mask = 0; /* all upper 32bit are R/W */
+ bar_ro_mask = r_size ? r_size - 1 : 0;
break;
default:
break;
@@ -501,22 +523,13 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
/* check whether we need to update the virtual region address or not */
switch (s->bases[index].bar_flag) {
+ case XEN_PT_BAR_FLAG_UPPER:
case XEN_PT_BAR_FLAG_MEM:
/* nothing to do */
break;
case XEN_PT_BAR_FLAG_IO:
/* nothing to do */
break;
- case XEN_PT_BAR_FLAG_UPPER:
- if (cfg_entry->data) {
- if (cfg_entry->data != (XEN_PT_BAR_ALLF & ~bar_ro_mask)) {
- XEN_PT_WARN(d, "Guest attempt to set high MMIO Base Address. "
- "Ignore mapping. "
- "(offset: 0x%02x, high address: 0x%08x)\n",
- reg->offset, cfg_entry->data);
- }
- }
- break;
default:
break;
}
@@ -562,7 +575,7 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s,
return 0;
}
-/* Header Type0 reg static infomation table */
+/* Header Type0 reg static information table */
static XenPTRegInfo xen_pt_emu_reg_header0[] = {
/* Vendor ID reg */
{
@@ -753,7 +766,7 @@ static XenPTRegInfo xen_pt_emu_reg_header0[] = {
* Vital Product Data Capability
*/
-/* Vital Product Data Capability Structure reg static infomation table */
+/* Vital Product Data Capability Structure reg static information table */
static XenPTRegInfo xen_pt_emu_reg_vpd[] = {
{
.offset = PCI_CAP_LIST_NEXT,
@@ -775,7 +788,7 @@ static XenPTRegInfo xen_pt_emu_reg_vpd[] = {
* Vendor Specific Capability
*/
-/* Vendor Specific Capability Structure reg static infomation table */
+/* Vendor Specific Capability Structure reg static information table */
static XenPTRegInfo xen_pt_emu_reg_vendor[] = {
{
.offset = PCI_CAP_LIST_NEXT,
@@ -866,7 +879,7 @@ static int xen_pt_linkctrl2_reg_init(XenPCIPassthroughState *s,
return 0;
}
-/* PCI Express Capability Structure reg static infomation table */
+/* PCI Express Capability Structure reg static information table */
static XenPTRegInfo xen_pt_emu_reg_pcie[] = {
/* Next Pointer reg */
{
@@ -981,7 +994,7 @@ static int xen_pt_pmcsr_reg_write(XenPCIPassthroughState *s,
return 0;
}
-/* Power Management Capability reg static infomation table */
+/* Power Management Capability reg static information table */
static XenPTRegInfo xen_pt_emu_reg_pm[] = {
/* Next Pointer reg */
{
@@ -1259,7 +1272,7 @@ static int xen_pt_msgdata_reg_write(XenPCIPassthroughState *s,
return 0;
}
-/* MSI Capability Structure reg static infomation table */
+/* MSI Capability Structure reg static information table */
static XenPTRegInfo xen_pt_emu_reg_msi[] = {
/* Next Pointer reg */
{
@@ -1396,7 +1409,7 @@ static int xen_pt_msixctrl_reg_write(XenPCIPassthroughState *s,
return 0;
}
-/* MSI-X Capability Structure reg static infomation table */
+/* MSI-X Capability Structure reg static information table */
static XenPTRegInfo xen_pt_emu_reg_msix[] = {
/* Next Pointer reg */
{
diff --git a/hw/xics.c b/hw/xics.c
index b674771dc..ce88aa750 100644
--- a/hw/xics.c
+++ b/hw/xics.c
@@ -165,11 +165,12 @@ struct ics_irq_state {
int server;
uint8_t priority;
uint8_t saved_priority;
- enum xics_irq_type type;
- int asserted:1;
- int sent:1;
- int rejected:1;
- int masked_pending:1;
+#define XICS_STATUS_ASSERTED 0x1
+#define XICS_STATUS_SENT 0x2
+#define XICS_STATUS_REJECTED 0x4
+#define XICS_STATUS_MASKED_PENDING 0x8
+ uint8_t status;
+ bool lsi;
};
struct ics_state {
@@ -191,8 +192,8 @@ static void resend_msi(struct ics_state *ics, int srcno)
struct ics_irq_state *irq = ics->irqs + srcno;
/* FIXME: filter by server#? */
- if (irq->rejected) {
- irq->rejected = 0;
+ if (irq->status & XICS_STATUS_REJECTED) {
+ irq->status &= ~XICS_STATUS_REJECTED;
if (irq->priority != 0xff) {
icp_irq(ics->icp, irq->server, srcno + ics->offset,
irq->priority);
@@ -204,8 +205,10 @@ static void resend_lsi(struct ics_state *ics, int srcno)
{
struct ics_irq_state *irq = ics->irqs + srcno;
- if ((irq->priority != 0xff) && irq->asserted && !irq->sent) {
- irq->sent = 1;
+ if ((irq->priority != 0xff)
+ && (irq->status & XICS_STATUS_ASSERTED)
+ && !(irq->status & XICS_STATUS_SENT)) {
+ irq->status |= XICS_STATUS_SENT;
icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority);
}
}
@@ -216,7 +219,7 @@ static void set_irq_msi(struct ics_state *ics, int srcno, int val)
if (val) {
if (irq->priority == 0xff) {
- irq->masked_pending = 1;
+ irq->status |= XICS_STATUS_MASKED_PENDING;
/* masked pending */ ;
} else {
icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority);
@@ -228,7 +231,11 @@ static void set_irq_lsi(struct ics_state *ics, int srcno, int val)
{
struct ics_irq_state *irq = ics->irqs + srcno;
- irq->asserted = val;
+ if (val) {
+ irq->status |= XICS_STATUS_ASSERTED;
+ } else {
+ irq->status &= ~XICS_STATUS_ASSERTED;
+ }
resend_lsi(ics, srcno);
}
@@ -237,7 +244,7 @@ static void ics_set_irq(void *opaque, int srcno, int val)
struct ics_state *ics = (struct ics_state *)opaque;
struct ics_irq_state *irq = ics->irqs + srcno;
- if (irq->type == XICS_LSI) {
+ if (irq->lsi) {
set_irq_lsi(ics, srcno, val);
} else {
set_irq_msi(ics, srcno, val);
@@ -248,11 +255,12 @@ static void write_xive_msi(struct ics_state *ics, int srcno)
{
struct ics_irq_state *irq = ics->irqs + srcno;
- if (!irq->masked_pending || (irq->priority == 0xff)) {
+ if (!(irq->status & XICS_STATUS_MASKED_PENDING)
+ || (irq->priority == 0xff)) {
return;
}
- irq->masked_pending = 0;
+ irq->status &= ~XICS_STATUS_MASKED_PENDING;
icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority);
}
@@ -262,15 +270,16 @@ static void write_xive_lsi(struct ics_state *ics, int srcno)
}
static void ics_write_xive(struct ics_state *ics, int nr, int server,
- uint8_t priority)
+ uint8_t priority, uint8_t saved_priority)
{
int srcno = nr - ics->offset;
struct ics_irq_state *irq = ics->irqs + srcno;
irq->server = server;
irq->priority = priority;
+ irq->saved_priority = saved_priority;
- if (irq->type == XICS_LSI) {
+ if (irq->lsi) {
write_xive_lsi(ics, srcno);
} else {
write_xive_msi(ics, srcno);
@@ -281,8 +290,8 @@ static void ics_reject(struct ics_state *ics, int nr)
{
struct ics_irq_state *irq = ics->irqs + nr - ics->offset;
- irq->rejected = 1; /* Irrelevant but harmless for LSI */
- irq->sent = 0; /* Irrelevant but harmless for MSI */
+ irq->status |= XICS_STATUS_REJECTED; /* Irrelevant but harmless for LSI */
+ irq->status &= ~XICS_STATUS_SENT; /* Irrelevant but harmless for MSI */
}
static void ics_resend(struct ics_state *ics)
@@ -293,7 +302,7 @@ static void ics_resend(struct ics_state *ics)
struct ics_irq_state *irq = ics->irqs + i;
/* FIXME: filter by server#? */
- if (irq->type == XICS_LSI) {
+ if (irq->lsi) {
resend_lsi(ics, i);
} else {
resend_msi(ics, i);
@@ -306,8 +315,8 @@ static void ics_eoi(struct ics_state *ics, int nr)
int srcno = nr - ics->offset;
struct ics_irq_state *irq = ics->irqs + srcno;
- if (irq->type == XICS_LSI) {
- irq->sent = 0;
+ if (irq->lsi) {
+ irq->status &= ~XICS_STATUS_SENT;
}
}
@@ -325,14 +334,12 @@ qemu_irq xics_get_qirq(struct icp_state *icp, int irq)
return icp->ics->qirqs[irq - icp->ics->offset];
}
-void xics_set_irq_type(struct icp_state *icp, int irq,
- enum xics_irq_type type)
+void xics_set_irq_type(struct icp_state *icp, int irq, bool lsi)
{
assert((irq >= icp->ics->offset)
&& (irq < (icp->ics->offset + icp->ics->nr_irqs)));
- assert((type == XICS_MSI) || (type == XICS_LSI));
- icp->ics->irqs[irq - icp->ics->offset].type = type;
+ icp->ics->irqs[irq - icp->ics->offset].lsi = lsi;
}
static target_ulong h_cppr(CPUPPCState *env, sPAPREnvironment *spapr,
@@ -399,7 +406,7 @@ static void rtas_set_xive(sPAPREnvironment *spapr, uint32_t token,
return;
}
- ics_write_xive(ics, nr, server, priority);
+ ics_write_xive(ics, nr, server, priority, priority);
rtas_st(rets, 0, 0); /* Success */
}
@@ -447,14 +454,8 @@ static void rtas_int_off(sPAPREnvironment *spapr, uint32_t token,
return;
}
- /* This is a NOP for now, since the described PAPR semantics don't
- * seem to gel with what Linux does */
-#if 0
- struct ics_irq_state *irq = xics->irqs + (nr - xics->offset);
-
- irq->saved_priority = irq->priority;
- ics_write_xive_msi(xics, nr, irq->server, 0xff);
-#endif
+ ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 0xff,
+ ics->irqs[nr - ics->offset].priority);
rtas_st(rets, 0, 0); /* Success */
}
@@ -478,22 +479,40 @@ static void rtas_int_on(sPAPREnvironment *spapr, uint32_t token,
return;
}
- /* This is a NOP for now, since the described PAPR semantics don't
- * seem to gel with what Linux does */
-#if 0
- struct ics_irq_state *irq = xics->irqs + (nr - xics->offset);
-
- ics_write_xive_msi(xics, nr, irq->server, irq->saved_priority);
-#endif
+ ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server,
+ ics->irqs[nr - ics->offset].saved_priority,
+ ics->irqs[nr - ics->offset].saved_priority);
rtas_st(rets, 0, 0); /* Success */
}
+static void xics_reset(void *opaque)
+{
+ struct icp_state *icp = (struct icp_state *)opaque;
+ struct ics_state *ics = icp->ics;
+ int i;
+
+ for (i = 0; i < icp->nr_servers; i++) {
+ icp->ss[i].xirr = 0;
+ icp->ss[i].pending_priority = 0;
+ icp->ss[i].mfrr = 0xff;
+ /* Make all outputs are deasserted */
+ qemu_set_irq(icp->ss[i].output, 0);
+ }
+
+ for (i = 0; i < ics->nr_irqs; i++) {
+ /* Reset everything *except* the type */
+ ics->irqs[i].server = 0;
+ ics->irqs[i].status = 0;
+ ics->irqs[i].priority = 0xff;
+ ics->irqs[i].saved_priority = 0xff;
+ }
+}
+
struct icp_state *xics_system_init(int nr_irqs)
{
CPUPPCState *env;
int max_server_num;
- int i;
struct icp_state *icp;
struct ics_state *ics;
@@ -508,10 +527,6 @@ struct icp_state *xics_system_init(int nr_irqs)
icp->nr_servers = max_server_num + 1;
icp->ss = g_malloc0(icp->nr_servers*sizeof(struct icp_server_state));
- for (i = 0; i < icp->nr_servers; i++) {
- icp->ss[i].mfrr = 0xff;
- }
-
for (env = first_cpu; env != NULL; env = env->next_cpu) {
struct icp_server_state *ss = &icp->ss[env->cpu_index];
@@ -539,11 +554,6 @@ struct icp_state *xics_system_init(int nr_irqs)
icp->ics = ics;
ics->icp = icp;
- for (i = 0; i < nr_irqs; i++) {
- ics->irqs[i].priority = 0xff;
- ics->irqs[i].saved_priority = 0xff;
- }
-
ics->qirqs = qemu_allocate_irqs(ics_set_irq, ics, nr_irqs);
spapr_register_hypercall(H_CPPR, h_cppr);
@@ -556,5 +566,7 @@ struct icp_state *xics_system_init(int nr_irqs)
spapr_rtas_register("ibm,int-off", rtas_int_off);
spapr_rtas_register("ibm,int-on", rtas_int_on);
+ qemu_register_reset(xics_reset, icp);
+
return icp;
}
diff --git a/hw/xics.h b/hw/xics.h
index 99b96ac85..681726869 100644
--- a/hw/xics.h
+++ b/hw/xics.h
@@ -31,14 +31,8 @@
struct icp_state;
-enum xics_irq_type {
- XICS_MSI, /* Message-signalled (edge) interrupt */
- XICS_LSI, /* Level-signalled interrupt */
-};
-
qemu_irq xics_get_qirq(struct icp_state *icp, int irq);
-void xics_set_irq_type(struct icp_state *icp, int irq,
- enum xics_irq_type type);
+void xics_set_irq_type(struct icp_state *icp, int irq, bool lsi);
struct icp_state *xics_system_init(int nr_irqs);
diff --git a/hw/xilinx.h b/hw/xilinx.h
index 556c5aa9f..98300477a 100644
--- a/hw/xilinx.h
+++ b/hw/xilinx.h
@@ -21,9 +21,9 @@ xilinx_timer_create(target_phys_addr_t base, qemu_irq irq, int oto, int freq)
{
DeviceState *dev;
- dev = qdev_create(NULL, "xlnx,xps-timer");
+ dev = qdev_create(NULL, "xlnx.xps-timer");
qdev_prop_set_uint32(dev, "one-timer-only", oto);
- qdev_prop_set_uint32(dev, "frequency", freq);
+ qdev_prop_set_uint32(dev, "clock-frequency", freq);
qdev_init_nofail(dev);
sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
@@ -55,13 +55,17 @@ xilinx_axiethernet_create(NICInfo *nd, StreamSlave *peer,
int txmem, int rxmem)
{
DeviceState *dev;
+ Error *errp = NULL;
+
qemu_check_nic_model(nd, "xlnx.axi-ethernet");
dev = qdev_create(NULL, "xlnx.axi-ethernet");
qdev_set_nic_properties(dev, nd);
qdev_prop_set_uint32(dev, "rxmem", rxmem);
qdev_prop_set_uint32(dev, "txmem", txmem);
- object_property_set_link(OBJECT(dev), OBJECT(peer), "tx_dev", NULL);
+ object_property_set_link(OBJECT(dev), OBJECT(peer), "axistream-connected",
+ &errp);
+ assert_no_error(errp);
qdev_init_nofail(dev);
sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
@@ -74,8 +78,12 @@ xilinx_axiethernetdma_init(DeviceState *dev, StreamSlave *peer,
target_phys_addr_t base, qemu_irq irq,
qemu_irq irq2, int freqhz)
{
+ Error *errp = NULL;
+
qdev_prop_set_uint32(dev, "freqhz", freqhz);
- object_property_set_link(OBJECT(dev), OBJECT(peer), "tx_dev", NULL);
+ object_property_set_link(OBJECT(dev), OBJECT(peer), "axistream-connected",
+ &errp);
+ assert_no_error(errp);
qdev_init_nofail(dev);
sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
diff --git a/hw/xilinx_spi.c b/hw/xilinx_spi.c
new file mode 100644
index 000000000..5cdf967c2
--- /dev/null
+++ b/hw/xilinx_spi.c
@@ -0,0 +1,385 @@
+/*
+ * QEMU model of the Xilinx SPI Controller
+ *
+ * Copyright (C) 2010 Edgar E. Iglesias.
+ * Copyright (C) 2012 Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>
+ * Copyright (C) 2012 PetaLogix
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "sysemu.h"
+#include "qemu-log.h"
+#include "fifo.h"
+
+#include "ssi.h"
+
+#ifdef XILINX_SPI_ERR_DEBUG
+#define DB_PRINT(...) do { \
+ fprintf(stderr, ": %s: ", __func__); \
+ fprintf(stderr, ## __VA_ARGS__); \
+ } while (0);
+#else
+ #define DB_PRINT(...)
+#endif
+
+#define R_DGIER (0x1c / 4)
+#define R_DGIER_IE (1 << 31)
+
+#define R_IPISR (0x20 / 4)
+#define IRQ_DRR_NOT_EMPTY (1 << (31 - 23))
+#define IRQ_DRR_OVERRUN (1 << (31 - 26))
+#define IRQ_DRR_FULL (1 << (31 - 27))
+#define IRQ_TX_FF_HALF_EMPTY (1 << 6)
+#define IRQ_DTR_UNDERRUN (1 << 3)
+#define IRQ_DTR_EMPTY (1 << (31 - 29))
+
+#define R_IPIER (0x28 / 4)
+#define R_SRR (0x40 / 4)
+#define R_SPICR (0x60 / 4)
+#define R_SPICR_TXFF_RST (1 << 5)
+#define R_SPICR_RXFF_RST (1 << 6)
+#define R_SPICR_MTI (1 << 8)
+
+#define R_SPISR (0x64 / 4)
+#define SR_TX_FULL (1 << 3)
+#define SR_TX_EMPTY (1 << 2)
+#define SR_RX_FULL (1 << 1)
+#define SR_RX_EMPTY (1 << 0)
+
+#define R_SPIDTR (0x68 / 4)
+#define R_SPIDRR (0x6C / 4)
+#define R_SPISSR (0x70 / 4)
+#define R_TX_FF_OCY (0x74 / 4)
+#define R_RX_FF_OCY (0x78 / 4)
+#define R_MAX (0x7C / 4)
+
+#define FIFO_CAPACITY 256
+
+typedef struct XilinxSPI {
+ SysBusDevice busdev;
+ MemoryRegion mmio;
+
+ qemu_irq irq;
+ int irqline;
+
+ uint8_t num_cs;
+ qemu_irq *cs_lines;
+
+ SSIBus *spi;
+
+ Fifo8 rx_fifo;
+ Fifo8 tx_fifo;
+
+ uint32_t regs[R_MAX];
+} XilinxSPI;
+
+static void txfifo_reset(XilinxSPI *s)
+{
+ fifo8_reset(&s->tx_fifo);
+
+ s->regs[R_SPISR] &= ~SR_TX_FULL;
+ s->regs[R_SPISR] |= SR_TX_EMPTY;
+}
+
+static void rxfifo_reset(XilinxSPI *s)
+{
+ fifo8_reset(&s->rx_fifo);
+
+ s->regs[R_SPISR] |= SR_RX_EMPTY;
+ s->regs[R_SPISR] &= ~SR_RX_FULL;
+}
+
+static void xlx_spi_update_cs(XilinxSPI *s)
+{
+ int i;
+
+ for (i = 0; i < s->num_cs; ++i) {
+ qemu_set_irq(s->cs_lines[i], !(~s->regs[R_SPISSR] & 1 << i));
+ }
+}
+
+static void xlx_spi_update_irq(XilinxSPI *s)
+{
+ uint32_t pending;
+
+ s->regs[R_IPISR] |=
+ (!fifo8_is_empty(&s->rx_fifo) ? IRQ_DRR_NOT_EMPTY : 0) |
+ (fifo8_is_full(&s->rx_fifo) ? IRQ_DRR_FULL : 0);
+
+ pending = s->regs[R_IPISR] & s->regs[R_IPIER];
+
+ pending = pending && (s->regs[R_DGIER] & R_DGIER_IE);
+ pending = !!pending;
+
+ /* This call lies right in the data paths so don't call the
+ irq chain unless things really changed. */
+ if (pending != s->irqline) {
+ s->irqline = pending;
+ DB_PRINT("irq_change of state %d ISR:%x IER:%X\n",
+ pending, s->regs[R_IPISR], s->regs[R_IPIER]);
+ qemu_set_irq(s->irq, pending);
+ }
+
+}
+
+static void xlx_spi_do_reset(XilinxSPI *s)
+{
+ memset(s->regs, 0, sizeof s->regs);
+
+ rxfifo_reset(s);
+ txfifo_reset(s);
+
+ s->regs[R_SPISSR] = ~0;
+ xlx_spi_update_irq(s);
+ xlx_spi_update_cs(s);
+}
+
+static void xlx_spi_reset(DeviceState *d)
+{
+ xlx_spi_do_reset(DO_UPCAST(XilinxSPI, busdev.qdev, d));
+}
+
+static inline int spi_master_enabled(XilinxSPI *s)
+{
+ return !(s->regs[R_SPICR] & R_SPICR_MTI);
+}
+
+static void spi_flush_txfifo(XilinxSPI *s)
+{
+ uint32_t tx;
+ uint32_t rx;
+
+ while (!fifo8_is_empty(&s->tx_fifo)) {
+ tx = (uint32_t)fifo8_pop(&s->tx_fifo);
+ DB_PRINT("data tx:%x\n", tx);
+ rx = ssi_transfer(s->spi, tx);
+ DB_PRINT("data rx:%x\n", rx);
+ if (fifo8_is_full(&s->rx_fifo)) {
+ s->regs[R_IPISR] |= IRQ_DRR_OVERRUN;
+ } else {
+ fifo8_push(&s->rx_fifo, (uint8_t)rx);
+ if (fifo8_is_full(&s->rx_fifo)) {
+ s->regs[R_SPISR] |= SR_RX_FULL;
+ s->regs[R_IPISR] |= IRQ_DRR_FULL;
+ }
+ }
+
+ s->regs[R_SPISR] &= ~SR_RX_EMPTY;
+ s->regs[R_SPISR] &= ~SR_TX_FULL;
+ s->regs[R_SPISR] |= SR_TX_EMPTY;
+
+ s->regs[R_IPISR] |= IRQ_DTR_EMPTY;
+ s->regs[R_IPISR] |= IRQ_DRR_NOT_EMPTY;
+ }
+
+}
+
+static uint64_t
+spi_read(void *opaque, target_phys_addr_t addr, unsigned int size)
+{
+ XilinxSPI *s = opaque;
+ uint32_t r = 0;
+
+ addr >>= 2;
+ switch (addr) {
+ case R_SPIDRR:
+ if (fifo8_is_empty(&s->rx_fifo)) {
+ DB_PRINT("Read from empty FIFO!\n");
+ return 0xdeadbeef;
+ }
+
+ s->regs[R_SPISR] &= ~SR_RX_FULL;
+ r = fifo8_pop(&s->rx_fifo);
+ if (fifo8_is_empty(&s->rx_fifo)) {
+ s->regs[R_SPISR] |= SR_RX_EMPTY;
+ }
+ break;
+
+ case R_SPISR:
+ r = s->regs[addr];
+ break;
+
+ default:
+ if (addr < ARRAY_SIZE(s->regs)) {
+ r = s->regs[addr];
+ }
+ break;
+
+ }
+ DB_PRINT("addr=" TARGET_FMT_plx " = %x\n", addr * 4, r);
+ xlx_spi_update_irq(s);
+ return r;
+}
+
+static void
+spi_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ XilinxSPI *s = opaque;
+ uint32_t value = val64;
+
+ DB_PRINT("addr=" TARGET_FMT_plx " = %x\n", addr, value);
+ addr >>= 2;
+ switch (addr) {
+ case R_SRR:
+ if (value != 0xa) {
+ DB_PRINT("Invalid write to SRR %x\n", value);
+ } else {
+ xlx_spi_do_reset(s);
+ }
+ break;
+
+ case R_SPIDTR:
+ s->regs[R_SPISR] &= ~SR_TX_EMPTY;
+ fifo8_push(&s->tx_fifo, (uint8_t)value);
+ if (fifo8_is_full(&s->tx_fifo)) {
+ s->regs[R_SPISR] |= SR_TX_FULL;
+ }
+ if (!spi_master_enabled(s)) {
+ goto done;
+ } else {
+ DB_PRINT("DTR and master enabled\n");
+ }
+ spi_flush_txfifo(s);
+ break;
+
+ case R_SPISR:
+ DB_PRINT("Invalid write to SPISR %x\n", value);
+ break;
+
+ case R_IPISR:
+ /* Toggle the bits. */
+ s->regs[addr] ^= value;
+ break;
+
+ /* Slave Select Register. */
+ case R_SPISSR:
+ s->regs[addr] = value;
+ xlx_spi_update_cs(s);
+ break;
+
+ case R_SPICR:
+ /* FIXME: reset irq and sr state to empty queues. */
+ if (value & R_SPICR_RXFF_RST) {
+ rxfifo_reset(s);
+ }
+
+ if (value & R_SPICR_TXFF_RST) {
+ txfifo_reset(s);
+ }
+ value &= ~(R_SPICR_RXFF_RST | R_SPICR_TXFF_RST);
+ s->regs[addr] = value;
+
+ if (!(value & R_SPICR_MTI)) {
+ spi_flush_txfifo(s);
+ }
+ break;
+
+ default:
+ if (addr < ARRAY_SIZE(s->regs)) {
+ s->regs[addr] = value;
+ }
+ break;
+ }
+
+done:
+ xlx_spi_update_irq(s);
+}
+
+static const MemoryRegionOps spi_ops = {
+ .read = spi_read,
+ .write = spi_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static int xilinx_spi_init(SysBusDevice *dev)
+{
+ int i;
+ XilinxSPI *s = FROM_SYSBUS(typeof(*s), dev);
+
+ DB_PRINT("\n");
+
+ s->spi = ssi_create_bus(&dev->qdev, "spi");
+
+ sysbus_init_irq(dev, &s->irq);
+ s->cs_lines = g_new(qemu_irq, s->num_cs);
+ ssi_auto_connect_slaves(DEVICE(s), s->cs_lines, s->spi);
+ for (i = 0; i < s->num_cs; ++i) {
+ sysbus_init_irq(dev, &s->cs_lines[i]);
+ }
+
+ memory_region_init_io(&s->mmio, &spi_ops, s, "xilinx-spi", R_MAX * 4);
+ sysbus_init_mmio(dev, &s->mmio);
+
+ s->irqline = -1;
+
+ fifo8_create(&s->tx_fifo, FIFO_CAPACITY);
+ fifo8_create(&s->rx_fifo, FIFO_CAPACITY);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_xilinx_spi = {
+ .name = "xilinx_spi",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_FIFO8(tx_fifo, XilinxSPI),
+ VMSTATE_FIFO8(rx_fifo, XilinxSPI),
+ VMSTATE_UINT32_ARRAY(regs, XilinxSPI, R_MAX),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property xilinx_spi_properties[] = {
+ DEFINE_PROP_UINT8("num-ss-bits", XilinxSPI, num_cs, 1),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xilinx_spi_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = xilinx_spi_init;
+ dc->reset = xlx_spi_reset;
+ dc->props = xilinx_spi_properties;
+ dc->vmsd = &vmstate_xilinx_spi;
+}
+
+static TypeInfo xilinx_spi_info = {
+ .name = "xlnx.xps-spi",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(XilinxSPI),
+ .class_init = xilinx_spi_class_init,
+};
+
+static void xilinx_spi_register_types(void)
+{
+ type_register_static(&xilinx_spi_info);
+}
+
+type_init(xilinx_spi_register_types)
diff --git a/hw/xilinx_spips.c b/hw/xilinx_spips.c
new file mode 100644
index 000000000..f64a7828f
--- /dev/null
+++ b/hw/xilinx_spips.c
@@ -0,0 +1,354 @@
+/*
+ * QEMU model of the Xilinx Zynq SPI controller
+ *
+ * Copyright (c) 2012 Peter A. G. Crosthwaite
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "sysemu.h"
+#include "ptimer.h"
+#include "qemu-log.h"
+#include "fifo.h"
+#include "ssi.h"
+
+#ifdef XILINX_SPIPS_ERR_DEBUG
+#define DB_PRINT(...) do { \
+ fprintf(stderr, ": %s: ", __func__); \
+ fprintf(stderr, ## __VA_ARGS__); \
+ } while (0);
+#else
+ #define DB_PRINT(...)
+#endif
+
+/* config register */
+#define R_CONFIG (0x00 / 4)
+#define MODEFAIL_GEN_EN (1 << 17)
+#define MAN_START_COM (1 << 16)
+#define MAN_START_EN (1 << 15)
+#define MANUAL_CS (1 << 14)
+#define CS (0xF << 10)
+#define CS_SHIFT (10)
+#define PERI_SEL (1 << 9)
+#define REF_CLK (1 << 8)
+#define FIFO_WIDTH (3 << 6)
+#define BAUD_RATE_DIV (7 << 3)
+#define CLK_PH (1 << 2)
+#define CLK_POL (1 << 1)
+#define MODE_SEL (1 << 0)
+
+/* interrupt mechanism */
+#define R_INTR_STATUS (0x04 / 4)
+#define R_INTR_EN (0x08 / 4)
+#define R_INTR_DIS (0x0C / 4)
+#define R_INTR_MASK (0x10 / 4)
+#define IXR_TX_FIFO_UNDERFLOW (1 << 6)
+#define IXR_RX_FIFO_FULL (1 << 5)
+#define IXR_RX_FIFO_NOT_EMPTY (1 << 4)
+#define IXR_TX_FIFO_FULL (1 << 3)
+#define IXR_TX_FIFO_NOT_FULL (1 << 2)
+#define IXR_TX_FIFO_MODE_FAIL (1 << 1)
+#define IXR_RX_FIFO_OVERFLOW (1 << 0)
+#define IXR_ALL ((IXR_TX_FIFO_UNDERFLOW<<1)-1)
+
+#define R_EN (0x14 / 4)
+#define R_DELAY (0x18 / 4)
+#define R_TX_DATA (0x1C / 4)
+#define R_RX_DATA (0x20 / 4)
+#define R_SLAVE_IDLE_COUNT (0x24 / 4)
+#define R_TX_THRES (0x28 / 4)
+#define R_RX_THRES (0x2C / 4)
+#define R_MOD_ID (0xFC / 4)
+
+#define R_MAX (R_MOD_ID+1)
+
+/* size of TXRX FIFOs */
+#define NUM_CS_LINES 4
+#define RXFF_A 32
+#define TXFF_A 32
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ qemu_irq irq;
+ int irqline;
+
+ qemu_irq cs_lines[NUM_CS_LINES];
+ SSIBus *spi;
+
+ Fifo8 rx_fifo;
+ Fifo8 tx_fifo;
+
+ uint32_t regs[R_MAX];
+} XilinxSPIPS;
+
+static void xilinx_spips_update_cs_lines(XilinxSPIPS *s)
+{
+ int i;
+ bool found = false;
+ int field = s->regs[R_CONFIG] >> CS_SHIFT;
+
+ for (i = 0; i < NUM_CS_LINES; i++) {
+ if (~field & (1 << i) && !found) {
+ found = true;
+ DB_PRINT("selecting slave %d\n", i);
+ qemu_set_irq(s->cs_lines[i], 0);
+ } else {
+ qemu_set_irq(s->cs_lines[i], 1);
+ }
+ }
+}
+
+static void xilinx_spips_update_ixr(XilinxSPIPS *s)
+{
+ /* These are set/cleared as they occur */
+ s->regs[R_INTR_STATUS] &= (IXR_TX_FIFO_UNDERFLOW | IXR_RX_FIFO_OVERFLOW |
+ IXR_TX_FIFO_MODE_FAIL);
+ /* these are pure functions of fifo state, set them here */
+ s->regs[R_INTR_STATUS] |=
+ (fifo8_is_full(&s->rx_fifo) ? IXR_RX_FIFO_FULL : 0) |
+ (s->rx_fifo.num >= s->regs[R_RX_THRES] ? IXR_RX_FIFO_NOT_EMPTY : 0) |
+ (fifo8_is_full(&s->tx_fifo) ? IXR_TX_FIFO_FULL : 0) |
+ (s->tx_fifo.num < s->regs[R_TX_THRES] ? IXR_TX_FIFO_NOT_FULL : 0);
+ /* drive external interrupt pin */
+ int new_irqline = !!(s->regs[R_INTR_MASK] & s->regs[R_INTR_STATUS] &
+ IXR_ALL);
+ if (new_irqline != s->irqline) {
+ s->irqline = new_irqline;
+ qemu_set_irq(s->irq, s->irqline);
+ }
+}
+
+static void xilinx_spips_reset(DeviceState *d)
+{
+ XilinxSPIPS *s = DO_UPCAST(XilinxSPIPS, busdev.qdev, d);
+
+ int i;
+ for (i = 0; i < R_MAX; i++) {
+ s->regs[i] = 0;
+ }
+
+ fifo8_reset(&s->rx_fifo);
+ fifo8_reset(&s->rx_fifo);
+ /* non zero resets */
+ s->regs[R_CONFIG] |= MODEFAIL_GEN_EN;
+ s->regs[R_SLAVE_IDLE_COUNT] = 0xFF;
+ s->regs[R_TX_THRES] = 1;
+ s->regs[R_RX_THRES] = 1;
+ /* FIXME: move magic number definition somewhere sensible */
+ s->regs[R_MOD_ID] = 0x01090106;
+ xilinx_spips_update_ixr(s);
+ xilinx_spips_update_cs_lines(s);
+}
+
+static void xilinx_spips_flush_txfifo(XilinxSPIPS *s)
+{
+ for (;;) {
+ uint32_t r;
+ uint8_t value;
+
+ if (fifo8_is_empty(&s->tx_fifo)) {
+ s->regs[R_INTR_STATUS] |= IXR_TX_FIFO_UNDERFLOW;
+ break;
+ } else {
+ value = fifo8_pop(&s->tx_fifo);
+ }
+
+ r = ssi_transfer(s->spi, (uint32_t)value);
+ DB_PRINT("tx = %02x rx = %02x\n", value, r);
+ if (fifo8_is_full(&s->rx_fifo)) {
+ s->regs[R_INTR_STATUS] |= IXR_RX_FIFO_OVERFLOW;
+ DB_PRINT("rx FIFO overflow");
+ } else {
+ fifo8_push(&s->rx_fifo, (uint8_t)r);
+ }
+ }
+ xilinx_spips_update_ixr(s);
+}
+
+static uint64_t xilinx_spips_read(void *opaque, target_phys_addr_t addr,
+ unsigned size)
+{
+ XilinxSPIPS *s = opaque;
+ uint32_t mask = ~0;
+ uint32_t ret;
+
+ addr >>= 2;
+ switch (addr) {
+ case R_CONFIG:
+ mask = 0x0002FFFF;
+ break;
+ case R_INTR_STATUS:
+ case R_INTR_MASK:
+ mask = IXR_ALL;
+ break;
+ case R_EN:
+ mask = 0x1;
+ break;
+ case R_SLAVE_IDLE_COUNT:
+ mask = 0xFF;
+ break;
+ case R_MOD_ID:
+ mask = 0x01FFFFFF;
+ break;
+ case R_INTR_EN:
+ case R_INTR_DIS:
+ case R_TX_DATA:
+ mask = 0;
+ break;
+ case R_RX_DATA:
+ ret = (uint32_t)fifo8_pop(&s->rx_fifo);
+ DB_PRINT("addr=" TARGET_FMT_plx " = %x\n", addr * 4, ret);
+ xilinx_spips_update_ixr(s);
+ return ret;
+ }
+ DB_PRINT("addr=" TARGET_FMT_plx " = %x\n", addr * 4, s->regs[addr] & mask);
+ return s->regs[addr] & mask;
+
+}
+
+static void xilinx_spips_write(void *opaque, target_phys_addr_t addr,
+ uint64_t value, unsigned size)
+{
+ int mask = ~0;
+ int man_start_com = 0;
+ XilinxSPIPS *s = opaque;
+
+ DB_PRINT("addr=" TARGET_FMT_plx " = %x\n", addr, (unsigned)value);
+ addr >>= 2;
+ switch (addr) {
+ case R_CONFIG:
+ mask = 0x0002FFFF;
+ if (value & MAN_START_COM) {
+ man_start_com = 1;
+ }
+ break;
+ case R_INTR_STATUS:
+ mask = IXR_ALL;
+ s->regs[R_INTR_STATUS] &= ~(mask & value);
+ goto no_reg_update;
+ case R_INTR_DIS:
+ mask = IXR_ALL;
+ s->regs[R_INTR_MASK] &= ~(mask & value);
+ goto no_reg_update;
+ case R_INTR_EN:
+ mask = IXR_ALL;
+ s->regs[R_INTR_MASK] |= mask & value;
+ goto no_reg_update;
+ case R_EN:
+ mask = 0x1;
+ break;
+ case R_SLAVE_IDLE_COUNT:
+ mask = 0xFF;
+ break;
+ case R_RX_DATA:
+ case R_INTR_MASK:
+ case R_MOD_ID:
+ mask = 0;
+ break;
+ case R_TX_DATA:
+ fifo8_push(&s->tx_fifo, (uint8_t)value);
+ goto no_reg_update;
+ }
+ s->regs[addr] = (s->regs[addr] & ~mask) | (value & mask);
+no_reg_update:
+ if (man_start_com) {
+ xilinx_spips_flush_txfifo(s);
+ }
+ xilinx_spips_update_ixr(s);
+ xilinx_spips_update_cs_lines(s);
+}
+
+static const MemoryRegionOps spips_ops = {
+ .read = xilinx_spips_read,
+ .write = xilinx_spips_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static int xilinx_spips_init(SysBusDevice *dev)
+{
+ XilinxSPIPS *s = FROM_SYSBUS(typeof(*s), dev);
+ int i;
+
+ DB_PRINT("inited device model\n");
+
+ s->spi = ssi_create_bus(&dev->qdev, "spi");
+
+ ssi_auto_connect_slaves(DEVICE(s), s->cs_lines, s->spi);
+ sysbus_init_irq(dev, &s->irq);
+ for (i = 0; i < NUM_CS_LINES; ++i) {
+ sysbus_init_irq(dev, &s->cs_lines[i]);
+ }
+
+ memory_region_init_io(&s->iomem, &spips_ops, s, "spi", R_MAX*4);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ s->irqline = -1;
+
+ fifo8_create(&s->rx_fifo, RXFF_A);
+ fifo8_create(&s->tx_fifo, TXFF_A);
+
+ return 0;
+}
+
+static int xilinx_spips_post_load(void *opaque, int version_id)
+{
+ xilinx_spips_update_ixr((XilinxSPIPS *)opaque);
+ xilinx_spips_update_cs_lines((XilinxSPIPS *)opaque);
+ return 0;
+}
+
+static const VMStateDescription vmstate_xilinx_spips = {
+ .name = "xilinx_spips",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = xilinx_spips_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_FIFO8(tx_fifo, XilinxSPIPS),
+ VMSTATE_FIFO8(rx_fifo, XilinxSPIPS),
+ VMSTATE_UINT32_ARRAY(regs, XilinxSPIPS, R_MAX),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void xilinx_spips_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = xilinx_spips_init;
+ dc->reset = xilinx_spips_reset;
+ dc->vmsd = &vmstate_xilinx_spips;
+}
+
+static const TypeInfo xilinx_spips_info = {
+ .name = "xilinx,spips",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(XilinxSPIPS),
+ .class_init = xilinx_spips_class_init,
+};
+
+static void xilinx_spips_register_types(void)
+{
+ type_register_static(&xilinx_spips_info);
+}
+
+type_init(xilinx_spips_register_types)
diff --git a/hw/xilinx_timer.c b/hw/xilinx_timer.c
index b562bd065..2e48ca2be 100644
--- a/hw/xilinx_timer.c
+++ b/hw/xilinx_timer.c
@@ -24,6 +24,7 @@
#include "sysbus.h"
#include "ptimer.h"
+#include "qemu-log.h"
#define D(x)
@@ -119,7 +120,7 @@ timer_read(void *opaque, target_phys_addr_t addr, unsigned int size)
break;
}
- D(printf("%s timer=%d %x=%x\n", __func__, timer, addr * 4, r));
+ D(fprintf(stderr, "%s timer=%d %x=%x\n", __func__, timer, addr * 4, r));
return r;
}
@@ -127,7 +128,7 @@ static void timer_enable(struct xlx_timer *xt)
{
uint64_t count;
- D(printf("%s timer=%d down=%d\n", __func__,
+ D(fprintf(stderr, "%s timer=%d down=%d\n", __func__,
xt->nr, xt->regs[R_TCSR] & TCSR_UDT));
ptimer_stop(xt->ptimer);
@@ -152,7 +153,7 @@ timer_write(void *opaque, target_phys_addr_t addr,
addr >>= 2;
timer = timer_from_addr(addr);
xt = &t->timers[timer];
- D(printf("%s addr=%x val=%x (timer=%d off=%d)\n",
+ D(fprintf(stderr, "%s addr=%x val=%x (timer=%d off=%d)\n",
__func__, addr * 4, value, timer, addr & 3));
/* Further decoding to address a specific timers reg. */
addr &= 3;
@@ -189,7 +190,7 @@ static void timer_hit(void *opaque)
{
struct xlx_timer *xt = opaque;
struct timerblock *t = xt->parent;
- D(printf("%s %d\n", __func__, timer));
+ D(fprintf(stderr, "%s %d\n", __func__, xt->nr));
xt->regs[R_TCSR] |= TCSR_TINT;
if (xt->regs[R_TCSR] & TCSR_ARHT)
@@ -217,14 +218,15 @@ static int xilinx_timer_init(SysBusDevice *dev)
ptimer_set_freq(xt->ptimer, t->freq_hz);
}
- memory_region_init_io(&t->mmio, &timer_ops, t, "xlnx,xps-timer",
+ memory_region_init_io(&t->mmio, &timer_ops, t, "xlnx.xps-timer",
R_MAX * 4 * num_timers(t));
sysbus_init_mmio(dev, &t->mmio);
return 0;
}
static Property xilinx_timer_properties[] = {
- DEFINE_PROP_UINT32("frequency", struct timerblock, freq_hz, 62 * 1000000),
+ DEFINE_PROP_UINT32("clock-frequency", struct timerblock, freq_hz,
+ 62 * 1000000),
DEFINE_PROP_UINT8("one-timer-only", struct timerblock, one_timer_only, 0),
DEFINE_PROP_END_OF_LIST(),
};
@@ -239,7 +241,7 @@ static void xilinx_timer_class_init(ObjectClass *klass, void *data)
}
static TypeInfo xilinx_timer_info = {
- .name = "xlnx,xps-timer",
+ .name = "xlnx.xps-timer",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(struct timerblock),
.class_init = xilinx_timer_class_init,
diff --git a/hw/xilinx_zynq.c b/hw/xilinx_zynq.c
index 7e6c27359..fd46ba252 100644
--- a/hw/xilinx_zynq.c
+++ b/hw/xilinx_zynq.c
@@ -24,6 +24,9 @@
#include "flash.h"
#include "blockdev.h"
#include "loader.h"
+#include "ssi.h"
+
+#define NUM_SPI_FLASHES 4
#define FLASH_SIZE (64 * 1024 * 1024)
#define FLASH_SECTOR_SIZE (128 * 1024)
@@ -46,6 +49,34 @@ static void gem_init(NICInfo *nd, uint32_t base, qemu_irq irq)
sysbus_connect_irq(s, 0, irq);
}
+static inline void zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq)
+{
+ DeviceState *dev;
+ SysBusDevice *busdev;
+ SSIBus *spi;
+ int i;
+
+ dev = qdev_create(NULL, "xilinx,spips");
+ qdev_init_nofail(dev);
+ busdev = sysbus_from_qdev(dev);
+ sysbus_mmio_map(busdev, 0, base_addr);
+ sysbus_connect_irq(busdev, 0, irq);
+
+ spi = (SSIBus *)qdev_get_child_bus(dev, "spi");
+
+ for (i = 0; i < NUM_SPI_FLASHES; ++i) {
+ qemu_irq cs_line;
+
+ dev = ssi_create_slave_no_init(spi, "m25p80");
+ qdev_prop_set_string(dev, "partname", "n25q128");
+ qdev_init_nofail(dev);
+
+ cs_line = qdev_get_gpio_in(dev, 0);
+ sysbus_connect_irq(busdev, i+1, cs_line);
+ }
+
+}
+
static void zynq_init(ram_addr_t ram_size, const char *boot_device,
const char *kernel_filename, const char *kernel_cmdline,
const char *initrd_filename, const char *cpu_model)
@@ -113,6 +144,9 @@ static void zynq_init(ram_addr_t ram_size, const char *boot_device,
pic[n] = qdev_get_gpio_in(dev, n);
}
+ zynq_init_spi_flashes(0xE0006000, pic[58-IRQ_OFFSET]);
+ zynq_init_spi_flashes(0xE0007000, pic[81-IRQ_OFFSET]);
+
sysbus_create_simple("cadence_uart", 0xE0000000, pic[59-IRQ_OFFSET]);
sysbus_create_simple("cadence_uart", 0xE0001000, pic[82-IRQ_OFFSET]);
diff --git a/hw/z2.c b/hw/z2.c
index 289cee90c..076fad20c 100644
--- a/hw/z2.c
+++ b/hw/z2.c
@@ -161,10 +161,11 @@ static int zipit_lcd_init(SSISlave *dev)
static VMStateDescription vmstate_zipit_lcd_state = {
.name = "zipit-lcd",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
.fields = (VMStateField[]) {
+ VMSTATE_SSI_SLAVE(ssidev, ZipitLCD),
VMSTATE_INT32(selected, ZipitLCD),
VMSTATE_INT32(enabled, ZipitLCD),
VMSTATE_BUFFER(buf, ZipitLCD),