summaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/9pfs/virtio-9p-device.c6
-rw-r--r--hw/9pfs/virtio-9p.c24
-rw-r--r--hw/9pfs/virtio-9p.h4
-rw-r--r--hw/acpi/Makefile.objs6
-rw-r--r--hw/acpi/aml-build.c385
-rw-r--r--hw/acpi/core.c23
-rw-r--r--hw/acpi/cpu_hotplug.c3
-rw-r--r--hw/acpi/ich9.c95
-rw-r--r--hw/acpi/memory_hotplug.c98
-rw-r--r--hw/acpi/pcihp.c10
-rw-r--r--hw/acpi/piix4.c51
-rw-r--r--hw/acpi/tco.c264
-rw-r--r--hw/alpha/dp264.c16
-rw-r--r--hw/alpha/typhoon.c11
-rw-r--r--hw/arm/Makefile.objs3
-rw-r--r--hw/arm/allwinner-a10.c6
-rw-r--r--hw/arm/boot.c92
-rw-r--r--hw/arm/digic.c6
-rw-r--r--hw/arm/highbank.c15
-rw-r--r--hw/arm/musicpal.c6
-rw-r--r--hw/arm/nseries.c10
-rw-r--r--hw/arm/omap1.c6
-rw-r--r--hw/arm/omap_sx1.c2
-rw-r--r--hw/arm/pxa2xx.c250
-rw-r--r--hw/arm/pxa2xx_gpio.c2
-rw-r--r--hw/arm/pxa2xx_pic.c2
-rw-r--r--hw/arm/strongarm.c4
-rw-r--r--hw/arm/sysbus-fdt.c247
-rw-r--r--hw/arm/vexpress.c4
-rw-r--r--hw/arm/virt-acpi-build.c697
-rw-r--r--hw/arm/virt.c259
-rw-r--r--hw/arm/xlnx-ep108.c82
-rw-r--r--hw/arm/xlnx-zynqmp.c272
-rw-r--r--hw/audio/gus.c20
-rw-r--r--hw/audio/sb16.c7
-rw-r--r--hw/block/dataplane/virtio-blk.c2
-rw-r--r--hw/block/fdc.c349
-rw-r--r--hw/block/m25p80.c5
-rw-r--r--hw/block/nvme.c43
-rw-r--r--hw/block/nvme.h1
-rw-r--r--hw/block/pflash_cfi01.c204
-rw-r--r--hw/block/virtio-blk.c43
-rw-r--r--hw/char/cadence_uart.c115
-rw-r--r--hw/char/parallel.c25
-rw-r--r--hw/char/sclpconsole-lm.c1
-rw-r--r--hw/char/sclpconsole.c1
-rw-r--r--hw/char/serial-pci.c21
-rw-r--r--hw/char/serial.c41
-rw-r--r--hw/char/spapr_vty.c8
-rw-r--r--hw/char/virtio-console.c10
-rw-r--r--hw/char/virtio-serial-bus.c22
-rw-r--r--hw/core/loader.c16
-rw-r--r--hw/core/machine.c9
-rw-r--r--hw/core/nmi.c22
-rw-r--r--hw/core/platform-bus.c1
-rw-r--r--hw/core/ptimer.c2
-rw-r--r--hw/core/qdev-properties-system.c51
-rw-r--r--hw/core/qdev-properties.c71
-rw-r--r--hw/core/qdev.c60
-rw-r--r--hw/core/sysbus.c17
-rw-r--r--hw/display/Makefile.objs6
-rw-r--r--hw/display/cg3.c7
-rw-r--r--hw/display/cirrus_vga.c8
-rw-r--r--hw/display/exynos4210_fimd.c22
-rw-r--r--hw/display/framebuffer.c73
-rw-r--r--hw/display/framebuffer.h44
-rw-r--r--hw/display/g364fb.c4
-rw-r--r--hw/display/milkymist-vgafb.c15
-rw-r--r--hw/display/omap_lcdc.c12
-rw-r--r--hw/display/pl110.c13
-rw-r--r--hw/display/pxa2xx_lcd.c29
-rw-r--r--hw/display/qxl-logger.c16
-rw-r--r--hw/display/qxl.c87
-rw-r--r--hw/display/qxl.h6
-rw-r--r--hw/display/sm501.c2
-rw-r--r--hw/display/tc6393xb.c4
-rw-r--r--hw/display/tcx.c7
-rw-r--r--hw/display/vga-pci.c145
-rw-r--r--hw/display/vga.c11
-rw-r--r--hw/display/vga_int.h6
-rw-r--r--hw/display/virtio-gpu-pci.c76
-rw-r--r--hw/display/virtio-gpu.c919
-rw-r--r--hw/display/virtio-vga.c182
-rw-r--r--hw/display/vmware_vga.c2
-rw-r--r--hw/dma/pl080.c20
-rw-r--r--hw/dma/rc4030.c462
-rw-r--r--hw/dma/sun4m_iommu.c3
-rw-r--r--hw/dma/xilinx_axidma.c1
-rw-r--r--hw/gpio/max7310.c2
-rw-r--r--hw/gpio/omap_gpio.c13
-rw-r--r--hw/gpio/pl061.c2
-rw-r--r--hw/gpio/zaurus.c2
-rw-r--r--hw/i2c/Makefile.objs2
-rw-r--r--hw/i2c/omap_i2c.c10
-rw-r--r--hw/i386/Makefile.objs3
-rw-r--r--hw/i386/acpi-build.c648
-rw-r--r--hw/i386/acpi-defs.h368
-rw-r--r--hw/i386/acpi-dsdt-mem-hotplug.dsl13
-rw-r--r--hw/i386/intel_iommu.c3
-rw-r--r--hw/i386/kvm/pci-assign.c39
-rw-r--r--hw/i386/pc.c409
-rw-r--r--hw/i386/pc_piix.c724
-rw-r--r--hw/i386/pc_q35.c253
-rw-r--r--hw/i386/ssdt-tpm.dsl43
-rw-r--r--hw/i386/ssdt-tpm.hex.generated95
-rw-r--r--hw/ide/ahci.c559
-rw-r--r--hw/ide/ahci.h51
-rw-r--r--hw/ide/core.c86
-rw-r--r--hw/ide/ich.c1
-rw-r--r--hw/ide/internal.h5
-rw-r--r--hw/ide/macio.c470
-rw-r--r--hw/ide/pci.c42
-rw-r--r--hw/input/Makefile.objs6
-rw-r--r--hw/input/hid.c32
-rw-r--r--hw/input/pckbd.c22
-rw-r--r--hw/input/ps2.c11
-rw-r--r--hw/input/virtio-input-hid.c514
-rw-r--r--hw/input/virtio-input-host.c189
-rw-r--r--hw/input/virtio-input.c293
-rw-r--r--hw/intc/Makefile.objs1
-rw-r--r--hw/intc/allwinner-a10-pic.c8
-rw-r--r--hw/intc/apic.c9
-rw-r--r--hw/intc/apic_common.c26
-rw-r--r--hw/intc/arm_gic.c471
-rw-r--r--hw/intc/arm_gic_common.c43
-rw-r--r--hw/intc/arm_gic_kvm.c65
-rw-r--r--hw/intc/arm_gicv2m.c192
-rw-r--r--hw/intc/armv7m_nvic.c17
-rw-r--r--hw/intc/exynos4210_gic.c7
-rw-r--r--hw/intc/gic_internal.h29
-rw-r--r--hw/intc/omap_intc.c9
-rw-r--r--hw/intc/openpic.c6
-rw-r--r--hw/intc/xics.c20
-rw-r--r--hw/intc/xics_kvm.c12
-rw-r--r--hw/isa/i82378.c7
-rw-r--r--hw/isa/isa-bus.c29
-rw-r--r--hw/isa/lpc_ich9.c60
-rw-r--r--hw/isa/piix4.c8
-rw-r--r--hw/isa/vt82c686.c49
-rw-r--r--hw/lm32/lm32_boards.c10
-rw-r--r--hw/lm32/milkymist.c5
-rw-r--r--hw/m68k/mcf_intc.c14
-rw-r--r--hw/mem/pc-dimm.c85
-rw-r--r--hw/microblaze/boot.c18
-rw-r--r--hw/microblaze/petalogix_ml605_mmu.c28
-rw-r--r--hw/microblaze/petalogix_s3adsp1800_mmu.c17
-rw-r--r--hw/mips/Makefile.objs3
-rw-r--r--hw/mips/mips_fulong2e.c1
-rw-r--r--hw/mips/mips_jazz.c59
-rw-r--r--hw/mips/mips_malta.c29
-rw-r--r--hw/mips/mips_r4k.c1
-rw-r--r--hw/misc/arm_integrator_debug.c2
-rw-r--r--hw/misc/edu.c2
-rw-r--r--hw/misc/ivshmem.c3
-rw-r--r--hw/misc/macio/cuda.c2
-rw-r--r--hw/misc/macio/mac_dbdma.c12
-rw-r--r--hw/misc/macio/macio.c85
-rw-r--r--hw/misc/slavio_misc.c5
-rw-r--r--hw/misc/zynq_slcr.c8
-rw-r--r--hw/net/Makefile.objs5
-rw-r--r--hw/net/cadence_gem.c97
-rw-r--r--hw/net/dp8393x.c390
-rw-r--r--hw/net/e1000.c17
-rw-r--r--hw/net/eepro100.c11
-rw-r--r--hw/net/etraxfs_eth.c6
-rw-r--r--hw/net/fsl_etsec/etsec.c20
-rw-r--r--hw/net/fsl_etsec/etsec.h4
-rw-r--r--hw/net/fsl_etsec/rings.c17
-rw-r--r--hw/net/lan9118.c6
-rw-r--r--hw/net/lance.c1
-rw-r--r--hw/net/mcf_fec.c101
-rw-r--r--hw/net/milkymist-minimac2.c33
-rw-r--r--hw/net/mipsnet.c9
-rw-r--r--hw/net/ne2000.c21
-rw-r--r--hw/net/pcnet-pci.c1
-rw-r--r--hw/net/pcnet.c17
-rw-r--r--hw/net/pcnet.h1
-rw-r--r--hw/net/rocker/qmp-norocker.c50
-rw-r--r--hw/net/rocker/rocker.c1553
-rw-r--r--hw/net/rocker/rocker.h84
-rw-r--r--hw/net/rocker/rocker_desc.c377
-rw-r--r--hw/net/rocker/rocker_desc.h53
-rw-r--r--hw/net/rocker/rocker_fp.c263
-rw-r--r--hw/net/rocker/rocker_fp.h53
-rw-r--r--hw/net/rocker/rocker_hw.h493
-rw-r--r--hw/net/rocker/rocker_of_dpa.c2630
-rw-r--r--hw/net/rocker/rocker_of_dpa.h22
-rw-r--r--hw/net/rocker/rocker_tlv.h244
-rw-r--r--hw/net/rocker/rocker_world.c106
-rw-r--r--hw/net/rocker/rocker_world.h60
-rw-r--r--hw/net/rtl8139.c19
-rw-r--r--hw/net/spapr_llan.c12
-rw-r--r--hw/net/stellaris_enet.c14
-rw-r--r--hw/net/vhost_net.c81
-rw-r--r--hw/net/virtio-net.c306
-rw-r--r--hw/net/vmxnet3.c93
-rw-r--r--hw/net/vmxnet_rx_pkt.c19
-rw-r--r--hw/net/vmxnet_rx_pkt.h20
-rw-r--r--hw/net/xen_nic.c25
-rw-r--r--hw/net/xgmac.c8
-rw-r--r--hw/net/xilinx_axienet.c18
-rw-r--r--hw/nvram/fw_cfg.c55
-rw-r--r--hw/nvram/spapr_nvram.c4
-rw-r--r--hw/pci-bridge/Makefile.objs1
-rw-r--r--hw/pci-bridge/i82801b11.c21
-rw-r--r--hw/pci-bridge/pci_bridge_dev.c117
-rw-r--r--hw/pci-bridge/pci_expander_bridge.c286
-rw-r--r--hw/pci-host/apb.c3
-rw-r--r--hw/pci-host/bonito.c2
-rw-r--r--hw/pci-host/pam.c20
-rw-r--r--hw/pci-host/piix.c111
-rw-r--r--hw/pci-host/prep.c6
-rw-r--r--hw/pci-host/q35.c142
-rw-r--r--hw/pci-host/uninorth.c5
-rw-r--r--hw/pci-host/versatile.c11
-rw-r--r--hw/pci/msi.c27
-rw-r--r--hw/pci/msix.c32
-rw-r--r--hw/pci/pci-stub.c17
-rw-r--r--hw/pci/pci.c178
-rw-r--r--hw/pci/pcie.c3
-rw-r--r--hw/pci/pcie_aer.c45
-rw-r--r--hw/pci/shpc.c11
-rw-r--r--hw/pci/slotid_cap.c2
-rw-r--r--hw/pcmcia/pxa2xx.c6
-rw-r--r--hw/ppc/Makefile.objs2
-rw-r--r--hw/ppc/e500.c1
-rw-r--r--hw/ppc/mac_newworld.c12
-rw-r--r--hw/ppc/mac_oldworld.c9
-rw-r--r--hw/ppc/ppc.c7
-rw-r--r--hw/ppc/ppc440_bamboo.c7
-rw-r--r--hw/ppc/ppce500_spin.c2
-rw-r--r--hw/ppc/prep.c12
-rw-r--r--hw/ppc/spapr.c569
-rw-r--r--hw/ppc/spapr_drc.c745
-rw-r--r--hw/ppc/spapr_events.c347
-rw-r--r--hw/ppc/spapr_hcall.c39
-rw-r--r--hw/ppc/spapr_iommu.c74
-rw-r--r--hw/ppc/spapr_pci.c758
-rw-r--r--hw/ppc/spapr_pci_vfio.c65
-rw-r--r--hw/ppc/spapr_rtas.c397
-rw-r--r--hw/ppc/spapr_rtc.c15
-rw-r--r--hw/ppc/spapr_vio.c18
-rw-r--r--hw/ppc/virtex_ml507.c9
-rw-r--r--hw/s390x/css.c48
-rw-r--r--hw/s390x/css.h1
-rw-r--r--hw/s390x/event-facility.c2
-rw-r--r--hw/s390x/ipl.c1
-rw-r--r--hw/s390x/s390-pci-bus.c9
-rw-r--r--hw/s390x/s390-pci-inst.c31
-rw-r--r--hw/s390x/s390-pci-inst.h7
-rw-r--r--hw/s390x/s390-virtio-bus.c140
-rw-r--r--hw/s390x/s390-virtio-bus.h1
-rw-r--r--hw/s390x/s390-virtio-ccw.c29
-rw-r--r--hw/s390x/s390-virtio.c19
-rw-r--r--hw/s390x/sclp.c9
-rw-r--r--hw/s390x/sclpcpu.c2
-rw-r--r--hw/s390x/sclpquiesce.c1
-rw-r--r--hw/s390x/virtio-ccw.c517
-rw-r--r--hw/s390x/virtio-ccw.h30
-rw-r--r--hw/scsi/megasas.c17
-rw-r--r--hw/scsi/scsi-bus.c11
-rw-r--r--hw/scsi/scsi-disk.c20
-rw-r--r--hw/scsi/vhost-scsi.c19
-rw-r--r--hw/scsi/virtio-scsi.c36
-rw-r--r--hw/scsi/vmw_pvscsi.c8
-rw-r--r--hw/sd/pxa2xx_mmci.c68
-rw-r--r--hw/sd/sd.c20
-rw-r--r--hw/sh4/r2d.c18
-rw-r--r--hw/sparc/sun4m.c8
-rw-r--r--hw/sparc64/sun4u.c2
-rw-r--r--hw/timer/arm_mptimer.c13
-rw-r--r--hw/timer/arm_timer.c6
-rw-r--r--hw/timer/cadence_ttc.c9
-rw-r--r--hw/timer/hpet.c17
-rw-r--r--hw/timer/mc146818rtc.c48
-rw-r--r--hw/tpm/Makefile.objs2
-rw-r--r--hw/tpm/tpm_int.h7
-rw-r--r--hw/tpm/tpm_passthrough.c80
-rw-r--r--hw/tpm/tpm_tis.c145
-rw-r--r--hw/tpm/tpm_tis.h1
-rw-r--r--hw/tpm/tpm_util.c126
-rw-r--r--hw/tpm/tpm_util.h28
-rw-r--r--hw/unicore32/puv3.c8
-rw-r--r--hw/usb/bus.c1
-rw-r--r--hw/usb/ccid-card-emulated.c1
-rw-r--r--hw/usb/ccid-card-passthru.c2
-rw-r--r--hw/usb/core.c41
-rw-r--r--hw/usb/dev-audio.c23
-rw-r--r--hw/usb/dev-bluetooth.c13
-rw-r--r--hw/usb/dev-hid.c34
-rw-r--r--hw/usb/dev-hub.c11
-rw-r--r--hw/usb/dev-mtp.c15
-rw-r--r--hw/usb/dev-network.c33
-rw-r--r--hw/usb/dev-serial.c45
-rw-r--r--hw/usb/dev-smartcard-reader.c56
-rw-r--r--hw/usb/dev-storage.c33
-rw-r--r--hw/usb/dev-uas.c15
-rw-r--r--hw/usb/dev-wacom.c9
-rw-r--r--hw/usb/hcd-ehci.c2
-rw-r--r--hw/usb/hcd-ehci.h1
-rw-r--r--hw/usb/hcd-ohci.c11
-rw-r--r--hw/usb/hcd-uhci.c45
-rw-r--r--hw/usb/hcd-xhci.c24
-rw-r--r--hw/usb/host-libusb.c4
-rw-r--r--hw/usb/redirect.c68
-rw-r--r--hw/vfio/Makefile.objs2
-rw-r--r--hw/vfio/calxeda-xgmac.c55
-rw-r--r--hw/vfio/common.c15
-rw-r--r--hw/vfio/pci.c223
-rw-r--r--hw/vfio/platform.c715
-rw-r--r--hw/virtio/dataplane/vring.c87
-rw-r--r--hw/virtio/vhost-user.c22
-rw-r--r--hw/virtio/vhost.c162
-rw-r--r--hw/virtio/virtio-balloon.c35
-rw-r--r--hw/virtio/virtio-bus.c24
-rw-r--r--hw/virtio/virtio-mmio.c208
-rw-r--r--hw/virtio/virtio-pci.c914
-rw-r--r--hw/virtio/virtio-pci.h87
-rw-r--r--hw/virtio/virtio-rng.c26
-rw-r--r--hw/virtio/virtio.c433
-rw-r--r--hw/watchdog/Makefile.objs1
-rw-r--r--hw/watchdog/watchdog.c10
-rw-r--r--hw/watchdog/wdt_diag288.c130
-rw-r--r--hw/watchdog/wdt_i6300esb.c14
-rw-r--r--hw/xen/xen_backend.c4
-rw-r--r--hw/xen/xen_pt.c112
-rw-r--r--hw/xen/xen_pt.h19
-rw-r--r--hw/xen/xen_pt_config_init.c238
-rw-r--r--hw/xen/xen_pt_msi.c36
329 files changed, 23671 insertions, 6182 deletions
diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c
index 30492ecb0..93a407c45 100644
--- a/hw/9pfs/virtio-9p-device.c
+++ b/hw/9pfs/virtio-9p-device.c
@@ -21,7 +21,8 @@
#include "virtio-9p-coth.h"
#include "hw/virtio/virtio-access.h"
-static uint32_t virtio_9p_get_features(VirtIODevice *vdev, uint32_t features)
+static uint64_t virtio_9p_get_features(VirtIODevice *vdev, uint64_t features,
+ Error **errp)
{
virtio_add_feature(&features, VIRTIO_9P_MOUNT_TAG);
return features;
@@ -140,7 +141,8 @@ out:
/* virtio-9p device */
static Property virtio_9p_properties[] = {
- DEFINE_VIRTIO_9P_PROPERTIES(V9fsState, fsconf),
+ DEFINE_PROP_STRING("mount_tag", V9fsState, fsconf.tag),
+ DEFINE_PROP_STRING("fsdev", V9fsState, fsconf.fsdev_id),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c
index 4964da0d7..f972731f5 100644
--- a/hw/9pfs/virtio-9p.c
+++ b/hw/9pfs/virtio-9p.c
@@ -13,6 +13,8 @@
#include "hw/virtio/virtio.h"
#include "hw/i386/pc.h"
+#include "qemu/error-report.h"
+#include "qemu/iov.h"
#include "qemu/sockets.h"
#include "virtio-9p.h"
#include "fsdev/qemu-fsdev.h"
@@ -3260,16 +3262,26 @@ void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq)
while ((pdu = alloc_pdu(s)) &&
(len = virtqueue_pop(vq, &pdu->elem)) != 0) {
- uint8_t *ptr;
+ struct {
+ uint32_t size_le;
+ uint8_t id;
+ uint16_t tag_le;
+ } QEMU_PACKED out;
+ int len;
+
pdu->s = s;
BUG_ON(pdu->elem.out_num == 0 || pdu->elem.in_num == 0);
- BUG_ON(pdu->elem.out_sg[0].iov_len < 7);
+ QEMU_BUILD_BUG_ON(sizeof out != 7);
+
+ len = iov_to_buf(pdu->elem.out_sg, pdu->elem.out_num, 0,
+ &out, sizeof out);
+ BUG_ON(len != sizeof out);
+
+ pdu->size = le32_to_cpu(out.size_le);
- ptr = pdu->elem.out_sg[0].iov_base;
+ pdu->id = out.id;
+ pdu->tag = le16_to_cpu(out.tag_le);
- pdu->size = le32_to_cpu(*(uint32_t *)ptr);
- pdu->id = ptr[4];
- pdu->tag = le16_to_cpu(*(uint16_t *)(ptr + 5));
qemu_co_queue_init(&pdu->complete);
submit_pdu(s, pdu);
}
diff --git a/hw/9pfs/virtio-9p.h b/hw/9pfs/virtio-9p.h
index 58dafa9e1..2e7d48857 100644
--- a/hw/9pfs/virtio-9p.h
+++ b/hw/9pfs/virtio-9p.h
@@ -391,8 +391,4 @@ extern int v9fs_name_to_path(V9fsState *s, V9fsPath *dirpath,
#define VIRTIO_9P(obj) \
OBJECT_CHECK(V9fsState, (obj), TYPE_VIRTIO_9P)
-#define DEFINE_VIRTIO_9P_PROPERTIES(_state, _field) \
- DEFINE_PROP_STRING("mount_tag", _state, _field.tag), \
- DEFINE_PROP_STRING("fsdev", _state, _field.fsdev_id)
-
#endif
diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
index b9fefa763..7d3230c2a 100644
--- a/hw/acpi/Makefile.objs
+++ b/hw/acpi/Makefile.objs
@@ -1,5 +1,7 @@
-common-obj-$(CONFIG_ACPI) += core.o piix4.o ich9.o pcihp.o cpu_hotplug.o
-common-obj-$(CONFIG_ACPI) += memory_hotplug.o
+common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o pcihp.o
+common-obj-$(CONFIG_ACPI_X86_ICH) += ich9.o tco.o
+common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o
+common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
common-obj-$(CONFIG_ACPI) += acpi_interface.o
common-obj-$(CONFIG_ACPI) += bios-linker-loader.o
common-obj-$(CONFIG_ACPI) += aml-build.o
diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c
index 41ff6a32c..0d4b3247b 100644
--- a/hw/acpi/aml-build.c
+++ b/hw/acpi/aml-build.c
@@ -19,6 +19,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include <glib/gprintf.h>
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
@@ -26,6 +27,8 @@
#include <string.h>
#include "hw/acpi/aml-build.h"
#include "qemu/bswap.h"
+#include "qemu/bitops.h"
+#include "hw/acpi/bios-linker-loader.h"
static GArray *build_alloc_array(void)
{
@@ -57,7 +60,6 @@ static void build_append_array(GArray *array, GArray *val)
static void
build_append_nameseg(GArray *array, const char *seg)
{
- /* It would be nicer to use g_string_vprintf but it's only there in 2.22 */
int len;
len = strlen(seg);
@@ -71,22 +73,12 @@ build_append_nameseg(GArray *array, const char *seg)
static void GCC_FMT_ATTR(2, 0)
build_append_namestringv(GArray *array, const char *format, va_list ap)
{
- /* It would be nicer to use g_string_vprintf but it's only there in 2.22 */
char *s;
- int len;
- va_list va_len;
char **segs;
char **segs_iter;
int seg_count = 0;
- va_copy(va_len, ap);
- len = vsnprintf(NULL, 0, format, va_len);
- va_end(va_len);
- len += 1;
- s = g_new(typeof(*s), len);
-
- len = vsnprintf(s, len, format, ap);
-
+ s = g_strdup_vprintf(format, ap);
segs = g_strsplit(s, ".", 0);
g_free(s);
@@ -454,6 +446,73 @@ Aml *aml_and(Aml *arg1, Aml *arg2)
return var;
}
+/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefOr */
+Aml *aml_or(Aml *arg1, Aml *arg2)
+{
+ Aml *var = aml_opcode(0x7D /* OrOp */);
+ aml_append(var, arg1);
+ aml_append(var, arg2);
+ build_append_byte(var->buf, 0x00 /* NullNameOp */);
+ return var;
+}
+
+/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefShiftLeft */
+Aml *aml_shiftleft(Aml *arg1, Aml *count)
+{
+ Aml *var = aml_opcode(0x79 /* ShiftLeftOp */);
+ aml_append(var, arg1);
+ aml_append(var, count);
+ build_append_byte(var->buf, 0x00); /* NullNameOp */
+ return var;
+}
+
+/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefShiftRight */
+Aml *aml_shiftright(Aml *arg1, Aml *count)
+{
+ Aml *var = aml_opcode(0x7A /* ShiftRightOp */);
+ aml_append(var, arg1);
+ aml_append(var, count);
+ build_append_byte(var->buf, 0x00); /* NullNameOp */
+ return var;
+}
+
+/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLLess */
+Aml *aml_lless(Aml *arg1, Aml *arg2)
+{
+ Aml *var = aml_opcode(0x95 /* LLessOp */);
+ aml_append(var, arg1);
+ aml_append(var, arg2);
+ return var;
+}
+
+/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAdd */
+Aml *aml_add(Aml *arg1, Aml *arg2)
+{
+ Aml *var = aml_opcode(0x72 /* AddOp */);
+ aml_append(var, arg1);
+ aml_append(var, arg2);
+ build_append_byte(var->buf, 0x00 /* NullNameOp */);
+ return var;
+}
+
+/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefIncrement */
+Aml *aml_increment(Aml *arg)
+{
+ Aml *var = aml_opcode(0x75 /* IncrementOp */);
+ aml_append(var, arg);
+ return var;
+}
+
+/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefIndex */
+Aml *aml_index(Aml *arg1, Aml *idx)
+{
+ Aml *var = aml_opcode(0x88 /* IndexOp */);
+ aml_append(var, arg1);
+ aml_append(var, idx);
+ build_append_byte(var->buf, 0x00 /* NullNameOp */);
+ return var;
+}
+
/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefNotify */
Aml *aml_notify(Aml *arg1, Aml *arg2)
{
@@ -505,6 +564,60 @@ Aml *aml_call4(const char *method, Aml *arg1, Aml *arg2, Aml *arg3, Aml *arg4)
return var;
}
+/*
+ * ACPI 1.0b: 6.4.3.4 32-Bit Fixed Location Memory Range Descriptor
+ * (Type 1, Large Item Name 0x6)
+ */
+Aml *aml_memory32_fixed(uint32_t addr, uint32_t size,
+ AmlReadAndWrite read_and_write)
+{
+ Aml *var = aml_alloc();
+ build_append_byte(var->buf, 0x86); /* Memory32Fixed Resource Descriptor */
+ build_append_byte(var->buf, 9); /* Length, bits[7:0] value = 9 */
+ build_append_byte(var->buf, 0); /* Length, bits[15:8] value = 0 */
+ build_append_byte(var->buf, read_and_write); /* Write status, 1 rw 0 ro */
+
+ /* Range base address */
+ build_append_byte(var->buf, extract32(addr, 0, 8)); /* bits[7:0] */
+ build_append_byte(var->buf, extract32(addr, 8, 8)); /* bits[15:8] */
+ build_append_byte(var->buf, extract32(addr, 16, 8)); /* bits[23:16] */
+ build_append_byte(var->buf, extract32(addr, 24, 8)); /* bits[31:24] */
+
+ /* Range length */
+ build_append_byte(var->buf, extract32(size, 0, 8)); /* bits[7:0] */
+ build_append_byte(var->buf, extract32(size, 8, 8)); /* bits[15:8] */
+ build_append_byte(var->buf, extract32(size, 16, 8)); /* bits[23:16] */
+ build_append_byte(var->buf, extract32(size, 24, 8)); /* bits[31:24] */
+ return var;
+}
+
+/*
+ * ACPI 5.0: 6.4.3.6 Extended Interrupt Descriptor
+ * Type 1, Large Item Name 0x9
+ */
+Aml *aml_interrupt(AmlConsumerAndProducer con_and_pro,
+ AmlLevelAndEdge level_and_edge,
+ AmlActiveHighAndLow high_and_low, AmlShared shared,
+ uint32_t irq)
+{
+ Aml *var = aml_alloc();
+ uint8_t irq_flags = con_and_pro | (level_and_edge << 1)
+ | (high_and_low << 2) | (shared << 3);
+
+ build_append_byte(var->buf, 0x89); /* Extended irq descriptor */
+ build_append_byte(var->buf, 6); /* Length, bits[7:0] minimum value = 6 */
+ build_append_byte(var->buf, 0); /* Length, bits[15:8] minimum value = 0 */
+ build_append_byte(var->buf, irq_flags); /* Interrupt Vector Information. */
+ build_append_byte(var->buf, 0x01); /* Interrupt table length = 1 */
+
+ /* Interrupt Number */
+ build_append_byte(var->buf, extract32(irq, 0, 8)); /* bits[7:0] */
+ build_append_byte(var->buf, extract32(irq, 8, 8)); /* bits[15:8] */
+ build_append_byte(var->buf, extract32(irq, 16, 8)); /* bits[23:16] */
+ build_append_byte(var->buf, extract32(irq, 24, 8)); /* bits[31:24] */
+ return var;
+}
+
/* ACPI 1.0b: 6.4.2.5 I/O Port Descriptor */
Aml *aml_io(AmlIODecode dec, uint16_t min_base, uint16_t max_base,
uint8_t aln, uint8_t len)
@@ -542,6 +655,14 @@ Aml *aml_irq_no_flags(uint8_t irq)
return var;
}
+/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLNot */
+Aml *aml_lnot(Aml *arg)
+{
+ Aml *var = aml_opcode(0x92 /* LNotOp */);
+ aml_append(var, arg);
+ return var;
+}
+
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLEqual */
Aml *aml_equal(Aml *arg1, Aml *arg2)
{
@@ -559,6 +680,21 @@ Aml *aml_if(Aml *predicate)
return var;
}
+/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefElse */
+Aml *aml_else(void)
+{
+ Aml *var = aml_bundle(0xA1 /* ElseOp */, AML_PACKAGE);
+ return var;
+}
+
+/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefWhile */
+Aml *aml_while(Aml *predicate)
+{
+ Aml *var = aml_bundle(0xA2 /* WhileOp */, AML_PACKAGE);
+ aml_append(var, predicate);
+ return var;
+}
+
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefMethod */
Aml *aml_method(const char *name, int arg_count)
{
@@ -587,10 +723,22 @@ Aml *aml_resource_template(void)
return var;
}
-/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefBuffer */
-Aml *aml_buffer(void)
+/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefBuffer
+ * Pass byte_list as NULL to request uninitialized buffer to reserve space.
+ */
+Aml *aml_buffer(int buffer_size, uint8_t *byte_list)
{
+ int i;
Aml *var = aml_bundle(0x11 /* BufferOp */, AML_BUFFER);
+
+ for (i = 0; i < buffer_size; i++) {
+ if (byte_list == NULL) {
+ build_append_byte(var->buf, 0x0);
+ } else {
+ build_append_byte(var->buf, byte_list[i]);
+ }
+ }
+
return var;
}
@@ -636,34 +784,40 @@ Aml *aml_reserved_field(unsigned length)
}
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefField */
-Aml *aml_field(const char *name, AmlFieldFlags flags)
+Aml *aml_field(const char *name, AmlAccessType type, AmlUpdateRule rule)
{
Aml *var = aml_bundle(0x81 /* FieldOp */, AML_EXT_PACKAGE);
+ uint8_t flags = rule << 5 | type;
+
build_append_namestring(var->buf, "%s", name);
build_append_byte(var->buf, flags);
return var;
}
+/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefCreateDWordField */
+Aml *aml_create_dword_field(Aml *srcbuf, Aml *index, const char *name)
+{
+ Aml *var = aml_alloc();
+ build_append_byte(var->buf, 0x8A); /* CreateDWordFieldOp */
+ aml_append(var, srcbuf);
+ aml_append(var, index);
+ build_append_namestring(var->buf, "%s", name);
+ return var;
+}
+
/* ACPI 1.0b: 16.2.3 Data Objects Encoding: String */
Aml *aml_string(const char *name_format, ...)
{
Aml *var = aml_opcode(0x0D /* StringPrefix */);
- va_list ap, va_len;
+ va_list ap;
char *s;
int len;
va_start(ap, name_format);
- va_copy(va_len, ap);
- len = vsnprintf(NULL, 0, name_format, va_len);
- va_end(va_len);
- len += 1;
- s = g_new0(typeof(*s), len);
-
- len = vsnprintf(s, len, name_format, ap);
+ len = g_vasprintf(&s, name_format, ap);
va_end(ap);
- g_array_append_vals(var->buf, s, len);
- build_append_byte(var->buf, 0x0); /* NullChar */
+ g_array_append_vals(var->buf, s, len + 1);
g_free(s);
return var;
@@ -831,7 +985,7 @@ Aml *aml_word_bus_number(AmlMinFixed min_fixed, AmlMaxFixed max_fixed,
uint16_t addr_trans, uint16_t len)
{
- return aml_word_as_desc(aml_bus_number_range, min_fixed, max_fixed, dec,
+ return aml_word_as_desc(AML_BUS_NUMBER_RANGE, min_fixed, max_fixed, dec,
addr_gran, addr_min, addr_max, addr_trans, len, 0);
}
@@ -848,7 +1002,25 @@ Aml *aml_word_io(AmlMinFixed min_fixed, AmlMaxFixed max_fixed,
uint16_t len)
{
- return aml_word_as_desc(aml_io_range, min_fixed, max_fixed, dec,
+ return aml_word_as_desc(AML_IO_RANGE, min_fixed, max_fixed, dec,
+ addr_gran, addr_min, addr_max, addr_trans, len,
+ isa_ranges);
+}
+
+/*
+ * ACPI 1.0b: 6.4.3.5.4 ASL Macros for DWORD Address Descriptor
+ *
+ * More verbose description at:
+ * ACPI 5.0: 19.5.33 DWordIO (DWord IO Resource Descriptor Macro)
+ */
+Aml *aml_dword_io(AmlMinFixed min_fixed, AmlMaxFixed max_fixed,
+ AmlDecode dec, AmlISARanges isa_ranges,
+ uint32_t addr_gran, uint32_t addr_min,
+ uint32_t addr_max, uint32_t addr_trans,
+ uint32_t len)
+
+{
+ return aml_dword_as_desc(AML_IO_RANGE, min_fixed, max_fixed, dec,
addr_gran, addr_min, addr_max, addr_trans, len,
isa_ranges);
}
@@ -860,7 +1032,7 @@ Aml *aml_word_io(AmlMinFixed min_fixed, AmlMaxFixed max_fixed,
* ACPI 5.0: 19.5.34 DWordMemory (DWord Memory Resource Descriptor Macro)
*/
Aml *aml_dword_memory(AmlDecode dec, AmlMinFixed min_fixed,
- AmlMaxFixed max_fixed, AmlCacheble cacheable,
+ AmlMaxFixed max_fixed, AmlCacheable cacheable,
AmlReadAndWrite read_and_write,
uint32_t addr_gran, uint32_t addr_min,
uint32_t addr_max, uint32_t addr_trans,
@@ -868,7 +1040,7 @@ Aml *aml_dword_memory(AmlDecode dec, AmlMinFixed min_fixed,
{
uint8_t flags = read_and_write | (cacheable << 1);
- return aml_dword_as_desc(aml_memory_range, min_fixed, max_fixed,
+ return aml_dword_as_desc(AML_MEMORY_RANGE, min_fixed, max_fixed,
dec, addr_gran, addr_min, addr_max,
addr_trans, len, flags);
}
@@ -880,7 +1052,7 @@ Aml *aml_dword_memory(AmlDecode dec, AmlMinFixed min_fixed,
* ACPI 5.0: 19.5.102 QWordMemory (QWord Memory Resource Descriptor Macro)
*/
Aml *aml_qword_memory(AmlDecode dec, AmlMinFixed min_fixed,
- AmlMaxFixed max_fixed, AmlCacheble cacheable,
+ AmlMaxFixed max_fixed, AmlCacheable cacheable,
AmlReadAndWrite read_and_write,
uint64_t addr_gran, uint64_t addr_min,
uint64_t addr_max, uint64_t addr_trans,
@@ -888,7 +1060,158 @@ Aml *aml_qword_memory(AmlDecode dec, AmlMinFixed min_fixed,
{
uint8_t flags = read_and_write | (cacheable << 1);
- return aml_qword_as_desc(aml_memory_range, min_fixed, max_fixed,
+ return aml_qword_as_desc(AML_MEMORY_RANGE, min_fixed, max_fixed,
dec, addr_gran, addr_min, addr_max,
addr_trans, len, flags);
}
+
+static uint8_t Hex2Byte(const char *src)
+{
+ int hi, lo;
+
+ hi = Hex2Digit(src[0]);
+ assert(hi >= 0);
+ assert(hi <= 15);
+
+ lo = Hex2Digit(src[1]);
+ assert(lo >= 0);
+ assert(lo <= 15);
+ return (hi << 4) | lo;
+}
+
+/*
+ * ACPI 3.0: 17.5.124 ToUUID (Convert String to UUID Macro)
+ * e.g. UUID: aabbccdd-eeff-gghh-iijj-kkllmmnnoopp
+ * call aml_touuid("aabbccdd-eeff-gghh-iijj-kkllmmnnoopp");
+ */
+Aml *aml_touuid(const char *uuid)
+{
+ Aml *var = aml_bundle(0x11 /* BufferOp */, AML_BUFFER);
+
+ assert(strlen(uuid) == 36);
+ assert(uuid[8] == '-');
+ assert(uuid[13] == '-');
+ assert(uuid[18] == '-');
+ assert(uuid[23] == '-');
+
+ build_append_byte(var->buf, Hex2Byte(uuid + 6)); /* dd - at offset 00 */
+ build_append_byte(var->buf, Hex2Byte(uuid + 4)); /* cc - at offset 01 */
+ build_append_byte(var->buf, Hex2Byte(uuid + 2)); /* bb - at offset 02 */
+ build_append_byte(var->buf, Hex2Byte(uuid + 0)); /* aa - at offset 03 */
+
+ build_append_byte(var->buf, Hex2Byte(uuid + 11)); /* ff - at offset 04 */
+ build_append_byte(var->buf, Hex2Byte(uuid + 9)); /* ee - at offset 05 */
+
+ build_append_byte(var->buf, Hex2Byte(uuid + 16)); /* hh - at offset 06 */
+ build_append_byte(var->buf, Hex2Byte(uuid + 14)); /* gg - at offset 07 */
+
+ build_append_byte(var->buf, Hex2Byte(uuid + 19)); /* ii - at offset 08 */
+ build_append_byte(var->buf, Hex2Byte(uuid + 21)); /* jj - at offset 09 */
+
+ build_append_byte(var->buf, Hex2Byte(uuid + 24)); /* kk - at offset 10 */
+ build_append_byte(var->buf, Hex2Byte(uuid + 26)); /* ll - at offset 11 */
+ build_append_byte(var->buf, Hex2Byte(uuid + 28)); /* mm - at offset 12 */
+ build_append_byte(var->buf, Hex2Byte(uuid + 30)); /* nn - at offset 13 */
+ build_append_byte(var->buf, Hex2Byte(uuid + 32)); /* oo - at offset 14 */
+ build_append_byte(var->buf, Hex2Byte(uuid + 34)); /* pp - at offset 15 */
+
+ return var;
+}
+
+/*
+ * ACPI 2.0b: 16.2.3.6.4.3 Unicode Macro (Convert Ascii String To Unicode)
+ */
+Aml *aml_unicode(const char *str)
+{
+ int i = 0;
+ Aml *var = aml_bundle(0x11 /* BufferOp */, AML_BUFFER);
+
+ do {
+ build_append_byte(var->buf, str[i]);
+ build_append_byte(var->buf, 0);
+ i++;
+ } while (i <= strlen(str));
+
+ return var;
+}
+
+void
+build_header(GArray *linker, GArray *table_data,
+ AcpiTableHeader *h, const char *sig, int len, uint8_t rev)
+{
+ memcpy(&h->signature, sig, 4);
+ h->length = cpu_to_le32(len);
+ h->revision = rev;
+ memcpy(h->oem_id, ACPI_BUILD_APPNAME6, 6);
+ memcpy(h->oem_table_id, ACPI_BUILD_APPNAME4, 4);
+ memcpy(h->oem_table_id + 4, sig, 4);
+ h->oem_revision = cpu_to_le32(1);
+ memcpy(h->asl_compiler_id, ACPI_BUILD_APPNAME4, 4);
+ h->asl_compiler_revision = cpu_to_le32(1);
+ h->checksum = 0;
+ /* Checksum to be filled in by Guest linker */
+ bios_linker_loader_add_checksum(linker, ACPI_BUILD_TABLE_FILE,
+ table_data->data, h, len, &h->checksum);
+}
+
+void *acpi_data_push(GArray *table_data, unsigned size)
+{
+ unsigned off = table_data->len;
+ g_array_set_size(table_data, off + size);
+ return table_data->data + off;
+}
+
+unsigned acpi_data_len(GArray *table)
+{
+#if GLIB_CHECK_VERSION(2, 22, 0)
+ assert(g_array_get_element_size(table) == 1);
+#endif
+ return table->len;
+}
+
+void acpi_add_table(GArray *table_offsets, GArray *table_data)
+{
+ uint32_t offset = cpu_to_le32(table_data->len);
+ g_array_append_val(table_offsets, offset);
+}
+
+void acpi_build_tables_init(AcpiBuildTables *tables)
+{
+ tables->rsdp = g_array_new(false, true /* clear */, 1);
+ tables->table_data = g_array_new(false, true /* clear */, 1);
+ tables->tcpalog = g_array_new(false, true /* clear */, 1);
+ tables->linker = bios_linker_loader_init();
+}
+
+void acpi_build_tables_cleanup(AcpiBuildTables *tables, bool mfre)
+{
+ void *linker_data = bios_linker_loader_cleanup(tables->linker);
+ g_free(linker_data);
+ g_array_free(tables->rsdp, true);
+ g_array_free(tables->table_data, true);
+ g_array_free(tables->tcpalog, mfre);
+}
+
+/* Build rsdt table */
+void
+build_rsdt(GArray *table_data, GArray *linker, GArray *table_offsets)
+{
+ AcpiRsdtDescriptorRev1 *rsdt;
+ size_t rsdt_len;
+ int i;
+ const int table_data_len = (sizeof(uint32_t) * table_offsets->len);
+
+ rsdt_len = sizeof(*rsdt) + table_data_len;
+ rsdt = acpi_data_push(table_data, rsdt_len);
+ memcpy(rsdt->table_offset_entry, table_offsets->data, table_data_len);
+ for (i = 0; i < table_offsets->len; ++i) {
+ /* rsdt->table_offset_entry to be filled by Guest linker */
+ bios_linker_loader_add_pointer(linker,
+ ACPI_BUILD_TABLE_FILE,
+ ACPI_BUILD_TABLE_FILE,
+ table_data, &rsdt->table_offset_entry[i],
+ sizeof(uint32_t));
+ }
+ build_header(linker, table_data,
+ (void *)rsdt, "RSDT", rsdt_len, 1);
+}
diff --git a/hw/acpi/core.c b/hw/acpi/core.c
index 51913d693..fe6215af4 100644
--- a/hw/acpi/core.c
+++ b/hw/acpi/core.c
@@ -22,6 +22,7 @@
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/acpi/acpi.h"
+#include "hw/nvram/fw_cfg.h"
#include "qemu/config-file.h"
#include "qapi/opts-visitor.h"
#include "qapi/dealloc-visitor.h"
@@ -527,6 +528,7 @@ void acpi_pm_tmr_init(ACPIREGS *ar, acpi_update_sci_fn update_sci,
ar->tmr.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, acpi_pm_tmr_timer, ar);
memory_region_init_io(&ar->tmr.io, memory_region_owner(parent),
&acpi_pm_tmr_ops, ar, "acpi-tmr", 4);
+ memory_region_clear_global_locking(&ar->tmr.io);
memory_region_add_subregion(parent, 8, &ar->tmr.io);
}
@@ -592,14 +594,26 @@ static const MemoryRegionOps acpi_pm_cnt_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
-void acpi_pm1_cnt_init(ACPIREGS *ar, MemoryRegion *parent, uint8_t s4_val)
+void acpi_pm1_cnt_init(ACPIREGS *ar, MemoryRegion *parent,
+ bool disable_s3, bool disable_s4, uint8_t s4_val)
{
+ FWCfgState *fw_cfg;
+
ar->pm1.cnt.s4_val = s4_val;
ar->wakeup.notify = acpi_notify_wakeup;
qemu_register_wakeup_notifier(&ar->wakeup);
memory_region_init_io(&ar->pm1.cnt.io, memory_region_owner(parent),
&acpi_pm_cnt_ops, ar, "acpi-cnt", 2);
memory_region_add_subregion(parent, 4, &ar->pm1.cnt.io);
+
+ fw_cfg = fw_cfg_find();
+ if (fw_cfg) {
+ uint8_t suspend[6] = {128, 0, 0, 129, 128, 128};
+ suspend[3] = 1 | ((!disable_s3) << 7);
+ suspend[4] = s4_val | ((!disable_s4) << 7);
+
+ fw_cfg_add_file(fw_cfg, "etc/system-states", g_memdup(suspend, 6), 6);
+ }
}
void acpi_pm1_cnt_reset(ACPIREGS *ar)
@@ -666,6 +680,13 @@ uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t addr)
return val;
}
+void acpi_send_gpe_event(ACPIREGS *ar, qemu_irq irq,
+ AcpiGPEStatusBits status)
+{
+ ar->gpe.sts[0] |= status;
+ acpi_update_sci(ar, irq);
+}
+
void acpi_update_sci(ACPIREGS *regs, qemu_irq irq)
{
int sci_level, pm1a_sts;
diff --git a/hw/acpi/cpu_hotplug.c b/hw/acpi/cpu_hotplug.c
index b8ebfadc3..f5b9972f2 100644
--- a/hw/acpi/cpu_hotplug.c
+++ b/hw/acpi/cpu_hotplug.c
@@ -59,8 +59,7 @@ void acpi_cpu_plug_cb(ACPIREGS *ar, qemu_irq irq,
return;
}
- ar->gpe.sts[0] |= ACPI_CPU_HOTPLUG_STATUS;
- acpi_update_sci(ar, irq);
+ acpi_send_gpe_event(ar, irq, ACPI_CPU_HOTPLUG_STATUS);
}
void acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner,
diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
index 5352e197c..1c7fcfa9d 100644
--- a/hw/acpi/ich9.c
+++ b/hw/acpi/ich9.c
@@ -30,6 +30,7 @@
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
#include "hw/acpi/acpi.h"
+#include "hw/acpi/tco.h"
#include "sysemu/kvm.h"
#include "exec/address-spaces.h"
@@ -92,9 +93,18 @@ static void ich9_smi_writel(void *opaque, hwaddr addr, uint64_t val,
unsigned width)
{
ICH9LPCPMRegs *pm = opaque;
+ TCOIORegs *tr = &pm->tco_regs;
+ uint64_t tco_en;
+
switch (addr) {
case 0:
- pm->smi_en = val;
+ tco_en = pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN;
+ /* once TCO_LOCK bit is set, TCO_EN bit cannot be overwritten */
+ if (tr->tco.cnt1 & TCO_LOCK) {
+ val = (val & ~ICH9_PMIO_SMI_EN_TCO_EN) | tco_en;
+ }
+ pm->smi_en &= ~pm->smi_en_wmask;
+ pm->smi_en |= (val & pm->smi_en_wmask);
break;
}
}
@@ -151,12 +161,32 @@ static const VMStateDescription vmstate_memhp_state = {
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
+ .needed = vmstate_test_use_memhp,
.fields = (VMStateField[]) {
VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug, ICH9LPCPMRegs),
VMSTATE_END_OF_LIST()
}
};
+static bool vmstate_test_use_tco(void *opaque)
+{
+ ICH9LPCPMRegs *s = opaque;
+ return s->enable_tco;
+}
+
+static const VMStateDescription vmstate_tco_io_state = {
+ .name = "ich9_pm/tco",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .needed = vmstate_test_use_tco,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT(tco_regs, ICH9LPCPMRegs, 1, vmstate_tco_io_sts,
+ TCOIORegs),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
const VMStateDescription vmstate_ich9_pm = {
.name = "ich9_pm",
.version_id = 1,
@@ -174,12 +204,10 @@ const VMStateDescription vmstate_ich9_pm = {
VMSTATE_UINT32(smi_sts, ICH9LPCPMRegs),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_memhp_state,
- .needed = vmstate_test_use_memhp,
- },
- VMSTATE_END_OF_LIST()
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_memhp_state,
+ &vmstate_tco_io_state,
+ NULL
}
};
@@ -193,11 +221,12 @@ static void pm_reset(void *opaque)
acpi_pm_tmr_reset(&pm->acpi_regs);
acpi_gpe_reset(&pm->acpi_regs);
- if (kvm_enabled()) {
- /* Mark SMM as already inited to prevent SMM from running. KVM does not
- * support SMM mode. */
+ pm->smi_en = 0;
+ if (!pm->smm_enabled) {
+ /* Mark SMM as already inited to prevent SMM from running. */
pm->smi_en |= ICH9_PMIO_SMI_EN_APMC_EN;
}
+ pm->smi_en_wmask = ~0;
acpi_update_sci(&pm->acpi_regs, pm->irq);
}
@@ -210,6 +239,7 @@ static void pm_powerdown_req(Notifier *n, void *opaque)
}
void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
+ bool smm_enabled, bool enable_tco,
qemu_irq sci_irq)
{
memory_region_init(&pm->io, OBJECT(lpc_pci), "ich9-pm", ICH9_PMIO_SIZE);
@@ -219,7 +249,8 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
acpi_pm_tmr_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io);
acpi_pm1_evt_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io);
- acpi_pm1_cnt_init(&pm->acpi_regs, &pm->io, pm->s4_val);
+ acpi_pm1_cnt_init(&pm->acpi_regs, &pm->io, pm->disable_s3, pm->disable_s4,
+ pm->s4_val);
acpi_gpe_init(&pm->acpi_regs, ICH9_PMIO_GPE0_LEN);
memory_region_init_io(&pm->io_gpe, OBJECT(lpc_pci), &ich9_gpe_ops, pm,
@@ -230,6 +261,13 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
"acpi-smi", 8);
memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi);
+ pm->smm_enabled = smm_enabled;
+
+ pm->enable_tco = enable_tco;
+ if (pm->enable_tco) {
+ acpi_pm_tco_init(&pm->tco_regs, &pm->io);
+ }
+
pm->irq = sci_irq;
qemu_register_reset(pm_reset, pm);
pm->powerdown_notifier.notify = pm_powerdown_req;
@@ -350,6 +388,18 @@ out:
error_propagate(errp, local_err);
}
+static bool ich9_pm_get_enable_tco(Object *obj, Error **errp)
+{
+ ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
+ return s->pm.enable_tco;
+}
+
+static void ich9_pm_set_enable_tco(Object *obj, bool value, Error **errp)
+{
+ ICH9LPCState *s = ICH9_LPC_DEVICE(obj);
+ s->pm.enable_tco = value;
+}
+
void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp)
{
static const uint32_t gpe0_len = ICH9_PMIO_GPE0_LEN;
@@ -381,6 +431,10 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp)
ich9_pm_get_s4_val,
ich9_pm_set_s4_val,
NULL, pm, NULL);
+ object_property_add_bool(obj, ACPI_PM_PROP_TCO_ENABLED,
+ ich9_pm_get_enable_tco,
+ ich9_pm_set_enable_tco,
+ NULL);
}
void ich9_pm_device_plug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, Error **errp)
@@ -400,15 +454,26 @@ void ich9_pm_device_plug_cb(ICH9LPCPMRegs *pm, DeviceState *dev, Error **errp)
void ich9_pm_device_unplug_request_cb(ICH9LPCPMRegs *pm, DeviceState *dev,
Error **errp)
{
- error_setg(errp, "acpi: device unplug request for not supported device"
- " type: %s", object_get_typename(OBJECT(dev)));
+ if (pm->acpi_memory_hotplug.is_enabled &&
+ object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ acpi_memory_unplug_request_cb(&pm->acpi_regs, pm->irq,
+ &pm->acpi_memory_hotplug, dev, errp);
+ } else {
+ error_setg(errp, "acpi: device unplug request for not supported device"
+ " type: %s", object_get_typename(OBJECT(dev)));
+ }
}
void ich9_pm_device_unplug_cb(ICH9LPCPMRegs *pm, DeviceState *dev,
Error **errp)
{
- error_setg(errp, "acpi: device unplug for not supported device"
- " type: %s", object_get_typename(OBJECT(dev)));
+ if (pm->acpi_memory_hotplug.is_enabled &&
+ object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ acpi_memory_unplug_cb(&pm->acpi_memory_hotplug, dev, errp);
+ } else {
+ error_setg(errp, "acpi: device unplug for not supported device"
+ " type: %s", object_get_typename(OBJECT(dev)));
+ }
}
void ich9_pm_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list)
diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c
index c6580dabb..2ff0d5ce1 100644
--- a/hw/acpi/memory_hotplug.c
+++ b/hw/acpi/memory_hotplug.c
@@ -2,6 +2,7 @@
#include "hw/acpi/pc-hotplug.h"
#include "hw/mem/pc-dimm.h"
#include "hw/boards.h"
+#include "hw/qdev-core.h"
#include "trace.h"
#include "qapi-event.h"
@@ -75,6 +76,7 @@ static uint64_t acpi_memory_hotplug_read(void *opaque, hwaddr addr,
case 0x14: /* pack and return is_* fields */
val |= mdev->is_enabled ? 1 : 0;
val |= mdev->is_inserting ? 2 : 0;
+ val |= mdev->is_removing ? 4 : 0;
trace_mhp_acpi_read_flags(mem_st->selector, val);
break;
default:
@@ -90,6 +92,9 @@ static void acpi_memory_hotplug_write(void *opaque, hwaddr addr, uint64_t data,
MemHotplugState *mem_st = opaque;
MemStatus *mdev;
ACPIOSTInfo *info;
+ DeviceState *dev = NULL;
+ HotplugHandler *hotplug_ctrl = NULL;
+ Error *local_err = NULL;
if (!mem_st->dev_count) {
return;
@@ -127,13 +132,36 @@ static void acpi_memory_hotplug_write(void *opaque, hwaddr addr, uint64_t data,
qapi_event_send_acpi_device_ost(info, &error_abort);
qapi_free_ACPIOSTInfo(info);
break;
- case 0x14:
+ case 0x14: /* set is_* fields */
mdev = &mem_st->devs[mem_st->selector];
if (data & 2) { /* clear insert event */
mdev->is_inserting = false;
trace_mhp_acpi_clear_insert_evt(mem_st->selector);
+ } else if (data & 4) {
+ mdev->is_removing = false;
+ trace_mhp_acpi_clear_remove_evt(mem_st->selector);
+ } else if (data & 8) {
+ if (!mdev->is_enabled) {
+ trace_mhp_acpi_ejecting_invalid_slot(mem_st->selector);
+ break;
+ }
+
+ dev = DEVICE(mdev->dimm);
+ hotplug_ctrl = qdev_get_hotplug_handler(dev);
+ /* call pc-dimm unplug cb */
+ hotplug_handler_unplug(hotplug_ctrl, dev, &local_err);
+ if (local_err) {
+ trace_mhp_acpi_pc_dimm_delete_failed(mem_st->selector);
+ qapi_event_send_mem_unplug_error(dev->id,
+ error_get_pretty(local_err),
+ &error_abort);
+ break;
+ }
+ trace_mhp_acpi_pc_dimm_deleted(mem_st->selector);
}
break;
+ default:
+ break;
}
}
@@ -163,39 +191,91 @@ void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner,
memory_region_add_subregion(as, ACPI_MEMORY_HOTPLUG_BASE, &state->io);
}
-void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st,
- DeviceState *dev, Error **errp)
+/**
+ * acpi_memory_slot_status:
+ * @mem_st: memory hotplug state
+ * @dev: device
+ * @errp: set in case of an error
+ *
+ * Obtain a single memory slot status.
+ *
+ * This function will be called by memory unplug request cb and unplug cb.
+ */
+static MemStatus *
+acpi_memory_slot_status(MemHotplugState *mem_st,
+ DeviceState *dev, Error **errp)
{
- MemStatus *mdev;
Error *local_err = NULL;
int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP,
&local_err);
if (local_err) {
error_propagate(errp, local_err);
- return;
+ return NULL;
}
if (slot >= mem_st->dev_count) {
char *dev_path = object_get_canonical_path(OBJECT(dev));
- error_setg(errp, "acpi_memory_plug_cb: "
+ error_setg(errp, "acpi_memory_slot_status: "
"device [%s] returned invalid memory slot[%d]",
dev_path, slot);
g_free(dev_path);
+ return NULL;
+ }
+
+ return &mem_st->devs[slot];
+}
+
+void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st,
+ DeviceState *dev, Error **errp)
+{
+ MemStatus *mdev;
+
+ mdev = acpi_memory_slot_status(mem_st, dev, errp);
+ if (!mdev) {
return;
}
- mdev = &mem_st->devs[slot];
mdev->dimm = dev;
mdev->is_enabled = true;
mdev->is_inserting = true;
/* do ACPI magic */
- ar->gpe.sts[0] |= ACPI_MEMORY_HOTPLUG_STATUS;
- acpi_update_sci(ar, irq);
+ acpi_send_gpe_event(ar, irq, ACPI_MEMORY_HOTPLUG_STATUS);
return;
}
+void acpi_memory_unplug_request_cb(ACPIREGS *ar, qemu_irq irq,
+ MemHotplugState *mem_st,
+ DeviceState *dev, Error **errp)
+{
+ MemStatus *mdev;
+
+ mdev = acpi_memory_slot_status(mem_st, dev, errp);
+ if (!mdev) {
+ return;
+ }
+
+ mdev->is_removing = true;
+
+ /* Do ACPI magic */
+ acpi_send_gpe_event(ar, irq, ACPI_MEMORY_HOTPLUG_STATUS);
+}
+
+void acpi_memory_unplug_cb(MemHotplugState *mem_st,
+ DeviceState *dev, Error **errp)
+{
+ MemStatus *mdev;
+
+ mdev = acpi_memory_slot_status(mem_st, dev, errp);
+ if (!mdev) {
+ return;
+ }
+
+ mdev->is_enabled = false;
+ mdev->dimm = NULL;
+}
+
static const VMStateDescription vmstate_memhp_sts = {
.name = "memory hotplug device state",
.version_id = 1,
diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c
index 612fec03e..fbbc4dde4 100644
--- a/hw/acpi/pcihp.c
+++ b/hw/acpi/pcihp.c
@@ -31,7 +31,6 @@
#include "hw/pci/pci.h"
#include "hw/acpi/acpi.h"
#include "sysemu/sysemu.h"
-#include "qemu/range.h"
#include "exec/ioport.h"
#include "exec/address-spaces.h"
#include "hw/pci/pci_bus.h"
@@ -46,7 +45,6 @@
# define ACPI_PCIHP_DPRINTF(format, ...) do { } while (0)
#endif
-#define ACPI_PCI_HOTPLUG_STATUS 2
#define ACPI_PCIHP_ADDR 0xae00
#define ACPI_PCIHP_SIZE 0x0014
#define ACPI_PCIHP_LEGACY_SIZE 0x000f
@@ -120,7 +118,7 @@ static bool acpi_pcihp_pc_no_hotplug(AcpiPciHpState *s, PCIDevice *dev)
static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slots)
{
BusChild *kid, *next;
- int slot = ffs(slots) - 1;
+ int slot = ctz32(slots);
PCIBus *bus = acpi_pcihp_find_hotplug_bus(s, bsel);
if (!bus) {
@@ -203,8 +201,7 @@ void acpi_pcihp_device_plug_cb(ACPIREGS *ar, qemu_irq irq, AcpiPciHpState *s,
s->acpi_pcihp_pci_status[bsel].up |= (1U << slot);
- ar->gpe.sts[0] |= ACPI_PCI_HOTPLUG_STATUS;
- acpi_update_sci(ar, irq);
+ acpi_send_gpe_event(ar, irq, ACPI_PCI_HOTPLUG_STATUS);
}
void acpi_pcihp_device_unplug_cb(ACPIREGS *ar, qemu_irq irq, AcpiPciHpState *s,
@@ -221,8 +218,7 @@ void acpi_pcihp_device_unplug_cb(ACPIREGS *ar, qemu_irq irq, AcpiPciHpState *s,
s->acpi_pcihp_pci_status[bsel].down |= (1U << slot);
- ar->gpe.sts[0] |= ACPI_PCI_HOTPLUG_STATUS;
- acpi_update_sci(ar, irq);
+ acpi_send_gpe_event(ar, irq, ACPI_PCI_HOTPLUG_STATUS);
}
static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size)
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
index d1f11793a..2cd2fee89 100644
--- a/hw/acpi/piix4.c
+++ b/hw/acpi/piix4.c
@@ -72,7 +72,7 @@ typedef struct PIIX4PMState {
qemu_irq irq;
qemu_irq smi_irq;
- int kvm_enabled;
+ int smm_enabled;
Notifier machine_ready;
Notifier powerdown_notifier;
@@ -112,6 +112,9 @@ static void apm_ctrl_changed(uint32_t val, void *arg)
/* ACPI specs 3.0, 4.7.2.5 */
acpi_pm1_cnt_update(&s->ar, val == ACPI_ENABLE, val == ACPI_DISABLE);
+ if (val == ACPI_ENABLE || val == ACPI_DISABLE) {
+ return;
+ }
if (d->config[0x5b] & (1 << 1)) {
if (s->smi_irq) {
@@ -260,6 +263,7 @@ static const VMStateDescription vmstate_memhp_state = {
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
+ .needed = vmstate_test_use_memhp,
.fields = (VMStateField[]) {
VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug, PIIX4PMState),
VMSTATE_END_OF_LIST()
@@ -298,12 +302,9 @@ static const VMStateDescription vmstate_acpi = {
vmstate_test_use_acpi_pci_hotplug),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_memhp_state,
- .needed = vmstate_test_use_memhp,
- },
- VMSTATE_END_OF_LIST()
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_memhp_state,
+ NULL
}
};
@@ -321,7 +322,7 @@ static void piix4_reset(void *opaque)
pci_conf[0x40] = 0x01; /* PM io base read only bit */
pci_conf[0x80] = 0;
- if (s->kvm_enabled) {
+ if (!s->smm_enabled) {
/* Mark SMM as already inited (until KVM supports SMM). */
pci_conf[0x5B] = 0x02;
}
@@ -361,7 +362,11 @@ static void piix4_device_unplug_request_cb(HotplugHandler *hotplug_dev,
{
PIIX4PMState *s = PIIX4_PM(hotplug_dev);
- if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
+ if (s->acpi_memory_hotplug.is_enabled &&
+ object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ acpi_memory_unplug_request_cb(&s->ar, s->irq, &s->acpi_memory_hotplug,
+ dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
acpi_pcihp_device_unplug_cb(&s->ar, s->irq, &s->acpi_pci_hotplug, dev,
errp);
} else {
@@ -373,8 +378,15 @@ static void piix4_device_unplug_request_cb(HotplugHandler *hotplug_dev,
static void piix4_device_unplug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
- error_setg(errp, "acpi: device unplug for not supported device"
- " type: %s", object_get_typename(OBJECT(dev)));
+ PIIX4PMState *s = PIIX4_PM(hotplug_dev);
+
+ if (s->acpi_memory_hotplug.is_enabled &&
+ object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ acpi_memory_unplug_cb(&s->acpi_memory_hotplug, dev, errp);
+ } else {
+ error_setg(errp, "acpi: device unplug for not supported device"
+ " type: %s", object_get_typename(OBJECT(dev)));
+ }
}
static void piix4_update_bus_hotplug(PCIBus *pci_bus, void *opaque)
@@ -441,7 +453,7 @@ static void piix4_pm_realize(PCIDevice *dev, Error **errp)
/* APM */
apm_init(dev, &s->apm, apm_ctrl_changed, s);
- if (s->kvm_enabled) {
+ if (!s->smm_enabled) {
/* Mark SMM as already inited to prevent SMM from running. KVM does not
* support SMM mode. */
pci_conf[0x5B] = 0x02;
@@ -464,7 +476,7 @@ static void piix4_pm_realize(PCIDevice *dev, Error **errp)
acpi_pm_tmr_init(&s->ar, pm_tmr_timer, &s->io);
acpi_pm1_evt_init(&s->ar, pm_tmr_timer, &s->io);
- acpi_pm1_cnt_init(&s->ar, &s->io, s->s4_val);
+ acpi_pm1_cnt_init(&s->ar, &s->io, s->disable_s3, s->disable_s4, s->s4_val);
acpi_gpe_init(&s->ar, GPE_LEN);
s->powerdown_notifier.notify = piix4_pm_powerdown_req;
@@ -492,8 +504,7 @@ Object *piix4_pm_find(void)
I2CBus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
qemu_irq sci_irq, qemu_irq smi_irq,
- int kvm_enabled, FWCfgState *fw_cfg,
- DeviceState **piix4_pm)
+ int smm_enabled, DeviceState **piix4_pm)
{
DeviceState *dev;
PIIX4PMState *s;
@@ -507,21 +518,13 @@ I2CBus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
s = PIIX4_PM(dev);
s->irq = sci_irq;
s->smi_irq = smi_irq;
- s->kvm_enabled = kvm_enabled;
+ s->smm_enabled = smm_enabled;
if (xen_enabled()) {
s->use_acpi_pci_hotplug = false;
}
qdev_init_nofail(dev);
- if (fw_cfg) {
- uint8_t suspend[6] = {128, 0, 0, 129, 128, 128};
- suspend[3] = 1 | ((!s->disable_s3) << 7);
- suspend[4] = s->s4_val | ((!s->disable_s4) << 7);
-
- fw_cfg_add_file(fw_cfg, "etc/system-states", g_memdup(suspend, 6), 6);
- }
-
return s->smb.smbus;
}
diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c
new file mode 100644
index 000000000..7a026c255
--- /dev/null
+++ b/hw/acpi/tco.c
@@ -0,0 +1,264 @@
+/*
+ * QEMU ICH9 TCO emulation
+ *
+ * Copyright (c) 2015 Paulo Alcantara <pcacjr@zytor.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#include "qemu-common.h"
+#include "sysemu/watchdog.h"
+#include "hw/i386/ich9.h"
+
+#include "hw/acpi/tco.h"
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define TCO_DEBUG(fmt, ...) \
+ do { \
+ fprintf(stderr, "%s "fmt, __func__, ## __VA_ARGS__); \
+ } while (0)
+#else
+#define TCO_DEBUG(fmt, ...) do { } while (0)
+#endif
+
+enum {
+ TCO_RLD_DEFAULT = 0x0000,
+ TCO_DAT_IN_DEFAULT = 0x00,
+ TCO_DAT_OUT_DEFAULT = 0x00,
+ TCO1_STS_DEFAULT = 0x0000,
+ TCO2_STS_DEFAULT = 0x0000,
+ TCO1_CNT_DEFAULT = 0x0000,
+ TCO2_CNT_DEFAULT = 0x0008,
+ TCO_MESSAGE1_DEFAULT = 0x00,
+ TCO_MESSAGE2_DEFAULT = 0x00,
+ TCO_WDCNT_DEFAULT = 0x00,
+ TCO_TMR_DEFAULT = 0x0004,
+ SW_IRQ_GEN_DEFAULT = 0x03,
+};
+
+static inline void tco_timer_reload(TCOIORegs *tr)
+{
+ tr->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ ((int64_t)(tr->tco.tmr & TCO_TMR_MASK) * TCO_TICK_NSEC);
+ timer_mod(tr->tco_timer, tr->expire_time);
+}
+
+static inline void tco_timer_stop(TCOIORegs *tr)
+{
+ tr->expire_time = -1;
+}
+
+static void tco_timer_expired(void *opaque)
+{
+ TCOIORegs *tr = opaque;
+ ICH9LPCPMRegs *pm = container_of(tr, ICH9LPCPMRegs, tco_regs);
+ ICH9LPCState *lpc = container_of(pm, ICH9LPCState, pm);
+ uint32_t gcs = pci_get_long(lpc->chip_config + ICH9_CC_GCS);
+
+ tr->tco.rld = 0;
+ tr->tco.sts1 |= TCO_TIMEOUT;
+ if (++tr->timeouts_no == 2) {
+ tr->tco.sts2 |= TCO_SECOND_TO_STS;
+ tr->tco.sts2 |= TCO_BOOT_STS;
+ tr->timeouts_no = 0;
+
+ if (!lpc->pin_strap.spkr_hi && !(gcs & ICH9_CC_GCS_NO_REBOOT)) {
+ watchdog_perform_action();
+ tco_timer_stop(tr);
+ return;
+ }
+ }
+
+ if (pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN) {
+ ich9_generate_smi();
+ } else {
+ ich9_generate_nmi();
+ }
+ tr->tco.rld = tr->tco.tmr;
+ tco_timer_reload(tr);
+}
+
+/* NOTE: values of 0 or 1 will be ignored by ICH */
+static inline int can_start_tco_timer(TCOIORegs *tr)
+{
+ return !(tr->tco.cnt1 & TCO_TMR_HLT) && tr->tco.tmr > 1;
+}
+
+static uint32_t tco_ioport_readw(TCOIORegs *tr, uint32_t addr)
+{
+ uint16_t rld;
+
+ switch (addr) {
+ case TCO_RLD:
+ if (tr->expire_time != -1) {
+ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ int64_t elapsed = (tr->expire_time - now) / TCO_TICK_NSEC;
+ rld = (uint16_t)elapsed | (tr->tco.rld & ~TCO_RLD_MASK);
+ } else {
+ rld = tr->tco.rld;
+ }
+ return rld;
+ case TCO_DAT_IN:
+ return tr->tco.din;
+ case TCO_DAT_OUT:
+ return tr->tco.dout;
+ case TCO1_STS:
+ return tr->tco.sts1;
+ case TCO2_STS:
+ return tr->tco.sts2;
+ case TCO1_CNT:
+ return tr->tco.cnt1;
+ case TCO2_CNT:
+ return tr->tco.cnt2;
+ case TCO_MESSAGE1:
+ return tr->tco.msg1;
+ case TCO_MESSAGE2:
+ return tr->tco.msg2;
+ case TCO_WDCNT:
+ return tr->tco.wdcnt;
+ case TCO_TMR:
+ return tr->tco.tmr;
+ case SW_IRQ_GEN:
+ return tr->sw_irq_gen;
+ }
+ return 0;
+}
+
+static void tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val)
+{
+ switch (addr) {
+ case TCO_RLD:
+ tr->timeouts_no = 0;
+ if (can_start_tco_timer(tr)) {
+ tr->tco.rld = tr->tco.tmr;
+ tco_timer_reload(tr);
+ } else {
+ tr->tco.rld = val;
+ }
+ break;
+ case TCO_DAT_IN:
+ tr->tco.din = val;
+ tr->tco.sts1 |= SW_TCO_SMI;
+ ich9_generate_smi();
+ break;
+ case TCO_DAT_OUT:
+ tr->tco.dout = val;
+ tr->tco.sts1 |= TCO_INT_STS;
+ /* TODO: cause an interrupt, as selected by the TCO_INT_SEL bits */
+ break;
+ case TCO1_STS:
+ tr->tco.sts1 = val & TCO1_STS_MASK;
+ break;
+ case TCO2_STS:
+ tr->tco.sts2 = val & TCO2_STS_MASK;
+ break;
+ case TCO1_CNT:
+ val &= TCO1_CNT_MASK;
+ /*
+ * once TCO_LOCK bit is set, it can not be cleared by software. a reset
+ * is required to change this bit from 1 to 0 -- it defaults to 0.
+ */
+ tr->tco.cnt1 = val | (tr->tco.cnt1 & TCO_LOCK);
+ if (can_start_tco_timer(tr)) {
+ tr->tco.rld = tr->tco.tmr;
+ tco_timer_reload(tr);
+ } else {
+ tco_timer_stop(tr);
+ }
+ break;
+ case TCO2_CNT:
+ tr->tco.cnt2 = val;
+ break;
+ case TCO_MESSAGE1:
+ tr->tco.msg1 = val;
+ break;
+ case TCO_MESSAGE2:
+ tr->tco.msg2 = val;
+ break;
+ case TCO_WDCNT:
+ tr->tco.wdcnt = val;
+ break;
+ case TCO_TMR:
+ tr->tco.tmr = val;
+ break;
+ case SW_IRQ_GEN:
+ tr->sw_irq_gen = val;
+ break;
+ }
+}
+
+static uint64_t tco_io_readw(void *opaque, hwaddr addr, unsigned width)
+{
+ TCOIORegs *tr = opaque;
+ return tco_ioport_readw(tr, addr);
+}
+
+static void tco_io_writew(void *opaque, hwaddr addr, uint64_t val,
+ unsigned width)
+{
+ TCOIORegs *tr = opaque;
+ tco_ioport_writew(tr, addr, val);
+}
+
+static const MemoryRegionOps tco_io_ops = {
+ .read = tco_io_readw,
+ .write = tco_io_writew,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .impl.min_access_size = 1,
+ .impl.max_access_size = 2,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+void acpi_pm_tco_init(TCOIORegs *tr, MemoryRegion *parent)
+{
+ *tr = (TCOIORegs) {
+ .tco = {
+ .rld = TCO_RLD_DEFAULT,
+ .din = TCO_DAT_IN_DEFAULT,
+ .dout = TCO_DAT_OUT_DEFAULT,
+ .sts1 = TCO1_STS_DEFAULT,
+ .sts2 = TCO2_STS_DEFAULT,
+ .cnt1 = TCO1_CNT_DEFAULT,
+ .cnt2 = TCO2_CNT_DEFAULT,
+ .msg1 = TCO_MESSAGE1_DEFAULT,
+ .msg2 = TCO_MESSAGE2_DEFAULT,
+ .wdcnt = TCO_WDCNT_DEFAULT,
+ .tmr = TCO_TMR_DEFAULT,
+ },
+ .sw_irq_gen = SW_IRQ_GEN_DEFAULT,
+ .tco_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tco_timer_expired, tr),
+ .expire_time = -1,
+ .timeouts_no = 0,
+ };
+ memory_region_init_io(&tr->io, memory_region_owner(parent),
+ &tco_io_ops, tr, "sm-tco", ICH9_PMIO_TCO_LEN);
+ memory_region_add_subregion(parent, ICH9_PMIO_TCO_RLD, &tr->io);
+}
+
+const VMStateDescription vmstate_tco_io_sts = {
+ .name = "tco io device status",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT16(tco.rld, TCOIORegs),
+ VMSTATE_UINT8(tco.din, TCOIORegs),
+ VMSTATE_UINT8(tco.dout, TCOIORegs),
+ VMSTATE_UINT16(tco.sts1, TCOIORegs),
+ VMSTATE_UINT16(tco.sts2, TCOIORegs),
+ VMSTATE_UINT16(tco.cnt1, TCOIORegs),
+ VMSTATE_UINT16(tco.cnt2, TCOIORegs),
+ VMSTATE_UINT8(tco.msg1, TCOIORegs),
+ VMSTATE_UINT8(tco.msg2, TCOIORegs),
+ VMSTATE_UINT8(tco.wdcnt, TCOIORegs),
+ VMSTATE_UINT16(tco.tmr, TCOIORegs),
+ VMSTATE_UINT8(sw_irq_gen, TCOIORegs),
+ VMSTATE_TIMER_PTR(tco_timer, TCOIORegs),
+ VMSTATE_INT64(expire_time, TCOIORegs),
+ VMSTATE_UINT8(timeouts_no, TCOIORegs),
+ VMSTATE_END_OF_LIST()
+ }
+};
diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c
index e82d61d28..f86e7bb83 100644
--- a/hw/alpha/dp264.c
+++ b/hw/alpha/dp264.c
@@ -55,7 +55,7 @@ static void clipper_init(MachineState *machine)
ISABus *isa_bus;
qemu_irq rtc_irq;
long size, i;
- const char *palcode_filename;
+ char *palcode_filename;
uint64_t palcode_entry, palcode_low, palcode_high;
uint64_t kernel_entry, kernel_low, kernel_high;
@@ -101,8 +101,8 @@ static void clipper_init(MachineState *machine)
/* Load PALcode. Given that this is not "real" cpu palcode,
but one explicitly written for the emulation, we might as
well load it directly from and ELF image. */
- palcode_filename = (bios_name ? bios_name : "palcode-clipper");
- palcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, palcode_filename);
+ palcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS,
+ bios_name ? bios_name : "palcode-clipper");
if (palcode_filename == NULL) {
hw_error("no palcode provided\n");
exit(1);
@@ -114,6 +114,7 @@ static void clipper_init(MachineState *machine)
hw_error("could not load palcode '%s'\n", palcode_filename);
exit(1);
}
+ g_free(palcode_filename);
/* Start all cpus at the PALcode RESET entry point. */
for (i = 0; i < smp_cpus; ++i) {
@@ -157,9 +158,12 @@ static void clipper_init(MachineState *machine)
load_image_targphys(initrd_filename, initrd_base,
ram_size - initrd_base);
- stq_phys(&address_space_memory,
- param_offset + 0x100, initrd_base + 0xfffffc0000000000ULL);
- stq_phys(&address_space_memory, param_offset + 0x108, initrd_size);
+ address_space_stq(&address_space_memory, param_offset + 0x100,
+ initrd_base + 0xfffffc0000000000ULL,
+ MEMTXATTRS_UNSPECIFIED,
+ NULL);
+ address_space_stq(&address_space_memory, param_offset + 0x108,
+ initrd_size, MEMTXATTRS_UNSPECIFIED, NULL);
}
}
}
diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c
index a6044f28c..421162e1d 100644
--- a/hw/alpha/typhoon.c
+++ b/hw/alpha/typhoon.c
@@ -613,7 +613,8 @@ static bool make_iommu_tlbe(hwaddr taddr, hwaddr mask, IOMMUTLBEntry *ret)
translation, given the address of the PTE. */
static bool pte_translate(hwaddr pte_addr, IOMMUTLBEntry *ret)
{
- uint64_t pte = ldq_phys(&address_space_memory, pte_addr);
+ uint64_t pte = address_space_ldq(&address_space_memory, pte_addr,
+ MEMTXATTRS_UNSPECIFIED, NULL);
/* Check valid bit. */
if ((pte & 1) == 0) {
@@ -840,7 +841,7 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus,
}
}
- *p_rtc_irq = *qemu_allocate_irqs(typhoon_set_timer_irq, s, 1);
+ *p_rtc_irq = qemu_allocate_irq(typhoon_set_timer_irq, s, 0);
/* Main memory region, 0x00.0000.0000. Real hardware supports 32GB,
but the address space hole reserved at this point is 8TB. */
@@ -917,11 +918,11 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus,
/* Init the ISA bus. */
/* ??? Technically there should be a cy82c693ub pci-isa bridge. */
{
- qemu_irq isa_pci_irq, *isa_irqs;
+ qemu_irq *isa_irqs;
*isa_bus = isa_bus_new(NULL, get_system_memory(), &s->pchip.reg_io);
- isa_pci_irq = *qemu_allocate_irqs(typhoon_set_isa_irq, s, 1);
- isa_irqs = i8259_init(*isa_bus, isa_pci_irq);
+ isa_irqs = i8259_init(*isa_bus,
+ qemu_allocate_irq(typhoon_set_isa_irq, s, 0));
isa_bus_irqs(*isa_bus, isa_irqs);
}
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 2577f6809..cf346c1d0 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -3,10 +3,13 @@ obj-$(CONFIG_DIGIC) += digic_boards.o
obj-y += integratorcp.o kzm.o mainstone.o musicpal.o nseries.o
obj-y += omap_sx1.o palm.o realview.o spitz.o stellaris.o
obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o
+obj-$(CONFIG_ACPI) += virt-acpi-build.o
obj-y += netduino2.o
+obj-y += sysbus-fdt.o
obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
obj-$(CONFIG_DIGIC) += digic.o
obj-y += omap1.o omap2.o strongarm.o
obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o
obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
+obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o
diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c
index ff249af33..43dc0a12d 100644
--- a/hw/arm/allwinner-a10.c
+++ b/hw/arm/allwinner-a10.c
@@ -103,6 +103,12 @@ static void aw_a10_class_init(ObjectClass *oc, void *data)
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = aw_a10_realize;
+
+ /*
+ * Reason: creates an ARM CPU, thus use after free(), see
+ * arm_cpu_class_init()
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo aw_a10_type_info = {
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index a48d1b28d..5b969cda1 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -168,10 +168,11 @@ static void default_write_secondary(ARMCPU *cpu,
static void default_reset_secondary(ARMCPU *cpu,
const struct arm_boot_info *info)
{
- CPUARMState *env = &cpu->env;
+ CPUState *cs = CPU(cpu);
- stl_phys_notdirty(&address_space_memory, info->smp_bootreg_addr, 0);
- env->regs[15] = info->smp_loader_start;
+ address_space_stl_notdirty(&address_space_memory, info->smp_bootreg_addr,
+ 0, MEMTXATTRS_UNSPECIFIED, NULL);
+ cpu_set_pc(cs, info->smp_loader_start);
}
static inline bool have_dtb(const struct arm_boot_info *info)
@@ -180,7 +181,8 @@ static inline bool have_dtb(const struct arm_boot_info *info)
}
#define WRITE_WORD(p, value) do { \
- stl_phys_notdirty(&address_space_memory, p, value); \
+ address_space_stl_notdirty(&address_space_memory, p, value, \
+ MEMTXATTRS_UNSPECIFIED, NULL); \
p += 4; \
} while (0)
@@ -443,19 +445,21 @@ fail:
static void do_cpu_reset(void *opaque)
{
ARMCPU *cpu = opaque;
+ CPUState *cs = CPU(cpu);
CPUARMState *env = &cpu->env;
const struct arm_boot_info *info = env->boot_info;
- cpu_reset(CPU(cpu));
+ cpu_reset(cs);
if (info) {
if (!info->is_linux) {
/* Jump to the entry point. */
- if (env->aarch64) {
- env->pc = info->entry;
- } else {
- env->regs[15] = info->entry & 0xfffffffe;
+ uint64_t entry = info->entry;
+
+ if (!env->aarch64) {
env->thumb = info->entry & 1;
+ entry &= 0xfffffffe;
}
+ cpu_set_pc(cs, entry);
} else {
/* If we are booting Linux then we need to check whether we are
* booting into secure or non-secure state and adjust the state
@@ -485,12 +489,8 @@ static void do_cpu_reset(void *opaque)
}
}
- if (CPU(cpu) == first_cpu) {
- if (env->aarch64) {
- env->pc = info->loader_start;
- } else {
- env->regs[15] = info->loader_start;
- }
+ if (cs == first_cpu) {
+ cpu_set_pc(cs, info->loader_start);
if (!have_dtb(info)) {
if (old_param) {
@@ -555,7 +555,7 @@ static void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key,
fw_cfg_add_bytes(fw_cfg, data_key, data, size);
}
-void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
+static void arm_load_kernel_notify(Notifier *notifier, void *data)
{
CPUState *cs;
int kernel_size;
@@ -566,15 +566,11 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
hwaddr entry, kernel_load_offset;
int big_endian;
static const ARMInsnFixup *primary_loader;
-
- /* CPU objects (unlike devices) are not automatically reset on system
- * reset, so we must always register a handler to do so. If we're
- * actually loading a kernel, the handler is also responsible for
- * arranging that we start it correctly.
- */
- for (cs = CPU(cpu); cs; cs = CPU_NEXT(cs)) {
- qemu_register_reset(do_cpu_reset, ARM_CPU(cs));
- }
+ ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier,
+ notifier, notifier);
+ ARMCPU *cpu = n->cpu;
+ struct arm_boot_info *info =
+ container_of(n, struct arm_boot_info, load_kernel_notifier);
/* Load the kernel. */
if (!info->kernel_filename || info->firmware_loaded) {
@@ -739,12 +735,28 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
* we point to the kernel args.
*/
if (have_dtb(info)) {
- /* Place the DTB after the initrd in memory. Note that some
- * kernels will trash anything in the 4K page the initrd
- * ends in, so make sure the DTB isn't caught up in that.
- */
- hwaddr dtb_start = QEMU_ALIGN_UP(info->initrd_start + initrd_size,
- 4096);
+ hwaddr align;
+ hwaddr dtb_start;
+
+ if (elf_machine == EM_AARCH64) {
+ /*
+ * Some AArch64 kernels on early bootup map the fdt region as
+ *
+ * [ ALIGN_DOWN(fdt, 2MB) ... ALIGN_DOWN(fdt, 2MB) + 2MB ]
+ *
+ * Let's play safe and prealign it to 2MB to give us some space.
+ */
+ align = 2 * 1024 * 1024;
+ } else {
+ /*
+ * Some 32bit kernels will trash anything in the 4K page the
+ * initrd ends in, so make sure the DTB isn't caught up in that.
+ */
+ align = 4096;
+ }
+
+ /* Place the DTB after the initrd in memory with alignment. */
+ dtb_start = QEMU_ALIGN_UP(info->initrd_start + initrd_size, align);
if (load_dtb(dtb_start, info, 0) < 0) {
exit(1);
}
@@ -773,3 +785,21 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
ARM_CPU(cs)->env.boot_info = info;
}
}
+
+void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
+{
+ CPUState *cs;
+
+ info->load_kernel_notifier.cpu = cpu;
+ info->load_kernel_notifier.notifier.notify = arm_load_kernel_notify;
+ qemu_add_machine_init_done_notifier(&info->load_kernel_notifier.notifier);
+
+ /* CPU objects (unlike devices) are not automatically reset on system
+ * reset, so we must always register a handler to do so. If we're
+ * actually loading a kernel, the handler is also responsible for
+ * arranging that we start it correctly.
+ */
+ for (cs = CPU(cpu); cs; cs = CPU_NEXT(cs)) {
+ qemu_register_reset(do_cpu_reset, ARM_CPU(cs));
+ }
+}
diff --git a/hw/arm/digic.c b/hw/arm/digic.c
index ec8c33060..90f8190c4 100644
--- a/hw/arm/digic.c
+++ b/hw/arm/digic.c
@@ -97,6 +97,12 @@ static void digic_class_init(ObjectClass *oc, void *data)
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = digic_realize;
+
+ /*
+ * Reason: creates an ARM CPU, thus use after free(), see
+ * arm_cpu_class_init()
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo digic_type_info = {
diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c
index dd2a67bcf..f8353a787 100644
--- a/hw/arm/highbank.c
+++ b/hw/arm/highbank.c
@@ -69,11 +69,17 @@ static void hb_reset_secondary(ARMCPU *cpu, const struct arm_boot_info *info)
switch (info->nb_cpus) {
case 4:
- stl_phys_notdirty(&address_space_memory, SMP_BOOT_REG + 0x30, 0);
+ address_space_stl_notdirty(&address_space_memory,
+ SMP_BOOT_REG + 0x30, 0,
+ MEMTXATTRS_UNSPECIFIED, NULL);
case 3:
- stl_phys_notdirty(&address_space_memory, SMP_BOOT_REG + 0x20, 0);
+ address_space_stl_notdirty(&address_space_memory,
+ SMP_BOOT_REG + 0x20, 0,
+ MEMTXATTRS_UNSPECIFIED, NULL);
case 2:
- stl_phys_notdirty(&address_space_memory, SMP_BOOT_REG + 0x10, 0);
+ address_space_stl_notdirty(&address_space_memory,
+ SMP_BOOT_REG + 0x10, 0,
+ MEMTXATTRS_UNSPECIFIED, NULL);
env->regs[15] = SMP_BOOT_ADDR;
break;
default:
@@ -211,6 +217,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
qemu_irq pic[128];
int n;
qemu_irq cpu_irq[4];
+ qemu_irq cpu_fiq[4];
MemoryRegion *sysram;
MemoryRegion *dram;
MemoryRegion *sysmem;
@@ -263,6 +270,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
exit(1);
}
cpu_irq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ);
+ cpu_fiq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_FIQ);
}
sysmem = get_system_memory();
@@ -307,6 +315,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE);
for (n = 0; n < smp_cpus; n++) {
sysbus_connect_irq(busdev, n, cpu_irq[n]);
+ sysbus_connect_irq(busdev, n + smp_cpus, cpu_fiq[n]);
}
for (n = 0; n < 128; n++) {
diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c
index a3b1314d9..42f66b33e 100644
--- a/hw/arm/musicpal.c
+++ b/hw/arm/musicpal.c
@@ -187,11 +187,6 @@ static void eth_rx_desc_get(uint32_t addr, mv88w8618_rx_desc *desc)
le32_to_cpus(&desc->next);
}
-static int eth_can_receive(NetClientState *nc)
-{
- return 1;
-}
-
static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size)
{
mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
@@ -381,7 +376,6 @@ static void eth_cleanup(NetClientState *nc)
static NetClientInfo net_mv88w8618_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = eth_can_receive,
.receive = eth_receive,
.cleanup = eth_cleanup,
};
diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c
index 2a5406d98..a659e8525 100644
--- a/hw/arm/nseries.c
+++ b/hw/arm/nseries.c
@@ -133,9 +133,8 @@ static void n800_mmc_cs_cb(void *opaque, int line, int level)
static void n8x0_gpio_setup(struct n800_s *s)
{
- qemu_irq *mmc_cs = qemu_allocate_irqs(n800_mmc_cs_cb, s->mpu->mmc, 1);
- qdev_connect_gpio_out(s->mpu->gpio, N8X0_MMC_CS_GPIO, mmc_cs[0]);
-
+ qdev_connect_gpio_out(s->mpu->gpio, N8X0_MMC_CS_GPIO,
+ qemu_allocate_irq(n800_mmc_cs_cb, s->mpu->mmc, 0));
qemu_irq_lower(qdev_get_gpio_in(s->mpu->gpio, N800_BAT_COVER_GPIO));
}
@@ -579,7 +578,10 @@ static uint32_t mipid_txrx(void *opaque, uint32_t cmd, int len)
case 0x26: /* GAMSET */
if (!s->pm) {
- s->gamma = ffs(s->param[0] & 0xf) - 1;
+ s->gamma = ctz32(s->param[0] & 0xf);
+ if (s->gamma == 32) {
+ s->gamma = -1; /* XXX: should this be 0? */
+ }
} else if (s->pm < 0) {
s->pm = 1;
}
diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c
index 91ffb589e..de2b28925 100644
--- a/hw/arm/omap1.c
+++ b/hw/arm/omap1.c
@@ -2004,8 +2004,7 @@ static void omap_mpuio_write(void *opaque, hwaddr addr,
case 0x04: /* OUTPUT_REG */
diff = (s->outputs ^ value) & ~s->dir;
s->outputs = value;
- while ((ln = ffs(diff))) {
- ln --;
+ while ((ln = ctz32(diff)) != 32) {
if (s->handler[ln])
qemu_set_irq(s->handler[ln], (value >> ln) & 1);
diff &= ~(1 << ln);
@@ -2017,8 +2016,7 @@ static void omap_mpuio_write(void *opaque, hwaddr addr,
s->dir = value;
value = s->outputs & ~s->dir;
- while ((ln = ffs(diff))) {
- ln --;
+ while ((ln = ctz32(diff)) != 32) {
if (s->handler[ln])
qemu_set_irq(s->handler[ln], (value >> ln) & 1);
diff &= ~(1 << ln);
diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c
index 671e02c4e..4b0f7f9c4 100644
--- a/hw/arm/omap_sx1.c
+++ b/hw/arm/omap_sx1.c
@@ -103,7 +103,6 @@ static void sx1_init(MachineState *machine, const int version)
struct omap_mpu_state_s *mpu;
MemoryRegion *address_space = get_system_memory();
MemoryRegion *flash = g_new(MemoryRegion, 1);
- MemoryRegion *flash_1 = g_new(MemoryRegion, 1);
MemoryRegion *cs = g_new(MemoryRegion, 4);
static uint32_t cs0val = 0x00213090;
static uint32_t cs1val = 0x00215070;
@@ -165,6 +164,7 @@ static void sx1_init(MachineState *machine, const int version)
if ((version == 1) &&
(dinfo = drive_get(IF_PFLASH, 0, fl_idx)) != NULL) {
+ MemoryRegion *flash_1 = g_new(MemoryRegion, 1);
memory_region_init_ram(flash_1, NULL, "omap_sx1.flash1-0", flash1_size,
&error_abort);
vmstate_register_ram_global(flash_1);
diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
index 165ba2a16..d94e20777 100644
--- a/hw/arm/pxa2xx.c
+++ b/hw/arm/pxa2xx.c
@@ -274,7 +274,7 @@ static void pxa2xx_pwrmode_write(CPUARMState *env, const ARMCPRegInfo *ri,
s->cpu->env.uncached_cpsr = ARM_CPU_MODE_SVC;
s->cpu->env.daif = PSTATE_A | PSTATE_F | PSTATE_I;
s->cpu->env.cp15.sctlr_ns = 0;
- s->cpu->env.cp15.c1_coproc = 0;
+ s->cpu->env.cp15.cpacr_el1 = 0;
s->cpu->env.cp15.ttbr0_el[1] = 0;
s->cpu->env.cp15.dacr_ns = 0;
s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */
@@ -334,10 +334,10 @@ static uint64_t pxa2xx_cpccnt_read(CPUARMState *env, const ARMCPRegInfo *ri)
static const ARMCPRegInfo pxa_cp_reginfo[] = {
/* cp14 crm==1: perf registers */
{ .name = "CPPMNC", .cp = 14, .crn = 0, .crm = 1, .opc1 = 0, .opc2 = 0,
- .access = PL1_RW,
+ .access = PL1_RW, .type = ARM_CP_IO,
.readfn = pxa2xx_cppmnc_read, .writefn = pxa2xx_cppmnc_write },
{ .name = "CPCCNT", .cp = 14, .crn = 1, .crm = 1, .opc1 = 0, .opc2 = 0,
- .access = PL1_RW,
+ .access = PL1_RW, .type = ARM_CP_IO,
.readfn = pxa2xx_cpccnt_read, .writefn = arm_cp_write_ignore },
{ .name = "CPINTEN", .cp = 14, .crn = 4, .crm = 1, .opc1 = 0, .opc2 = 0,
.access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
@@ -356,11 +356,11 @@ static const ARMCPRegInfo pxa_cp_reginfo[] = {
.access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
/* cp14 crn==6: CLKCFG */
{ .name = "CLKCFG", .cp = 14, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0,
- .access = PL1_RW,
+ .access = PL1_RW, .type = ARM_CP_IO,
.readfn = pxa2xx_clkcfg_read, .writefn = pxa2xx_clkcfg_write },
/* cp14 crn==7: PWRMODE */
{ .name = "PWRMODE", .cp = 14, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 0,
- .access = PL1_RW,
+ .access = PL1_RW, .type = ARM_CP_IO,
.readfn = arm_cp_read_zero, .writefn = pxa2xx_pwrmode_write },
REGINFO_SENTINEL
};
@@ -457,7 +457,7 @@ typedef struct {
MemoryRegion iomem;
qemu_irq irq;
- int enable;
+ uint32_t enable;
SSIBus *bus;
uint32_t sscr[2];
@@ -470,10 +470,39 @@ typedef struct {
uint8_t ssacd;
uint32_t rx_fifo[16];
- int rx_level;
- int rx_start;
+ uint32_t rx_level;
+ uint32_t rx_start;
} PXA2xxSSPState;
+static bool pxa2xx_ssp_vmstate_validate(void *opaque, int version_id)
+{
+ PXA2xxSSPState *s = opaque;
+
+ return s->rx_start < sizeof(s->rx_fifo);
+}
+
+static const VMStateDescription vmstate_pxa2xx_ssp = {
+ .name = "pxa2xx-ssp",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(enable, PXA2xxSSPState),
+ VMSTATE_UINT32_ARRAY(sscr, PXA2xxSSPState, 2),
+ VMSTATE_UINT32(sspsp, PXA2xxSSPState),
+ VMSTATE_UINT32(ssto, PXA2xxSSPState),
+ VMSTATE_UINT32(ssitr, PXA2xxSSPState),
+ VMSTATE_UINT32(sssr, PXA2xxSSPState),
+ VMSTATE_UINT8(sstsa, PXA2xxSSPState),
+ VMSTATE_UINT8(ssrsa, PXA2xxSSPState),
+ VMSTATE_UINT8(ssacd, PXA2xxSSPState),
+ VMSTATE_UINT32(rx_level, PXA2xxSSPState),
+ VMSTATE_UINT32(rx_start, PXA2xxSSPState),
+ VMSTATE_VALIDATE("fifo is 16 bytes", pxa2xx_ssp_vmstate_validate),
+ VMSTATE_UINT32_ARRAY(rx_fifo, PXA2xxSSPState, 16),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
#define SSCR0 0x00 /* SSP Control register 0 */
#define SSCR1 0x04 /* SSP Control register 1 */
#define SSSR 0x08 /* SSP Status register */
@@ -705,55 +734,20 @@ static const MemoryRegionOps pxa2xx_ssp_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static void pxa2xx_ssp_save(QEMUFile *f, void *opaque)
+static void pxa2xx_ssp_reset(DeviceState *d)
{
- PXA2xxSSPState *s = (PXA2xxSSPState *) opaque;
- int i;
-
- qemu_put_be32(f, s->enable);
+ PXA2xxSSPState *s = PXA2XX_SSP(d);
- qemu_put_be32s(f, &s->sscr[0]);
- qemu_put_be32s(f, &s->sscr[1]);
- qemu_put_be32s(f, &s->sspsp);
- qemu_put_be32s(f, &s->ssto);
- qemu_put_be32s(f, &s->ssitr);
- qemu_put_be32s(f, &s->sssr);
- qemu_put_8s(f, &s->sstsa);
- qemu_put_8s(f, &s->ssrsa);
- qemu_put_8s(f, &s->ssacd);
-
- qemu_put_byte(f, s->rx_level);
- for (i = 0; i < s->rx_level; i ++)
- qemu_put_byte(f, s->rx_fifo[(s->rx_start + i) & 0xf]);
-}
-
-static int pxa2xx_ssp_load(QEMUFile *f, void *opaque, int version_id)
-{
- PXA2xxSSPState *s = (PXA2xxSSPState *) opaque;
- int i, v;
-
- s->enable = qemu_get_be32(f);
-
- qemu_get_be32s(f, &s->sscr[0]);
- qemu_get_be32s(f, &s->sscr[1]);
- qemu_get_be32s(f, &s->sspsp);
- qemu_get_be32s(f, &s->ssto);
- qemu_get_be32s(f, &s->ssitr);
- qemu_get_be32s(f, &s->sssr);
- qemu_get_8s(f, &s->sstsa);
- qemu_get_8s(f, &s->ssrsa);
- qemu_get_8s(f, &s->ssacd);
-
- v = qemu_get_byte(f);
- if (v < 0 || v > ARRAY_SIZE(s->rx_fifo)) {
- return -EINVAL;
- }
- s->rx_level = v;
- s->rx_start = 0;
- for (i = 0; i < s->rx_level; i ++)
- s->rx_fifo[i] = qemu_get_byte(f);
-
- return 0;
+ s->enable = 0;
+ s->sscr[0] = s->sscr[1] = 0;
+ s->sspsp = 0;
+ s->ssto = 0;
+ s->ssitr = 0;
+ s->sssr = 0;
+ s->sstsa = 0;
+ s->ssrsa = 0;
+ s->ssacd = 0;
+ s->rx_start = s->rx_level = 0;
}
static int pxa2xx_ssp_init(SysBusDevice *sbd)
@@ -766,8 +760,6 @@ static int pxa2xx_ssp_init(SysBusDevice *sbd)
memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_ssp_ops, s,
"pxa2xx-ssp", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
- register_savevm(dev, "pxa2xx_ssp", -1, 0,
- pxa2xx_ssp_save, pxa2xx_ssp_load, s);
s->bus = ssi_create_bus(dev, "ssi");
return 0;
@@ -1759,24 +1751,33 @@ static PXA2xxI2SState *pxa2xx_i2s_init(MemoryRegion *sysmem,
}
/* PXA Fast Infra-red Communications Port */
+#define TYPE_PXA2XX_FIR "pxa2xx-fir"
+#define PXA2XX_FIR(obj) OBJECT_CHECK(PXA2xxFIrState, (obj), TYPE_PXA2XX_FIR)
+
struct PXA2xxFIrState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+
MemoryRegion iomem;
qemu_irq irq;
qemu_irq rx_dma;
qemu_irq tx_dma;
- int enable;
+ uint32_t enable;
CharDriverState *chr;
uint8_t control[3];
uint8_t status[2];
- int rx_len;
- int rx_start;
+ uint32_t rx_len;
+ uint32_t rx_start;
uint8_t rx_fifo[64];
};
-static void pxa2xx_fir_reset(PXA2xxFIrState *s)
+static void pxa2xx_fir_reset(DeviceState *d)
{
+ PXA2xxFIrState *s = PXA2XX_FIR(d);
+
s->control[0] = 0x00;
s->control[1] = 0x00;
s->control[2] = 0x00;
@@ -1953,73 +1954,94 @@ static void pxa2xx_fir_event(void *opaque, int event)
{
}
-static void pxa2xx_fir_save(QEMUFile *f, void *opaque)
+static void pxa2xx_fir_instance_init(Object *obj)
{
- PXA2xxFIrState *s = (PXA2xxFIrState *) opaque;
- int i;
+ PXA2xxFIrState *s = PXA2XX_FIR(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
- qemu_put_be32(f, s->enable);
-
- qemu_put_8s(f, &s->control[0]);
- qemu_put_8s(f, &s->control[1]);
- qemu_put_8s(f, &s->control[2]);
- qemu_put_8s(f, &s->status[0]);
- qemu_put_8s(f, &s->status[1]);
-
- qemu_put_byte(f, s->rx_len);
- for (i = 0; i < s->rx_len; i ++)
- qemu_put_byte(f, s->rx_fifo[(s->rx_start + i) & 63]);
+ memory_region_init_io(&s->iomem, obj, &pxa2xx_fir_ops, s,
+ "pxa2xx-fir", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->irq);
+ sysbus_init_irq(sbd, &s->rx_dma);
+ sysbus_init_irq(sbd, &s->tx_dma);
}
-static int pxa2xx_fir_load(QEMUFile *f, void *opaque, int version_id)
+static void pxa2xx_fir_realize(DeviceState *dev, Error **errp)
{
- PXA2xxFIrState *s = (PXA2xxFIrState *) opaque;
- int i;
-
- s->enable = qemu_get_be32(f);
+ PXA2xxFIrState *s = PXA2XX_FIR(dev);
- qemu_get_8s(f, &s->control[0]);
- qemu_get_8s(f, &s->control[1]);
- qemu_get_8s(f, &s->control[2]);
- qemu_get_8s(f, &s->status[0]);
- qemu_get_8s(f, &s->status[1]);
+ if (s->chr) {
+ qemu_chr_fe_claim_no_fail(s->chr);
+ qemu_chr_add_handlers(s->chr, pxa2xx_fir_is_empty,
+ pxa2xx_fir_rx, pxa2xx_fir_event, s);
+ }
+}
- s->rx_len = qemu_get_byte(f);
- s->rx_start = 0;
- for (i = 0; i < s->rx_len; i ++)
- s->rx_fifo[i] = qemu_get_byte(f);
+static bool pxa2xx_fir_vmstate_validate(void *opaque, int version_id)
+{
+ PXA2xxFIrState *s = opaque;
- return 0;
+ return s->rx_start < ARRAY_SIZE(s->rx_fifo);
}
-static PXA2xxFIrState *pxa2xx_fir_init(MemoryRegion *sysmem,
- hwaddr base,
- qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma,
- CharDriverState *chr)
-{
- PXA2xxFIrState *s = (PXA2xxFIrState *)
- g_malloc0(sizeof(PXA2xxFIrState));
+static const VMStateDescription pxa2xx_fir_vmsd = {
+ .name = "pxa2xx-fir",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(enable, PXA2xxFIrState),
+ VMSTATE_UINT8_ARRAY(control, PXA2xxFIrState, 3),
+ VMSTATE_UINT8_ARRAY(status, PXA2xxFIrState, 2),
+ VMSTATE_UINT32(rx_len, PXA2xxFIrState),
+ VMSTATE_UINT32(rx_start, PXA2xxFIrState),
+ VMSTATE_VALIDATE("fifo is 64 bytes", pxa2xx_fir_vmstate_validate),
+ VMSTATE_UINT8_ARRAY(rx_fifo, PXA2xxFIrState, 64),
+ VMSTATE_END_OF_LIST()
+ }
+};
- s->irq = irq;
- s->rx_dma = rx_dma;
- s->tx_dma = tx_dma;
- s->chr = chr;
+static Property pxa2xx_fir_properties[] = {
+ DEFINE_PROP_CHR("chardev", PXA2xxFIrState, chr),
+ DEFINE_PROP_END_OF_LIST(),
+};
- pxa2xx_fir_reset(s);
+static void pxa2xx_fir_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
- memory_region_init_io(&s->iomem, NULL, &pxa2xx_fir_ops, s, "pxa2xx-fir", 0x1000);
- memory_region_add_subregion(sysmem, base, &s->iomem);
+ dc->realize = pxa2xx_fir_realize;
+ dc->vmsd = &pxa2xx_fir_vmsd;
+ dc->props = pxa2xx_fir_properties;
+ dc->reset = pxa2xx_fir_reset;
+}
- if (chr) {
- qemu_chr_fe_claim_no_fail(chr);
- qemu_chr_add_handlers(chr, pxa2xx_fir_is_empty,
- pxa2xx_fir_rx, pxa2xx_fir_event, s);
- }
+static const TypeInfo pxa2xx_fir_info = {
+ .name = TYPE_PXA2XX_FIR,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(PXA2xxFIrState),
+ .class_init = pxa2xx_fir_class_init,
+ .instance_init = pxa2xx_fir_instance_init,
+};
- register_savevm(NULL, "pxa2xx_fir", 0, 0, pxa2xx_fir_save,
- pxa2xx_fir_load, s);
+static PXA2xxFIrState *pxa2xx_fir_init(MemoryRegion *sysmem,
+ hwaddr base,
+ qemu_irq irq, qemu_irq rx_dma,
+ qemu_irq tx_dma,
+ CharDriverState *chr)
+{
+ DeviceState *dev;
+ SysBusDevice *sbd;
- return s;
+ dev = qdev_create(NULL, TYPE_PXA2XX_FIR);
+ qdev_prop_set_chr(dev, "chardev", chr);
+ qdev_init_nofail(dev);
+ sbd = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(sbd, 0, base);
+ sysbus_connect_irq(sbd, 0, irq);
+ sysbus_connect_irq(sbd, 1, rx_dma);
+ sysbus_connect_irq(sbd, 2, tx_dma);
+ return PXA2XX_FIR(dev);
}
static void pxa2xx_reset(void *opaque, int line, int level)
@@ -2306,8 +2328,11 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size)
static void pxa2xx_ssp_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
sdc->init = pxa2xx_ssp_init;
+ dc->reset = pxa2xx_ssp_reset;
+ dc->vmsd = &vmstate_pxa2xx_ssp;
}
static const TypeInfo pxa2xx_ssp_info = {
@@ -2323,6 +2348,7 @@ static void pxa2xx_register_types(void)
type_register_static(&pxa2xx_ssp_info);
type_register_static(&pxa2xx_i2c_info);
type_register_static(&pxa2xx_rtc_sysbus_info);
+ type_register_static(&pxa2xx_fir_info);
}
type_init(pxa2xx_register_types)
diff --git a/hw/arm/pxa2xx_gpio.c b/hw/arm/pxa2xx_gpio.c
index 354ccf1ea..c89c8045c 100644
--- a/hw/arm/pxa2xx_gpio.c
+++ b/hw/arm/pxa2xx_gpio.c
@@ -137,7 +137,7 @@ static void pxa2xx_gpio_handler_update(PXA2xxGPIOInfo *s) {
level = s->olevel[i] & s->dir[i];
for (diff = s->prev_level[i] ^ level; diff; diff ^= 1 << bit) {
- bit = ffs(diff) - 1;
+ bit = ctz32(diff);
line = bit + 32 * i;
qemu_set_irq(s->handler[line], (level >> bit) & 1);
}
diff --git a/hw/arm/pxa2xx_pic.c b/hw/arm/pxa2xx_pic.c
index 9cfc71487..d41ac9341 100644
--- a/hw/arm/pxa2xx_pic.c
+++ b/hw/arm/pxa2xx_pic.c
@@ -232,7 +232,7 @@ static void pxa2xx_pic_cp_write(CPUARMState *env, const ARMCPRegInfo *ri,
#define REGINFO_FOR_PIC_CP(NAME, CRN) \
{ .name = NAME, .cp = 6, .crn = CRN, .crm = 0, .opc1 = 0, .opc2 = 0, \
- .access = PL1_RW, \
+ .access = PL1_RW, .type = ARM_CP_IO, \
.readfn = pxa2xx_pic_cp_read, .writefn = pxa2xx_pic_cp_write }
static const ARMCPRegInfo pxa_pic_cp_reginfo[] = {
diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c
index 1ddea6d89..da9fc1d51 100644
--- a/hw/arm/strongarm.c
+++ b/hw/arm/strongarm.c
@@ -528,7 +528,7 @@ static void strongarm_gpio_handler_update(StrongARMGPIOInfo *s)
level = s->olevel & s->dir;
for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
- bit = ffs(diff) - 1;
+ bit = ctz32(diff);
qemu_set_irq(s->handler[bit], (level >> bit) & 1);
}
@@ -745,7 +745,7 @@ static void strongarm_ppc_handler_update(StrongARMPPCInfo *s)
level = s->olevel & s->dir;
for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
- bit = ffs(diff) - 1;
+ bit = ctz32(diff);
qemu_set_irq(s->handler[bit], (level >> bit) & 1);
}
diff --git a/hw/arm/sysbus-fdt.c b/hw/arm/sysbus-fdt.c
new file mode 100644
index 000000000..9d28797c8
--- /dev/null
+++ b/hw/arm/sysbus-fdt.c
@@ -0,0 +1,247 @@
+/*
+ * ARM Platform Bus device tree generation helpers
+ *
+ * Copyright (c) 2014 Linaro Limited
+ *
+ * Authors:
+ * Alex Graf <agraf@suse.de>
+ * Eric Auger <eric.auger@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/arm/sysbus-fdt.h"
+#include "qemu/error-report.h"
+#include "sysemu/device_tree.h"
+#include "hw/platform-bus.h"
+#include "sysemu/sysemu.h"
+#include "hw/vfio/vfio-platform.h"
+#include "hw/vfio/vfio-calxeda-xgmac.h"
+#include "hw/arm/fdt.h"
+
+/*
+ * internal struct that contains the information to create dynamic
+ * sysbus device node
+ */
+typedef struct PlatformBusFDTData {
+ void *fdt; /* device tree handle */
+ int irq_start; /* index of the first IRQ usable by platform bus devices */
+ const char *pbus_node_name; /* name of the platform bus node */
+ PlatformBusDevice *pbus;
+} PlatformBusFDTData;
+
+/*
+ * struct used when calling the machine init done notifier
+ * that constructs the fdt nodes of platform bus devices
+ */
+typedef struct PlatformBusFDTNotifierParams {
+ Notifier notifier;
+ ARMPlatformBusFDTParams *fdt_params;
+} PlatformBusFDTNotifierParams;
+
+/* struct that associates a device type name and a node creation function */
+typedef struct NodeCreationPair {
+ const char *typename;
+ int (*add_fdt_node_fn)(SysBusDevice *sbdev, void *opaque);
+} NodeCreationPair;
+
+/* Device Specific Code */
+
+/**
+ * add_calxeda_midway_xgmac_fdt_node
+ *
+ * Generates a simple node with following properties:
+ * compatible string, regs, interrupts, dma-coherent
+ */
+static int add_calxeda_midway_xgmac_fdt_node(SysBusDevice *sbdev, void *opaque)
+{
+ PlatformBusFDTData *data = opaque;
+ PlatformBusDevice *pbus = data->pbus;
+ void *fdt = data->fdt;
+ const char *parent_node = data->pbus_node_name;
+ int compat_str_len, i, ret = -1;
+ char *nodename;
+ uint32_t *irq_attr, *reg_attr;
+ uint64_t mmio_base, irq_number;
+ VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev);
+ VFIODevice *vbasedev = &vdev->vbasedev;
+
+ mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
+ nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node,
+ vbasedev->name, mmio_base);
+ qemu_fdt_add_subnode(fdt, nodename);
+
+ compat_str_len = strlen(vdev->compat) + 1;
+ qemu_fdt_setprop(fdt, nodename, "compatible",
+ vdev->compat, compat_str_len);
+
+ qemu_fdt_setprop(fdt, nodename, "dma-coherent", "", 0);
+
+ reg_attr = g_new(uint32_t, vbasedev->num_regions * 2);
+ for (i = 0; i < vbasedev->num_regions; i++) {
+ mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i);
+ reg_attr[2 * i] = cpu_to_be32(mmio_base);
+ reg_attr[2 * i + 1] = cpu_to_be32(
+ memory_region_size(&vdev->regions[i]->mem));
+ }
+ ret = qemu_fdt_setprop(fdt, nodename, "reg", reg_attr,
+ vbasedev->num_regions * 2 * sizeof(uint32_t));
+ if (ret) {
+ error_report("could not set reg property of node %s", nodename);
+ goto fail_reg;
+ }
+
+ irq_attr = g_new(uint32_t, vbasedev->num_irqs * 3);
+ for (i = 0; i < vbasedev->num_irqs; i++) {
+ irq_number = platform_bus_get_irqn(pbus, sbdev , i)
+ + data->irq_start;
+ irq_attr[3 * i] = cpu_to_be32(GIC_FDT_IRQ_TYPE_SPI);
+ irq_attr[3 * i + 1] = cpu_to_be32(irq_number);
+ irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_LEVEL_HI);
+ }
+ ret = qemu_fdt_setprop(fdt, nodename, "interrupts",
+ irq_attr, vbasedev->num_irqs * 3 * sizeof(uint32_t));
+ if (ret) {
+ error_report("could not set interrupts property of node %s",
+ nodename);
+ }
+ g_free(irq_attr);
+fail_reg:
+ g_free(reg_attr);
+ g_free(nodename);
+ return ret;
+}
+
+/* list of supported dynamic sysbus devices */
+static const NodeCreationPair add_fdt_node_functions[] = {
+ {TYPE_VFIO_CALXEDA_XGMAC, add_calxeda_midway_xgmac_fdt_node},
+ {"", NULL}, /* last element */
+};
+
+/* Generic Code */
+
+/**
+ * add_fdt_node - add the device tree node of a dynamic sysbus device
+ *
+ * @sbdev: handle to the sysbus device
+ * @opaque: handle to the PlatformBusFDTData
+ *
+ * Checks the sysbus type belongs to the list of device types that
+ * are dynamically instantiable and if so call the node creation
+ * function.
+ */
+static int add_fdt_node(SysBusDevice *sbdev, void *opaque)
+{
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(add_fdt_node_functions); i++) {
+ if (!strcmp(object_get_typename(OBJECT(sbdev)),
+ add_fdt_node_functions[i].typename)) {
+ ret = add_fdt_node_functions[i].add_fdt_node_fn(sbdev, opaque);
+ assert(!ret);
+ return 0;
+ }
+ }
+ error_report("Device %s can not be dynamically instantiated",
+ qdev_fw_name(DEVICE(sbdev)));
+ exit(1);
+}
+
+/**
+ * add_all_platform_bus_fdt_nodes - create all the platform bus nodes
+ *
+ * builds the parent platform bus node and all the nodes of dynamic
+ * sysbus devices attached to it.
+ */
+static void add_all_platform_bus_fdt_nodes(ARMPlatformBusFDTParams *fdt_params)
+{
+ const char platcomp[] = "qemu,platform\0simple-bus";
+ PlatformBusDevice *pbus;
+ DeviceState *dev;
+ gchar *node;
+ uint64_t addr, size;
+ int irq_start, dtb_size;
+ struct arm_boot_info *info = fdt_params->binfo;
+ const ARMPlatformBusSystemParams *params = fdt_params->system_params;
+ const char *intc = fdt_params->intc;
+ void *fdt = info->get_dtb(info, &dtb_size);
+
+ /*
+ * If the user provided a dtb, we assume the dynamic sysbus nodes
+ * already are integrated there. This corresponds to a use case where
+ * the dynamic sysbus nodes are complex and their generation is not yet
+ * supported. In that case the user can take charge of the guest dt
+ * while qemu takes charge of the qom stuff.
+ */
+ if (info->dtb_filename) {
+ return;
+ }
+
+ assert(fdt);
+
+ node = g_strdup_printf("/platform@%"PRIx64, params->platform_bus_base);
+ addr = params->platform_bus_base;
+ size = params->platform_bus_size;
+ irq_start = params->platform_bus_first_irq;
+
+ /* Create a /platform node that we can put all devices into */
+ qemu_fdt_add_subnode(fdt, node);
+ qemu_fdt_setprop(fdt, node, "compatible", platcomp, sizeof(platcomp));
+
+ /* Our platform bus region is less than 32bits, so 1 cell is enough for
+ * address and size
+ */
+ qemu_fdt_setprop_cells(fdt, node, "#size-cells", 1);
+ qemu_fdt_setprop_cells(fdt, node, "#address-cells", 1);
+ qemu_fdt_setprop_cells(fdt, node, "ranges", 0, addr >> 32, addr, size);
+
+ qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", intc);
+
+ dev = qdev_find_recursive(sysbus_get_default(), TYPE_PLATFORM_BUS_DEVICE);
+ pbus = PLATFORM_BUS_DEVICE(dev);
+
+ /* We can only create dt nodes for dynamic devices when they're ready */
+ assert(pbus->done_gathering);
+
+ PlatformBusFDTData data = {
+ .fdt = fdt,
+ .irq_start = irq_start,
+ .pbus_node_name = node,
+ .pbus = pbus,
+ };
+
+ /* Loop through all dynamic sysbus devices and create their node */
+ foreach_dynamic_sysbus_device(add_fdt_node, &data);
+
+ g_free(node);
+}
+
+static void platform_bus_fdt_notify(Notifier *notifier, void *data)
+{
+ PlatformBusFDTNotifierParams *p = DO_UPCAST(PlatformBusFDTNotifierParams,
+ notifier, notifier);
+
+ add_all_platform_bus_fdt_nodes(p->fdt_params);
+ g_free(p->fdt_params);
+ g_free(p);
+}
+
+void arm_register_platform_bus_fdt_creator(ARMPlatformBusFDTParams *fdt_params)
+{
+ PlatformBusFDTNotifierParams *p = g_new(PlatformBusFDTNotifierParams, 1);
+
+ p->fdt_params = fdt_params;
+ p->notifier.notify = platform_bus_fdt_notify;
+ qemu_add_machine_init_done_notifier(&p->notifier);
+}
diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c
index 3989bc52b..da217884e 100644
--- a/hw/arm/vexpress.c
+++ b/hw/arm/vexpress.c
@@ -253,6 +253,8 @@ static void init_cpus(const char *cpu_model, const char *privdev,
DeviceState *cpudev = DEVICE(qemu_get_cpu(n));
sysbus_connect_irq(busdev, n, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
+ sysbus_connect_irq(busdev, n + smp_cpus,
+ qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
}
}
@@ -523,7 +525,7 @@ static pflash_t *ve_pflash_cfi01_register(hwaddr base, const char *name,
qdev_prop_set_uint64(dev, "sector-length", VEXPRESS_FLASH_SECT_SIZE);
qdev_prop_set_uint8(dev, "width", 4);
qdev_prop_set_uint8(dev, "device-width", 2);
- qdev_prop_set_uint8(dev, "big-endian", 0);
+ qdev_prop_set_bit(dev, "big-endian", false);
qdev_prop_set_uint16(dev, "id0", 0x89);
qdev_prop_set_uint16(dev, "id1", 0x18);
qdev_prop_set_uint16(dev, "id2", 0x00);
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
new file mode 100644
index 000000000..f36514031
--- /dev/null
+++ b/hw/arm/virt-acpi-build.c
@@ -0,0 +1,697 @@
+/* Support for generating ACPI tables and passing them to Guests
+ *
+ * ARM virt ACPI generation
+ *
+ * Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net>
+ * Copyright (C) 2006 Fabrice Bellard
+ * Copyright (C) 2013 Red Hat Inc
+ *
+ * Author: Michael S. Tsirkin <mst@redhat.com>
+ *
+ * Copyright (c) 2015 HUAWEI TECHNOLOGIES CO.,LTD.
+ *
+ * Author: Shannon Zhao <zhaoshenglong@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu-common.h"
+#include "hw/arm/virt-acpi-build.h"
+#include "qemu/bitmap.h"
+#include "trace.h"
+#include "qom/cpu.h"
+#include "target-arm/cpu.h"
+#include "hw/acpi/acpi-defs.h"
+#include "hw/acpi/acpi.h"
+#include "hw/nvram/fw_cfg.h"
+#include "hw/acpi/bios-linker-loader.h"
+#include "hw/loader.h"
+#include "hw/hw.h"
+#include "hw/acpi/aml-build.h"
+#include "hw/pci/pcie_host.h"
+#include "hw/pci/pci.h"
+
+#define ARM_SPI_BASE 32
+
+typedef struct VirtAcpiCpuInfo {
+ DECLARE_BITMAP(found_cpus, VIRT_ACPI_CPU_ID_LIMIT);
+} VirtAcpiCpuInfo;
+
+static void virt_acpi_get_cpu_info(VirtAcpiCpuInfo *cpuinfo)
+{
+ CPUState *cpu;
+
+ memset(cpuinfo->found_cpus, 0, sizeof cpuinfo->found_cpus);
+ CPU_FOREACH(cpu) {
+ set_bit(cpu->cpu_index, cpuinfo->found_cpus);
+ }
+}
+
+static void acpi_dsdt_add_cpus(Aml *scope, int smp_cpus)
+{
+ uint16_t i;
+
+ for (i = 0; i < smp_cpus; i++) {
+ Aml *dev = aml_device("C%03x", i);
+ aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0007")));
+ aml_append(dev, aml_name_decl("_UID", aml_int(i)));
+ aml_append(scope, dev);
+ }
+}
+
+static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
+ int uart_irq)
+{
+ Aml *dev = aml_device("COM0");
+ aml_append(dev, aml_name_decl("_HID", aml_string("ARMH0011")));
+ aml_append(dev, aml_name_decl("_UID", aml_int(0)));
+
+ Aml *crs = aml_resource_template();
+ aml_append(crs, aml_memory32_fixed(uart_memmap->base,
+ uart_memmap->size, AML_READ_WRITE));
+ aml_append(crs,
+ aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
+ AML_EXCLUSIVE, uart_irq));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+
+ /* The _ADR entry is used to link this device to the UART described
+ * in the SPCR table, i.e. SPCR.base_address.address == _ADR.
+ */
+ aml_append(dev, aml_name_decl("_ADR", aml_int(uart_memmap->base)));
+
+ aml_append(scope, dev);
+}
+
+static void acpi_dsdt_add_rtc(Aml *scope, const MemMapEntry *rtc_memmap,
+ int rtc_irq)
+{
+ Aml *dev = aml_device("RTC0");
+ aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0013")));
+ aml_append(dev, aml_name_decl("_UID", aml_int(0)));
+
+ Aml *crs = aml_resource_template();
+ aml_append(crs, aml_memory32_fixed(rtc_memmap->base,
+ rtc_memmap->size, AML_READ_WRITE));
+ aml_append(crs,
+ aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
+ AML_EXCLUSIVE, rtc_irq));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+ aml_append(scope, dev);
+}
+
+static void acpi_dsdt_add_flash(Aml *scope, const MemMapEntry *flash_memmap)
+{
+ Aml *dev, *crs;
+ hwaddr base = flash_memmap->base;
+ hwaddr size = flash_memmap->size;
+
+ dev = aml_device("FLS0");
+ aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015")));
+ aml_append(dev, aml_name_decl("_UID", aml_int(0)));
+
+ crs = aml_resource_template();
+ aml_append(crs, aml_memory32_fixed(base, size, AML_READ_WRITE));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+ aml_append(scope, dev);
+
+ dev = aml_device("FLS1");
+ aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015")));
+ aml_append(dev, aml_name_decl("_UID", aml_int(1)));
+ crs = aml_resource_template();
+ aml_append(crs, aml_memory32_fixed(base + size, size, AML_READ_WRITE));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+ aml_append(scope, dev);
+}
+
+static void acpi_dsdt_add_virtio(Aml *scope,
+ const MemMapEntry *virtio_mmio_memmap,
+ int mmio_irq, int num)
+{
+ hwaddr base = virtio_mmio_memmap->base;
+ hwaddr size = virtio_mmio_memmap->size;
+ int irq = mmio_irq;
+ int i;
+
+ for (i = 0; i < num; i++) {
+ Aml *dev = aml_device("VR%02u", i);
+ aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0005")));
+ aml_append(dev, aml_name_decl("_UID", aml_int(i)));
+
+ Aml *crs = aml_resource_template();
+ aml_append(crs, aml_memory32_fixed(base, size, AML_READ_WRITE));
+ aml_append(crs,
+ aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
+ AML_EXCLUSIVE, irq + i));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+ aml_append(scope, dev);
+ base += size;
+ }
+}
+
+static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq)
+{
+ Aml *method, *crs, *ifctx, *UUID, *ifctx1, *elsectx, *buf;
+ int i, bus_no;
+ hwaddr base_mmio = memmap[VIRT_PCIE_MMIO].base;
+ hwaddr size_mmio = memmap[VIRT_PCIE_MMIO].size;
+ hwaddr base_pio = memmap[VIRT_PCIE_PIO].base;
+ hwaddr size_pio = memmap[VIRT_PCIE_PIO].size;
+ hwaddr base_ecam = memmap[VIRT_PCIE_ECAM].base;
+ hwaddr size_ecam = memmap[VIRT_PCIE_ECAM].size;
+ int nr_pcie_buses = size_ecam / PCIE_MMCFG_SIZE_MIN;
+
+ Aml *dev = aml_device("%s", "PCI0");
+ aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A08")));
+ aml_append(dev, aml_name_decl("_CID", aml_string("PNP0A03")));
+ aml_append(dev, aml_name_decl("_SEG", aml_int(0)));
+ aml_append(dev, aml_name_decl("_BBN", aml_int(0)));
+ aml_append(dev, aml_name_decl("_ADR", aml_int(0)));
+ aml_append(dev, aml_name_decl("_UID", aml_string("PCI0")));
+ aml_append(dev, aml_name_decl("_STR", aml_unicode("PCIe 0 Device")));
+
+ /* Declare the PCI Routing Table. */
+ Aml *rt_pkg = aml_package(nr_pcie_buses * PCI_NUM_PINS);
+ for (bus_no = 0; bus_no < nr_pcie_buses; bus_no++) {
+ for (i = 0; i < PCI_NUM_PINS; i++) {
+ int gsi = (i + bus_no) % PCI_NUM_PINS;
+ Aml *pkg = aml_package(4);
+ aml_append(pkg, aml_int((bus_no << 16) | 0xFFFF));
+ aml_append(pkg, aml_int(i));
+ aml_append(pkg, aml_name("GSI%d", gsi));
+ aml_append(pkg, aml_int(0));
+ aml_append(rt_pkg, pkg);
+ }
+ }
+ aml_append(dev, aml_name_decl("_PRT", rt_pkg));
+
+ /* Create GSI link device */
+ for (i = 0; i < PCI_NUM_PINS; i++) {
+ Aml *dev_gsi = aml_device("GSI%d", i);
+ aml_append(dev_gsi, aml_name_decl("_HID", aml_string("PNP0C0F")));
+ aml_append(dev_gsi, aml_name_decl("_UID", aml_int(0)));
+ crs = aml_resource_template();
+ aml_append(crs,
+ aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
+ AML_EXCLUSIVE, irq + i));
+ aml_append(dev_gsi, aml_name_decl("_PRS", crs));
+ crs = aml_resource_template();
+ aml_append(crs,
+ aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
+ AML_EXCLUSIVE, irq + i));
+ aml_append(dev_gsi, aml_name_decl("_CRS", crs));
+ method = aml_method("_SRS", 1);
+ aml_append(dev_gsi, method);
+ aml_append(dev, dev_gsi);
+ }
+
+ method = aml_method("_CBA", 0);
+ aml_append(method, aml_return(aml_int(base_ecam)));
+ aml_append(dev, method);
+
+ method = aml_method("_CRS", 0);
+ Aml *rbuf = aml_resource_template();
+ aml_append(rbuf,
+ aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE,
+ 0x0000, 0x0000, nr_pcie_buses - 1, 0x0000,
+ nr_pcie_buses));
+ aml_append(rbuf,
+ aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
+ AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000, base_mmio,
+ base_mmio + size_mmio - 1, 0x0000, size_mmio));
+ aml_append(rbuf,
+ aml_dword_io(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE,
+ AML_ENTIRE_RANGE, 0x0000, 0x0000, size_pio - 1, base_pio,
+ size_pio));
+
+ aml_append(method, aml_name_decl("RBUF", rbuf));
+ aml_append(method, aml_return(rbuf));
+ aml_append(dev, method);
+
+ /* Declare an _OSC (OS Control Handoff) method */
+ aml_append(dev, aml_name_decl("SUPP", aml_int(0)));
+ aml_append(dev, aml_name_decl("CTRL", aml_int(0)));
+ method = aml_method("_OSC", 4);
+ aml_append(method,
+ aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1"));
+
+ /* PCI Firmware Specification 3.0
+ * 4.5.1. _OSC Interface for PCI Host Bridge Devices
+ * The _OSC interface for a PCI/PCI-X/PCI Express hierarchy is
+ * identified by the Universal Unique IDentifier (UUID)
+ * 33DB4D5B-1FF7-401C-9657-7441C03DD766
+ */
+ UUID = aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766");
+ ifctx = aml_if(aml_equal(aml_arg(0), UUID));
+ aml_append(ifctx,
+ aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2"));
+ aml_append(ifctx,
+ aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3"));
+ aml_append(ifctx, aml_store(aml_name("CDW2"), aml_name("SUPP")));
+ aml_append(ifctx, aml_store(aml_name("CDW3"), aml_name("CTRL")));
+ aml_append(ifctx, aml_store(aml_and(aml_name("CTRL"), aml_int(0x1D)),
+ aml_name("CTRL")));
+
+ ifctx1 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(0x1))));
+ aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x08)),
+ aml_name("CDW1")));
+ aml_append(ifctx, ifctx1);
+
+ ifctx1 = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), aml_name("CTRL"))));
+ aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x10)),
+ aml_name("CDW1")));
+ aml_append(ifctx, ifctx1);
+
+ aml_append(ifctx, aml_store(aml_name("CTRL"), aml_name("CDW3")));
+ aml_append(ifctx, aml_return(aml_arg(3)));
+ aml_append(method, ifctx);
+
+ elsectx = aml_else();
+ aml_append(elsectx, aml_store(aml_or(aml_name("CDW1"), aml_int(4)),
+ aml_name("CDW1")));
+ aml_append(elsectx, aml_return(aml_arg(3)));
+ aml_append(method, elsectx);
+ aml_append(dev, method);
+
+ method = aml_method("_DSM", 4);
+
+ /* PCI Firmware Specification 3.0
+ * 4.6.1. _DSM for PCI Express Slot Information
+ * The UUID in _DSM in this context is
+ * {E5C937D0-3553-4D7A-9117-EA4D19C3434D}
+ */
+ UUID = aml_touuid("E5C937D0-3553-4D7A-9117-EA4D19C3434D");
+ ifctx = aml_if(aml_equal(aml_arg(0), UUID));
+ ifctx1 = aml_if(aml_equal(aml_arg(2), aml_int(0)));
+ uint8_t byte_list[1] = {1};
+ buf = aml_buffer(1, byte_list);
+ aml_append(ifctx1, aml_return(buf));
+ aml_append(ifctx, ifctx1);
+ aml_append(method, ifctx);
+
+ byte_list[0] = 0;
+ buf = aml_buffer(1, byte_list);
+ aml_append(method, aml_return(buf));
+ aml_append(dev, method);
+
+ Aml *dev_rp0 = aml_device("%s", "RP0");
+ aml_append(dev_rp0, aml_name_decl("_ADR", aml_int(0)));
+ aml_append(dev, dev_rp0);
+ aml_append(scope, dev);
+}
+
+/* RSDP */
+static GArray *
+build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt)
+{
+ AcpiRsdpDescriptor *rsdp = acpi_data_push(rsdp_table, sizeof *rsdp);
+
+ bios_linker_loader_alloc(linker, ACPI_BUILD_RSDP_FILE, 16,
+ true /* fseg memory */);
+
+ memcpy(&rsdp->signature, "RSD PTR ", sizeof(rsdp->signature));
+ memcpy(rsdp->oem_id, ACPI_BUILD_APPNAME6, sizeof(rsdp->oem_id));
+ rsdp->length = cpu_to_le32(sizeof(*rsdp));
+ rsdp->revision = 0x02;
+
+ /* Point to RSDT */
+ rsdp->rsdt_physical_address = cpu_to_le32(rsdt);
+ /* Address to be filled by Guest linker */
+ bios_linker_loader_add_pointer(linker, ACPI_BUILD_RSDP_FILE,
+ ACPI_BUILD_TABLE_FILE,
+ rsdp_table, &rsdp->rsdt_physical_address,
+ sizeof rsdp->rsdt_physical_address);
+ rsdp->checksum = 0;
+ /* Checksum to be filled by Guest linker */
+ bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE,
+ rsdp, rsdp, sizeof *rsdp, &rsdp->checksum);
+
+ return rsdp_table;
+}
+
+static void
+build_spcr(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
+{
+ AcpiSerialPortConsoleRedirection *spcr;
+ const MemMapEntry *uart_memmap = &guest_info->memmap[VIRT_UART];
+ int irq = guest_info->irqmap[VIRT_UART] + ARM_SPI_BASE;
+
+ spcr = acpi_data_push(table_data, sizeof(*spcr));
+
+ spcr->interface_type = 0x3; /* ARM PL011 UART */
+
+ spcr->base_address.space_id = AML_SYSTEM_MEMORY;
+ spcr->base_address.bit_width = 8;
+ spcr->base_address.bit_offset = 0;
+ spcr->base_address.access_width = 1;
+ spcr->base_address.address = cpu_to_le64(uart_memmap->base);
+
+ spcr->interrupt_types = (1 << 3); /* Bit[3] ARMH GIC interrupt */
+ spcr->gsi = cpu_to_le32(irq); /* Global System Interrupt */
+
+ spcr->baud = 3; /* Baud Rate: 3 = 9600 */
+ spcr->parity = 0; /* No Parity */
+ spcr->stopbits = 1; /* 1 Stop bit */
+ spcr->flowctrl = (1 << 1); /* Bit[1] = RTS/CTS hardware flow control */
+ spcr->term_type = 0; /* Terminal Type: 0 = VT100 */
+
+ spcr->pci_device_id = 0xffff; /* PCI Device ID: not a PCI device */
+ spcr->pci_vendor_id = 0xffff; /* PCI Vendor ID: not a PCI device */
+
+ build_header(linker, table_data, (void *)spcr, "SPCR", sizeof(*spcr), 2);
+}
+
+static void
+build_mcfg(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
+{
+ AcpiTableMcfg *mcfg;
+ const MemMapEntry *memmap = guest_info->memmap;
+ int len = sizeof(*mcfg) + sizeof(mcfg->allocation[0]);
+
+ mcfg = acpi_data_push(table_data, len);
+ mcfg->allocation[0].address = cpu_to_le64(memmap[VIRT_PCIE_ECAM].base);
+
+ /* Only a single allocation so no need to play with segments */
+ mcfg->allocation[0].pci_segment = cpu_to_le16(0);
+ mcfg->allocation[0].start_bus_number = 0;
+ mcfg->allocation[0].end_bus_number = (memmap[VIRT_PCIE_ECAM].size
+ / PCIE_MMCFG_SIZE_MIN) - 1;
+
+ build_header(linker, table_data, (void *)mcfg, "MCFG", len, 1);
+}
+
+/* GTDT */
+static void
+build_gtdt(GArray *table_data, GArray *linker)
+{
+ int gtdt_start = table_data->len;
+ AcpiGenericTimerTable *gtdt;
+
+ gtdt = acpi_data_push(table_data, sizeof *gtdt);
+ /* The interrupt values are the same with the device tree when adding 16 */
+ gtdt->secure_el1_interrupt = ARCH_TIMER_S_EL1_IRQ + 16;
+ gtdt->secure_el1_flags = ACPI_EDGE_SENSITIVE;
+
+ gtdt->non_secure_el1_interrupt = ARCH_TIMER_NS_EL1_IRQ + 16;
+ gtdt->non_secure_el1_flags = ACPI_EDGE_SENSITIVE;
+
+ gtdt->virtual_timer_interrupt = ARCH_TIMER_VIRT_IRQ + 16;
+ gtdt->virtual_timer_flags = ACPI_EDGE_SENSITIVE;
+
+ gtdt->non_secure_el2_interrupt = ARCH_TIMER_NS_EL2_IRQ + 16;
+ gtdt->non_secure_el2_flags = ACPI_EDGE_SENSITIVE;
+
+ build_header(linker, table_data,
+ (void *)(table_data->data + gtdt_start), "GTDT",
+ table_data->len - gtdt_start, 2);
+}
+
+/* MADT */
+static void
+build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info,
+ VirtAcpiCpuInfo *cpuinfo)
+{
+ int madt_start = table_data->len;
+ const MemMapEntry *memmap = guest_info->memmap;
+ const int *irqmap = guest_info->irqmap;
+ AcpiMultipleApicTable *madt;
+ AcpiMadtGenericDistributor *gicd;
+ AcpiMadtGenericMsiFrame *gic_msi;
+ int i;
+
+ madt = acpi_data_push(table_data, sizeof *madt);
+
+ for (i = 0; i < guest_info->smp_cpus; i++) {
+ AcpiMadtGenericInterrupt *gicc = acpi_data_push(table_data,
+ sizeof *gicc);
+ gicc->type = ACPI_APIC_GENERIC_INTERRUPT;
+ gicc->length = sizeof(*gicc);
+ gicc->base_address = memmap[VIRT_GIC_CPU].base;
+ gicc->cpu_interface_number = i;
+ gicc->arm_mpidr = i;
+ gicc->uid = i;
+ if (test_bit(i, cpuinfo->found_cpus)) {
+ gicc->flags = cpu_to_le32(ACPI_GICC_ENABLED);
+ }
+ }
+
+ gicd = acpi_data_push(table_data, sizeof *gicd);
+ gicd->type = ACPI_APIC_GENERIC_DISTRIBUTOR;
+ gicd->length = sizeof(*gicd);
+ gicd->base_address = memmap[VIRT_GIC_DIST].base;
+
+ gic_msi = acpi_data_push(table_data, sizeof *gic_msi);
+ gic_msi->type = ACPI_APIC_GENERIC_MSI_FRAME;
+ gic_msi->length = sizeof(*gic_msi);
+ gic_msi->gic_msi_frame_id = 0;
+ gic_msi->base_address = cpu_to_le64(memmap[VIRT_GIC_V2M].base);
+ gic_msi->flags = cpu_to_le32(1);
+ gic_msi->spi_count = cpu_to_le16(NUM_GICV2M_SPIS);
+ gic_msi->spi_base = cpu_to_le16(irqmap[VIRT_GIC_V2M] + ARM_SPI_BASE);
+
+ build_header(linker, table_data,
+ (void *)(table_data->data + madt_start), "APIC",
+ table_data->len - madt_start, 3);
+}
+
+/* FADT */
+static void
+build_fadt(GArray *table_data, GArray *linker, unsigned dsdt)
+{
+ AcpiFadtDescriptorRev5_1 *fadt = acpi_data_push(table_data, sizeof(*fadt));
+
+ /* Hardware Reduced = 1 and use PSCI 0.2+ and with HVC */
+ fadt->flags = cpu_to_le32(1 << ACPI_FADT_F_HW_REDUCED_ACPI);
+ fadt->arm_boot_flags = cpu_to_le16((1 << ACPI_FADT_ARM_USE_PSCI_G_0_2) |
+ (1 << ACPI_FADT_ARM_PSCI_USE_HVC));
+
+ /* ACPI v5.1 (fadt->revision.fadt->minor_revision) */
+ fadt->minor_revision = 0x1;
+
+ fadt->dsdt = cpu_to_le32(dsdt);
+ /* DSDT address to be filled by Guest linker */
+ bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE,
+ ACPI_BUILD_TABLE_FILE,
+ table_data, &fadt->dsdt,
+ sizeof fadt->dsdt);
+
+ build_header(linker, table_data,
+ (void *)fadt, "FACP", sizeof(*fadt), 5);
+}
+
+/* DSDT */
+static void
+build_dsdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
+{
+ Aml *scope, *dsdt;
+ const MemMapEntry *memmap = guest_info->memmap;
+ const int *irqmap = guest_info->irqmap;
+
+ dsdt = init_aml_allocator();
+ /* Reserve space for header */
+ acpi_data_push(dsdt->buf, sizeof(AcpiTableHeader));
+
+ scope = aml_scope("\\_SB");
+ acpi_dsdt_add_cpus(scope, guest_info->smp_cpus);
+ acpi_dsdt_add_uart(scope, &memmap[VIRT_UART],
+ (irqmap[VIRT_UART] + ARM_SPI_BASE));
+ acpi_dsdt_add_rtc(scope, &memmap[VIRT_RTC],
+ (irqmap[VIRT_RTC] + ARM_SPI_BASE));
+ acpi_dsdt_add_flash(scope, &memmap[VIRT_FLASH]);
+ acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO],
+ (irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS);
+ acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE));
+
+ aml_append(dsdt, scope);
+
+ /* copy AML table into ACPI tables blob and patch header there */
+ g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
+ build_header(linker, table_data,
+ (void *)(table_data->data + table_data->len - dsdt->buf->len),
+ "DSDT", dsdt->buf->len, 2);
+ free_aml_allocator();
+}
+
+typedef
+struct AcpiBuildState {
+ /* Copy of table in RAM (for patching). */
+ MemoryRegion *table_mr;
+ MemoryRegion *rsdp_mr;
+ MemoryRegion *linker_mr;
+ /* Is table patched? */
+ bool patched;
+ VirtGuestInfo *guest_info;
+} AcpiBuildState;
+
+static
+void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
+{
+ GArray *table_offsets;
+ unsigned dsdt, rsdt;
+ VirtAcpiCpuInfo cpuinfo;
+ GArray *tables_blob = tables->table_data;
+
+ virt_acpi_get_cpu_info(&cpuinfo);
+
+ table_offsets = g_array_new(false, true /* clear */,
+ sizeof(uint32_t));
+
+ bios_linker_loader_alloc(tables->linker, ACPI_BUILD_TABLE_FILE,
+ 64, false /* high memory */);
+
+ /*
+ * The ACPI v5.1 tables for Hardware-reduced ACPI platform are:
+ * RSDP
+ * RSDT
+ * FADT
+ * GTDT
+ * MADT
+ * MCFG
+ * DSDT
+ */
+
+ /* DSDT is pointed to by FADT */
+ dsdt = tables_blob->len;
+ build_dsdt(tables_blob, tables->linker, guest_info);
+
+ /* FADT MADT GTDT MCFG SPCR pointed to by RSDT */
+ acpi_add_table(table_offsets, tables_blob);
+ build_fadt(tables_blob, tables->linker, dsdt);
+
+ acpi_add_table(table_offsets, tables_blob);
+ build_madt(tables_blob, tables->linker, guest_info, &cpuinfo);
+
+ acpi_add_table(table_offsets, tables_blob);
+ build_gtdt(tables_blob, tables->linker);
+
+ acpi_add_table(table_offsets, tables_blob);
+ build_mcfg(tables_blob, tables->linker, guest_info);
+
+ acpi_add_table(table_offsets, tables_blob);
+ build_spcr(tables_blob, tables->linker, guest_info);
+
+ /* RSDT is pointed to by RSDP */
+ rsdt = tables_blob->len;
+ build_rsdt(tables_blob, tables->linker, table_offsets);
+
+ /* RSDP is in FSEG memory, so allocate it separately */
+ build_rsdp(tables->rsdp, tables->linker, rsdt);
+
+ /* Cleanup memory that's no longer used. */
+ g_array_free(table_offsets, true);
+}
+
+static void acpi_ram_update(MemoryRegion *mr, GArray *data)
+{
+ uint32_t size = acpi_data_len(data);
+
+ /* Make sure RAM size is correct - in case it got changed
+ * e.g. by migration */
+ memory_region_ram_resize(mr, size, &error_abort);
+
+ memcpy(memory_region_get_ram_ptr(mr), data->data, size);
+ memory_region_set_dirty(mr, 0, size);
+}
+
+static void virt_acpi_build_update(void *build_opaque, uint32_t offset)
+{
+ AcpiBuildState *build_state = build_opaque;
+ AcpiBuildTables tables;
+
+ /* No state to update or already patched? Nothing to do. */
+ if (!build_state || build_state->patched) {
+ return;
+ }
+ build_state->patched = true;
+
+ acpi_build_tables_init(&tables);
+
+ virt_acpi_build(build_state->guest_info, &tables);
+
+ acpi_ram_update(build_state->table_mr, tables.table_data);
+ acpi_ram_update(build_state->rsdp_mr, tables.rsdp);
+ acpi_ram_update(build_state->linker_mr, tables.linker);
+
+
+ acpi_build_tables_cleanup(&tables, true);
+}
+
+static void virt_acpi_build_reset(void *build_opaque)
+{
+ AcpiBuildState *build_state = build_opaque;
+ build_state->patched = false;
+}
+
+static MemoryRegion *acpi_add_rom_blob(AcpiBuildState *build_state,
+ GArray *blob, const char *name,
+ uint64_t max_size)
+{
+ return rom_add_blob(name, blob->data, acpi_data_len(blob), max_size, -1,
+ name, virt_acpi_build_update, build_state);
+}
+
+static const VMStateDescription vmstate_virt_acpi_build = {
+ .name = "virt_acpi_build",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL(patched, AcpiBuildState),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+void virt_acpi_setup(VirtGuestInfo *guest_info)
+{
+ AcpiBuildTables tables;
+ AcpiBuildState *build_state;
+
+ if (!guest_info->fw_cfg) {
+ trace_virt_acpi_setup();
+ return;
+ }
+
+ if (!acpi_enabled) {
+ trace_virt_acpi_setup();
+ return;
+ }
+
+ build_state = g_malloc0(sizeof *build_state);
+ build_state->guest_info = guest_info;
+
+ acpi_build_tables_init(&tables);
+ virt_acpi_build(build_state->guest_info, &tables);
+
+ /* Now expose it all to Guest */
+ build_state->table_mr = acpi_add_rom_blob(build_state, tables.table_data,
+ ACPI_BUILD_TABLE_FILE,
+ ACPI_BUILD_TABLE_MAX_SIZE);
+ assert(build_state->table_mr != NULL);
+
+ build_state->linker_mr =
+ acpi_add_rom_blob(build_state, tables.linker, "etc/table-loader", 0);
+
+ fw_cfg_add_file(guest_info->fw_cfg, ACPI_BUILD_TPMLOG_FILE,
+ tables.tcpalog->data, acpi_data_len(tables.tcpalog));
+
+ build_state->rsdp_mr = acpi_add_rom_blob(build_state, tables.rsdp,
+ ACPI_BUILD_RSDP_FILE, 0);
+
+ qemu_register_reset(virt_acpi_build_reset, build_state);
+ virt_acpi_build_reset(build_state);
+ vmstate_register(NULL, 0, &vmstate_virt_acpi_build, build_state);
+
+ /* Cleanup tables but don't free the memory: we track it
+ * in build_state.
+ */
+ acpi_build_tables_cleanup(&tables, false);
+}
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 565f57313..484689264 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -31,6 +31,7 @@
#include "hw/sysbus.h"
#include "hw/arm/arm.h"
#include "hw/arm/primecell.h"
+#include "hw/arm/virt.h"
#include "hw/devices.h"
#include "net/net.h"
#include "sysemu/block-backend.h"
@@ -43,40 +44,17 @@
#include "qemu/bitops.h"
#include "qemu/error-report.h"
#include "hw/pci-host/gpex.h"
-
-#define NUM_VIRTIO_TRANSPORTS 32
+#include "hw/arm/virt-acpi-build.h"
+#include "hw/arm/sysbus-fdt.h"
+#include "hw/platform-bus.h"
+#include "hw/arm/fdt.h"
/* Number of external interrupt lines to configure the GIC with */
-#define NUM_IRQS 128
-
-#define GIC_FDT_IRQ_TYPE_SPI 0
-#define GIC_FDT_IRQ_TYPE_PPI 1
-
-#define GIC_FDT_IRQ_FLAGS_EDGE_LO_HI 1
-#define GIC_FDT_IRQ_FLAGS_EDGE_HI_LO 2
-#define GIC_FDT_IRQ_FLAGS_LEVEL_HI 4
-#define GIC_FDT_IRQ_FLAGS_LEVEL_LO 8
-
-#define GIC_FDT_IRQ_PPI_CPU_START 8
-#define GIC_FDT_IRQ_PPI_CPU_WIDTH 8
-
-enum {
- VIRT_FLASH,
- VIRT_MEM,
- VIRT_CPUPERIPHS,
- VIRT_GIC_DIST,
- VIRT_GIC_CPU,
- VIRT_UART,
- VIRT_MMIO,
- VIRT_RTC,
- VIRT_FW_CFG,
- VIRT_PCIE,
-};
+#define NUM_IRQS 256
-typedef struct MemMapEntry {
- hwaddr base;
- hwaddr size;
-} MemMapEntry;
+#define PLATFORM_BUS_NUM_IRQS 64
+
+static ARMPlatformBusSystemParams platform_bus_params;
typedef struct VirtBoardInfo {
struct arm_boot_info bootinfo;
@@ -87,6 +65,8 @@ typedef struct VirtBoardInfo {
void *fdt;
int fdt_size;
uint32_t clock_phandle;
+ uint32_t gic_phandle;
+ uint32_t v2m_phandle;
} VirtBoardInfo;
typedef struct {
@@ -121,25 +101,22 @@ typedef struct {
*/
static const MemMapEntry a15memmap[] = {
/* Space up to 0x8000000 is reserved for a boot ROM */
- [VIRT_FLASH] = { 0, 0x08000000 },
- [VIRT_CPUPERIPHS] = { 0x08000000, 0x00020000 },
+ [VIRT_FLASH] = { 0, 0x08000000 },
+ [VIRT_CPUPERIPHS] = { 0x08000000, 0x00020000 },
/* GIC distributor and CPU interfaces sit inside the CPU peripheral space */
- [VIRT_GIC_DIST] = { 0x08000000, 0x00010000 },
- [VIRT_GIC_CPU] = { 0x08010000, 0x00010000 },
- [VIRT_UART] = { 0x09000000, 0x00001000 },
- [VIRT_RTC] = { 0x09010000, 0x00001000 },
- [VIRT_FW_CFG] = { 0x09020000, 0x0000000a },
- [VIRT_MMIO] = { 0x0a000000, 0x00000200 },
+ [VIRT_GIC_DIST] = { 0x08000000, 0x00010000 },
+ [VIRT_GIC_CPU] = { 0x08010000, 0x00010000 },
+ [VIRT_GIC_V2M] = { 0x08020000, 0x00001000 },
+ [VIRT_UART] = { 0x09000000, 0x00001000 },
+ [VIRT_RTC] = { 0x09010000, 0x00001000 },
+ [VIRT_FW_CFG] = { 0x09020000, 0x0000000a },
+ [VIRT_MMIO] = { 0x0a000000, 0x00000200 },
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
- /*
- * PCIE verbose map:
- *
- * MMIO window { 0x10000000, 0x2eff0000 },
- * PIO window { 0x3eff0000, 0x00010000 },
- * ECAM { 0x3f000000, 0x01000000 },
- */
- [VIRT_PCIE] = { 0x10000000, 0x30000000 },
- [VIRT_MEM] = { 0x40000000, 30ULL * 1024 * 1024 * 1024 },
+ [VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
+ [VIRT_PCIE_MMIO] = { 0x10000000, 0x2eff0000 },
+ [VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 },
+ [VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 },
+ [VIRT_MEM] = { 0x40000000, 30ULL * 1024 * 1024 * 1024 },
};
static const int a15irqmap[] = {
@@ -147,6 +124,8 @@ static const int a15irqmap[] = {
[VIRT_RTC] = 2,
[VIRT_PCIE] = 3, /* ... to 6 */
[VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
+ [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
+ [VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1 */
};
static VirtBoardInfo machines[] = {
@@ -156,6 +135,11 @@ static VirtBoardInfo machines[] = {
.irqmap = a15irqmap,
},
{
+ .cpu_model = "cortex-a53",
+ .memmap = a15memmap,
+ .irqmap = a15irqmap,
+ },
+ {
.cpu_model = "cortex-a57",
.memmap = a15memmap,
.irqmap = a15irqmap,
@@ -289,10 +273,10 @@ static void fdt_add_timer_nodes(const VirtBoardInfo *vbi)
"arm,armv7-timer");
}
qemu_fdt_setprop_cells(vbi->fdt, "/timer", "interrupts",
- GIC_FDT_IRQ_TYPE_PPI, 13, irqflags,
- GIC_FDT_IRQ_TYPE_PPI, 14, irqflags,
- GIC_FDT_IRQ_TYPE_PPI, 11, irqflags,
- GIC_FDT_IRQ_TYPE_PPI, 10, irqflags);
+ GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_S_EL1_IRQ, irqflags,
+ GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_NS_EL1_IRQ, irqflags,
+ GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_VIRT_IRQ, irqflags,
+ GIC_FDT_IRQ_TYPE_PPI, ARCH_TIMER_NS_EL2_IRQ, irqflags);
}
static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi)
@@ -317,17 +301,28 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi)
"enable-method", "psci");
}
- qemu_fdt_setprop_cell(vbi->fdt, nodename, "reg", cpu);
+ qemu_fdt_setprop_cell(vbi->fdt, nodename, "reg", armcpu->mp_affinity);
g_free(nodename);
}
}
-static uint32_t fdt_add_gic_node(const VirtBoardInfo *vbi)
+static void fdt_add_v2m_gic_node(VirtBoardInfo *vbi)
{
- uint32_t gic_phandle;
+ vbi->v2m_phandle = qemu_fdt_alloc_phandle(vbi->fdt);
+ qemu_fdt_add_subnode(vbi->fdt, "/intc/v2m");
+ qemu_fdt_setprop_string(vbi->fdt, "/intc/v2m", "compatible",
+ "arm,gic-v2m-frame");
+ qemu_fdt_setprop(vbi->fdt, "/intc/v2m", "msi-controller", NULL, 0);
+ qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc/v2m", "reg",
+ 2, vbi->memmap[VIRT_GIC_V2M].base,
+ 2, vbi->memmap[VIRT_GIC_V2M].size);
+ qemu_fdt_setprop_cell(vbi->fdt, "/intc/v2m", "phandle", vbi->v2m_phandle);
+}
- gic_phandle = qemu_fdt_alloc_phandle(vbi->fdt);
- qemu_fdt_setprop_cell(vbi->fdt, "/", "interrupt-parent", gic_phandle);
+static void fdt_add_gic_node(VirtBoardInfo *vbi)
+{
+ vbi->gic_phandle = qemu_fdt_alloc_phandle(vbi->fdt);
+ qemu_fdt_setprop_cell(vbi->fdt, "/", "interrupt-parent", vbi->gic_phandle);
qemu_fdt_add_subnode(vbi->fdt, "/intc");
/* 'cortex-a15-gic' means 'GIC v2' */
@@ -340,12 +335,32 @@ static uint32_t fdt_add_gic_node(const VirtBoardInfo *vbi)
2, vbi->memmap[VIRT_GIC_DIST].size,
2, vbi->memmap[VIRT_GIC_CPU].base,
2, vbi->memmap[VIRT_GIC_CPU].size);
- qemu_fdt_setprop_cell(vbi->fdt, "/intc", "phandle", gic_phandle);
+ qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#address-cells", 0x2);
+ qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#size-cells", 0x2);
+ qemu_fdt_setprop(vbi->fdt, "/intc", "ranges", NULL, 0);
+ qemu_fdt_setprop_cell(vbi->fdt, "/intc", "phandle", vbi->gic_phandle);
+}
+
+static void create_v2m(VirtBoardInfo *vbi, qemu_irq *pic)
+{
+ int i;
+ int irq = vbi->irqmap[VIRT_GIC_V2M];
+ DeviceState *dev;
- return gic_phandle;
+ dev = qdev_create(NULL, "arm-gicv2m");
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vbi->memmap[VIRT_GIC_V2M].base);
+ qdev_prop_set_uint32(dev, "base-spi", irq);
+ qdev_prop_set_uint32(dev, "num-spi", NUM_GICV2M_SPIS);
+ qdev_init_nofail(dev);
+
+ for (i = 0; i < NUM_GICV2M_SPIS; i++) {
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]);
+ }
+
+ fdt_add_v2m_gic_node(vbi);
}
-static uint32_t create_gic(const VirtBoardInfo *vbi, qemu_irq *pic)
+static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic)
{
/* We create a standalone GIC v2 */
DeviceState *gicdev;
@@ -386,13 +401,17 @@ static uint32_t create_gic(const VirtBoardInfo *vbi, qemu_irq *pic)
qdev_get_gpio_in(gicdev, ppibase + 27));
sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
+ sysbus_connect_irq(gicbusdev, i + smp_cpus,
+ qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
}
for (i = 0; i < NUM_IRQS; i++) {
pic[i] = qdev_get_gpio_in(gicdev, i);
}
- return fdt_add_gic_node(vbi);
+ fdt_add_gic_node(vbi);
+
+ create_v2m(vbi, pic);
}
static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic)
@@ -531,7 +550,7 @@ static void create_one_flash(const char *name, hwaddr flashbase,
qdev_prop_set_uint64(dev, "sector-length", sectorlength);
qdev_prop_set_uint8(dev, "width", 4);
qdev_prop_set_uint8(dev, "device-width", 2);
- qdev_prop_set_uint8(dev, "big-endian", 0);
+ qdev_prop_set_bit(dev, "big-endian", false);
qdev_prop_set_uint16(dev, "id0", 0x89);
qdev_prop_set_uint16(dev, "id1", 0x18);
qdev_prop_set_uint16(dev, "id2", 0x00);
@@ -608,7 +627,7 @@ static void create_pcie_irq_map(const VirtBoardInfo *vbi, uint32_t gic_phandle,
int first_irq, const char *nodename)
{
int devfn, pin;
- uint32_t full_irq_map[4 * 4 * 8] = { 0 };
+ uint32_t full_irq_map[4 * 4 * 10] = { 0 };
uint32_t *irq_map = full_irq_map;
for (devfn = 0; devfn <= 0x18; devfn += 0x8) {
@@ -621,13 +640,13 @@ static void create_pcie_irq_map(const VirtBoardInfo *vbi, uint32_t gic_phandle,
uint32_t map[] = {
devfn << 8, 0, 0, /* devfn */
pin + 1, /* PCI pin */
- gic_phandle, irq_type, irq_nr, irq_level }; /* GIC irq */
+ gic_phandle, 0, 0, irq_type, irq_nr, irq_level }; /* GIC irq */
/* Convert map to big endian */
- for (i = 0; i < 8; i++) {
+ for (i = 0; i < 10; i++) {
irq_map[i] = cpu_to_be32(map[i]);
}
- irq_map += 8;
+ irq_map += 10;
}
}
@@ -639,19 +658,16 @@ static void create_pcie_irq_map(const VirtBoardInfo *vbi, uint32_t gic_phandle,
0x7 /* PCI irq */);
}
-static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
- uint32_t gic_phandle)
+static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic)
{
- hwaddr base = vbi->memmap[VIRT_PCIE].base;
- hwaddr size = vbi->memmap[VIRT_PCIE].size;
- hwaddr end = base + size;
- hwaddr size_mmio;
- hwaddr size_ioport = 64 * 1024;
- int nr_pcie_buses = 16;
- hwaddr size_ecam = PCIE_MMCFG_SIZE_MIN * nr_pcie_buses;
- hwaddr base_mmio = base;
- hwaddr base_ioport;
- hwaddr base_ecam;
+ hwaddr base_mmio = vbi->memmap[VIRT_PCIE_MMIO].base;
+ hwaddr size_mmio = vbi->memmap[VIRT_PCIE_MMIO].size;
+ hwaddr base_pio = vbi->memmap[VIRT_PCIE_PIO].base;
+ hwaddr size_pio = vbi->memmap[VIRT_PCIE_PIO].size;
+ hwaddr base_ecam = vbi->memmap[VIRT_PCIE_ECAM].base;
+ hwaddr size_ecam = vbi->memmap[VIRT_PCIE_ECAM].size;
+ hwaddr base = base_mmio;
+ int nr_pcie_buses = size_ecam / PCIE_MMCFG_SIZE_MIN;
int irq = vbi->irqmap[VIRT_PCIE];
MemoryRegion *mmio_alias;
MemoryRegion *mmio_reg;
@@ -661,10 +677,6 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
char *nodename;
int i;
- base_ecam = QEMU_ALIGN_DOWN(end - size_ecam, size_ecam);
- base_ioport = QEMU_ALIGN_DOWN(base_ecam - size_ioport, size_ioport);
- size_mmio = base_ioport - base;
-
dev = qdev_create(NULL, TYPE_GPEX_HOST);
qdev_init_nofail(dev);
@@ -687,7 +699,7 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
memory_region_add_subregion(get_system_memory(), base_mmio, mmio_alias);
/* Map IO port space */
- sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, base_ioport);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, base_pio);
for (i = 0; i < GPEX_NUM_IRQS; i++) {
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]);
@@ -703,20 +715,63 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
qemu_fdt_setprop_cells(vbi->fdt, nodename, "bus-range", 0,
nr_pcie_buses - 1);
+ qemu_fdt_setprop_cells(vbi->fdt, nodename, "msi-parent", vbi->v2m_phandle);
+
qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
2, base_ecam, 2, size_ecam);
qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "ranges",
1, FDT_PCI_RANGE_IOPORT, 2, 0,
- 2, base_ioport, 2, size_ioport,
+ 2, base_pio, 2, size_pio,
1, FDT_PCI_RANGE_MMIO, 2, base_mmio,
2, base_mmio, 2, size_mmio);
qemu_fdt_setprop_cell(vbi->fdt, nodename, "#interrupt-cells", 1);
- create_pcie_irq_map(vbi, gic_phandle, irq, nodename);
+ create_pcie_irq_map(vbi, vbi->gic_phandle, irq, nodename);
g_free(nodename);
}
+static void create_platform_bus(VirtBoardInfo *vbi, qemu_irq *pic)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ int i;
+ ARMPlatformBusFDTParams *fdt_params = g_new(ARMPlatformBusFDTParams, 1);
+ MemoryRegion *sysmem = get_system_memory();
+
+ platform_bus_params.platform_bus_base = vbi->memmap[VIRT_PLATFORM_BUS].base;
+ platform_bus_params.platform_bus_size = vbi->memmap[VIRT_PLATFORM_BUS].size;
+ platform_bus_params.platform_bus_first_irq = vbi->irqmap[VIRT_PLATFORM_BUS];
+ platform_bus_params.platform_bus_num_irqs = PLATFORM_BUS_NUM_IRQS;
+
+ fdt_params->system_params = &platform_bus_params;
+ fdt_params->binfo = &vbi->bootinfo;
+ fdt_params->intc = "/intc";
+ /*
+ * register a machine init done notifier that creates the device tree
+ * nodes of the platform bus and its children dynamic sysbus devices
+ */
+ arm_register_platform_bus_fdt_creator(fdt_params);
+
+ dev = qdev_create(NULL, TYPE_PLATFORM_BUS_DEVICE);
+ dev->id = TYPE_PLATFORM_BUS_DEVICE;
+ qdev_prop_set_uint32(dev, "num_irqs",
+ platform_bus_params.platform_bus_num_irqs);
+ qdev_prop_set_uint32(dev, "mmio_size",
+ platform_bus_params.platform_bus_size);
+ qdev_init_nofail(dev);
+ s = SYS_BUS_DEVICE(dev);
+
+ for (i = 0; i < platform_bus_params.platform_bus_num_irqs; i++) {
+ int irqn = platform_bus_params.platform_bus_first_irq + i;
+ sysbus_connect_irq(s, i, pic[irqn]);
+ }
+
+ memory_region_add_subregion(sysmem,
+ platform_bus_params.platform_bus_base,
+ sysbus_mmio_get_region(s, 0));
+}
+
static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size)
{
const VirtBoardInfo *board = (const VirtBoardInfo *)binfo;
@@ -725,6 +780,14 @@ static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size)
return board->fdt;
}
+static
+void virt_guest_info_machine_done(Notifier *notifier, void *data)
+{
+ VirtGuestInfoState *guest_info_state = container_of(notifier,
+ VirtGuestInfoState, machine_done);
+ virt_acpi_setup(&guest_info_state->info);
+}
+
static void machvirt_init(MachineState *machine)
{
VirtMachineState *vms = VIRT_MACHINE(machine);
@@ -734,7 +797,8 @@ static void machvirt_init(MachineState *machine)
MemoryRegion *ram = g_new(MemoryRegion, 1);
const char *cpu_model = machine->cpu_model;
VirtBoardInfo *vbi;
- uint32_t gic_phandle;
+ VirtGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state);
+ VirtGuestInfo *guest_info = &guest_info_state->info;
char **cpustr;
if (!cpu_model) {
@@ -811,13 +875,13 @@ static void machvirt_init(MachineState *machine)
create_flash(vbi);
- gic_phandle = create_gic(vbi, pic);
+ create_gic(vbi, pic);
create_uart(vbi, pic);
create_rtc(vbi, pic);
- create_pcie(vbi, pic, gic_phandle);
+ create_pcie(vbi, pic);
/* Create mmio transports, so the user can create virtio backends
* (which will be automatically plugged in to the transports). If
@@ -826,6 +890,14 @@ static void machvirt_init(MachineState *machine)
create_virtio_devices(vbi, pic);
create_fw_cfg(vbi);
+ rom_set_fw(fw_cfg_find());
+
+ guest_info->smp_cpus = smp_cpus;
+ guest_info->fw_cfg = fw_cfg_find();
+ guest_info->memmap = vbi->memmap;
+ guest_info->irqmap = vbi->irqmap;
+ guest_info_state->machine_done.notify = virt_guest_info_machine_done;
+ qemu_add_machine_init_done_notifier(&guest_info_state->machine_done);
vbi->bootinfo.ram_size = machine->ram_size;
vbi->bootinfo.kernel_filename = machine->kernel_filename;
@@ -837,6 +909,14 @@ static void machvirt_init(MachineState *machine)
vbi->bootinfo.get_dtb = machvirt_dtb;
vbi->bootinfo.firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0);
arm_load_kernel(ARM_CPU(first_cpu), &vbi->bootinfo);
+
+ /*
+ * arm_load_kernel machine init done notifier registration must
+ * happen before the platform_bus_create call. In this latter,
+ * another notifier is registered which adds platform bus nodes.
+ * Notifiers are executed in registration reverse order.
+ */
+ create_platform_bus(vbi, pic);
}
static bool virt_get_secure(Object *obj, Error **errp)
@@ -875,6 +955,9 @@ static void virt_class_init(ObjectClass *oc, void *data)
mc->desc = "ARM Virtual Machine",
mc->init = machvirt_init;
mc->max_cpus = 8;
+ mc->has_dynamic_sysbus = true;
+ mc->block_default_type = IF_VIRTIO;
+ mc->no_cdrom = 1;
}
static const TypeInfo machvirt_info = {
diff --git a/hw/arm/xlnx-ep108.c b/hw/arm/xlnx-ep108.c
new file mode 100644
index 000000000..f94da86cb
--- /dev/null
+++ b/hw/arm/xlnx-ep108.c
@@ -0,0 +1,82 @@
+/*
+ * Xilinx ZynqMP EP108 board
+ *
+ * Copyright (C) 2015 Xilinx Inc
+ * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "hw/arm/xlnx-zynqmp.h"
+#include "hw/boards.h"
+#include "qemu/error-report.h"
+#include "exec/address-spaces.h"
+
+typedef struct XlnxEP108 {
+ XlnxZynqMPState soc;
+ MemoryRegion ddr_ram;
+} XlnxEP108;
+
+/* Max 2GB RAM */
+#define EP108_MAX_RAM_SIZE 0x80000000ull
+
+static struct arm_boot_info xlnx_ep108_binfo;
+
+static void xlnx_ep108_init(MachineState *machine)
+{
+ XlnxEP108 *s = g_new0(XlnxEP108, 1);
+ Error *err = NULL;
+
+ object_initialize(&s->soc, sizeof(s->soc), TYPE_XLNX_ZYNQMP);
+ object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
+ &error_abort);
+
+ object_property_set_bool(OBJECT(&s->soc), true, "realized", &err);
+ if (err) {
+ error_report("%s", error_get_pretty(err));
+ exit(1);
+ }
+
+ if (machine->ram_size > EP108_MAX_RAM_SIZE) {
+ error_report("WARNING: RAM size " RAM_ADDR_FMT " above max supported, "
+ "reduced to %llx", machine->ram_size, EP108_MAX_RAM_SIZE);
+ machine->ram_size = EP108_MAX_RAM_SIZE;
+ }
+
+ if (machine->ram_size <= 0x08000000) {
+ qemu_log("WARNING: RAM size " RAM_ADDR_FMT " is small for EP108",
+ machine->ram_size);
+ }
+
+ memory_region_allocate_system_memory(&s->ddr_ram, NULL, "ddr-ram",
+ machine->ram_size);
+ memory_region_add_subregion(get_system_memory(), 0, &s->ddr_ram);
+
+ xlnx_ep108_binfo.ram_size = machine->ram_size;
+ xlnx_ep108_binfo.kernel_filename = machine->kernel_filename;
+ xlnx_ep108_binfo.kernel_cmdline = machine->kernel_cmdline;
+ xlnx_ep108_binfo.initrd_filename = machine->initrd_filename;
+ xlnx_ep108_binfo.loader_start = 0;
+ arm_load_kernel(s->soc.boot_cpu_ptr, &xlnx_ep108_binfo);
+}
+
+static QEMUMachine xlnx_ep108_machine = {
+ .name = "xlnx-ep108",
+ .desc = "Xilinx ZynqMP EP108 board",
+ .init = xlnx_ep108_init,
+};
+
+static void xlnx_ep108_machine_init(void)
+{
+ qemu_register_machine(&xlnx_ep108_machine);
+}
+
+machine_init(xlnx_ep108_machine_init);
diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c
new file mode 100644
index 000000000..5157565f9
--- /dev/null
+++ b/hw/arm/xlnx-zynqmp.c
@@ -0,0 +1,272 @@
+/*
+ * Xilinx Zynq MPSoC emulation
+ *
+ * Copyright (C) 2015 Xilinx Inc
+ * Written by Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "hw/arm/xlnx-zynqmp.h"
+#include "hw/intc/arm_gic_common.h"
+#include "exec/address-spaces.h"
+
+#define GIC_NUM_SPI_INTR 160
+
+#define ARM_PHYS_TIMER_PPI 30
+#define ARM_VIRT_TIMER_PPI 27
+
+#define GIC_BASE_ADDR 0xf9000000
+#define GIC_DIST_ADDR 0xf9010000
+#define GIC_CPU_ADDR 0xf9020000
+
+static const uint64_t gem_addr[XLNX_ZYNQMP_NUM_GEMS] = {
+ 0xFF0B0000, 0xFF0C0000, 0xFF0D0000, 0xFF0E0000,
+};
+
+static const int gem_intr[XLNX_ZYNQMP_NUM_GEMS] = {
+ 57, 59, 61, 63,
+};
+
+static const uint64_t uart_addr[XLNX_ZYNQMP_NUM_UARTS] = {
+ 0xFF000000, 0xFF010000,
+};
+
+static const int uart_intr[XLNX_ZYNQMP_NUM_UARTS] = {
+ 21, 22,
+};
+
+typedef struct XlnxZynqMPGICRegion {
+ int region_index;
+ uint32_t address;
+} XlnxZynqMPGICRegion;
+
+static const XlnxZynqMPGICRegion xlnx_zynqmp_gic_regions[] = {
+ { .region_index = 0, .address = GIC_DIST_ADDR, },
+ { .region_index = 1, .address = GIC_CPU_ADDR, },
+};
+
+static inline int arm_gic_ppi_index(int cpu_nr, int ppi_index)
+{
+ return GIC_NUM_SPI_INTR + cpu_nr * GIC_INTERNAL + ppi_index;
+}
+
+static void xlnx_zynqmp_init(Object *obj)
+{
+ XlnxZynqMPState *s = XLNX_ZYNQMP(obj);
+ int i;
+
+ for (i = 0; i < XLNX_ZYNQMP_NUM_APU_CPUS; i++) {
+ object_initialize(&s->apu_cpu[i], sizeof(s->apu_cpu[i]),
+ "cortex-a53-" TYPE_ARM_CPU);
+ object_property_add_child(obj, "apu-cpu[*]", OBJECT(&s->apu_cpu[i]),
+ &error_abort);
+ }
+
+ for (i = 0; i < XLNX_ZYNQMP_NUM_RPU_CPUS; i++) {
+ object_initialize(&s->rpu_cpu[i], sizeof(s->rpu_cpu[i]),
+ "cortex-r5-" TYPE_ARM_CPU);
+ object_property_add_child(obj, "rpu-cpu[*]", OBJECT(&s->rpu_cpu[i]),
+ &error_abort);
+ }
+
+ object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC);
+ qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default());
+
+ for (i = 0; i < XLNX_ZYNQMP_NUM_GEMS; i++) {
+ object_initialize(&s->gem[i], sizeof(s->gem[i]), TYPE_CADENCE_GEM);
+ qdev_set_parent_bus(DEVICE(&s->gem[i]), sysbus_get_default());
+ }
+
+ for (i = 0; i < XLNX_ZYNQMP_NUM_UARTS; i++) {
+ object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_CADENCE_UART);
+ qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default());
+ }
+}
+
+static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
+{
+ XlnxZynqMPState *s = XLNX_ZYNQMP(dev);
+ MemoryRegion *system_memory = get_system_memory();
+ uint8_t i;
+ const char *boot_cpu = s->boot_cpu ? s->boot_cpu : "apu-cpu[0]";
+ qemu_irq gic_spi[GIC_NUM_SPI_INTR];
+ Error *err = NULL;
+
+ qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", GIC_NUM_SPI_INTR + 32);
+ qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 2);
+ qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", XLNX_ZYNQMP_NUM_APU_CPUS);
+ object_property_set_bool(OBJECT(&s->gic), true, "realized", &err);
+ if (err) {
+ error_propagate((errp), (err));
+ return;
+ }
+ assert(ARRAY_SIZE(xlnx_zynqmp_gic_regions) == XLNX_ZYNQMP_GIC_REGIONS);
+ for (i = 0; i < XLNX_ZYNQMP_GIC_REGIONS; i++) {
+ SysBusDevice *gic = SYS_BUS_DEVICE(&s->gic);
+ const XlnxZynqMPGICRegion *r = &xlnx_zynqmp_gic_regions[i];
+ MemoryRegion *mr = sysbus_mmio_get_region(gic, r->region_index);
+ uint32_t addr = r->address;
+ int j;
+
+ sysbus_mmio_map(gic, r->region_index, addr);
+
+ for (j = 0; j < XLNX_ZYNQMP_GIC_ALIASES; j++) {
+ MemoryRegion *alias = &s->gic_mr[i][j];
+
+ addr += XLNX_ZYNQMP_GIC_REGION_SIZE;
+ memory_region_init_alias(alias, OBJECT(s), "zynqmp-gic-alias", mr,
+ 0, XLNX_ZYNQMP_GIC_REGION_SIZE);
+ memory_region_add_subregion(system_memory, addr, alias);
+ }
+ }
+
+ for (i = 0; i < XLNX_ZYNQMP_NUM_APU_CPUS; i++) {
+ qemu_irq irq;
+ char *name;
+
+ object_property_set_int(OBJECT(&s->apu_cpu[i]), QEMU_PSCI_CONDUIT_SMC,
+ "psci-conduit", &error_abort);
+
+ name = object_get_canonical_path_component(OBJECT(&s->apu_cpu[i]));
+ if (strcmp(name, boot_cpu)) {
+ /* Secondary CPUs start in PSCI powered-down state */
+ object_property_set_bool(OBJECT(&s->apu_cpu[i]), true,
+ "start-powered-off", &error_abort);
+ } else {
+ s->boot_cpu_ptr = &s->apu_cpu[i];
+ }
+ g_free(name);
+
+ object_property_set_int(OBJECT(&s->apu_cpu[i]), GIC_BASE_ADDR,
+ "reset-cbar", &err);
+ if (err) {
+ error_propagate((errp), (err));
+ return;
+ }
+
+ object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, "realized",
+ &err);
+ if (err) {
+ error_propagate((errp), (err));
+ return;
+ }
+
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i,
+ qdev_get_gpio_in(DEVICE(&s->apu_cpu[i]),
+ ARM_CPU_IRQ));
+ irq = qdev_get_gpio_in(DEVICE(&s->gic),
+ arm_gic_ppi_index(i, ARM_PHYS_TIMER_PPI));
+ qdev_connect_gpio_out(DEVICE(&s->apu_cpu[i]), 0, irq);
+ irq = qdev_get_gpio_in(DEVICE(&s->gic),
+ arm_gic_ppi_index(i, ARM_VIRT_TIMER_PPI));
+ qdev_connect_gpio_out(DEVICE(&s->apu_cpu[i]), 1, irq);
+ }
+
+ for (i = 0; i < XLNX_ZYNQMP_NUM_RPU_CPUS; i++) {
+ char *name;
+
+ name = object_get_canonical_path_component(OBJECT(&s->rpu_cpu[i]));
+ if (strcmp(name, boot_cpu)) {
+ /* Secondary CPUs start in PSCI powered-down state */
+ object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true,
+ "start-powered-off", &error_abort);
+ } else {
+ s->boot_cpu_ptr = &s->rpu_cpu[i];
+ }
+ g_free(name);
+
+ object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "reset-hivecs",
+ &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "realized",
+ &err);
+ if (err) {
+ error_propagate((errp), (err));
+ return;
+ }
+ }
+
+ if (!s->boot_cpu_ptr) {
+ error_setg(errp, "ZynqMP Boot cpu %s not found\n", boot_cpu);
+ return;
+ }
+
+ for (i = 0; i < GIC_NUM_SPI_INTR; i++) {
+ gic_spi[i] = qdev_get_gpio_in(DEVICE(&s->gic), i);
+ }
+
+ for (i = 0; i < XLNX_ZYNQMP_NUM_GEMS; i++) {
+ NICInfo *nd = &nd_table[i];
+
+ if (nd->used) {
+ qemu_check_nic_model(nd, TYPE_CADENCE_GEM);
+ qdev_set_nic_properties(DEVICE(&s->gem[i]), nd);
+ }
+ object_property_set_bool(OBJECT(&s->gem[i]), true, "realized", &err);
+ if (err) {
+ error_propagate((errp), (err));
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gem[i]), 0, gem_addr[i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gem[i]), 0,
+ gic_spi[gem_intr[i]]);
+ }
+
+ for (i = 0; i < XLNX_ZYNQMP_NUM_UARTS; i++) {
+ object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err);
+ if (err) {
+ error_propagate((errp), (err));
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, uart_addr[i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0,
+ gic_spi[uart_intr[i]]);
+ }
+}
+
+static Property xlnx_zynqmp_props[] = {
+ DEFINE_PROP_STRING("boot-cpu", XlnxZynqMPState, boot_cpu),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static void xlnx_zynqmp_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->props = xlnx_zynqmp_props;
+ dc->realize = xlnx_zynqmp_realize;
+
+ /*
+ * Reason: creates an ARM CPU, thus use after free(), see
+ * arm_cpu_class_init()
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
+}
+
+static const TypeInfo xlnx_zynqmp_type_info = {
+ .name = TYPE_XLNX_ZYNQMP,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(XlnxZynqMPState),
+ .instance_init = xlnx_zynqmp_init,
+ .class_init = xlnx_zynqmp_class_init,
+};
+
+static void xlnx_zynqmp_register_types(void)
+{
+ type_register_static(&xlnx_zynqmp_type_info);
+}
+
+type_init(xlnx_zynqmp_register_types)
diff --git a/hw/audio/gus.c b/hw/audio/gus.c
index 4a43ce7ad..86223a954 100644
--- a/hw/audio/gus.c
+++ b/hw/audio/gus.c
@@ -71,13 +71,6 @@ IO_READ_PROTO (gus_readb)
return gus_read (&s->emu, nport, 1);
}
-IO_READ_PROTO (gus_readw)
-{
- GUSState *s = opaque;
-
- return gus_read (&s->emu, nport, 2);
-}
-
IO_WRITE_PROTO (gus_writeb)
{
GUSState *s = opaque;
@@ -85,13 +78,6 @@ IO_WRITE_PROTO (gus_writeb)
gus_write (&s->emu, nport, 1, val);
}
-IO_WRITE_PROTO (gus_writew)
-{
- GUSState *s = opaque;
-
- gus_write (&s->emu, nport, 2, val);
-}
-
static int write_audio (GUSState *s, int samples)
{
int net = 0;
@@ -236,17 +222,13 @@ static const VMStateDescription vmstate_gus = {
static const MemoryRegionPortio gus_portio_list1[] = {
{0x000, 1, 1, .write = gus_writeb },
- {0x000, 1, 2, .write = gus_writew },
{0x006, 10, 1, .read = gus_readb, .write = gus_writeb },
- {0x006, 10, 2, .read = gus_readw, .write = gus_writew },
{0x100, 8, 1, .read = gus_readb, .write = gus_writeb },
- {0x100, 8, 2, .read = gus_readw, .write = gus_writew },
PORTIO_END_OF_LIST (),
};
static const MemoryRegionPortio gus_portio_list2[] = {
- {0, 1, 1, .read = gus_readb },
- {0, 1, 2, .read = gus_readw },
+ {0, 2, 1, .read = gus_readb },
PORTIO_END_OF_LIST (),
};
diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c
index 444eb9e41..b052de5f7 100644
--- a/hw/audio/sb16.c
+++ b/hw/audio/sb16.c
@@ -1121,12 +1121,6 @@ static IO_WRITE_PROTO (mixer_write_datab)
s->mixer_regs[s->mixer_nreg] = val;
}
-static IO_WRITE_PROTO (mixer_write_indexw)
-{
- mixer_write_indexb (opaque, nport, val & 0xff);
- mixer_write_datab (opaque, nport, (val >> 8) & 0xff);
-}
-
static IO_READ_PROTO (mixer_read)
{
SB16State *s = opaque;
@@ -1345,7 +1339,6 @@ static const VMStateDescription vmstate_sb16 = {
static const MemoryRegionPortio sb16_ioport_list[] = {
{ 4, 1, 1, .write = mixer_write_indexb },
- { 4, 1, 2, .write = mixer_write_indexw },
{ 5, 1, 1, .read = mixer_read, .write = mixer_write_datab },
{ 6, 1, 1, .read = dsp_read, .write = dsp_write },
{ 10, 1, 1, .read = dsp_read },
diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c
index 3db139b8a..6106e4615 100644
--- a/hw/block/dataplane/virtio-blk.c
+++ b/hw/block/dataplane/virtio-blk.c
@@ -223,8 +223,8 @@ void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s)
virtio_blk_data_plane_stop(s);
blk_op_unblock_all(s->conf->conf.blk, s->blocker);
error_free(s->blocker);
- object_unref(OBJECT(s->iothread));
qemu_bh_delete(s->bh);
+ object_unref(OBJECT(s->iothread));
g_free(s);
}
diff --git a/hw/block/fdc.c b/hw/block/fdc.c
index a9de4ab17..5e1b67ee4 100644
--- a/hw/block/fdc.c
+++ b/hw/block/fdc.c
@@ -324,7 +324,7 @@ static void fd_revalidate(FDrive *drv)
/* Intel 82078 floppy disk controller emulation */
static void fdctrl_reset(FDCtrl *fdctrl, int do_irq);
-static void fdctrl_reset_fifo(FDCtrl *fdctrl);
+static void fdctrl_to_command_phase(FDCtrl *fdctrl);
static int fdctrl_transfer_handler (void *opaque, int nchan,
int dma_pos, int dma_len);
static void fdctrl_raise_irq(FDCtrl *fdctrl);
@@ -495,6 +495,33 @@ enum {
FD_DIR_DSKCHG = 0x80,
};
+/*
+ * See chapter 5.0 "Controller phases" of the spec:
+ *
+ * Command phase:
+ * The host writes a command and its parameters into the FIFO. The command
+ * phase is completed when all parameters for the command have been supplied,
+ * and execution phase is entered.
+ *
+ * Execution phase:
+ * Data transfers, either DMA or non-DMA. For non-DMA transfers, the FIFO
+ * contains the payload now, otherwise it's unused. When all bytes of the
+ * required data have been transferred, the state is switched to either result
+ * phase (if the command produces status bytes) or directly back into the
+ * command phase for the next command.
+ *
+ * Result phase:
+ * The host reads out the FIFO, which contains one or more result bytes now.
+ */
+enum {
+ /* Only for migration: reconstruct phase from registers like qemu 2.3 */
+ FD_PHASE_RECONSTRUCT = 0,
+
+ FD_PHASE_COMMAND = 1,
+ FD_PHASE_EXECUTION = 2,
+ FD_PHASE_RESULT = 3,
+};
+
#define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI)
#define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT)
@@ -504,6 +531,7 @@ struct FDCtrl {
/* Controller state */
QEMUTimer *result_timer;
int dma_chann;
+ uint8_t phase;
/* Controller's identification */
uint8_t version;
/* HW */
@@ -535,8 +563,6 @@ struct FDCtrl {
uint8_t pwrd;
/* Floppy drives */
uint8_t num_floppies;
- /* Sun4m quirks? */
- int sun4m;
FDrive drives[MAX_FD];
int reset_sensei;
uint32_t check_media_rate;
@@ -673,6 +699,7 @@ static const VMStateDescription vmstate_fdrive_media_changed = {
.name = "fdrive/media_changed",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = fdrive_media_changed_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8(media_changed, FDrive),
VMSTATE_END_OF_LIST()
@@ -690,6 +717,7 @@ static const VMStateDescription vmstate_fdrive_media_rate = {
.name = "fdrive/media_rate",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = fdrive_media_rate_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8(media_rate, FDrive),
VMSTATE_END_OF_LIST()
@@ -707,6 +735,7 @@ static const VMStateDescription vmstate_fdrive_perpendicular = {
.name = "fdrive/perpendicular",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = fdrive_perpendicular_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8(perpendicular, FDrive),
VMSTATE_END_OF_LIST()
@@ -730,22 +759,36 @@ static const VMStateDescription vmstate_fdrive = {
VMSTATE_UINT8(sect, FDrive),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_fdrive_media_changed,
- .needed = &fdrive_media_changed_needed,
- } , {
- .vmsd = &vmstate_fdrive_media_rate,
- .needed = &fdrive_media_rate_needed,
- } , {
- .vmsd = &vmstate_fdrive_perpendicular,
- .needed = &fdrive_perpendicular_needed,
- } , {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_fdrive_media_changed,
+ &vmstate_fdrive_media_rate,
+ &vmstate_fdrive_perpendicular,
+ NULL
}
};
+/*
+ * Reconstructs the phase from register values according to the logic that was
+ * implemented in qemu 2.3. This is the default value that is used if the phase
+ * subsection is not present on migration.
+ *
+ * Don't change this function to reflect newer qemu versions, it is part of
+ * the migration ABI.
+ */
+static int reconstruct_phase(FDCtrl *fdctrl)
+{
+ if (fdctrl->msr & FD_MSR_NONDMA) {
+ return FD_PHASE_EXECUTION;
+ } else if ((fdctrl->msr & FD_MSR_RQM) == 0) {
+ /* qemu 2.3 disabled RQM only during DMA transfers */
+ return FD_PHASE_EXECUTION;
+ } else if (fdctrl->msr & FD_MSR_DIO) {
+ return FD_PHASE_RESULT;
+ } else {
+ return FD_PHASE_COMMAND;
+ }
+}
+
static void fdc_pre_save(void *opaque)
{
FDCtrl *s = opaque;
@@ -753,12 +796,24 @@ static void fdc_pre_save(void *opaque)
s->dor_vmstate = s->dor | GET_CUR_DRV(s);
}
+static int fdc_pre_load(void *opaque)
+{
+ FDCtrl *s = opaque;
+ s->phase = FD_PHASE_RECONSTRUCT;
+ return 0;
+}
+
static int fdc_post_load(void *opaque, int version_id)
{
FDCtrl *s = opaque;
SET_CUR_DRV(s, s->dor_vmstate & FD_DOR_SELMASK);
s->dor = s->dor_vmstate & ~FD_DOR_SELMASK;
+
+ if (s->phase == FD_PHASE_RECONSTRUCT) {
+ s->phase = reconstruct_phase(s);
+ }
+
return 0;
}
@@ -773,6 +828,7 @@ static const VMStateDescription vmstate_fdc_reset_sensei = {
.name = "fdc/reset_sensei",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = fdc_reset_sensei_needed,
.fields = (VMStateField[]) {
VMSTATE_INT32(reset_sensei, FDCtrl),
VMSTATE_END_OF_LIST()
@@ -790,17 +846,37 @@ static const VMStateDescription vmstate_fdc_result_timer = {
.name = "fdc/result_timer",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = fdc_result_timer_needed,
.fields = (VMStateField[]) {
VMSTATE_TIMER_PTR(result_timer, FDCtrl),
VMSTATE_END_OF_LIST()
}
};
+static bool fdc_phase_needed(void *opaque)
+{
+ FDCtrl *fdctrl = opaque;
+
+ return reconstruct_phase(fdctrl) != fdctrl->phase;
+}
+
+static const VMStateDescription vmstate_fdc_phase = {
+ .name = "fdc/phase",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = fdc_phase_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(phase, FDCtrl),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_fdc = {
.name = "fdc",
.version_id = 2,
.minimum_version_id = 2,
.pre_save = fdc_pre_save,
+ .pre_load = fdc_pre_load,
.post_load = fdc_post_load,
.fields = (VMStateField[]) {
/* Controller State */
@@ -833,16 +909,11 @@ static const VMStateDescription vmstate_fdc = {
vmstate_fdrive, FDrive),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_fdc_reset_sensei,
- .needed = fdc_reset_sensei_needed,
- } , {
- .vmsd = &vmstate_fdc_result_timer,
- .needed = fdc_result_timer_needed,
- } , {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_fdc_reset_sensei,
+ &vmstate_fdc_result_timer,
+ &vmstate_fdc_phase,
+ NULL
}
};
@@ -885,13 +956,6 @@ static void fdctrl_reset_irq(FDCtrl *fdctrl)
static void fdctrl_raise_irq(FDCtrl *fdctrl)
{
- /* Sparc mutation */
- if (fdctrl->sun4m && (fdctrl->msr & FD_MSR_CMDBUSY)) {
- /* XXX: not sure */
- fdctrl->msr &= ~FD_MSR_CMDBUSY;
- fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO;
- return;
- }
if (!(fdctrl->sra & FD_SRA_INTPEND)) {
qemu_set_irq(fdctrl->irq, 1);
fdctrl->sra |= FD_SRA_INTPEND;
@@ -927,7 +991,7 @@ static void fdctrl_reset(FDCtrl *fdctrl, int do_irq)
fdctrl->data_dir = FD_DIR_WRITE;
for (i = 0; i < MAX_FD; i++)
fd_recalibrate(&fdctrl->drives[i]);
- fdctrl_reset_fifo(fdctrl);
+ fdctrl_to_command_phase(fdctrl);
if (do_irq) {
fdctrl->status0 |= FD_SR0_RDYCHG;
fdctrl_raise_irq(fdctrl);
@@ -1080,12 +1144,6 @@ static uint32_t fdctrl_read_main_status(FDCtrl *fdctrl)
fdctrl->dsr &= ~FD_DSR_PWRDOWN;
fdctrl->dor |= FD_DOR_nRESET;
- /* Sparc mutation */
- if (fdctrl->sun4m) {
- retval |= FD_MSR_DIO;
- fdctrl_reset_irq(fdctrl);
- };
-
FLOPPY_DPRINTF("main status register: 0x%02x\n", retval);
return retval;
@@ -1149,17 +1207,22 @@ static uint32_t fdctrl_read_dir(FDCtrl *fdctrl)
return retval;
}
-/* FIFO state control */
-static void fdctrl_reset_fifo(FDCtrl *fdctrl)
+/* Clear the FIFO and update the state for receiving the next command */
+static void fdctrl_to_command_phase(FDCtrl *fdctrl)
{
+ fdctrl->phase = FD_PHASE_COMMAND;
fdctrl->data_dir = FD_DIR_WRITE;
fdctrl->data_pos = 0;
+ fdctrl->data_len = 1; /* Accept command byte, adjust for params later */
fdctrl->msr &= ~(FD_MSR_CMDBUSY | FD_MSR_DIO);
+ fdctrl->msr |= FD_MSR_RQM;
}
-/* Set FIFO status for the host to read */
-static void fdctrl_set_fifo(FDCtrl *fdctrl, int fifo_len)
+/* Update the state to allow the guest to read out the command status.
+ * @fifo_len is the number of result bytes to be read out. */
+static void fdctrl_to_result_phase(FDCtrl *fdctrl, int fifo_len)
{
+ fdctrl->phase = FD_PHASE_RESULT;
fdctrl->data_dir = FD_DIR_READ;
fdctrl->data_len = fifo_len;
fdctrl->data_pos = 0;
@@ -1172,7 +1235,7 @@ static void fdctrl_unimplemented(FDCtrl *fdctrl, int direction)
qemu_log_mask(LOG_UNIMP, "fdc: unimplemented command 0x%02x\n",
fdctrl->fifo[0]);
fdctrl->fifo[0] = FD_SR0_INVCMD;
- fdctrl_set_fifo(fdctrl, 1);
+ fdctrl_to_result_phase(fdctrl, 1);
}
/* Seek to next sector
@@ -1253,7 +1316,7 @@ static void fdctrl_stop_transfer(FDCtrl *fdctrl, uint8_t status0,
fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO;
fdctrl->msr &= ~FD_MSR_NONDMA;
- fdctrl_set_fifo(fdctrl, 7);
+ fdctrl_to_result_phase(fdctrl, 7);
fdctrl_raise_irq(fdctrl);
}
@@ -1367,7 +1430,7 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction)
}
}
FLOPPY_DPRINTF("start non-DMA transfer\n");
- fdctrl->msr |= FD_MSR_NONDMA;
+ fdctrl->msr |= FD_MSR_NONDMA | FD_MSR_RQM;
if (direction != FD_DIR_WRITE)
fdctrl->msr |= FD_MSR_DIO;
/* IO based transfer: calculate len */
@@ -1520,9 +1583,16 @@ static uint32_t fdctrl_read_data(FDCtrl *fdctrl)
FLOPPY_DPRINTF("error: controller not ready for reading\n");
return 0;
}
+
+ /* If data_len spans multiple sectors, the current position in the FIFO
+ * wraps around while fdctrl->data_pos is the real position in the whole
+ * request. */
pos = fdctrl->data_pos;
pos %= FD_SECTOR_LEN;
- if (fdctrl->msr & FD_MSR_NONDMA) {
+
+ switch (fdctrl->phase) {
+ case FD_PHASE_EXECUTION:
+ assert(fdctrl->msr & FD_MSR_NONDMA);
if (pos == 0) {
if (fdctrl->data_pos != 0)
if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) {
@@ -1538,20 +1608,28 @@ static uint32_t fdctrl_read_data(FDCtrl *fdctrl)
memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
}
}
- }
- retval = fdctrl->fifo[pos];
- if (++fdctrl->data_pos == fdctrl->data_len) {
- fdctrl->data_pos = 0;
- /* Switch from transfer mode to status mode
- * then from status mode to command mode
- */
- if (fdctrl->msr & FD_MSR_NONDMA) {
+
+ if (++fdctrl->data_pos == fdctrl->data_len) {
+ fdctrl->msr &= ~FD_MSR_RQM;
fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
- } else {
- fdctrl_reset_fifo(fdctrl);
+ }
+ break;
+
+ case FD_PHASE_RESULT:
+ assert(!(fdctrl->msr & FD_MSR_NONDMA));
+ if (++fdctrl->data_pos == fdctrl->data_len) {
+ fdctrl->msr &= ~FD_MSR_RQM;
+ fdctrl_to_command_phase(fdctrl);
fdctrl_reset_irq(fdctrl);
}
+ break;
+
+ case FD_PHASE_COMMAND:
+ default:
+ abort();
}
+
+ retval = fdctrl->fifo[pos];
FLOPPY_DPRINTF("data register: 0x%02x\n", retval);
return retval;
@@ -1621,7 +1699,7 @@ static void fdctrl_handle_lock(FDCtrl *fdctrl, int direction)
{
fdctrl->lock = (fdctrl->fifo[0] & 0x80) ? 1 : 0;
fdctrl->fifo[0] = fdctrl->lock << 4;
- fdctrl_set_fifo(fdctrl, 1);
+ fdctrl_to_result_phase(fdctrl, 1);
}
static void fdctrl_handle_dumpreg(FDCtrl *fdctrl, int direction)
@@ -1646,20 +1724,20 @@ static void fdctrl_handle_dumpreg(FDCtrl *fdctrl, int direction)
(cur_drv->perpendicular << 2);
fdctrl->fifo[8] = fdctrl->config;
fdctrl->fifo[9] = fdctrl->precomp_trk;
- fdctrl_set_fifo(fdctrl, 10);
+ fdctrl_to_result_phase(fdctrl, 10);
}
static void fdctrl_handle_version(FDCtrl *fdctrl, int direction)
{
/* Controller's version */
fdctrl->fifo[0] = fdctrl->version;
- fdctrl_set_fifo(fdctrl, 1);
+ fdctrl_to_result_phase(fdctrl, 1);
}
static void fdctrl_handle_partid(FDCtrl *fdctrl, int direction)
{
fdctrl->fifo[0] = 0x41; /* Stepping 1 */
- fdctrl_set_fifo(fdctrl, 1);
+ fdctrl_to_result_phase(fdctrl, 1);
}
static void fdctrl_handle_restore(FDCtrl *fdctrl, int direction)
@@ -1682,7 +1760,7 @@ static void fdctrl_handle_restore(FDCtrl *fdctrl, int direction)
fdctrl->config = fdctrl->fifo[11];
fdctrl->precomp_trk = fdctrl->fifo[12];
fdctrl->pwrd = fdctrl->fifo[13];
- fdctrl_reset_fifo(fdctrl);
+ fdctrl_to_command_phase(fdctrl);
}
static void fdctrl_handle_save(FDCtrl *fdctrl, int direction)
@@ -1712,7 +1790,7 @@ static void fdctrl_handle_save(FDCtrl *fdctrl, int direction)
fdctrl->fifo[12] = fdctrl->pwrd;
fdctrl->fifo[13] = 0;
fdctrl->fifo[14] = 0;
- fdctrl_set_fifo(fdctrl, 15);
+ fdctrl_to_result_phase(fdctrl, 15);
}
static void fdctrl_handle_readid(FDCtrl *fdctrl, int direction)
@@ -1761,7 +1839,7 @@ static void fdctrl_handle_specify(FDCtrl *fdctrl, int direction)
else
fdctrl->dor |= FD_DOR_DMAEN;
/* No result back */
- fdctrl_reset_fifo(fdctrl);
+ fdctrl_to_command_phase(fdctrl);
}
static void fdctrl_handle_sense_drive_status(FDCtrl *fdctrl, int direction)
@@ -1777,7 +1855,7 @@ static void fdctrl_handle_sense_drive_status(FDCtrl *fdctrl, int direction)
(cur_drv->head << 2) |
GET_CUR_DRV(fdctrl) |
0x28;
- fdctrl_set_fifo(fdctrl, 1);
+ fdctrl_to_result_phase(fdctrl, 1);
}
static void fdctrl_handle_recalibrate(FDCtrl *fdctrl, int direction)
@@ -1787,7 +1865,7 @@ static void fdctrl_handle_recalibrate(FDCtrl *fdctrl, int direction)
SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
cur_drv = get_cur_drv(fdctrl);
fd_recalibrate(cur_drv);
- fdctrl_reset_fifo(fdctrl);
+ fdctrl_to_command_phase(fdctrl);
/* Raise Interrupt */
fdctrl->status0 |= FD_SR0_SEEK;
fdctrl_raise_irq(fdctrl);
@@ -1803,7 +1881,7 @@ static void fdctrl_handle_sense_interrupt_status(FDCtrl *fdctrl, int direction)
fdctrl->reset_sensei--;
} else if (!(fdctrl->sra & FD_SRA_INTPEND)) {
fdctrl->fifo[0] = FD_SR0_INVCMD;
- fdctrl_set_fifo(fdctrl, 1);
+ fdctrl_to_result_phase(fdctrl, 1);
return;
} else {
fdctrl->fifo[0] =
@@ -1812,7 +1890,7 @@ static void fdctrl_handle_sense_interrupt_status(FDCtrl *fdctrl, int direction)
}
fdctrl->fifo[1] = cur_drv->track;
- fdctrl_set_fifo(fdctrl, 2);
+ fdctrl_to_result_phase(fdctrl, 2);
fdctrl_reset_irq(fdctrl);
fdctrl->status0 = FD_SR0_RDYCHG;
}
@@ -1823,7 +1901,7 @@ static void fdctrl_handle_seek(FDCtrl *fdctrl, int direction)
SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
cur_drv = get_cur_drv(fdctrl);
- fdctrl_reset_fifo(fdctrl);
+ fdctrl_to_command_phase(fdctrl);
/* The seek command just sends step pulses to the drive and doesn't care if
* there is a medium inserted of if it's banging the head against the drive.
*/
@@ -1840,7 +1918,7 @@ static void fdctrl_handle_perpendicular_mode(FDCtrl *fdctrl, int direction)
if (fdctrl->fifo[1] & 0x80)
cur_drv->perpendicular = fdctrl->fifo[1] & 0x7;
/* No result back */
- fdctrl_reset_fifo(fdctrl);
+ fdctrl_to_command_phase(fdctrl);
}
static void fdctrl_handle_configure(FDCtrl *fdctrl, int direction)
@@ -1848,20 +1926,20 @@ static void fdctrl_handle_configure(FDCtrl *fdctrl, int direction)
fdctrl->config = fdctrl->fifo[2];
fdctrl->precomp_trk = fdctrl->fifo[3];
/* No result back */
- fdctrl_reset_fifo(fdctrl);
+ fdctrl_to_command_phase(fdctrl);
}
static void fdctrl_handle_powerdown_mode(FDCtrl *fdctrl, int direction)
{
fdctrl->pwrd = fdctrl->fifo[1];
fdctrl->fifo[0] = fdctrl->fifo[1];
- fdctrl_set_fifo(fdctrl, 1);
+ fdctrl_to_result_phase(fdctrl, 1);
}
static void fdctrl_handle_option(FDCtrl *fdctrl, int direction)
{
/* No result back */
- fdctrl_reset_fifo(fdctrl);
+ fdctrl_to_command_phase(fdctrl);
}
static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direction)
@@ -1877,15 +1955,15 @@ static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direct
fdctrl->fifo[0] = fdctrl->fifo[1];
fdctrl->fifo[2] = 0;
fdctrl->fifo[3] = 0;
- fdctrl_set_fifo(fdctrl, 4);
+ fdctrl_to_result_phase(fdctrl, 4);
} else {
- fdctrl_reset_fifo(fdctrl);
+ fdctrl_to_command_phase(fdctrl);
}
} else if (fdctrl->data_len > 7) {
/* ERROR */
fdctrl->fifo[0] = 0x80 |
(cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
- fdctrl_set_fifo(fdctrl, 1);
+ fdctrl_to_result_phase(fdctrl, 1);
}
}
@@ -1902,7 +1980,7 @@ static void fdctrl_handle_relative_seek_in(FDCtrl *fdctrl, int direction)
fd_seek(cur_drv, cur_drv->head,
cur_drv->track + fdctrl->fifo[2], cur_drv->sect, 1);
}
- fdctrl_reset_fifo(fdctrl);
+ fdctrl_to_command_phase(fdctrl);
/* Raise Interrupt */
fdctrl->status0 |= FD_SR0_SEEK;
fdctrl_raise_irq(fdctrl);
@@ -1920,20 +1998,25 @@ static void fdctrl_handle_relative_seek_out(FDCtrl *fdctrl, int direction)
fd_seek(cur_drv, cur_drv->head,
cur_drv->track - fdctrl->fifo[2], cur_drv->sect, 1);
}
- fdctrl_reset_fifo(fdctrl);
+ fdctrl_to_command_phase(fdctrl);
/* Raise Interrupt */
fdctrl->status0 |= FD_SR0_SEEK;
fdctrl_raise_irq(fdctrl);
}
-static const struct {
+/*
+ * Handlers for the execution phase of each command
+ */
+typedef struct FDCtrlCommand {
uint8_t value;
uint8_t mask;
const char* name;
int parameters;
void (*handler)(FDCtrl *fdctrl, int direction);
int direction;
-} handlers[] = {
+} FDCtrlCommand;
+
+static const FDCtrlCommand handlers[] = {
{ FD_CMD_READ, 0x1f, "READ", 8, fdctrl_start_transfer, FD_DIR_READ },
{ FD_CMD_WRITE, 0x3f, "WRITE", 8, fdctrl_start_transfer, FD_DIR_WRITE },
{ FD_CMD_SEEK, 0xff, "SEEK", 2, fdctrl_handle_seek },
@@ -1970,9 +2053,19 @@ static const struct {
/* Associate command to an index in the 'handlers' array */
static uint8_t command_to_handler[256];
+static const FDCtrlCommand *get_command(uint8_t cmd)
+{
+ int idx;
+
+ idx = command_to_handler[cmd];
+ FLOPPY_DPRINTF("%s command\n", handlers[idx].name);
+ return &handlers[idx];
+}
+
static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value)
{
FDrive *cur_drv;
+ const FDCtrlCommand *cmd;
uint32_t pos;
/* Reset mode */
@@ -1985,12 +2078,27 @@ static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value)
return;
}
fdctrl->dsr &= ~FD_DSR_PWRDOWN;
- /* Is it write command time ? */
- if (fdctrl->msr & FD_MSR_NONDMA) {
+
+ FLOPPY_DPRINTF("%s: %02x\n", __func__, value);
+
+ /* If data_len spans multiple sectors, the current position in the FIFO
+ * wraps around while fdctrl->data_pos is the real position in the whole
+ * request. */
+ pos = fdctrl->data_pos++;
+ pos %= FD_SECTOR_LEN;
+ fdctrl->fifo[pos] = value;
+
+ if (fdctrl->data_pos == fdctrl->data_len) {
+ fdctrl->msr &= ~FD_MSR_RQM;
+ }
+
+ switch (fdctrl->phase) {
+ case FD_PHASE_EXECUTION:
+ /* For DMA requests, RQM should be cleared during execution phase, so
+ * we would have errored out above. */
+ assert(fdctrl->msr & FD_MSR_NONDMA);
+
/* FIFO data write */
- pos = fdctrl->data_pos++;
- pos %= FD_SECTOR_LEN;
- fdctrl->fifo[pos] = value;
if (pos == FD_SECTOR_LEN - 1 ||
fdctrl->data_pos == fdctrl->data_len) {
cur_drv = get_cur_drv(fdctrl);
@@ -1998,45 +2106,54 @@ static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value)
< 0) {
FLOPPY_DPRINTF("error writing sector %d\n",
fd_sector(cur_drv));
- return;
+ break;
}
if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) {
FLOPPY_DPRINTF("error seeking to next sector %d\n",
fd_sector(cur_drv));
- return;
+ break;
}
}
- /* Switch from transfer mode to status mode
- * then from status mode to command mode
- */
- if (fdctrl->data_pos == fdctrl->data_len)
+
+ /* Switch to result phase when done with the transfer */
+ if (fdctrl->data_pos == fdctrl->data_len) {
fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
- return;
- }
- if (fdctrl->data_pos == 0) {
- /* Command */
- pos = command_to_handler[value & 0xff];
- FLOPPY_DPRINTF("%s command\n", handlers[pos].name);
- fdctrl->data_len = handlers[pos].parameters + 1;
- fdctrl->msr |= FD_MSR_CMDBUSY;
- }
+ }
+ break;
- FLOPPY_DPRINTF("%s: %02x\n", __func__, value);
- pos = fdctrl->data_pos++;
- pos %= FD_SECTOR_LEN;
- fdctrl->fifo[pos] = value;
- if (fdctrl->data_pos == fdctrl->data_len) {
- /* We now have all parameters
- * and will be able to treat the command
- */
- if (fdctrl->data_state & FD_STATE_FORMAT) {
- fdctrl_format_sector(fdctrl);
- return;
+ case FD_PHASE_COMMAND:
+ assert(!(fdctrl->msr & FD_MSR_NONDMA));
+ assert(fdctrl->data_pos < FD_SECTOR_LEN);
+
+ if (pos == 0) {
+ /* The first byte specifies the command. Now we start reading
+ * as many parameters as this command requires. */
+ cmd = get_command(value);
+ fdctrl->data_len = cmd->parameters + 1;
+ if (cmd->parameters) {
+ fdctrl->msr |= FD_MSR_RQM;
+ }
+ fdctrl->msr |= FD_MSR_CMDBUSY;
}
- pos = command_to_handler[fdctrl->fifo[0] & 0xff];
- FLOPPY_DPRINTF("treat %s command\n", handlers[pos].name);
- (*handlers[pos].handler)(fdctrl, handlers[pos].direction);
+ if (fdctrl->data_pos == fdctrl->data_len) {
+ /* We have all parameters now, execute the command */
+ fdctrl->phase = FD_PHASE_EXECUTION;
+
+ if (fdctrl->data_state & FD_STATE_FORMAT) {
+ fdctrl_format_sector(fdctrl);
+ break;
+ }
+
+ cmd = get_command(fdctrl->fifo[0]);
+ FLOPPY_DPRINTF("Calling handler for '%s'\n", cmd->name);
+ cmd->handler(fdctrl, cmd->direction);
+ }
+ break;
+
+ case FD_PHASE_RESULT:
+ default:
+ abort();
}
}
@@ -2246,8 +2363,6 @@ static void sun4m_fdc_initfn(Object *obj)
FDCtrlSysBus *sys = SYSBUS_FDC(obj);
FDCtrl *fdctrl = &sys->state;
- fdctrl->sun4m = 1;
-
memory_region_init_io(&fdctrl->iomem, obj, &fdctrl_mem_strict_ops,
fdctrl, "fdctrl", 0x08);
sysbus_init_mmio(sbd, &fdctrl->iomem);
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index afe243b81..efc43dde6 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -621,7 +621,6 @@ static int m25p80_init(SSISlave *ss)
s->size = s->pi->sector_size * s->pi->n_sectors;
s->dirty_page = -1;
- s->storage = blk_blockalign(s->blk, s->size);
/* FIXME use a qdev drive property instead of drive_get_next() */
dinfo = drive_get_next(IF_MTD);
@@ -629,6 +628,9 @@ static int m25p80_init(SSISlave *ss)
if (dinfo) {
DB_PRINT_L(0, "Binding to IF_MTD drive\n");
s->blk = blk_by_legacy_dinfo(dinfo);
+ blk_attach_dev_nofail(s->blk, s);
+
+ s->storage = blk_blockalign(s->blk, s->size);
/* FIXME: Move to late init */
if (blk_read(s->blk, 0, s->storage,
@@ -638,6 +640,7 @@ static int m25p80_init(SSISlave *ss)
}
} else {
DB_PRINT_L(0, "No BDRV - binding to RAM\n");
+ s->storage = blk_blockalign(NULL, s->size);
memset(s->storage, 0xFF, s->size);
}
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
index 1e071662d..40d488032 100644
--- a/hw/block/nvme.c
+++ b/hw/block/nvme.c
@@ -154,6 +154,7 @@ static uint16_t nvme_dma_read_prp(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
qemu_sglist_destroy(&qsg);
return NVME_INVALID_FIELD | NVME_DNR;
}
+ qemu_sglist_destroy(&qsg);
return NVME_SUCCESS;
}
@@ -206,11 +207,23 @@ static void nvme_rw_cb(void *opaque, int ret)
} else {
req->status = NVME_INTERNAL_DEV_ERROR;
}
-
- qemu_sglist_destroy(&req->qsg);
+ if (req->has_sg) {
+ qemu_sglist_destroy(&req->qsg);
+ }
nvme_enqueue_req_completion(cq, req);
}
+static uint16_t nvme_flush(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
+ NvmeRequest *req)
+{
+ req->has_sg = false;
+ block_acct_start(blk_get_stats(n->conf.blk), &req->acct, 0,
+ BLOCK_ACCT_FLUSH);
+ req->aiocb = blk_aio_flush(n->conf.blk, nvme_rw_cb, req);
+
+ return NVME_NO_COMPLETE;
+}
+
static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
NvmeRequest *req)
{
@@ -234,6 +247,7 @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
}
assert((nlb << data_shift) == req->qsg.size);
+ req->has_sg = true;
dma_acct_start(n->conf.blk, &req->acct, &req->qsg,
is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ);
req->aiocb = is_write ?
@@ -255,7 +269,7 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
ns = &n->namespaces[nsid - 1];
switch (cmd->opcode) {
case NVME_CMD_FLUSH:
- return NVME_SUCCESS;
+ return nvme_flush(n, ns, cmd, req);
case NVME_CMD_WRITE:
case NVME_CMD_READ:
return nvme_rw(n, ns, cmd, req);
@@ -473,23 +487,32 @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeCmd *cmd)
static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
{
uint32_t dw10 = le32_to_cpu(cmd->cdw10);
+ uint32_t result;
switch (dw10) {
+ case NVME_VOLATILE_WRITE_CACHE:
+ result = blk_enable_write_cache(n->conf.blk);
+ break;
case NVME_NUMBER_OF_QUEUES:
- req->cqe.result =
- cpu_to_le32((n->num_queues - 1) | ((n->num_queues - 1) << 16));
+ result = cpu_to_le32((n->num_queues - 1) | ((n->num_queues - 1) << 16));
break;
default:
return NVME_INVALID_FIELD | NVME_DNR;
}
+
+ req->cqe.result = result;
return NVME_SUCCESS;
}
static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
{
uint32_t dw10 = le32_to_cpu(cmd->cdw10);
+ uint32_t dw11 = le32_to_cpu(cmd->cdw11);
switch (dw10) {
+ case NVME_VOLATILE_WRITE_CACHE:
+ blk_set_enable_write_cache(n->conf.blk, dw11 & 1);
+ break;
case NVME_NUMBER_OF_QUEUES:
req->cqe.result =
cpu_to_le32((n->num_queues - 1) | ((n->num_queues - 1) << 16));
@@ -615,6 +638,13 @@ static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data,
n->bar.intmc = n->bar.intms;
break;
case 0x14:
+ /* Windows first sends data, then sends enable bit */
+ if (!NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc) &&
+ !NVME_CC_SHN(data) && !NVME_CC_SHN(n->bar.cc))
+ {
+ n->bar.cc = data;
+ }
+
if (NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc)) {
n->bar.cc = data;
if (nvme_start_ctrl(n)) {
@@ -807,6 +837,9 @@ static int nvme_init(PCIDevice *pci_dev)
id->psd[0].mp = cpu_to_le16(0x9c4);
id->psd[0].enlat = cpu_to_le32(0x10);
id->psd[0].exlat = cpu_to_le32(0x4);
+ if (blk_enable_write_cache(n->conf.blk)) {
+ id->vwc = 1;
+ }
n->bar.cap = 0;
NVME_CAP_SET_MQES(n->bar.cap, 0x7ff);
diff --git a/hw/block/nvme.h b/hw/block/nvme.h
index b6ccb655a..bf3a3ccac 100644
--- a/hw/block/nvme.h
+++ b/hw/block/nvme.h
@@ -638,6 +638,7 @@ typedef struct NvmeRequest {
struct NvmeSQueue *sq;
BlockAIOCB *aiocb;
uint16_t status;
+ bool has_sg;
NvmeCqe cqe;
BlockAcctCookie acct;
QEMUSGList qsg;
diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c
index d28269508..2ba6c7729 100644
--- a/hw/block/pflash_cfi01.c
+++ b/hw/block/pflash_cfi01.c
@@ -64,6 +64,9 @@ do { \
#define TYPE_CFI_PFLASH01 "cfi.pflash01"
#define CFI_PFLASH01(obj) OBJECT_CHECK(pflash_t, (obj), TYPE_CFI_PFLASH01)
+#define PFLASH_BE 0
+#define PFLASH_SECURE 1
+
struct pflash_t {
/*< private >*/
SysBusDevice parent_obj;
@@ -75,7 +78,7 @@ struct pflash_t {
uint8_t bank_width;
uint8_t device_width; /* If 0, device width not specified. */
uint8_t max_device_width; /* max device width in bytes */
- uint8_t be;
+ uint32_t features;
uint8_t wcycle; /* if 0, the flash is read normally */
int ro;
uint8_t cmd;
@@ -235,12 +238,57 @@ static uint32_t pflash_devid_query(pflash_t *pfl, hwaddr offset)
return resp;
}
+static uint32_t pflash_data_read(pflash_t *pfl, hwaddr offset,
+ int width, int be)
+{
+ uint8_t *p;
+ uint32_t ret;
+
+ p = pfl->storage;
+ switch (width) {
+ case 1:
+ ret = p[offset];
+ DPRINTF("%s: data offset " TARGET_FMT_plx " %02x\n",
+ __func__, offset, ret);
+ break;
+ case 2:
+ if (be) {
+ ret = p[offset] << 8;
+ ret |= p[offset + 1];
+ } else {
+ ret = p[offset];
+ ret |= p[offset + 1] << 8;
+ }
+ DPRINTF("%s: data offset " TARGET_FMT_plx " %04x\n",
+ __func__, offset, ret);
+ break;
+ case 4:
+ if (be) {
+ ret = p[offset] << 24;
+ ret |= p[offset + 1] << 16;
+ ret |= p[offset + 2] << 8;
+ ret |= p[offset + 3];
+ } else {
+ ret = p[offset];
+ ret |= p[offset + 1] << 8;
+ ret |= p[offset + 2] << 16;
+ ret |= p[offset + 3] << 24;
+ }
+ DPRINTF("%s: data offset " TARGET_FMT_plx " %08x\n",
+ __func__, offset, ret);
+ break;
+ default:
+ DPRINTF("BUG in %s\n", __func__);
+ abort();
+ }
+ return ret;
+}
+
static uint32_t pflash_read (pflash_t *pfl, hwaddr offset,
int width, int be)
{
hwaddr boff;
uint32_t ret;
- uint8_t *p;
ret = -1;
@@ -257,43 +305,7 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset,
/* fall through to read code */
case 0x00:
/* Flash area read */
- p = pfl->storage;
- switch (width) {
- case 1:
- ret = p[offset];
- DPRINTF("%s: data offset " TARGET_FMT_plx " %02x\n",
- __func__, offset, ret);
- break;
- case 2:
- if (be) {
- ret = p[offset] << 8;
- ret |= p[offset + 1];
- } else {
- ret = p[offset];
- ret |= p[offset + 1] << 8;
- }
- DPRINTF("%s: data offset " TARGET_FMT_plx " %04x\n",
- __func__, offset, ret);
- break;
- case 4:
- if (be) {
- ret = p[offset] << 24;
- ret |= p[offset + 1] << 16;
- ret |= p[offset + 2] << 8;
- ret |= p[offset + 3];
- } else {
- ret = p[offset];
- ret |= p[offset + 1] << 8;
- ret |= p[offset + 2] << 16;
- ret |= p[offset + 3] << 24;
- }
- DPRINTF("%s: data offset " TARGET_FMT_plx " %08x\n",
- __func__, offset, ret);
- break;
- default:
- DPRINTF("BUG in %s\n", __func__);
- }
-
+ ret = pflash_data_read(pfl, offset, width, be);
break;
case 0x10: /* Single byte program */
case 0x20: /* Block erase */
@@ -648,101 +660,37 @@ static void pflash_write(pflash_t *pfl, hwaddr offset,
}
-static uint32_t pflash_readb_be(void *opaque, hwaddr addr)
-{
- return pflash_read(opaque, addr, 1, 1);
-}
-
-static uint32_t pflash_readb_le(void *opaque, hwaddr addr)
-{
- return pflash_read(opaque, addr, 1, 0);
-}
-
-static uint32_t pflash_readw_be(void *opaque, hwaddr addr)
-{
- pflash_t *pfl = opaque;
-
- return pflash_read(pfl, addr, 2, 1);
-}
-
-static uint32_t pflash_readw_le(void *opaque, hwaddr addr)
-{
- pflash_t *pfl = opaque;
-
- return pflash_read(pfl, addr, 2, 0);
-}
-
-static uint32_t pflash_readl_be(void *opaque, hwaddr addr)
-{
- pflash_t *pfl = opaque;
-
- return pflash_read(pfl, addr, 4, 1);
-}
-
-static uint32_t pflash_readl_le(void *opaque, hwaddr addr)
+static MemTxResult pflash_mem_read_with_attrs(void *opaque, hwaddr addr, uint64_t *value,
+ unsigned len, MemTxAttrs attrs)
{
pflash_t *pfl = opaque;
+ bool be = !!(pfl->features & (1 << PFLASH_BE));
- return pflash_read(pfl, addr, 4, 0);
-}
-
-static void pflash_writeb_be(void *opaque, hwaddr addr,
- uint32_t value)
-{
- pflash_write(opaque, addr, value, 1, 1);
-}
-
-static void pflash_writeb_le(void *opaque, hwaddr addr,
- uint32_t value)
-{
- pflash_write(opaque, addr, value, 1, 0);
-}
-
-static void pflash_writew_be(void *opaque, hwaddr addr,
- uint32_t value)
-{
- pflash_t *pfl = opaque;
-
- pflash_write(pfl, addr, value, 2, 1);
-}
-
-static void pflash_writew_le(void *opaque, hwaddr addr,
- uint32_t value)
-{
- pflash_t *pfl = opaque;
-
- pflash_write(pfl, addr, value, 2, 0);
-}
-
-static void pflash_writel_be(void *opaque, hwaddr addr,
- uint32_t value)
-{
- pflash_t *pfl = opaque;
-
- pflash_write(pfl, addr, value, 4, 1);
+ if ((pfl->features & (1 << PFLASH_SECURE)) && !attrs.secure) {
+ *value = pflash_data_read(opaque, addr, len, be);
+ } else {
+ *value = pflash_read(opaque, addr, len, be);
+ }
+ return MEMTX_OK;
}
-static void pflash_writel_le(void *opaque, hwaddr addr,
- uint32_t value)
+static MemTxResult pflash_mem_write_with_attrs(void *opaque, hwaddr addr, uint64_t value,
+ unsigned len, MemTxAttrs attrs)
{
pflash_t *pfl = opaque;
+ bool be = !!(pfl->features & (1 << PFLASH_BE));
- pflash_write(pfl, addr, value, 4, 0);
+ if ((pfl->features & (1 << PFLASH_SECURE)) && !attrs.secure) {
+ return MEMTX_ERROR;
+ } else {
+ pflash_write(opaque, addr, value, len, be);
+ return MEMTX_OK;
+ }
}
-static const MemoryRegionOps pflash_cfi01_ops_be = {
- .old_mmio = {
- .read = { pflash_readb_be, pflash_readw_be, pflash_readl_be, },
- .write = { pflash_writeb_be, pflash_writew_be, pflash_writel_be, },
- },
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static const MemoryRegionOps pflash_cfi01_ops_le = {
- .old_mmio = {
- .read = { pflash_readb_le, pflash_readw_le, pflash_readl_le, },
- .write = { pflash_writeb_le, pflash_writew_le, pflash_writel_le, },
- },
+static const MemoryRegionOps pflash_cfi01_ops = {
+ .read_with_attrs = pflash_mem_read_with_attrs,
+ .write_with_attrs = pflash_mem_write_with_attrs,
.endianness = DEVICE_NATIVE_ENDIAN,
};
@@ -773,7 +721,8 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
memory_region_init_rom_device(
&pfl->mem, OBJECT(dev),
- pfl->be ? &pflash_cfi01_ops_be : &pflash_cfi01_ops_le, pfl,
+ &pflash_cfi01_ops,
+ pfl,
pfl->name, total_len, &local_err);
if (local_err) {
error_propagate(errp, local_err);
@@ -925,7 +874,8 @@ static Property pflash_cfi01_properties[] = {
DEFINE_PROP_UINT8("width", struct pflash_t, bank_width, 0),
DEFINE_PROP_UINT8("device-width", struct pflash_t, device_width, 0),
DEFINE_PROP_UINT8("max-device-width", struct pflash_t, max_device_width, 0),
- DEFINE_PROP_UINT8("big-endian", struct pflash_t, be, 0),
+ DEFINE_PROP_BIT("big-endian", struct pflash_t, features, PFLASH_BE, 0),
+ DEFINE_PROP_BIT("secure", struct pflash_t, features, PFLASH_SECURE, 0),
DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0),
DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0),
DEFINE_PROP_UINT16("id2", struct pflash_t, ident2, 0),
@@ -975,7 +925,7 @@ pflash_t *pflash_cfi01_register(hwaddr base,
qdev_prop_set_uint32(dev, "num-blocks", nb_blocs);
qdev_prop_set_uint64(dev, "sector-length", sector_len);
qdev_prop_set_uint8(dev, "width", bank_width);
- qdev_prop_set_uint8(dev, "big-endian", !!be);
+ qdev_prop_set_bit(dev, "big-endian", !!be);
qdev_prop_set_uint16(dev, "id0", id0);
qdev_prop_set_uint16(dev, "id1", id1);
qdev_prop_set_uint16(dev, "id2", id2);
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index 9546fd291..5625a9fa7 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -499,8 +499,7 @@ void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
iov_discard_front(&iov, &out_num, sizeof(req->out));
- if (in_num < 1 ||
- in_iov[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) {
+ if (in_iov[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) {
error_report("virtio-blk request inhdr too short");
exit(1);
}
@@ -515,7 +514,7 @@ void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
type = virtio_ldl_p(VIRTIO_DEVICE(req->dev), &req->out.type);
/* VIRTIO_BLK_T_OUT defines the command direction. VIRTIO_BLK_T_BARRIER
- * is an optional flag. Altough a guest should not send this flag if
+ * is an optional flag. Although a guest should not send this flag if
* not negotiated we ignored it in the past. So keep ignoring it. */
switch (type & ~(VIRTIO_BLK_T_OUT | VIRTIO_BLK_T_BARRIER)) {
case VIRTIO_BLK_T_IN:
@@ -651,16 +650,21 @@ static void virtio_blk_dma_restart_cb(void *opaque, int running,
static void virtio_blk_reset(VirtIODevice *vdev)
{
VirtIOBlock *s = VIRTIO_BLK(vdev);
-
- if (s->dataplane) {
- virtio_blk_data_plane_stop(s->dataplane);
- }
+ AioContext *ctx;
/*
* This should cancel pending requests, but can't do nicely until there
* are per-device request lists.
*/
- blk_drain_all();
+ ctx = blk_get_aio_context(s->blk);
+ aio_context_acquire(ctx);
+ blk_drain(s->blk);
+
+ if (s->dataplane) {
+ virtio_blk_data_plane_stop(s->dataplane);
+ }
+ aio_context_release(ctx);
+
blk_set_enable_write_cache(s->blk, s->original_wce);
}
@@ -718,7 +722,8 @@ static void virtio_blk_set_config(VirtIODevice *vdev, const uint8_t *config)
aio_context_release(blk_get_aio_context(s->blk));
}
-static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features)
+static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features,
+ Error **errp)
{
VirtIOBlock *s = VIRTIO_BLK(vdev);
@@ -726,7 +731,15 @@ static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features)
virtio_add_feature(&features, VIRTIO_BLK_F_GEOMETRY);
virtio_add_feature(&features, VIRTIO_BLK_F_TOPOLOGY);
virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE);
- virtio_add_feature(&features, VIRTIO_BLK_F_SCSI);
+ if (virtio_has_feature(features, VIRTIO_F_VERSION_1)) {
+ if (s->conf.scsi) {
+ error_setg(errp, "Please set scsi=off for virtio-blk devices in order to use virtio 1.0");
+ return 0;
+ }
+ } else {
+ virtio_clear_feature(&features, VIRTIO_F_ANY_LAYOUT);
+ virtio_add_feature(&features, VIRTIO_BLK_F_SCSI);
+ }
if (s->conf.config_wce) {
virtio_add_feature(&features, VIRTIO_BLK_F_CONFIG_WCE);
@@ -769,10 +782,11 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status)
*
* s->blk would erroneously be placed in writethrough mode.
*/
- if (!virtio_has_feature(vdev, VIRTIO_BLK_F_CONFIG_WCE)) {
+ if (!virtio_vdev_has_feature(vdev, VIRTIO_BLK_F_CONFIG_WCE)) {
aio_context_acquire(blk_get_aio_context(s->blk));
blk_set_enable_write_cache(s->blk,
- virtio_has_feature(vdev, VIRTIO_BLK_F_WCE));
+ virtio_vdev_has_feature(vdev,
+ VIRTIO_BLK_F_WCE));
aio_context_release(blk_get_aio_context(s->blk));
}
}
@@ -780,6 +794,11 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status)
static void virtio_blk_save(QEMUFile *f, void *opaque)
{
VirtIODevice *vdev = VIRTIO_DEVICE(opaque);
+ VirtIOBlock *s = VIRTIO_BLK(vdev);
+
+ if (s->dataplane) {
+ virtio_blk_data_plane_stop(s->dataplane);
+ }
virtio_save(vdev, f);
}
diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
index d1453782a..9d379e5b1 100644
--- a/hw/char/cadence_uart.c
+++ b/hw/char/cadence_uart.c
@@ -16,9 +16,7 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
-#include "hw/sysbus.h"
-#include "sysemu/char.h"
-#include "qemu/timer.h"
+#include "hw/char/cadence_uart.h"
#ifdef CADENCE_UART_ERR_DEBUG
#define DB_PRINT(...) do { \
@@ -85,8 +83,6 @@
#define LOCAL_LOOPBACK (0x2 << UART_MR_CHMODE_SH)
#define REMOTE_LOOPBACK (0x3 << UART_MR_CHMODE_SH)
-#define RX_FIFO_SIZE 16
-#define TX_FIFO_SIZE 16
#define UART_INPUT_CLK 50000000
#define R_CR (0x00/4)
@@ -108,38 +104,18 @@
#define R_PWID (0x40/4)
#define R_TTRIG (0x44/4)
-#define R_MAX (R_TTRIG + 1)
-
-#define TYPE_CADENCE_UART "cadence_uart"
-#define CADENCE_UART(obj) OBJECT_CHECK(UartState, (obj), TYPE_CADENCE_UART)
-
-typedef struct {
- /*< private >*/
- SysBusDevice parent_obj;
- /*< public >*/
-
- MemoryRegion iomem;
- uint32_t r[R_MAX];
- uint8_t rx_fifo[RX_FIFO_SIZE];
- uint8_t tx_fifo[TX_FIFO_SIZE];
- uint32_t rx_wpos;
- uint32_t rx_count;
- uint32_t tx_count;
- uint64_t char_tx_time;
- CharDriverState *chr;
- qemu_irq irq;
- QEMUTimer *fifo_trigger_handle;
-} UartState;
-
-static void uart_update_status(UartState *s)
+
+static void uart_update_status(CadenceUARTState *s)
{
s->r[R_SR] = 0;
- s->r[R_SR] |= s->rx_count == RX_FIFO_SIZE ? UART_SR_INTR_RFUL : 0;
+ s->r[R_SR] |= s->rx_count == CADENCE_UART_RX_FIFO_SIZE ? UART_SR_INTR_RFUL
+ : 0;
s->r[R_SR] |= !s->rx_count ? UART_SR_INTR_REMPTY : 0;
s->r[R_SR] |= s->rx_count >= s->r[R_RTRIG] ? UART_SR_INTR_RTRIG : 0;
- s->r[R_SR] |= s->tx_count == TX_FIFO_SIZE ? UART_SR_INTR_TFUL : 0;
+ s->r[R_SR] |= s->tx_count == CADENCE_UART_TX_FIFO_SIZE ? UART_SR_INTR_TFUL
+ : 0;
s->r[R_SR] |= !s->tx_count ? UART_SR_INTR_TEMPTY : 0;
s->r[R_SR] |= s->tx_count >= s->r[R_TTRIG] ? UART_SR_TTRIG : 0;
@@ -150,14 +126,14 @@ static void uart_update_status(UartState *s)
static void fifo_trigger_update(void *opaque)
{
- UartState *s = (UartState *)opaque;
+ CadenceUARTState *s = opaque;
s->r[R_CISR] |= UART_INTR_TIMEOUT;
uart_update_status(s);
}
-static void uart_rx_reset(UartState *s)
+static void uart_rx_reset(CadenceUARTState *s)
{
s->rx_wpos = 0;
s->rx_count = 0;
@@ -166,12 +142,12 @@ static void uart_rx_reset(UartState *s)
}
}
-static void uart_tx_reset(UartState *s)
+static void uart_tx_reset(CadenceUARTState *s)
{
s->tx_count = 0;
}
-static void uart_send_breaks(UartState *s)
+static void uart_send_breaks(CadenceUARTState *s)
{
int break_enabled = 1;
@@ -181,7 +157,7 @@ static void uart_send_breaks(UartState *s)
}
}
-static void uart_parameters_setup(UartState *s)
+static void uart_parameters_setup(CadenceUARTState *s)
{
QEMUSerialSetParams ssp;
unsigned int baud_rate, packet_size;
@@ -236,20 +212,20 @@ static void uart_parameters_setup(UartState *s)
static int uart_can_receive(void *opaque)
{
- UartState *s = (UartState *)opaque;
- int ret = MAX(RX_FIFO_SIZE, TX_FIFO_SIZE);
+ CadenceUARTState *s = opaque;
+ int ret = MAX(CADENCE_UART_RX_FIFO_SIZE, CADENCE_UART_TX_FIFO_SIZE);
uint32_t ch_mode = s->r[R_MR] & UART_MR_CHMODE;
if (ch_mode == NORMAL_MODE || ch_mode == ECHO_MODE) {
- ret = MIN(ret, RX_FIFO_SIZE - s->rx_count);
+ ret = MIN(ret, CADENCE_UART_RX_FIFO_SIZE - s->rx_count);
}
if (ch_mode == REMOTE_LOOPBACK || ch_mode == ECHO_MODE) {
- ret = MIN(ret, TX_FIFO_SIZE - s->tx_count);
+ ret = MIN(ret, CADENCE_UART_TX_FIFO_SIZE - s->tx_count);
}
return ret;
}
-static void uart_ctrl_update(UartState *s)
+static void uart_ctrl_update(CadenceUARTState *s)
{
if (s->r[R_CR] & UART_CR_TXRST) {
uart_tx_reset(s);
@@ -268,7 +244,7 @@ static void uart_ctrl_update(UartState *s)
static void uart_write_rx_fifo(void *opaque, const uint8_t *buf, int size)
{
- UartState *s = (UartState *)opaque;
+ CadenceUARTState *s = opaque;
uint64_t new_rx_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
int i;
@@ -276,12 +252,12 @@ static void uart_write_rx_fifo(void *opaque, const uint8_t *buf, int size)
return;
}
- if (s->rx_count == RX_FIFO_SIZE) {
+ if (s->rx_count == CADENCE_UART_RX_FIFO_SIZE) {
s->r[R_CISR] |= UART_INTR_ROVR;
} else {
for (i = 0; i < size; i++) {
s->rx_fifo[s->rx_wpos] = buf[i];
- s->rx_wpos = (s->rx_wpos + 1) % RX_FIFO_SIZE;
+ s->rx_wpos = (s->rx_wpos + 1) % CADENCE_UART_RX_FIFO_SIZE;
s->rx_count++;
}
timer_mod(s->fifo_trigger_handle, new_rx_time +
@@ -293,7 +269,7 @@ static void uart_write_rx_fifo(void *opaque, const uint8_t *buf, int size)
static gboolean cadence_uart_xmit(GIOChannel *chan, GIOCondition cond,
void *opaque)
{
- UartState *s = opaque;
+ CadenceUARTState *s = opaque;
int ret;
/* instant drain the fifo when there's no back-end */
@@ -320,14 +296,15 @@ static gboolean cadence_uart_xmit(GIOChannel *chan, GIOCondition cond,
return FALSE;
}
-static void uart_write_tx_fifo(UartState *s, const uint8_t *buf, int size)
+static void uart_write_tx_fifo(CadenceUARTState *s, const uint8_t *buf,
+ int size)
{
if ((s->r[R_CR] & UART_CR_TX_DIS) || !(s->r[R_CR] & UART_CR_TX_EN)) {
return;
}
- if (size > TX_FIFO_SIZE - s->tx_count) {
- size = TX_FIFO_SIZE - s->tx_count;
+ if (size > CADENCE_UART_TX_FIFO_SIZE - s->tx_count) {
+ size = CADENCE_UART_TX_FIFO_SIZE - s->tx_count;
/*
* This can only be a guest error via a bad tx fifo register push,
* as can_receive() should stop remote loop and echo modes ever getting
@@ -345,7 +322,7 @@ static void uart_write_tx_fifo(UartState *s, const uint8_t *buf, int size)
static void uart_receive(void *opaque, const uint8_t *buf, int size)
{
- UartState *s = (UartState *)opaque;
+ CadenceUARTState *s = opaque;
uint32_t ch_mode = s->r[R_MR] & UART_MR_CHMODE;
if (ch_mode == NORMAL_MODE || ch_mode == ECHO_MODE) {
@@ -358,7 +335,7 @@ static void uart_receive(void *opaque, const uint8_t *buf, int size)
static void uart_event(void *opaque, int event)
{
- UartState *s = (UartState *)opaque;
+ CadenceUARTState *s = opaque;
uint8_t buf = '\0';
if (event == CHR_EVENT_BREAK) {
@@ -368,15 +345,15 @@ static void uart_event(void *opaque, int event)
uart_update_status(s);
}
-static void uart_read_rx_fifo(UartState *s, uint32_t *c)
+static void uart_read_rx_fifo(CadenceUARTState *s, uint32_t *c)
{
if ((s->r[R_CR] & UART_CR_RX_DIS) || !(s->r[R_CR] & UART_CR_RX_EN)) {
return;
}
if (s->rx_count) {
- uint32_t rx_rpos =
- (RX_FIFO_SIZE + s->rx_wpos - s->rx_count) % RX_FIFO_SIZE;
+ uint32_t rx_rpos = (CADENCE_UART_RX_FIFO_SIZE + s->rx_wpos -
+ s->rx_count) % CADENCE_UART_RX_FIFO_SIZE;
*c = s->rx_fifo[rx_rpos];
s->rx_count--;
@@ -393,7 +370,7 @@ static void uart_read_rx_fifo(UartState *s, uint32_t *c)
static void uart_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
- UartState *s = (UartState *)opaque;
+ CadenceUARTState *s = opaque;
DB_PRINT(" offset:%x data:%08x\n", (unsigned)offset, (unsigned)value);
offset >>= 2;
@@ -437,11 +414,11 @@ static void uart_write(void *opaque, hwaddr offset,
static uint64_t uart_read(void *opaque, hwaddr offset,
unsigned size)
{
- UartState *s = (UartState *)opaque;
+ CadenceUARTState *s = opaque;
uint32_t c = 0;
offset >>= 2;
- if (offset >= R_MAX) {
+ if (offset >= CADENCE_UART_R_MAX) {
c = 0;
} else if (offset == R_TX_RX) {
uart_read_rx_fifo(s, &c);
@@ -461,7 +438,7 @@ static const MemoryRegionOps uart_ops = {
static void cadence_uart_reset(DeviceState *dev)
{
- UartState *s = CADENCE_UART(dev);
+ CadenceUARTState *s = CADENCE_UART(dev);
s->r[R_CR] = 0x00000128;
s->r[R_IMR] = 0;
@@ -478,7 +455,7 @@ static void cadence_uart_reset(DeviceState *dev)
static void cadence_uart_realize(DeviceState *dev, Error **errp)
{
- UartState *s = CADENCE_UART(dev);
+ CadenceUARTState *s = CADENCE_UART(dev);
s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL,
fifo_trigger_update, s);
@@ -495,7 +472,7 @@ static void cadence_uart_realize(DeviceState *dev, Error **errp)
static void cadence_uart_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
- UartState *s = CADENCE_UART(obj);
+ CadenceUARTState *s = CADENCE_UART(obj);
memory_region_init_io(&s->iomem, obj, &uart_ops, s, "uart", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
@@ -506,7 +483,7 @@ static void cadence_uart_init(Object *obj)
static int cadence_uart_post_load(void *opaque, int version_id)
{
- UartState *s = opaque;
+ CadenceUARTState *s = opaque;
uart_parameters_setup(s);
uart_update_status(s);
@@ -519,13 +496,15 @@ static const VMStateDescription vmstate_cadence_uart = {
.minimum_version_id = 2,
.post_load = cadence_uart_post_load,
.fields = (VMStateField[]) {
- VMSTATE_UINT32_ARRAY(r, UartState, R_MAX),
- VMSTATE_UINT8_ARRAY(rx_fifo, UartState, RX_FIFO_SIZE),
- VMSTATE_UINT8_ARRAY(tx_fifo, UartState, RX_FIFO_SIZE),
- VMSTATE_UINT32(rx_count, UartState),
- VMSTATE_UINT32(tx_count, UartState),
- VMSTATE_UINT32(rx_wpos, UartState),
- VMSTATE_TIMER_PTR(fifo_trigger_handle, UartState),
+ VMSTATE_UINT32_ARRAY(r, CadenceUARTState, CADENCE_UART_R_MAX),
+ VMSTATE_UINT8_ARRAY(rx_fifo, CadenceUARTState,
+ CADENCE_UART_RX_FIFO_SIZE),
+ VMSTATE_UINT8_ARRAY(tx_fifo, CadenceUARTState,
+ CADENCE_UART_TX_FIFO_SIZE),
+ VMSTATE_UINT32(rx_count, CadenceUARTState),
+ VMSTATE_UINT32(tx_count, CadenceUARTState),
+ VMSTATE_UINT32(rx_wpos, CadenceUARTState),
+ VMSTATE_TIMER_PTR(fifo_trigger_handle, CadenceUARTState),
VMSTATE_END_OF_LIST()
}
};
@@ -544,7 +523,7 @@ static void cadence_uart_class_init(ObjectClass *klass, void *data)
static const TypeInfo cadence_uart_info = {
.name = TYPE_CADENCE_UART,
.parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(UartState),
+ .instance_size = sizeof(CadenceUARTState),
.instance_init = cadence_uart_init,
.class_init = cadence_uart_class_init,
};
diff --git a/hw/char/parallel.c b/hw/char/parallel.c
index 4079554bb..c2b553f0d 100644
--- a/hw/char/parallel.c
+++ b/hw/char/parallel.c
@@ -641,28 +641,3 @@ static void parallel_register_types(void)
}
type_init(parallel_register_types)
-
-static void parallel_init(ISABus *bus, int index, CharDriverState *chr)
-{
- DeviceState *dev;
- ISADevice *isadev;
-
- isadev = isa_create(bus, "isa-parallel");
- dev = DEVICE(isadev);
- qdev_prop_set_uint32(dev, "index", index);
- qdev_prop_set_chr(dev, "chardev", chr);
- qdev_init_nofail(dev);
-}
-
-void parallel_hds_isa_init(ISABus *bus, int n)
-{
- int i;
-
- assert(n <= MAX_PARALLEL_PORTS);
-
- for (i = 0; i < n; i++) {
- if (parallel_hds[i]) {
- parallel_init(bus, i, parallel_hds[i]);
- }
- }
-}
diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c
index a9f5e62f2..02ac80b65 100644
--- a/hw/char/sclpconsole-lm.c
+++ b/hw/char/sclpconsole-lm.c
@@ -364,6 +364,7 @@ static void console_class_init(ObjectClass *klass, void *data)
ec->can_handle_event = can_handle_event;
ec->read_event_data = read_event_data;
ec->write_event_data = write_event_data;
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
}
static const TypeInfo sclp_console_info = {
diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c
index 79891dfc5..b014c7f52 100644
--- a/hw/char/sclpconsole.c
+++ b/hw/char/sclpconsole.c
@@ -266,6 +266,7 @@ static void console_class_init(ObjectClass *klass, void *data)
ec->can_handle_event = can_handle_event;
ec->read_event_data = read_event_data;
ec->write_event_data = write_event_data;
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
}
static const TypeInfo sclp_console_info = {
diff --git a/hw/char/serial-pci.c b/hw/char/serial-pci.c
index 467c3b4f9..1c8b9be5c 100644
--- a/hw/char/serial-pci.c
+++ b/hw/char/serial-pci.c
@@ -27,7 +27,6 @@
#include "hw/char/serial.h"
#include "hw/pci/pci.h"
-#include "qapi/qmp/qerror.h"
#define PCI_SERIAL_MAX_PORTS 4
@@ -48,6 +47,8 @@ typedef struct PCIMultiSerialState {
uint8_t prog_if;
} PCIMultiSerialState;
+static void multi_serial_pci_exit(PCIDevice *dev);
+
static void serial_pci_realize(PCIDevice *dev, Error **errp)
{
PCISerialState *pci = DO_UPCAST(PCISerialState, dev, dev);
@@ -89,32 +90,33 @@ static void multi_serial_pci_realize(PCIDevice *dev, Error **errp)
PCIMultiSerialState *pci = DO_UPCAST(PCIMultiSerialState, dev, dev);
SerialState *s;
Error *err = NULL;
- int i;
+ int i, nr_ports = 0;
switch (pc->device_id) {
case 0x0003:
- pci->ports = 2;
+ nr_ports = 2;
break;
case 0x0004:
- pci->ports = 4;
+ nr_ports = 4;
break;
}
- assert(pci->ports > 0);
- assert(pci->ports <= PCI_SERIAL_MAX_PORTS);
+ assert(nr_ports > 0);
+ assert(nr_ports <= PCI_SERIAL_MAX_PORTS);
pci->dev.config[PCI_CLASS_PROG] = pci->prog_if;
pci->dev.config[PCI_INTERRUPT_PIN] = 0x01;
- memory_region_init(&pci->iobar, OBJECT(pci), "multiserial", 8 * pci->ports);
+ memory_region_init(&pci->iobar, OBJECT(pci), "multiserial", 8 * nr_ports);
pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->iobar);
pci->irqs = qemu_allocate_irqs(multi_serial_irq_mux, pci,
- pci->ports);
+ nr_ports);
- for (i = 0; i < pci->ports; i++) {
+ for (i = 0; i < nr_ports; i++) {
s = pci->state + i;
s->baudbase = 115200;
serial_realize_core(s, &err);
if (err != NULL) {
error_propagate(errp, err);
+ multi_serial_pci_exit(dev);
return;
}
s->irq = pci->irqs[i];
@@ -122,6 +124,7 @@ static void multi_serial_pci_realize(PCIDevice *dev, Error **errp)
memory_region_init_io(&s->io, OBJECT(pci), &serial_io_ops, s,
pci->name[i], 8);
memory_region_add_subregion(&pci->iobar, 8 * i, &s->io);
+ pci->ports++;
}
}
diff --git a/hw/char/serial.c b/hw/char/serial.c
index 55011cfd2..513d73c27 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -662,6 +662,7 @@ static const VMStateDescription vmstate_serial_thr_ipending = {
.name = "serial/thr_ipending",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = serial_thr_ipending_needed,
.fields = (VMStateField[]) {
VMSTATE_INT32(thr_ipending, SerialState),
VMSTATE_END_OF_LIST()
@@ -678,6 +679,7 @@ static const VMStateDescription vmstate_serial_tsr = {
.name = "serial/tsr",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = serial_tsr_needed,
.fields = (VMStateField[]) {
VMSTATE_INT32(tsr_retry, SerialState),
VMSTATE_UINT8(thr, SerialState),
@@ -697,6 +699,7 @@ static const VMStateDescription vmstate_serial_recv_fifo = {
.name = "serial/recv_fifo",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = serial_recv_fifo_needed,
.fields = (VMStateField[]) {
VMSTATE_STRUCT(recv_fifo, SerialState, 1, vmstate_fifo8, Fifo8),
VMSTATE_END_OF_LIST()
@@ -713,6 +716,7 @@ static const VMStateDescription vmstate_serial_xmit_fifo = {
.name = "serial/xmit_fifo",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = serial_xmit_fifo_needed,
.fields = (VMStateField[]) {
VMSTATE_STRUCT(xmit_fifo, SerialState, 1, vmstate_fifo8, Fifo8),
VMSTATE_END_OF_LIST()
@@ -729,6 +733,7 @@ static const VMStateDescription vmstate_serial_fifo_timeout_timer = {
.name = "serial/fifo_timeout_timer",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = serial_fifo_timeout_timer_needed,
.fields = (VMStateField[]) {
VMSTATE_TIMER_PTR(fifo_timeout_timer, SerialState),
VMSTATE_END_OF_LIST()
@@ -745,6 +750,7 @@ static const VMStateDescription vmstate_serial_timeout_ipending = {
.name = "serial/timeout_ipending",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = serial_timeout_ipending_needed,
.fields = (VMStateField[]) {
VMSTATE_INT32(timeout_ipending, SerialState),
VMSTATE_END_OF_LIST()
@@ -760,6 +766,7 @@ static bool serial_poll_needed(void *opaque)
static const VMStateDescription vmstate_serial_poll = {
.name = "serial/poll",
.version_id = 1,
+ .needed = serial_poll_needed,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_INT32(poll_msl, SerialState),
@@ -788,31 +795,15 @@ const VMStateDescription vmstate_serial = {
VMSTATE_UINT8_V(fcr_vmstate, SerialState, 3),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_serial_thr_ipending,
- .needed = &serial_thr_ipending_needed,
- } , {
- .vmsd = &vmstate_serial_tsr,
- .needed = &serial_tsr_needed,
- } , {
- .vmsd = &vmstate_serial_recv_fifo,
- .needed = &serial_recv_fifo_needed,
- } , {
- .vmsd = &vmstate_serial_xmit_fifo,
- .needed = &serial_xmit_fifo_needed,
- } , {
- .vmsd = &vmstate_serial_fifo_timeout_timer,
- .needed = &serial_fifo_timeout_timer_needed,
- } , {
- .vmsd = &vmstate_serial_timeout_ipending,
- .needed = &serial_timeout_ipending_needed,
- } , {
- .vmsd = &vmstate_serial_poll,
- .needed = &serial_poll_needed,
- } , {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_serial_thr_ipending,
+ &vmstate_serial_tsr,
+ &vmstate_serial_recv_fifo,
+ &vmstate_serial_xmit_fifo,
+ &vmstate_serial_fifo_timeout_timer,
+ &vmstate_serial_timeout_ipending,
+ &vmstate_serial_poll,
+ NULL
}
};
diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c
index c7f824ed1..36b328b9a 100644
--- a/hw/char/spapr_vty.c
+++ b/hw/char/spapr_vty.c
@@ -74,7 +74,7 @@ static void spapr_vty_realize(VIOsPAPRDevice *sdev, Error **errp)
}
/* Forward declaration */
-static target_ulong h_put_term_char(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_put_term_char(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
target_ulong reg = args[0];
@@ -101,7 +101,7 @@ static target_ulong h_put_term_char(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return H_SUCCESS;
}
-static target_ulong h_get_term_char(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_get_term_char(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
target_ulong reg = args[0];
@@ -193,7 +193,7 @@ VIOsPAPRDevice *spapr_vty_get_default(VIOsPAPRBus *bus)
DeviceState *iter = kid->child;
/* Only look at VTY devices */
- if (!object_dynamic_cast(OBJECT(iter), "spapr-vty")) {
+ if (!object_dynamic_cast(OBJECT(iter), TYPE_VIO_SPAPR_VTY_DEVICE)) {
continue;
}
@@ -214,7 +214,7 @@ VIOsPAPRDevice *spapr_vty_get_default(VIOsPAPRBus *bus)
return selected;
}
-VIOsPAPRDevice *vty_lookup(sPAPREnvironment *spapr, target_ulong reg)
+VIOsPAPRDevice *vty_lookup(sPAPRMachineState *spapr, target_ulong reg)
{
VIOsPAPRDevice *sdev;
diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c
index 752ed2c3c..2a867cb4e 100644
--- a/hw/char/virtio-console.c
+++ b/hw/char/virtio-console.c
@@ -95,6 +95,15 @@ static void set_guest_connected(VirtIOSerialPort *port, int guest_connected)
}
}
+static void guest_writable(VirtIOSerialPort *port)
+{
+ VirtConsole *vcon = VIRTIO_CONSOLE(port);
+
+ if (vcon->chr) {
+ qemu_chr_accept_input(vcon->chr);
+ }
+}
+
/* Readiness of the guest to accept data on a port */
static int chr_can_read(void *opaque)
{
@@ -188,6 +197,7 @@ static void virtserialport_class_init(ObjectClass *klass, void *data)
k->unrealize = virtconsole_unrealize;
k->have_data = flush_buf;
k->set_guest_connected = set_guest_connected;
+ k->guest_writable = guest_writable;
dc->props = virtserialport_properties;
}
diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c
index e336bdb4a..be9705871 100644
--- a/hw/char/virtio-serial-bus.c
+++ b/hw/char/virtio-serial-bus.c
@@ -20,6 +20,7 @@
#include "qemu/iov.h"
#include "monitor/monitor.h"
+#include "qemu/error-report.h"
#include "qemu/queue.h"
#include "hw/sysbus.h"
#include "trace.h"
@@ -75,7 +76,7 @@ static VirtIOSerialPort *find_port_by_name(char *name)
static bool use_multiport(VirtIOSerial *vser)
{
VirtIODevice *vdev = VIRTIO_DEVICE(vser);
- return virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT);
+ return virtio_vdev_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT);
}
static size_t write_to_port(VirtIOSerialPort *port,
@@ -194,7 +195,8 @@ static size_t send_control_msg(VirtIOSerial *vser, void *buf, size_t len)
return 0;
}
- memcpy(elem.in_sg[0].iov_base, buf, len);
+ /* TODO: detect a buffer that's too short, set NEEDS_RESET */
+ iov_from_buf(elem.in_sg, elem.in_num, 0, buf, len);
virtqueue_push(vq, &elem, len);
virtio_notify(VIRTIO_DEVICE(vser), vq);
@@ -498,7 +500,8 @@ static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
}
}
-static uint32_t get_features(VirtIODevice *vdev, uint32_t features)
+static uint64_t get_features(VirtIODevice *vdev, uint64_t features,
+ Error **errp)
{
VirtIOSerial *vser;
@@ -814,12 +817,12 @@ static uint32_t find_free_port_id(VirtIOSerial *vser)
max_nr_ports = vser->serial.max_virtserial_ports;
for (i = 0; i < (max_nr_ports + 31) / 32; i++) {
- uint32_t map, bit;
+ uint32_t map, zeroes;
map = vser->ports_map[i];
- bit = ffs(~map);
- if (bit) {
- return (bit - 1) + i * 32;
+ zeroes = ctz32(~map);
+ if (zeroes != 32) {
+ return zeroes + i * 32;
}
}
return VIRTIO_CONSOLE_BAD_ID;
@@ -973,7 +976,7 @@ static void virtio_serial_device_realize(DeviceState *dev, Error **errp)
}
/* Each port takes 2 queues, and one pair is for the control queue */
- max_supported_ports = VIRTIO_PCI_QUEUE_MAX / 2 - 1;
+ max_supported_ports = VIRTIO_QUEUE_MAX / 2 - 1;
if (vser->serial.max_virtserial_ports > max_supported_ports) {
error_setg(errp, "maximum ports supported: %u", max_supported_ports);
@@ -1083,7 +1086,8 @@ static void virtio_serial_device_unrealize(DeviceState *dev, Error **errp)
}
static Property virtio_serial_properties[] = {
- DEFINE_VIRTIO_SERIAL_PROPERTIES(VirtIOSerial, serial),
+ DEFINE_PROP_UINT32("max_ports", VirtIOSerial, serial.max_virtserial_ports,
+ 31),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/core/loader.c b/hw/core/loader.c
index d4c441fd1..216eeeb91 100644
--- a/hw/core/loader.c
+++ b/hw/core/loader.c
@@ -835,12 +835,12 @@ err:
return -1;
}
-ram_addr_t rom_add_blob(const char *name, const void *blob, size_t len,
+MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len,
size_t max_len, hwaddr addr, const char *fw_file_name,
FWCfgReadCallback fw_callback, void *callback_opaque)
{
Rom *rom;
- ram_addr_t ret = RAM_ADDR_MAX;
+ MemoryRegion *mr = NULL;
rom = g_malloc0(sizeof(*rom));
rom->name = g_strdup(name);
@@ -858,7 +858,7 @@ ram_addr_t rom_add_blob(const char *name, const void *blob, size_t len,
if (rom_file_has_mr) {
data = rom_set_mr(rom, OBJECT(fw_cfg), devpath);
- ret = memory_region_get_ram_addr(rom->mr);
+ mr = rom->mr;
} else {
data = rom->data;
}
@@ -867,7 +867,7 @@ ram_addr_t rom_add_blob(const char *name, const void *blob, size_t len,
fw_callback, callback_opaque,
data, rom->datasize);
}
- return ret;
+ return mr;
}
/* This function is specific for elf program because we don't need to allocate
@@ -933,7 +933,7 @@ static void rom_reset(void *unused)
}
}
-int rom_load_all(void)
+int rom_check_and_register_reset(void)
{
hwaddr addr = 0;
MemoryRegionSection section;
@@ -957,12 +957,8 @@ int rom_load_all(void)
memory_region_unref(section.mr);
}
qemu_register_reset(rom_reset, NULL);
- return 0;
-}
-
-void rom_load_done(void)
-{
roms_loaded = 1;
+ return 0;
}
void rom_set_fw(FWCfgState *f)
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 25c45e6f9..ac4654e9d 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -294,6 +294,14 @@ static void machine_init_notify(Notifier *notifier, void *data)
foreach_dynamic_sysbus_device(error_on_sysbus_device, NULL);
}
+static void machine_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ /* Default 128 MB as guest ram size */
+ mc->default_ram_size = 128 * M_BYTE;
+}
+
static void machine_initfn(Object *obj)
{
MachineState *ms = MACHINE(obj);
@@ -463,6 +471,7 @@ static const TypeInfo machine_info = {
.parent = TYPE_OBJECT,
.abstract = true,
.class_size = sizeof(MachineClass),
+ .class_init = machine_class_init,
.instance_size = sizeof(MachineState),
.instance_init = machine_initfn,
.instance_finalize = machine_finalize,
diff --git a/hw/core/nmi.c b/hw/core/nmi.c
index 3dff02065..de1d1f8cb 100644
--- a/hw/core/nmi.c
+++ b/hw/core/nmi.c
@@ -21,6 +21,7 @@
#include "hw/nmi.h"
#include "qapi/qmp/qerror.h"
+#include "monitor/monitor.h"
struct do_nmi_s {
int cpu_index;
@@ -66,10 +67,29 @@ void nmi_monitor_handle(int cpu_index, Error **errp)
if (ns.handled) {
error_propagate(errp, ns.errp);
} else {
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
}
}
+void inject_nmi(void)
+{
+#if defined(TARGET_I386)
+ CPUState *cs;
+
+ CPU_FOREACH(cs) {
+ X86CPU *cpu = X86_CPU(cs);
+
+ if (!cpu->apic_state) {
+ cpu_interrupt(cs, CPU_INTERRUPT_NMI);
+ } else {
+ apic_deliver_nmi(cpu->apic_state);
+ }
+ }
+#else
+ nmi_monitor_handle(0, NULL);
+#endif
+}
+
static const TypeInfo nmi_info = {
.name = TYPE_NMI,
.parent = TYPE_INTERFACE,
diff --git a/hw/core/platform-bus.c b/hw/core/platform-bus.c
index 0f052b333..70e051890 100644
--- a/hw/core/platform-bus.c
+++ b/hw/core/platform-bus.c
@@ -20,7 +20,6 @@
*/
#include "hw/platform-bus.h"
-#include "monitor/monitor.h"
#include "exec/address-spaces.h"
#include "sysemu/sysemu.h"
diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c
index 2abad1fa3..8437bd6e8 100644
--- a/hw/core/ptimer.c
+++ b/hw/core/ptimer.c
@@ -189,7 +189,7 @@ void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload)
* on the current generation of host machines.
*/
- if (limit * s->period < 10000 && s->period) {
+ if (!use_icount && limit * s->period < 10000 && s->period) {
limit = 10000 / s->period;
}
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index c413226a9..921e799db 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -35,15 +35,15 @@ static void get_pointer(Object *obj, Visitor *v, Property *prop,
}
static void set_pointer(Object *obj, Visitor *v, Property *prop,
- int (*parse)(DeviceState *dev, const char *str,
- void **ptr),
+ void (*parse)(DeviceState *dev, const char *str,
+ void **ptr, const char *propname,
+ Error **errp),
const char *name, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Error *local_err = NULL;
void **ptr = qdev_get_prop_ptr(dev, prop);
char *str;
- int ret;
if (dev->realized) {
qdev_prop_set_after_realize(dev, name, errp);
@@ -60,26 +60,38 @@ static void set_pointer(Object *obj, Visitor *v, Property *prop,
*ptr = NULL;
return;
}
- ret = parse(dev, str, ptr);
- error_set_from_qdev_prop_error(errp, ret, dev, prop, str);
+ parse(dev, str, ptr, prop->name, errp);
g_free(str);
}
/* --- drive --- */
-static int parse_drive(DeviceState *dev, const char *str, void **ptr)
+static void parse_drive(DeviceState *dev, const char *str, void **ptr,
+ const char *propname, Error **errp)
{
BlockBackend *blk;
blk = blk_by_name(str);
if (!blk) {
- return -ENOENT;
+ error_setg(errp, "Property '%s.%s' can't find value '%s'",
+ object_get_typename(OBJECT(dev)), propname, str);
+ return;
}
if (blk_attach_dev(blk, dev) < 0) {
- return -EEXIST;
+ DriveInfo *dinfo = blk_legacy_dinfo(blk);
+
+ if (dinfo->type != IF_NONE) {
+ error_setg(errp, "Drive '%s' is already in use because "
+ "it has been automatically connected to another "
+ "device (did you need 'if=none' in the drive options?)",
+ str);
+ } else {
+ error_setg(errp, "Drive '%s' is already in use by another device",
+ str);
+ }
+ return;
}
*ptr = blk;
- return 0;
}
static void release_drive(Object *obj, const char *name, void *opaque)
@@ -121,17 +133,21 @@ PropertyInfo qdev_prop_drive = {
/* --- character device --- */
-static int parse_chr(DeviceState *dev, const char *str, void **ptr)
+static void parse_chr(DeviceState *dev, const char *str, void **ptr,
+ const char *propname, Error **errp)
{
CharDriverState *chr = qemu_chr_find(str);
if (chr == NULL) {
- return -ENOENT;
+ error_setg(errp, "Property '%s.%s' can't find value '%s'",
+ object_get_typename(OBJECT(dev)), propname, str);
+ return;
}
if (qemu_chr_fe_claim(chr) != 0) {
- return -EEXIST;
+ error_setg(errp, "Property '%s.%s' can't take value '%s', it's in use",
+ object_get_typename(OBJECT(dev)), propname, str);
+ return;
}
*ptr = chr;
- return 0;
}
static void release_chr(Object *obj, const char *name, void *opaque)
@@ -326,8 +342,8 @@ static void set_vlan(Object *obj, Visitor *v, void *opaque,
hubport = net_hub_port_find(id);
if (!hubport) {
- error_set(errp, QERR_INVALID_PARAMETER_VALUE,
- name, prop->info->name);
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
+ name, prop->info->name);
return;
}
*ptr = hubport;
@@ -389,7 +405,7 @@ void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd)
nd->instantiated = 1;
}
-static int qdev_add_one_global(QemuOpts *opts, void *opaque)
+static int qdev_add_one_global(void *opaque, QemuOpts *opts, Error **errp)
{
GlobalProperty *g;
@@ -404,5 +420,6 @@ static int qdev_add_one_global(QemuOpts *opts, void *opaque)
void qemu_add_globals(void)
{
- qemu_opts_foreach(qemu_find_opts("global"), qdev_add_one_global, NULL, 0);
+ qemu_opts_foreach(qemu_find_opts("global"),
+ qdev_add_one_global, NULL, NULL);
}
diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
index 570d5f0ba..04fd80a4d 100644
--- a/hw/core/qdev-properties.c
+++ b/hw/core/qdev-properties.c
@@ -1,6 +1,7 @@
#include "net/net.h"
#include "hw/qdev.h"
#include "qapi/qmp/qerror.h"
+#include "qemu/error-report.h"
#include "sysemu/block-backend.h"
#include "hw/block/block.h"
#include "net/hub.h"
@@ -125,6 +126,64 @@ PropertyInfo qdev_prop_bit = {
.set = prop_set_bit,
};
+/* Bit64 */
+
+static uint64_t qdev_get_prop_mask64(Property *prop)
+{
+ assert(prop->info == &qdev_prop_bit64);
+ return 0x1ull << prop->bitnr;
+}
+
+static void bit64_prop_set(DeviceState *dev, Property *props, bool val)
+{
+ uint64_t *p = qdev_get_prop_ptr(dev, props);
+ uint64_t mask = qdev_get_prop_mask64(props);
+ if (val) {
+ *p |= mask;
+ } else {
+ *p &= ~mask;
+ }
+}
+
+static void prop_get_bit64(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ uint64_t *p = qdev_get_prop_ptr(dev, prop);
+ bool value = (*p & qdev_get_prop_mask64(prop)) != 0;
+
+ visit_type_bool(v, &value, name, errp);
+}
+
+static void prop_set_bit64(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ Property *prop = opaque;
+ Error *local_err = NULL;
+ bool value;
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+
+ visit_type_bool(v, &value, name, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ bit64_prop_set(dev, prop, value);
+}
+
+PropertyInfo qdev_prop_bit64 = {
+ .name = "bool",
+ .description = "on/off",
+ .get = prop_get_bit64,
+ .set = prop_set_bit64,
+};
+
/* --- bool --- */
static void get_bool(Object *obj, Visitor *v, void *opaque,
@@ -512,8 +571,8 @@ static void set_pci_devfn(Object *obj, Visitor *v, void *opaque,
if (local_err) {
error_propagate(errp, local_err);
} else if (value < -1 || value > 255) {
- error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
- "pci_devfn");
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
+ name ? name : "null", "pci_devfn");
} else {
*ptr = value;
}
@@ -582,8 +641,8 @@ static void set_blocksize(Object *obj, Visitor *v, void *opaque,
}
/* value of 0 means "unset" */
if (value && (value < min || value > max)) {
- error_set(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE,
- dev->id?:"", name, (int64_t)value, min, max);
+ error_setg(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE,
+ dev->id ? : "", name, (int64_t)value, min, max);
return;
}
@@ -865,8 +924,8 @@ void error_set_from_qdev_prop_error(Error **errp, int ret, DeviceState *dev,
break;
default:
case -EINVAL:
- error_set(errp, QERR_PROPERTY_VALUE_BAD,
- object_get_typename(OBJECT(dev)), prop->name, value);
+ error_setg(errp, QERR_PROPERTY_VALUE_BAD,
+ object_get_typename(OBJECT(dev)), prop->name, value);
break;
case -ENOENT:
error_setg(errp, "Property '%s.%s' can't find value '%s'",
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index 6e6a65d49..b2f404a76 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -32,6 +32,7 @@
#include "qapi/qmp/qerror.h"
#include "qapi/visitor.h"
#include "qapi/qmp/qjson.h"
+#include "qemu/error-report.h"
#include "hw/hotplug.h"
#include "hw/boards.h"
#include "qapi-event.h"
@@ -126,9 +127,9 @@ void qbus_set_bus_hotplug_handler(BusState *bus, Error **errp)
qbus_set_hotplug_handler_internal(bus, OBJECT(bus), errp);
}
-/* Create a new device. This only initializes the device state structure
- and allows properties to be set. qdev_init should be called to
- initialize the actual device emulation. */
+/* Create a new device. This only initializes the device state
+ structure and allows properties to be set. The device still needs
+ to be realized. See qdev-core.h. */
DeviceState *qdev_create(BusState *bus, const char *name)
{
DeviceState *dev;
@@ -168,27 +169,6 @@ DeviceState *qdev_try_create(BusState *bus, const char *type)
return dev;
}
-/* Initialize a device. Device properties should be set before calling
- this function. IRQs and MMIO regions should be connected/mapped after
- calling this function.
- On failure, destroy the device and return negative value.
- Return 0 on success. */
-int qdev_init(DeviceState *dev)
-{
- Error *local_err = NULL;
-
- assert(!dev->realized);
-
- object_property_set_bool(OBJECT(dev), true, "realized", &local_err);
- if (local_err != NULL) {
- qerror_report_err(local_err);
- error_free(local_err);
- object_unparent(OBJECT(dev));
- return -1;
- }
- return 0;
-}
-
static QTAILQ_HEAD(device_listeners, DeviceListener) device_listeners
= QTAILQ_HEAD_INITIALIZER(device_listeners);
@@ -273,7 +253,7 @@ void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id,
dev->alias_required_for_version = required_for_version;
}
-static HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev)
+HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev)
{
HotplugHandler *hotplug_ctrl = NULL;
@@ -297,13 +277,13 @@ void qdev_unplug(DeviceState *dev, Error **errp)
HotplugHandlerClass *hdc;
if (dev->parent_bus && !qbus_is_hotpluggable(dev->parent_bus)) {
- error_set(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name);
+ error_setg(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name);
return;
}
if (!dc->hotpluggable) {
- error_set(errp, QERR_DEVICE_NO_HOTPLUG,
- object_get_typename(OBJECT(dev)));
+ error_setg(errp, QERR_DEVICE_NO_HOTPLUG,
+ object_get_typename(OBJECT(dev)));
return;
}
@@ -364,13 +344,19 @@ void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev,
object_unparent(OBJECT(dev));
}
-/* Like qdev_init(), but terminate program via error_report() instead of
- returning an error value. This is okay during machine creation.
- Don't use for hotplug, because there callers need to recover from
- failure. Exception: if you know the device's init() callback can't
- fail, then qdev_init_nofail() can't fail either, and is therefore
- usable even then. But relying on the device implementation that
- way is somewhat unclean, and best avoided. */
+/*
+ * Realize @dev.
+ * Device properties should be set before calling this function. IRQs
+ * and MMIO regions should be connected/mapped after calling this
+ * function.
+ * On failure, report an error with error_report() and terminate the
+ * program. This is okay during machine creation. Don't use for
+ * hotplug, because there callers need to recover from failure.
+ * Exception: if you know the device's init() callback can't fail,
+ * then qdev_init_nofail() can't fail either, and is therefore usable
+ * even then. But relying on the device implementation that way is
+ * somewhat unclean, and best avoided.
+ */
void qdev_init_nofail(DeviceState *dev)
{
Error *err = NULL;
@@ -563,6 +549,7 @@ void qdev_pass_gpios(DeviceState *dev, DeviceState *container,
object_property_add_alias(OBJECT(container), propname,
OBJECT(dev), propname,
&error_abort);
+ g_free(propname);
}
for (i = 0; i < ngl->num_out; i++) {
const char *nm = ngl->name ? ngl->name : "unnamed-gpio-out";
@@ -571,6 +558,7 @@ void qdev_pass_gpios(DeviceState *dev, DeviceState *container,
object_property_add_alias(OBJECT(container), propname,
OBJECT(dev), propname,
&error_abort);
+ g_free(propname);
}
QLIST_REMOVE(ngl, node);
QLIST_INSERT_HEAD(&container->gpios, ngl, node);
@@ -1039,7 +1027,7 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
Error *local_err = NULL;
if (dev->hotplugged && !dc->hotpluggable) {
- error_set(errp, QERR_DEVICE_NO_HOTPLUG, object_get_typename(obj));
+ error_setg(errp, QERR_DEVICE_NO_HOTPLUG, object_get_typename(obj));
return;
}
diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c
index 92eced942..3c5862989 100644
--- a/hw/core/sysbus.c
+++ b/hw/core/sysbus.c
@@ -109,7 +109,13 @@ qemu_irq sysbus_get_connected_irq(SysBusDevice *dev, int n)
void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq)
{
+ SysBusDeviceClass *sbd = SYS_BUS_DEVICE_GET_CLASS(dev);
+
qdev_connect_gpio_out_named(DEVICE(dev), SYSBUS_DEVICE_GPIO_IRQ, n, irq);
+
+ if (sbd->connect_irq_notifier) {
+ sbd->connect_irq_notifier(dev, irq);
+ }
}
/* Check whether an MMIO region exists */
@@ -281,6 +287,9 @@ static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent)
static char *sysbus_get_fw_dev_path(DeviceState *dev)
{
SysBusDevice *s = SYS_BUS_DEVICE(dev);
+ SysBusDeviceClass *sbc = SYS_BUS_DEVICE_GET_CLASS(s);
+ /* for the explicit unit address fallback case: */
+ char *addr, *fw_dev_path;
if (s->num_mmio) {
return g_strdup_printf("%s@" TARGET_FMT_plx, qdev_fw_name(dev),
@@ -289,6 +298,14 @@ static char *sysbus_get_fw_dev_path(DeviceState *dev)
if (s->num_pio) {
return g_strdup_printf("%s@i%04x", qdev_fw_name(dev), s->pio[0]);
}
+ if (sbc->explicit_ofw_unit_address) {
+ addr = sbc->explicit_ofw_unit_address(s);
+ if (addr) {
+ fw_dev_path = g_strdup_printf("%s@%s", qdev_fw_name(dev), addr);
+ g_free(addr);
+ return fw_dev_path;
+ }
+ }
return g_strdup(qdev_fw_name(dev));
}
diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs
index e73cb7d8e..dd8ea76d1 100644
--- a/hw/display/Makefile.objs
+++ b/hw/display/Makefile.objs
@@ -21,7 +21,7 @@ common-obj-$(CONFIG_ZAURUS) += tc6393xb.o
ifeq ($(CONFIG_MILKYMIST_TMU2),y)
common-obj-y += milkymist-tmu2.o
milkymist-tmu2.o-cflags := $(OPENGL_CFLAGS)
-libs_softmmu += $(OPENGL_LIBS)
+milkymist-tmu2.o-libs += $(OPENGL_LIBS)
endif
obj-$(CONFIG_OMAP) += omap_dss.o
@@ -34,3 +34,7 @@ obj-$(CONFIG_CG3) += cg3.o
obj-$(CONFIG_VGA) += vga.o
common-obj-$(CONFIG_QXL) += qxl.o qxl-logger.o qxl-render.o
+
+obj-$(CONFIG_VIRTIO) += virtio-gpu.o
+obj-$(CONFIG_VIRTIO_PCI) += virtio-gpu-pci.o
+obj-$(CONFIG_VIRTIO_VGA) += virtio-vga.o
diff --git a/hw/display/cg3.c b/hw/display/cg3.c
index 1e6ff2b54..2d3bd7081 100644
--- a/hw/display/cg3.c
+++ b/hw/display/cg3.c
@@ -106,6 +106,7 @@ static void cg3_update_display(void *opaque)
pix = memory_region_get_ram_ptr(&s->vram_mem);
data = (uint32_t *)surface_data(surface);
+ memory_region_sync_dirty_bitmap(&s->vram_mem);
for (y = 0; y < height; y++) {
int update = s->full_update;
@@ -279,12 +280,12 @@ static void cg3_initfn(Object *obj)
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
CG3State *s = CG3(obj);
- memory_region_init_ram(&s->rom, NULL, "cg3.prom", FCODE_MAX_ROM_SIZE,
+ memory_region_init_ram(&s->rom, obj, "cg3.prom", FCODE_MAX_ROM_SIZE,
&error_abort);
memory_region_set_readonly(&s->rom, true);
sysbus_init_mmio(sbd, &s->rom);
- memory_region_init_io(&s->reg, NULL, &cg3_reg_ops, s, "cg3.reg",
+ memory_region_init_io(&s->reg, obj, &cg3_reg_ops, s, "cg3.reg",
CG3_REG_SIZE);
sysbus_init_mmio(sbd, &s->reg);
}
@@ -302,6 +303,7 @@ static void cg3_realizefn(DeviceState *dev, Error **errp)
if (fcode_filename) {
ret = load_image_targphys(fcode_filename, s->prom_addr,
FCODE_MAX_ROM_SIZE);
+ g_free(fcode_filename);
if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) {
error_report("cg3: could not load prom '%s'", CG3_ROM_FILE);
}
@@ -309,6 +311,7 @@ static void cg3_realizefn(DeviceState *dev, Error **errp)
memory_region_init_ram(&s->vram_mem, NULL, "cg3.vram", s->vram_size,
&error_abort);
+ memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA);
vmstate_register_ram_global(&s->vram_mem);
sysbus_init_mmio(sbd, &s->vram_mem);
diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c
index 8765a7e1d..5198037d8 100644
--- a/hw/display/cirrus_vga.c
+++ b/hw/display/cirrus_vga.c
@@ -235,6 +235,10 @@ typedef struct PCICirrusVGAState {
CirrusVGAState cirrus_vga;
} PCICirrusVGAState;
+#define TYPE_PCI_CIRRUS_VGA "cirrus-vga"
+#define PCI_CIRRUS_VGA(obj) \
+ OBJECT_CHECK(PCICirrusVGAState, (obj), TYPE_PCI_CIRRUS_VGA)
+
#define TYPE_ISA_CIRRUS_VGA "isa-cirrus-vga"
#define ISA_CIRRUS_VGA(obj) \
OBJECT_CHECK(ISACirrusVGAState, (obj), TYPE_ISA_CIRRUS_VGA)
@@ -3008,7 +3012,7 @@ static const TypeInfo isa_cirrus_vga_info = {
static void pci_cirrus_vga_realize(PCIDevice *dev, Error **errp)
{
- PCICirrusVGAState *d = DO_UPCAST(PCICirrusVGAState, dev, dev);
+ PCICirrusVGAState *d = PCI_CIRRUS_VGA(dev);
CirrusVGAState *s = &d->cirrus_vga;
PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
int16_t device_id = pc->device_id;
@@ -3070,7 +3074,7 @@ static void cirrus_vga_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo cirrus_vga_info = {
- .name = "cirrus-vga",
+ .name = TYPE_PCI_CIRRUS_VGA,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(PCICirrusVGAState),
.class_init = cirrus_vga_class_init,
diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c
index 45c62afac..603ef5056 100644
--- a/hw/display/exynos4210_fimd.c
+++ b/hw/display/exynos4210_fimd.c
@@ -337,7 +337,7 @@ static inline void fimd_swap_data(unsigned int swap_ctl, uint64_t *data)
if (swap_ctl & FIMD_WINCON_SWAP_BITS) {
res = 0;
for (i = 0; i < 64; i++) {
- if (x & (1ULL << (64 - i))) {
+ if (x & (1ULL << (63 - i))) {
res |= (1ULL << i);
}
}
@@ -1109,6 +1109,12 @@ static inline int fimd_get_buffer_id(Exynos4210fimdWindow *w)
}
}
+static void exynos4210_fimd_invalidate(void *opaque)
+{
+ Exynos4210fimdState *s = (Exynos4210fimdState *)opaque;
+ s->invalidate = true;
+}
+
/* Updates specified window's MemorySection based on values of WINCON,
* VIDOSDA, VIDOSDB, VIDWADDx and SHADOWCON registers */
static void fimd_update_memory_section(Exynos4210fimdState *s, unsigned win)
@@ -1136,7 +1142,11 @@ static void fimd_update_memory_section(Exynos4210fimdState *s, unsigned win)
/* TODO: add .exit and unref the region there. Not needed yet since sysbus
* does not support hot-unplug.
*/
- memory_region_unref(w->mem_section.mr);
+ if (w->mem_section.mr) {
+ memory_region_set_log(w->mem_section.mr, false, DIRTY_MEMORY_VGA);
+ memory_region_unref(w->mem_section.mr);
+ }
+
w->mem_section = memory_region_find(sysbus_address_space(sbd),
fb_start_addr, w->fb_len);
assert(w->mem_section.mr);
@@ -1162,6 +1172,8 @@ static void fimd_update_memory_section(Exynos4210fimdState *s, unsigned win)
cpu_physical_memory_unmap(w->host_fb_addr, fb_mapped_len, 0, 0);
goto error_return;
}
+ memory_region_set_log(w->mem_section.mr, true, DIRTY_MEMORY_VGA);
+ exynos4210_fimd_invalidate(s);
return;
error_return:
@@ -1224,12 +1236,6 @@ static void exynos4210_fimd_update_irq(Exynos4210fimdState *s)
}
}
-static void exynos4210_fimd_invalidate(void *opaque)
-{
- Exynos4210fimdState *s = (Exynos4210fimdState *)opaque;
- s->invalidate = true;
-}
-
static void exynos4210_update_resolution(Exynos4210fimdState *s)
{
DisplaySurface *surface = qemu_console_surface(s->console);
diff --git a/hw/display/framebuffer.c b/hw/display/framebuffer.c
index 4546e4265..7f075ce77 100644
--- a/hw/display/framebuffer.c
+++ b/hw/display/framebuffer.c
@@ -21,12 +21,40 @@
#include "ui/console.h"
#include "framebuffer.h"
+void framebuffer_update_memory_section(
+ MemoryRegionSection *mem_section,
+ MemoryRegion *root,
+ hwaddr base,
+ unsigned rows,
+ unsigned src_width)
+{
+ hwaddr src_len = (hwaddr)rows * src_width;
+
+ if (mem_section->mr) {
+ memory_region_set_log(mem_section->mr, false, DIRTY_MEMORY_VGA);
+ memory_region_unref(mem_section->mr);
+ mem_section->mr = NULL;
+ }
+
+ *mem_section = memory_region_find(root, base, src_len);
+ if (!mem_section->mr) {
+ return;
+ }
+
+ if (int128_get64(mem_section->size) < src_len ||
+ !memory_region_is_ram(mem_section->mr)) {
+ memory_region_unref(mem_section->mr);
+ mem_section->mr = NULL;
+ return;
+ }
+
+ memory_region_set_log(mem_section->mr, true, DIRTY_MEMORY_VGA);
+}
+
/* Render an image from a shared memory framebuffer. */
-
void framebuffer_update_display(
DisplaySurface *ds,
- MemoryRegion *address_space,
- hwaddr base,
+ MemoryRegionSection *mem_section,
int cols, /* Width in pixels. */
int rows, /* Height in pixels. */
int src_width, /* Length of source line, in bytes. */
@@ -41,47 +69,33 @@ void framebuffer_update_display(
hwaddr src_len;
uint8_t *dest;
uint8_t *src;
- uint8_t *src_base;
int first, last = 0;
int dirty;
int i;
ram_addr_t addr;
- MemoryRegionSection mem_section;
MemoryRegion *mem;
i = *first_row;
*first_row = -1;
src_len = src_width * rows;
- mem_section = memory_region_find(address_space, base, src_len);
- mem = mem_section.mr;
- if (int128_get64(mem_section.size) != src_len ||
- !memory_region_is_ram(mem_section.mr)) {
- goto out;
+ mem = mem_section->mr;
+ if (!mem) {
+ return;
}
- assert(mem);
- assert(mem_section.offset_within_address_space == base);
-
memory_region_sync_dirty_bitmap(mem);
- src_base = cpu_physical_memory_map(base, &src_len, 0);
- /* If we can't map the framebuffer then bail. We could try harder,
- but it's not really worth it as dirty flag tracking will probably
- already have failed above. */
- if (!src_base)
- goto out;
- if (src_len != src_width * rows) {
- cpu_physical_memory_unmap(src_base, src_len, 0, 0);
- goto out;
- }
- src = src_base;
+
+ addr = mem_section->offset_within_region;
+ src = memory_region_get_ram_ptr(mem) + addr;
+
dest = surface_data(ds);
- if (dest_col_pitch < 0)
+ if (dest_col_pitch < 0) {
dest -= dest_col_pitch * (cols - 1);
+ }
if (dest_row_pitch < 0) {
dest -= dest_row_pitch * (rows - 1);
}
first = -1;
- addr = mem_section.offset_within_region;
addr += i * src_width;
src += i * src_width;
@@ -100,14 +114,11 @@ void framebuffer_update_display(
src += src_width;
dest += dest_row_pitch;
}
- cpu_physical_memory_unmap(src_base, src_len, 0, 0);
if (first < 0) {
- goto out;
+ return;
}
- memory_region_reset_dirty(mem, mem_section.offset_within_region, src_len,
+ memory_region_reset_dirty(mem, mem_section->offset_within_region, src_len,
DIRTY_MEMORY_VGA);
*first_row = first;
*last_row = last;
-out:
- memory_region_unref(mem);
}
diff --git a/hw/display/framebuffer.h b/hw/display/framebuffer.h
index 6eae035b7..38fa0dcec 100644
--- a/hw/display/framebuffer.h
+++ b/hw/display/framebuffer.h
@@ -7,10 +7,50 @@
typedef void (*drawfn)(void *, uint8_t *, const uint8_t *, int, int);
+/* framebuffer_update_memory_section: Update framebuffer
+ * #MemoryRegionSection, for example if the framebuffer is switched to
+ * a different memory area.
+ *
+ * @mem_section: Output #MemoryRegionSection, to be passed to
+ * framebuffer_update_display().
+ * @root: #MemoryRegion within which the framebuffer lies
+ * @base: Base address of the framebuffer within @root.
+ * @rows: Height of the screen.
+ * @src_width: Number of bytes in framebuffer memory between two rows.
+ */
+void framebuffer_update_memory_section(
+ MemoryRegionSection *mem_section,
+ MemoryRegion *root,
+ hwaddr base,
+ unsigned rows,
+ unsigned src_width);
+
+/* framebuffer_update_display: Draw the framebuffer on a surface.
+ *
+ * @ds: #DisplaySurface to draw to.
+ * @mem_section: #MemoryRegionSection provided by
+ * framebuffer_update_memory_section().
+ * @cols: Width the screen.
+ * @rows: Height of the screen.
+ * @src_width: Number of bytes in framebuffer memory between two rows.
+ * @dest_row_pitch: Number of bytes in the surface data between two rows.
+ * Negative if the framebuffer is stored in the opposite order (e.g.
+ * bottom-to-top) compared to the framebuffer.
+ * @dest_col_pitch: Number of bytes in the surface data between two pixels.
+ * Negative if the framebuffer is stored in the opposite order (e.g.
+ * right-to-left) compared to the framebuffer.
+ * @invalidate: True if the function should redraw the whole screen
+ * without checking the DIRTY_MEMORY_VGA dirty bitmap.
+ * @fn: Drawing function to be called for each row that has to be drawn.
+ * @opaque: Opaque pointer passed to @fn.
+ * @first_row: Pointer to an integer, receives the number of the first row
+ * that was drawn (either the first dirty row, or 0 if @invalidate is true).
+ * @last_row: Pointer to an integer, receives the number of the last row that
+ * was drawn (either the last dirty row, or @rows-1 if @invalidate is true).
+ */
void framebuffer_update_display(
DisplaySurface *ds,
- MemoryRegion *address_space,
- hwaddr base,
+ MemoryRegionSection *mem_section,
int cols,
int rows,
int src_width,
diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c
index 46f7b4121..7f83a007b 100644
--- a/hw/display/g364fb.c
+++ b/hw/display/g364fb.c
@@ -18,6 +18,7 @@
*/
#include "hw/hw.h"
+#include "qemu/error-report.h"
#include "ui/console.h"
#include "ui/pixel_ops.h"
#include "trace.h"
@@ -260,6 +261,7 @@ static void g364fb_update_display(void *opaque)
qemu_console_resize(s->con, s->width, s->height);
}
+ memory_region_sync_dirty_bitmap(&s->mem_vram);
if (s->ctla & CTLA_FORCE_BLANK) {
g364fb_draw_blank(s);
} else if (s->depth == 8) {
@@ -489,7 +491,7 @@ static void g364fb_init(DeviceState *dev, G364State *s)
memory_region_init_ram_ptr(&s->mem_vram, NULL, "vram",
s->vram_size, s->vram);
vmstate_register_ram(&s->mem_vram, dev);
- memory_region_set_coalescing(&s->mem_vram);
+ memory_region_set_log(&s->mem_vram, true, DIRTY_MEMORY_VGA);
}
#define TYPE_G364 "sysbus-g364"
diff --git a/hw/display/milkymist-vgafb.c b/hw/display/milkymist-vgafb.c
index 9b35e76ff..ab3074fad 100644
--- a/hw/display/milkymist-vgafb.c
+++ b/hw/display/milkymist-vgafb.c
@@ -71,6 +71,7 @@ struct MilkymistVgafbState {
SysBusDevice parent_obj;
MemoryRegion regs_region;
+ MemoryRegionSection fbsection;
QemuConsole *con;
int invalidate;
@@ -91,6 +92,7 @@ static void vgafb_update_display(void *opaque)
MilkymistVgafbState *s = opaque;
SysBusDevice *sbd;
DisplaySurface *surface = qemu_console_surface(s->con);
+ int src_width;
int first = 0;
int last = 0;
drawfn fn;
@@ -129,11 +131,18 @@ static void vgafb_update_display(void *opaque)
break;
}
- framebuffer_update_display(surface, sysbus_address_space(sbd),
- s->regs[R_BASEADDRESS] + s->fb_offset,
+ src_width = s->regs[R_HRES] * 2;
+ if (s->invalidate) {
+ framebuffer_update_memory_section(&s->fbsection,
+ sysbus_address_space(sbd),
+ s->regs[R_BASEADDRESS] + s->fb_offset,
+ s->regs[R_VRES], src_width);
+ }
+
+ framebuffer_update_display(surface, &s->fbsection,
s->regs[R_HRES],
s->regs[R_VRES],
- s->regs[R_HRES] * 2,
+ src_width,
dest_width,
0,
s->invalidate,
diff --git a/hw/display/omap_lcdc.c b/hw/display/omap_lcdc.c
index fda81baff..a7c6cd79b 100644
--- a/hw/display/omap_lcdc.c
+++ b/hw/display/omap_lcdc.c
@@ -25,6 +25,7 @@
struct omap_lcd_panel_s {
MemoryRegion *sysmem;
MemoryRegion iomem;
+ MemoryRegionSection fbsection;
qemu_irq irq;
QemuConsole *con;
@@ -215,12 +216,19 @@ static void omap_update_display(void *opaque)
step = width * bpp >> 3;
linesize = surface_stride(surface);
- framebuffer_update_display(surface, omap_lcd->sysmem,
- frame_base, width, height,
+ if (omap_lcd->invalidate) {
+ framebuffer_update_memory_section(&omap_lcd->fbsection,
+ omap_lcd->sysmem, frame_base,
+ height, step);
+ }
+
+ framebuffer_update_display(surface, &omap_lcd->fbsection,
+ width, height,
step, linesize, 0,
omap_lcd->invalidate,
draw_line, omap_lcd->palette,
&first, &last);
+
if (first >= 0) {
dpy_gfx_update(omap_lcd->con, 0, first, width, last - first + 1);
}
diff --git a/hw/display/pl110.c b/hw/display/pl110.c
index c574cf1a8..ef1a7b1a5 100644
--- a/hw/display/pl110.c
+++ b/hw/display/pl110.c
@@ -46,6 +46,7 @@ typedef struct PL110State {
SysBusDevice parent_obj;
MemoryRegion iomem;
+ MemoryRegionSection fbsection;
QemuConsole *con;
int version;
@@ -238,12 +239,20 @@ static void pl110_update_display(void *opaque)
}
dest_width *= s->cols;
first = 0;
- framebuffer_update_display(surface, sysbus_address_space(sbd),
- s->upbase, s->cols, s->rows,
+ if (s->invalidate) {
+ framebuffer_update_memory_section(&s->fbsection,
+ sysbus_address_space(sbd),
+ s->upbase,
+ s->rows, src_width);
+ }
+
+ framebuffer_update_display(surface, &s->fbsection,
+ s->cols, s->rows,
src_width, dest_width, 0,
s->invalidate,
fn, s->palette,
&first, &last);
+
if (first >= 0) {
dpy_gfx_update(s->con, 0, first, s->cols, last - first + 1);
}
diff --git a/hw/display/pxa2xx_lcd.c b/hw/display/pxa2xx_lcd.c
index ac3c01882..494700d07 100644
--- a/hw/display/pxa2xx_lcd.c
+++ b/hw/display/pxa2xx_lcd.c
@@ -35,6 +35,7 @@ struct DMAChannel {
struct PXA2xxLCDState {
MemoryRegion *sysmem;
MemoryRegion iomem;
+ MemoryRegionSection fbsection;
qemu_irq irq;
int irqlevel;
@@ -687,8 +688,11 @@ static void pxa2xx_lcdc_dma0_redraw_rot0(PXA2xxLCDState *s,
dest_width = s->xres * s->dest_width;
*miny = 0;
- framebuffer_update_display(surface, s->sysmem,
- addr, s->xres, s->yres,
+ if (s->invalidated) {
+ framebuffer_update_memory_section(&s->fbsection, s->sysmem,
+ addr, s->yres, src_width);
+ }
+ framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres,
src_width, dest_width, s->dest_width,
s->invalidated,
fn, s->dma_ch[0].palette, miny, maxy);
@@ -715,8 +719,11 @@ static void pxa2xx_lcdc_dma0_redraw_rot90(PXA2xxLCDState *s,
dest_width = s->yres * s->dest_width;
*miny = 0;
- framebuffer_update_display(surface, s->sysmem,
- addr, s->xres, s->yres,
+ if (s->invalidated) {
+ framebuffer_update_memory_section(&s->fbsection, s->sysmem,
+ addr, s->yres, src_width);
+ }
+ framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres,
src_width, s->dest_width, -dest_width,
s->invalidated,
fn, s->dma_ch[0].palette,
@@ -747,8 +754,11 @@ static void pxa2xx_lcdc_dma0_redraw_rot180(PXA2xxLCDState *s,
dest_width = s->xres * s->dest_width;
*miny = 0;
- framebuffer_update_display(surface, s->sysmem,
- addr, s->xres, s->yres,
+ if (s->invalidated) {
+ framebuffer_update_memory_section(&s->fbsection, s->sysmem,
+ addr, s->yres, src_width);
+ }
+ framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres,
src_width, -dest_width, -s->dest_width,
s->invalidated,
fn, s->dma_ch[0].palette, miny, maxy);
@@ -778,8 +788,11 @@ static void pxa2xx_lcdc_dma0_redraw_rot270(PXA2xxLCDState *s,
dest_width = s->yres * s->dest_width;
*miny = 0;
- framebuffer_update_display(surface, s->sysmem,
- addr, s->xres, s->yres,
+ if (s->invalidated) {
+ framebuffer_update_memory_section(&s->fbsection, s->sysmem,
+ addr, s->yres, src_width);
+ }
+ framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres,
src_width, -s->dest_width, dest_width,
s->invalidated,
fn, s->dma_ch[0].palette,
diff --git a/hw/display/qxl-logger.c b/hw/display/qxl-logger.c
index c900c2ca4..d944d3fdb 100644
--- a/hw/display/qxl-logger.c
+++ b/hw/display/qxl-logger.c
@@ -22,7 +22,7 @@
#include "qemu/timer.h"
#include "qxl.h"
-static const char *qxl_type[] = {
+static const char *const qxl_type[] = {
[ QXL_CMD_NOP ] = "nop",
[ QXL_CMD_DRAW ] = "draw",
[ QXL_CMD_UPDATE ] = "update",
@@ -31,7 +31,7 @@ static const char *qxl_type[] = {
[ QXL_CMD_SURFACE ] = "surface",
};
-static const char *qxl_draw_type[] = {
+static const char *const qxl_draw_type[] = {
[ QXL_DRAW_NOP ] = "nop",
[ QXL_DRAW_FILL ] = "fill",
[ QXL_DRAW_OPAQUE ] = "opaque",
@@ -48,7 +48,7 @@ static const char *qxl_draw_type[] = {
[ QXL_DRAW_ALPHA_BLEND ] = "alpha-blend",
};
-static const char *qxl_draw_effect[] = {
+static const char *const qxl_draw_effect[] = {
[ QXL_EFFECT_BLEND ] = "blend",
[ QXL_EFFECT_OPAQUE ] = "opaque",
[ QXL_EFFECT_REVERT_ON_DUP ] = "revert-on-dup",
@@ -59,12 +59,12 @@ static const char *qxl_draw_effect[] = {
[ QXL_EFFECT_OPAQUE_BRUSH ] = "opaque-brush",
};
-static const char *qxl_surface_cmd[] = {
+static const char *const qxl_surface_cmd[] = {
[ QXL_SURFACE_CMD_CREATE ] = "create",
[ QXL_SURFACE_CMD_DESTROY ] = "destroy",
};
-static const char *spice_surface_fmt[] = {
+static const char *const spice_surface_fmt[] = {
[ SPICE_SURFACE_FMT_INVALID ] = "invalid",
[ SPICE_SURFACE_FMT_1_A ] = "alpha/1",
[ SPICE_SURFACE_FMT_8_A ] = "alpha/8",
@@ -74,14 +74,14 @@ static const char *spice_surface_fmt[] = {
[ SPICE_SURFACE_FMT_32_ARGB ] = "ARGB/32",
};
-static const char *qxl_cursor_cmd[] = {
+static const char *const qxl_cursor_cmd[] = {
[ QXL_CURSOR_SET ] = "set",
[ QXL_CURSOR_MOVE ] = "move",
[ QXL_CURSOR_HIDE ] = "hide",
[ QXL_CURSOR_TRAIL ] = "trail",
};
-static const char *spice_cursor_type[] = {
+static const char *const spice_cursor_type[] = {
[ SPICE_CURSOR_TYPE_ALPHA ] = "alpha",
[ SPICE_CURSOR_TYPE_MONO ] = "mono",
[ SPICE_CURSOR_TYPE_COLOR4 ] = "color4",
@@ -91,7 +91,7 @@ static const char *spice_cursor_type[] = {
[ SPICE_CURSOR_TYPE_COLOR32 ] = "color32",
};
-static const char *qxl_v2n(const char *n[], size_t l, int v)
+static const char *qxl_v2n(const char *const n[], size_t l, int v)
{
if (v >= l || !n[v]) {
return "???";
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index b6d65b948..2288238d0 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -25,7 +25,6 @@
#include "qemu/timer.h"
#include "qemu/queue.h"
#include "qemu/atomic.h"
-#include "monitor/monitor.h"
#include "sysemu/sysemu.h"
#include "trace.h"
@@ -272,6 +271,11 @@ static void qxl_spice_monitors_config_async(PCIQXLDevice *qxl, int replay)
QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG,
0));
} else {
+#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */
+ if (qxl->max_outputs) {
+ spice_qxl_set_max_monitors(&qxl->ssd.qxl, qxl->max_outputs);
+ }
+#endif
qxl->guest_monitors_config = qxl->ram->monitors_config;
spice_qxl_monitors_config_async(&qxl->ssd.qxl,
qxl->ram->monitors_config,
@@ -504,6 +508,10 @@ static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
{
PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+ if (!qemu_spice_display_is_running(&qxl->ssd)) {
+ return;
+ }
+
trace_qxl_interface_set_mm_time(qxl->id, mm_time);
qxl->shadow_rom.mm_clock = cpu_to_le32(mm_time);
qxl->rom->mm_clock = cpu_to_le32(mm_time);
@@ -696,7 +704,7 @@ static inline void qxl_push_free_res(PCIQXLDevice *d, int flush)
/* called from spice server thread context only */
static void interface_release_resource(QXLInstance *sin,
- struct QXLReleaseInfoExt ext)
+ QXLReleaseInfoExt ext)
{
PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
QXLReleaseRing *ring;
@@ -988,6 +996,7 @@ static int interface_client_monitors_config(QXLInstance *sin,
PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
QXLRom *rom = memory_region_get_ram_ptr(&qxl->rom_bar);
int i;
+ unsigned max_outputs = ARRAY_SIZE(rom->client_monitors_config.heads);
if (qxl->revision < 4) {
trace_qxl_client_monitors_config_unsupported_by_device(qxl->id,
@@ -1010,17 +1019,23 @@ static int interface_client_monitors_config(QXLInstance *sin,
if (!monitors_config) {
return 1;
}
+
+#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */
+ /* limit number of outputs based on setting limit */
+ if (qxl->max_outputs && qxl->max_outputs <= max_outputs) {
+ max_outputs = qxl->max_outputs;
+ }
+#endif
+
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)) {
+ if (rom->client_monitors_config.count >= max_outputs) {
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);
+ max_outputs);
+ rom->client_monitors_config.count = max_outputs;
}
for (i = 0 ; i < rom->client_monitors_config.count ; ++i) {
VDAgentMonConfig *monitor = &monitors_config->monitors[i];
@@ -1181,7 +1196,7 @@ static void qxl_hard_reset(PCIQXLDevice *d, int loadvm)
static void qxl_reset_handler(DeviceState *dev)
{
- PCIQXLDevice *d = DO_UPCAST(PCIQXLDevice, pci.qdev, dev);
+ PCIQXLDevice *d = PCI_QXL(PCI_DEVICE(dev));
qxl_hard_reset(d, 0);
}
@@ -2026,7 +2041,7 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp)
static void qxl_realize_primary(PCIDevice *dev, Error **errp)
{
- PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev);
+ PCIQXLDevice *qxl = PCI_QXL(dev);
VGACommonState *vga = &qxl->vga;
Error *local_err = NULL;
@@ -2059,7 +2074,7 @@ static void qxl_realize_primary(PCIDevice *dev, Error **errp)
static void qxl_realize_secondary(PCIDevice *dev, Error **errp)
{
static int device_id = 1;
- PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev);
+ PCIQXLDevice *qxl = PCI_QXL(dev);
qxl->id = device_id++;
qxl_init_ramsize(qxl);
@@ -2216,6 +2231,7 @@ static VMStateDescription qxl_vmstate_monitors_config = {
.name = "qxl/monitors-config",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = qxl_monitors_config_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64(guest_monitors_config, PCIQXLDevice),
VMSTATE_END_OF_LIST()
@@ -2249,13 +2265,9 @@ static VMStateDescription qxl_vmstate = {
VMSTATE_UINT64(guest_cursor, PCIQXLDevice),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &qxl_vmstate_monitors_config,
- .needed = qxl_monitors_config_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &qxl_vmstate_monitors_config,
+ NULL
}
};
@@ -2274,31 +2286,48 @@ static Property qxl_properties[] = {
DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, -1),
DEFINE_PROP_UINT32("vgamem_mb", PCIQXLDevice, vgamem_size_mb, 16),
DEFINE_PROP_INT32("surfaces", PCIQXLDevice, ssd.num_surfaces, 1024),
+#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */
+ DEFINE_PROP_UINT16("max_outputs", PCIQXLDevice, max_outputs, 0),
+#endif
DEFINE_PROP_END_OF_LIST(),
};
-static void qxl_primary_class_init(ObjectClass *klass, void *data)
+static void qxl_pci_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- k->realize = qxl_realize_primary;
- k->romfile = "vgabios-qxl.bin";
k->vendor_id = REDHAT_PCI_VENDOR_ID;
k->device_id = QXL_DEVICE_ID_STABLE;
- k->class_id = PCI_CLASS_DISPLAY_VGA;
set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
- dc->desc = "Spice QXL GPU (primary, vga compatible)";
dc->reset = qxl_reset_handler;
dc->vmsd = &qxl_vmstate;
dc->props = qxl_properties;
+}
+
+static const TypeInfo qxl_pci_type_info = {
+ .name = TYPE_PCI_QXL,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PCIQXLDevice),
+ .abstract = true,
+ .class_init = qxl_pci_class_init,
+};
+
+static void qxl_primary_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = qxl_realize_primary;
+ k->romfile = "vgabios-qxl.bin";
+ k->class_id = PCI_CLASS_DISPLAY_VGA;
+ dc->desc = "Spice QXL GPU (primary, vga compatible)";
dc->hotpluggable = false;
}
static const TypeInfo qxl_primary_info = {
.name = "qxl-vga",
- .parent = TYPE_PCI_DEVICE,
- .instance_size = sizeof(PCIQXLDevice),
+ .parent = TYPE_PCI_QXL,
.class_init = qxl_primary_class_init,
};
@@ -2308,25 +2337,19 @@ static void qxl_secondary_class_init(ObjectClass *klass, void *data)
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
k->realize = qxl_realize_secondary;
- k->vendor_id = REDHAT_PCI_VENDOR_ID;
- k->device_id = QXL_DEVICE_ID_STABLE;
k->class_id = PCI_CLASS_DISPLAY_OTHER;
- set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
dc->desc = "Spice QXL GPU (secondary)";
- dc->reset = qxl_reset_handler;
- dc->vmsd = &qxl_vmstate;
- dc->props = qxl_properties;
}
static const TypeInfo qxl_secondary_info = {
.name = "qxl",
- .parent = TYPE_PCI_DEVICE,
- .instance_size = sizeof(PCIQXLDevice),
+ .parent = TYPE_PCI_QXL,
.class_init = qxl_secondary_class_init,
};
static void qxl_register_types(void)
{
+ type_register_static(&qxl_pci_type_info);
type_register_static(&qxl_primary_info);
type_register_static(&qxl_secondary_info);
}
diff --git a/hw/display/qxl.h b/hw/display/qxl.h
index 412e346b6..2ddf065e1 100644
--- a/hw/display/qxl.h
+++ b/hw/display/qxl.h
@@ -99,6 +99,9 @@ typedef struct PCIQXLDevice {
QXLModes *modes;
uint32_t rom_size;
MemoryRegion rom_bar;
+#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */
+ uint16_t max_outputs;
+#endif
/* vram pci bar */
uint32_t vram_size;
@@ -122,6 +125,9 @@ typedef struct PCIQXLDevice {
QEMUBH *update_area_bh;
} PCIQXLDevice;
+#define TYPE_PCI_QXL "pci-qxl"
+#define PCI_QXL(obj) OBJECT_CHECK(PCIQXLDevice, (obj), TYPE_PCI_QXL)
+
#define PANIC_ON(x) if ((x)) { \
printf("%s: PANIC %s failed\n", __FUNCTION__, #x); \
abort(); \
diff --git a/hw/display/sm501.c b/hw/display/sm501.c
index c72154b6f..15a5ba800 100644
--- a/hw/display/sm501.c
+++ b/hw/display/sm501.c
@@ -1322,6 +1322,7 @@ static void sm501_draw_crt(SM501State * s)
}
/* draw each line according to conditions */
+ memory_region_sync_dirty_bitmap(&s->local_mem_region);
for (y = 0; y < height; y++) {
int update_hwc = draw_hwc_line ? within_hwc_y_range(s, y, 1) : 0;
int update = full_update || update_hwc;
@@ -1412,6 +1413,7 @@ void sm501_init(MemoryRegion *address_space_mem, uint32_t base,
memory_region_init_ram(&s->local_mem_region, NULL, "sm501.local",
local_mem_bytes, &error_abort);
vmstate_register_ram_global(&s->local_mem_region);
+ memory_region_set_log(&s->local_mem_region, true, DIRTY_MEMORY_VGA);
s->local_mem = memory_region_get_ram_ptr(&s->local_mem_region);
memory_region_add_subregion(address_space_mem, base, &s->local_mem_region);
diff --git a/hw/display/tc6393xb.c b/hw/display/tc6393xb.c
index 4306adc95..f5f3f3e69 100644
--- a/hw/display/tc6393xb.c
+++ b/hw/display/tc6393xb.c
@@ -171,7 +171,7 @@ static void tc6393xb_gpio_handler_update(TC6393xbState *s)
level = s->gpio_level & s->gpio_dir;
for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
- bit = ffs(diff) - 1;
+ bit = ctz32(diff);
qemu_set_irq(s->handler[bit], (level >> bit) & 1);
}
@@ -571,7 +571,7 @@ TC6393xbState *tc6393xb_init(MemoryRegion *sysmem, uint32_t base, qemu_irq irq)
s->irq = irq;
s->gpio_in = qemu_allocate_irqs(tc6393xb_gpio_set, s, TC6393XB_GPIOS);
- s->l3v = *qemu_allocate_irqs(tc6393xb_l3v, s, 1);
+ s->l3v = qemu_allocate_irq(tc6393xb_l3v, s, 0);
s->blanked = 1;
s->sub_irqs = qemu_allocate_irqs(tc6393xb_sub_irq, s, TC6393XB_NR_IRQS);
diff --git a/hw/display/tcx.c b/hw/display/tcx.c
index a9f9f66d1..327ce3019 100644
--- a/hw/display/tcx.c
+++ b/hw/display/tcx.c
@@ -27,6 +27,7 @@
#include "ui/pixel_ops.h"
#include "hw/loader.h"
#include "hw/sysbus.h"
+#include "qemu/error-report.h"
#define TCX_ROM_FILE "QEMU,tcx.bin"
#define FCODE_MAX_ROM_SIZE 0x10000
@@ -353,6 +354,7 @@ static void tcx_update_display(void *opaque)
return;
}
+ memory_region_sync_dirty_bitmap(&ts->vram_mem);
for (y = 0; y < ts->height; page += TARGET_PAGE_SIZE) {
if (memory_region_get_dirty(&ts->vram_mem, page, TARGET_PAGE_SIZE,
DIRTY_MEMORY_VGA)) {
@@ -446,6 +448,7 @@ static void tcx24_update_display(void *opaque)
dd = surface_stride(surface);
ds = 1024;
+ memory_region_sync_dirty_bitmap(&ts->vram_mem);
for (y = 0; y < ts->height; page += TARGET_PAGE_SIZE,
page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) {
if (tcx24_check_dirty(ts, page, page24, cpage)) {
@@ -941,7 +944,7 @@ static void tcx_initfn(Object *obj)
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
TCXState *s = TCX(obj);
- memory_region_init_ram(&s->rom, NULL, "tcx.prom", FCODE_MAX_ROM_SIZE,
+ memory_region_init_ram(&s->rom, OBJECT(s), "tcx.prom", FCODE_MAX_ROM_SIZE,
&error_abort);
memory_region_set_readonly(&s->rom, true);
sysbus_init_mmio(sbd, &s->rom);
@@ -1006,6 +1009,7 @@ static void tcx_realizefn(DeviceState *dev, Error **errp)
memory_region_init_ram(&s->vram_mem, OBJECT(s), "tcx.vram",
s->vram_size * (1 + 4 + 4), &error_abort);
vmstate_register_ram_global(&s->vram_mem);
+ memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA);
vram_base = memory_region_get_ram_ptr(&s->vram_mem);
/* 10/ROM : FCode ROM */
@@ -1014,6 +1018,7 @@ static void tcx_realizefn(DeviceState *dev, Error **errp)
if (fcode_filename) {
ret = load_image_targphys(fcode_filename, s->prom_addr,
FCODE_MAX_ROM_SIZE);
+ g_free(fcode_filename);
if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) {
error_report("tcx: could not load prom '%s'", TCX_ROM_FILE);
}
diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c
index aabfc23cd..1dfa331e6 100644
--- a/hw/display/vga-pci.c
+++ b/hw/display/vga-pci.c
@@ -54,11 +54,12 @@ typedef struct PCIVGAState {
VGACommonState vga;
uint32_t flags;
MemoryRegion mmio;
- MemoryRegion ioport;
- MemoryRegion bochs;
- MemoryRegion qext;
+ MemoryRegion mrs[3];
} PCIVGAState;
+#define TYPE_PCI_VGA "pci-vga"
+#define PCI_VGA(obj) OBJECT_CHECK(PCIVGAState, (obj), TYPE_PCI_VGA)
+
static const VMStateDescription vmstate_vga_pci = {
.name = "vga",
.version_id = 2,
@@ -73,16 +74,16 @@ static const VMStateDescription vmstate_vga_pci = {
static uint64_t pci_vga_ioport_read(void *ptr, hwaddr addr,
unsigned size)
{
- PCIVGAState *d = ptr;
+ VGACommonState *s = ptr;
uint64_t ret = 0;
switch (size) {
case 1:
- ret = vga_ioport_read(&d->vga, addr);
+ ret = vga_ioport_read(s, addr + 0x3c0);
break;
case 2:
- ret = vga_ioport_read(&d->vga, addr);
- ret |= vga_ioport_read(&d->vga, addr+1) << 8;
+ ret = vga_ioport_read(s, addr + 0x3c0);
+ ret |= vga_ioport_read(s, addr + 0x3c1) << 8;
break;
}
return ret;
@@ -91,11 +92,11 @@ static uint64_t pci_vga_ioport_read(void *ptr, hwaddr addr,
static void pci_vga_ioport_write(void *ptr, hwaddr addr,
uint64_t val, unsigned size)
{
- PCIVGAState *d = ptr;
+ VGACommonState *s = ptr;
switch (size) {
case 1:
- vga_ioport_write(&d->vga, addr + 0x3c0, val);
+ vga_ioport_write(s, addr + 0x3c0, val);
break;
case 2:
/*
@@ -103,8 +104,8 @@ static void pci_vga_ioport_write(void *ptr, hwaddr addr,
* indexed registers with a single word write because the
* index byte is updated first.
*/
- vga_ioport_write(&d->vga, addr + 0x3c0, val & 0xff);
- vga_ioport_write(&d->vga, addr + 0x3c1, (val >> 8) & 0xff);
+ vga_ioport_write(s, addr + 0x3c0, val & 0xff);
+ vga_ioport_write(s, addr + 0x3c1, (val >> 8) & 0xff);
break;
}
}
@@ -122,21 +123,21 @@ static const MemoryRegionOps pci_vga_ioport_ops = {
static uint64_t pci_vga_bochs_read(void *ptr, hwaddr addr,
unsigned size)
{
- PCIVGAState *d = ptr;
+ VGACommonState *s = ptr;
int index = addr >> 1;
- vbe_ioport_write_index(&d->vga, 0, index);
- return vbe_ioport_read_data(&d->vga, 0);
+ vbe_ioport_write_index(s, 0, index);
+ return vbe_ioport_read_data(s, 0);
}
static void pci_vga_bochs_write(void *ptr, hwaddr addr,
uint64_t val, unsigned size)
{
- PCIVGAState *d = ptr;
+ VGACommonState *s = ptr;
int index = addr >> 1;
- vbe_ioport_write_index(&d->vga, 0, index);
- vbe_ioport_write_data(&d->vga, 0, val);
+ vbe_ioport_write_index(s, 0, index);
+ vbe_ioport_write_data(s, 0, val);
}
static const MemoryRegionOps pci_vga_bochs_ops = {
@@ -151,13 +152,13 @@ static const MemoryRegionOps pci_vga_bochs_ops = {
static uint64_t pci_vga_qext_read(void *ptr, hwaddr addr, unsigned size)
{
- PCIVGAState *d = ptr;
+ VGACommonState *s = ptr;
switch (addr) {
case PCI_VGA_QEXT_REG_SIZE:
return PCI_VGA_QEXT_SIZE;
case PCI_VGA_QEXT_REG_BYTEORDER:
- return d->vga.big_endian_fb ?
+ return s->big_endian_fb ?
PCI_VGA_QEXT_BIG_ENDIAN : PCI_VGA_QEXT_LITTLE_ENDIAN;
default:
return 0;
@@ -167,15 +168,15 @@ static uint64_t pci_vga_qext_read(void *ptr, hwaddr addr, unsigned size)
static void pci_vga_qext_write(void *ptr, hwaddr addr,
uint64_t val, unsigned size)
{
- PCIVGAState *d = ptr;
+ VGACommonState *s = ptr;
switch (addr) {
case PCI_VGA_QEXT_REG_BYTEORDER:
if (val == PCI_VGA_QEXT_BIG_ENDIAN) {
- d->vga.big_endian_fb = true;
+ s->big_endian_fb = true;
}
if (val == PCI_VGA_QEXT_LITTLE_ENDIAN) {
- d->vga.big_endian_fb = false;
+ s->big_endian_fb = false;
}
break;
}
@@ -183,14 +184,14 @@ static void pci_vga_qext_write(void *ptr, hwaddr addr,
static bool vga_get_big_endian_fb(Object *obj, Error **errp)
{
- PCIVGAState *d = DO_UPCAST(PCIVGAState, dev, PCI_DEVICE(obj));
+ PCIVGAState *d = PCI_VGA(PCI_DEVICE(obj));
return d->vga.big_endian_fb;
}
static void vga_set_big_endian_fb(Object *obj, bool value, Error **errp)
{
- PCIVGAState *d = DO_UPCAST(PCIVGAState, dev, PCI_DEVICE(obj));
+ PCIVGAState *d = PCI_VGA(PCI_DEVICE(obj));
d->vga.big_endian_fb = value;
}
@@ -203,10 +204,34 @@ static const MemoryRegionOps pci_vga_qext_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
+void pci_std_vga_mmio_region_init(VGACommonState *s,
+ MemoryRegion *parent,
+ MemoryRegion *subs,
+ bool qext)
+{
+ memory_region_init_io(&subs[0], NULL, &pci_vga_ioport_ops, s,
+ "vga ioports remapped", PCI_VGA_IOPORT_SIZE);
+ memory_region_add_subregion(parent, PCI_VGA_IOPORT_OFFSET,
+ &subs[0]);
+
+ memory_region_init_io(&subs[1], NULL, &pci_vga_bochs_ops, s,
+ "bochs dispi interface", PCI_VGA_BOCHS_SIZE);
+ memory_region_add_subregion(parent, PCI_VGA_BOCHS_OFFSET,
+ &subs[1]);
+
+ if (qext) {
+ memory_region_init_io(&subs[2], NULL, &pci_vga_qext_ops, s,
+ "qemu extended regs", PCI_VGA_QEXT_SIZE);
+ memory_region_add_subregion(parent, PCI_VGA_QEXT_OFFSET,
+ &subs[2]);
+ }
+}
+
static void pci_std_vga_realize(PCIDevice *dev, Error **errp)
{
- PCIVGAState *d = DO_UPCAST(PCIVGAState, dev, dev);
+ PCIVGAState *d = PCI_VGA(dev);
VGACommonState *s = &d->vga;
+ bool qext = false;
/* vga + console init */
vga_common_init(s, OBJECT(dev), true);
@@ -221,23 +246,12 @@ static void pci_std_vga_realize(PCIDevice *dev, Error **errp)
/* mmio bar for vga register access */
if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_MMIO)) {
memory_region_init(&d->mmio, NULL, "vga.mmio", 4096);
- memory_region_init_io(&d->ioport, NULL, &pci_vga_ioport_ops, d,
- "vga ioports remapped", PCI_VGA_IOPORT_SIZE);
- memory_region_init_io(&d->bochs, NULL, &pci_vga_bochs_ops, d,
- "bochs dispi interface", PCI_VGA_BOCHS_SIZE);
-
- memory_region_add_subregion(&d->mmio, PCI_VGA_IOPORT_OFFSET,
- &d->ioport);
- memory_region_add_subregion(&d->mmio, PCI_VGA_BOCHS_OFFSET,
- &d->bochs);
if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_QEXT)) {
- memory_region_init_io(&d->qext, NULL, &pci_vga_qext_ops, d,
- "qemu extended regs", PCI_VGA_QEXT_SIZE);
- memory_region_add_subregion(&d->mmio, PCI_VGA_QEXT_OFFSET,
- &d->qext);
+ qext = true;
pci_set_byte(&d->dev.config[PCI_REVISION_ID], 2);
}
+ pci_std_vga_mmio_region_init(s, &d->mmio, d->mrs, qext);
pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
}
@@ -257,8 +271,9 @@ static void pci_std_vga_init(Object *obj)
static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp)
{
- PCIVGAState *d = DO_UPCAST(PCIVGAState, dev, dev);
+ PCIVGAState *d = PCI_VGA(dev);
VGACommonState *s = &d->vga;
+ bool qext = false;
/* vga + console init */
vga_common_init(s, OBJECT(dev), false);
@@ -266,23 +281,12 @@ static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp)
/* mmio bar */
memory_region_init(&d->mmio, OBJECT(dev), "vga.mmio", 4096);
- memory_region_init_io(&d->ioport, OBJECT(dev), &pci_vga_ioport_ops, d,
- "vga ioports remapped", PCI_VGA_IOPORT_SIZE);
- memory_region_init_io(&d->bochs, OBJECT(dev), &pci_vga_bochs_ops, d,
- "bochs dispi interface", PCI_VGA_BOCHS_SIZE);
-
- memory_region_add_subregion(&d->mmio, PCI_VGA_IOPORT_OFFSET,
- &d->ioport);
- memory_region_add_subregion(&d->mmio, PCI_VGA_BOCHS_OFFSET,
- &d->bochs);
if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_QEXT)) {
- memory_region_init_io(&d->qext, NULL, &pci_vga_qext_ops, d,
- "qemu extended regs", PCI_VGA_QEXT_SIZE);
- memory_region_add_subregion(&d->mmio, PCI_VGA_QEXT_OFFSET,
- &d->qext);
+ qext = true;
pci_set_byte(&d->dev.config[PCI_REVISION_ID], 2);
}
+ pci_std_vga_mmio_region_init(s, &d->mmio, d->mrs, qext);
pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram);
pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
@@ -297,8 +301,7 @@ static void pci_secondary_vga_init(Object *obj)
static void pci_secondary_vga_reset(DeviceState *dev)
{
- PCIVGAState *d = DO_UPCAST(PCIVGAState, dev.qdev, dev);
-
+ PCIVGAState *d = PCI_VGA(PCI_DEVICE(dev));
vga_common_reset(&d->vga);
}
@@ -317,6 +320,25 @@ static Property secondary_pci_properties[] = {
DEFINE_PROP_END_OF_LIST(),
};
+static void vga_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->vendor_id = PCI_VENDOR_ID_QEMU;
+ k->device_id = PCI_DEVICE_ID_QEMU_VGA;
+ dc->vmsd = &vmstate_vga_pci;
+ set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
+}
+
+static const TypeInfo vga_pci_type_info = {
+ .name = TYPE_PCI_VGA,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PCIVGAState),
+ .abstract = true,
+ .class_init = vga_pci_class_init,
+};
+
static void vga_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -324,13 +346,9 @@ static void vga_class_init(ObjectClass *klass, void *data)
k->realize = pci_std_vga_realize;
k->romfile = "vgabios-stdvga.bin";
- k->vendor_id = PCI_VENDOR_ID_QEMU;
- k->device_id = PCI_DEVICE_ID_QEMU_VGA;
k->class_id = PCI_CLASS_DISPLAY_VGA;
- dc->vmsd = &vmstate_vga_pci;
dc->props = vga_pci_properties;
dc->hotpluggable = false;
- set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
}
static void secondary_class_init(ObjectClass *klass, void *data)
@@ -339,33 +357,28 @@ static void secondary_class_init(ObjectClass *klass, void *data)
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
k->realize = pci_secondary_vga_realize;
- k->vendor_id = PCI_VENDOR_ID_QEMU;
- k->device_id = PCI_DEVICE_ID_QEMU_VGA;
k->class_id = PCI_CLASS_DISPLAY_OTHER;
- dc->vmsd = &vmstate_vga_pci;
dc->props = secondary_pci_properties;
dc->reset = pci_secondary_vga_reset;
- set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
}
static const TypeInfo vga_info = {
.name = "VGA",
- .parent = TYPE_PCI_DEVICE,
+ .parent = TYPE_PCI_VGA,
.instance_init = pci_std_vga_init,
- .instance_size = sizeof(PCIVGAState),
.class_init = vga_class_init,
};
static const TypeInfo secondary_info = {
.name = "secondary-vga",
- .parent = TYPE_PCI_DEVICE,
+ .parent = TYPE_PCI_VGA,
.instance_init = pci_secondary_vga_init,
- .instance_size = sizeof(PCIVGAState),
.class_init = secondary_class_init,
};
static void vga_register_types(void)
{
+ type_register_static(&vga_pci_type_info);
type_register_static(&vga_info);
type_register_static(&secondary_info);
}
diff --git a/hw/display/vga.c b/hw/display/vga.c
index d1d296c74..b35d523e6 100644
--- a/hw/display/vga.c
+++ b/hw/display/vga.c
@@ -2035,6 +2035,7 @@ static const VMStateDescription vmstate_vga_endian = {
.name = "vga.endian",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = vga_endian_state_needed,
.fields = (VMStateField[]) {
VMSTATE_BOOL(big_endian_fb, VGACommonState),
VMSTATE_END_OF_LIST()
@@ -2078,13 +2079,9 @@ const VMStateDescription vmstate_vga_common = {
VMSTATE_UINT32(vbe_bank_mask, VGACommonState),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection []) {
- {
- .vmsd = &vmstate_vga_endian,
- .needed = vga_endian_state_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_vga_endian,
+ NULL
}
};
diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h
index fcfcc5f43..40ba6a420 100644
--- a/hw/display/vga_int.h
+++ b/hw/display/vga_int.h
@@ -219,4 +219,10 @@ extern const uint8_t gr_mask[16];
extern const MemoryRegionOps vga_mem_ops;
+/* vga-pci.c */
+void pci_std_vga_mmio_region_init(VGACommonState *s,
+ MemoryRegion *parent,
+ MemoryRegion *subs,
+ bool qext);
+
#endif
diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c
new file mode 100644
index 000000000..5bc62cf34
--- /dev/null
+++ b/hw/display/virtio-gpu-pci.c
@@ -0,0 +1,76 @@
+/*
+ * Virtio video device
+ *
+ * Copyright Red Hat
+ *
+ * Authors:
+ * Dave Airlie
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#include "hw/pci/pci.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-bus.h"
+#include "hw/virtio/virtio-pci.h"
+#include "hw/virtio/virtio-gpu.h"
+
+static Property virtio_gpu_pci_properties[] = {
+ DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_gpu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ VirtIOGPUPCI *vgpu = VIRTIO_GPU_PCI(vpci_dev);
+ VirtIOGPU *g = &vgpu->vdev;
+ DeviceState *vdev = DEVICE(&vgpu->vdev);
+ int i;
+
+ qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+ /* force virtio-1.0 */
+ vpci_dev->flags &= ~VIRTIO_PCI_FLAG_DISABLE_MODERN;
+ vpci_dev->flags |= VIRTIO_PCI_FLAG_DISABLE_LEGACY;
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
+
+ for (i = 0; i < g->conf.max_outputs; i++) {
+ object_property_set_link(OBJECT(g->scanout[i].con),
+ OBJECT(vpci_dev),
+ "device", errp);
+ }
+}
+
+static void virtio_gpu_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
+ dc->props = virtio_gpu_pci_properties;
+ k->realize = virtio_gpu_pci_realize;
+ pcidev_k->class_id = PCI_CLASS_DISPLAY_OTHER;
+}
+
+static void virtio_gpu_initfn(Object *obj)
+{
+ VirtIOGPUPCI *dev = VIRTIO_GPU_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VIRTIO_GPU);
+}
+
+static const TypeInfo virtio_gpu_pci_info = {
+ .name = TYPE_VIRTIO_GPU_PCI,
+ .parent = TYPE_VIRTIO_PCI,
+ .instance_size = sizeof(VirtIOGPUPCI),
+ .instance_init = virtio_gpu_initfn,
+ .class_init = virtio_gpu_pci_class_init,
+};
+
+static void virtio_gpu_pci_register_types(void)
+{
+ type_register_static(&virtio_gpu_pci_info);
+}
+type_init(virtio_gpu_pci_register_types)
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
new file mode 100644
index 000000000..a67d927f5
--- /dev/null
+++ b/hw/display/virtio-gpu.c
@@ -0,0 +1,919 @@
+/*
+ * Virtio GPU Device
+ *
+ * Copyright Red Hat, Inc. 2013-2014
+ *
+ * Authors:
+ * Dave Airlie <airlied@redhat.com>
+ * Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu-common.h"
+#include "qemu/iov.h"
+#include "ui/console.h"
+#include "trace.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-gpu.h"
+#include "hw/virtio/virtio-bus.h"
+
+static struct virtio_gpu_simple_resource*
+virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id);
+
+static void update_cursor_data_simple(VirtIOGPU *g,
+ struct virtio_gpu_scanout *s,
+ uint32_t resource_id)
+{
+ struct virtio_gpu_simple_resource *res;
+ uint32_t pixels;
+
+ res = virtio_gpu_find_resource(g, resource_id);
+ if (!res) {
+ return;
+ }
+
+ if (pixman_image_get_width(res->image) != s->current_cursor->width ||
+ pixman_image_get_height(res->image) != s->current_cursor->height) {
+ return;
+ }
+
+ pixels = s->current_cursor->width * s->current_cursor->height;
+ memcpy(s->current_cursor->data,
+ pixman_image_get_data(res->image),
+ pixels * sizeof(uint32_t));
+}
+
+static void update_cursor(VirtIOGPU *g, struct virtio_gpu_update_cursor *cursor)
+{
+ struct virtio_gpu_scanout *s;
+
+ if (cursor->pos.scanout_id >= g->conf.max_outputs) {
+ return;
+ }
+ s = &g->scanout[cursor->pos.scanout_id];
+
+ if (cursor->hdr.type != VIRTIO_GPU_CMD_MOVE_CURSOR) {
+ if (!s->current_cursor) {
+ s->current_cursor = cursor_alloc(64, 64);
+ }
+
+ s->current_cursor->hot_x = cursor->hot_x;
+ s->current_cursor->hot_y = cursor->hot_y;
+
+ if (cursor->resource_id > 0) {
+ update_cursor_data_simple(g, s, cursor->resource_id);
+ }
+ dpy_cursor_define(s->con, s->current_cursor);
+ }
+ dpy_mouse_set(s->con, cursor->pos.x, cursor->pos.y,
+ cursor->resource_id ? 1 : 0);
+}
+
+static void virtio_gpu_get_config(VirtIODevice *vdev, uint8_t *config)
+{
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+ memcpy(config, &g->virtio_config, sizeof(g->virtio_config));
+}
+
+static void virtio_gpu_set_config(VirtIODevice *vdev, const uint8_t *config)
+{
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+ struct virtio_gpu_config vgconfig;
+
+ memcpy(&vgconfig, config, sizeof(g->virtio_config));
+
+ if (vgconfig.events_clear) {
+ g->virtio_config.events_read &= ~vgconfig.events_clear;
+ }
+}
+
+static uint64_t virtio_gpu_get_features(VirtIODevice *vdev, uint64_t features,
+ Error **errp)
+{
+ return features;
+}
+
+static void virtio_gpu_notify_event(VirtIOGPU *g, uint32_t event_type)
+{
+ g->virtio_config.events_read |= event_type;
+ virtio_notify_config(&g->parent_obj);
+}
+
+static struct virtio_gpu_simple_resource *
+virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id)
+{
+ struct virtio_gpu_simple_resource *res;
+
+ QTAILQ_FOREACH(res, &g->reslist, next) {
+ if (res->resource_id == resource_id) {
+ return res;
+ }
+ }
+ return NULL;
+}
+
+void virtio_gpu_ctrl_response(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd,
+ struct virtio_gpu_ctrl_hdr *resp,
+ size_t resp_len)
+{
+ size_t s;
+
+ if (cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_FENCE) {
+ resp->flags |= VIRTIO_GPU_FLAG_FENCE;
+ resp->fence_id = cmd->cmd_hdr.fence_id;
+ resp->ctx_id = cmd->cmd_hdr.ctx_id;
+ }
+ s = iov_from_buf(cmd->elem.in_sg, cmd->elem.in_num, 0, resp, resp_len);
+ if (s != resp_len) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: response size incorrect %zu vs %zu\n",
+ __func__, s, resp_len);
+ }
+ virtqueue_push(cmd->vq, &cmd->elem, s);
+ virtio_notify(VIRTIO_DEVICE(g), cmd->vq);
+ cmd->finished = true;
+}
+
+void virtio_gpu_ctrl_response_nodata(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd,
+ enum virtio_gpu_ctrl_type type)
+{
+ struct virtio_gpu_ctrl_hdr resp;
+
+ memset(&resp, 0, sizeof(resp));
+ resp.type = type;
+ virtio_gpu_ctrl_response(g, cmd, &resp, sizeof(resp));
+}
+
+static void
+virtio_gpu_fill_display_info(VirtIOGPU *g,
+ struct virtio_gpu_resp_display_info *dpy_info)
+{
+ int i;
+
+ for (i = 0; i < g->conf.max_outputs; i++) {
+ if (g->enabled_output_bitmask & (1 << i)) {
+ dpy_info->pmodes[i].enabled = 1;
+ dpy_info->pmodes[i].r.width = g->req_state[i].width;
+ dpy_info->pmodes[i].r.height = g->req_state[i].height;
+ }
+ }
+}
+
+void virtio_gpu_get_display_info(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_resp_display_info display_info;
+
+ trace_virtio_gpu_cmd_get_display_info();
+ memset(&display_info, 0, sizeof(display_info));
+ display_info.hdr.type = VIRTIO_GPU_RESP_OK_DISPLAY_INFO;
+ virtio_gpu_fill_display_info(g, &display_info);
+ virtio_gpu_ctrl_response(g, cmd, &display_info.hdr,
+ sizeof(display_info));
+}
+
+static pixman_format_code_t get_pixman_format(uint32_t virtio_gpu_format)
+{
+ switch (virtio_gpu_format) {
+#ifdef HOST_WORDS_BIGENDIAN
+ case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM:
+ return PIXMAN_b8g8r8x8;
+ case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM:
+ return PIXMAN_b8g8r8a8;
+ case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM:
+ return PIXMAN_x8r8g8b8;
+ case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM:
+ return PIXMAN_a8r8g8b8;
+ case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM:
+ return PIXMAN_r8g8b8x8;
+ case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM:
+ return PIXMAN_r8g8b8a8;
+ case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM:
+ return PIXMAN_x8b8g8r8;
+ case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM:
+ return PIXMAN_a8b8g8r8;
+#else
+ case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM:
+ return PIXMAN_x8r8g8b8;
+ case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM:
+ return PIXMAN_a8r8g8b8;
+ case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM:
+ return PIXMAN_b8g8r8x8;
+ case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM:
+ return PIXMAN_b8g8r8a8;
+ case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM:
+ return PIXMAN_x8b8g8r8;
+ case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM:
+ return PIXMAN_a8b8g8r8;
+ case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM:
+ return PIXMAN_r8g8b8x8;
+ case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM:
+ return PIXMAN_r8g8b8a8;
+#endif
+ default:
+ return 0;
+ }
+}
+
+static void virtio_gpu_resource_create_2d(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ pixman_format_code_t pformat;
+ struct virtio_gpu_simple_resource *res;
+ struct virtio_gpu_resource_create_2d c2d;
+
+ VIRTIO_GPU_FILL_CMD(c2d);
+ trace_virtio_gpu_cmd_res_create_2d(c2d.resource_id, c2d.format,
+ c2d.width, c2d.height);
+
+ if (c2d.resource_id == 0) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: resource id 0 is not allowed\n",
+ __func__);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+
+ res = virtio_gpu_find_resource(g, c2d.resource_id);
+ if (res) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: resource already exists %d\n",
+ __func__, c2d.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+
+ res = g_new0(struct virtio_gpu_simple_resource, 1);
+
+ res->width = c2d.width;
+ res->height = c2d.height;
+ res->format = c2d.format;
+ res->resource_id = c2d.resource_id;
+
+ pformat = get_pixman_format(c2d.format);
+ if (!pformat) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: host couldn't handle guest format %d\n",
+ __func__, c2d.format);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+ return;
+ }
+ res->image = pixman_image_create_bits(pformat,
+ c2d.width,
+ c2d.height,
+ NULL, 0);
+
+ if (!res->image) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: resource creation failed %d %d %d\n",
+ __func__, c2d.resource_id, c2d.width, c2d.height);
+ g_free(res);
+ cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY;
+ return;
+ }
+
+ QTAILQ_INSERT_HEAD(&g->reslist, res, next);
+}
+
+static void virtio_gpu_resource_destroy(VirtIOGPU *g,
+ struct virtio_gpu_simple_resource *res)
+{
+ pixman_image_unref(res->image);
+ QTAILQ_REMOVE(&g->reslist, res, next);
+ g_free(res);
+}
+
+static void virtio_gpu_resource_unref(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_simple_resource *res;
+ struct virtio_gpu_resource_unref unref;
+
+ VIRTIO_GPU_FILL_CMD(unref);
+ trace_virtio_gpu_cmd_res_unref(unref.resource_id);
+
+ res = virtio_gpu_find_resource(g, unref.resource_id);
+ if (!res) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
+ __func__, unref.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+ virtio_gpu_resource_destroy(g, res);
+}
+
+static void virtio_gpu_transfer_to_host_2d(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_simple_resource *res;
+ int h;
+ uint32_t src_offset, dst_offset, stride;
+ int bpp;
+ pixman_format_code_t format;
+ struct virtio_gpu_transfer_to_host_2d t2d;
+
+ VIRTIO_GPU_FILL_CMD(t2d);
+ trace_virtio_gpu_cmd_res_xfer_toh_2d(t2d.resource_id);
+
+ res = virtio_gpu_find_resource(g, t2d.resource_id);
+ if (!res || !res->iov) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
+ __func__, t2d.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+
+ if (t2d.r.x > res->width ||
+ t2d.r.y > res->height ||
+ t2d.r.width > res->width ||
+ t2d.r.height > res->height ||
+ t2d.r.x + t2d.r.width > res->width ||
+ t2d.r.y + t2d.r.height > res->height) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: transfer bounds outside resource"
+ " bounds for resource %d: %d %d %d %d vs %d %d\n",
+ __func__, t2d.resource_id, t2d.r.x, t2d.r.y,
+ t2d.r.width, t2d.r.height, res->width, res->height);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+ return;
+ }
+
+ format = pixman_image_get_format(res->image);
+ bpp = (PIXMAN_FORMAT_BPP(format) + 7) / 8;
+ stride = pixman_image_get_stride(res->image);
+
+ if (t2d.offset || t2d.r.x || t2d.r.y ||
+ t2d.r.width != pixman_image_get_width(res->image)) {
+ void *img_data = pixman_image_get_data(res->image);
+ for (h = 0; h < t2d.r.height; h++) {
+ src_offset = t2d.offset + stride * h;
+ dst_offset = (t2d.r.y + h) * stride + (t2d.r.x * bpp);
+
+ iov_to_buf(res->iov, res->iov_cnt, src_offset,
+ (uint8_t *)img_data
+ + dst_offset, t2d.r.width * bpp);
+ }
+ } else {
+ iov_to_buf(res->iov, res->iov_cnt, 0,
+ pixman_image_get_data(res->image),
+ pixman_image_get_stride(res->image)
+ * pixman_image_get_height(res->image));
+ }
+}
+
+static void virtio_gpu_resource_flush(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_simple_resource *res;
+ struct virtio_gpu_resource_flush rf;
+ pixman_region16_t flush_region;
+ int i;
+
+ VIRTIO_GPU_FILL_CMD(rf);
+ trace_virtio_gpu_cmd_res_flush(rf.resource_id,
+ rf.r.width, rf.r.height, rf.r.x, rf.r.y);
+
+ res = virtio_gpu_find_resource(g, rf.resource_id);
+ if (!res) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
+ __func__, rf.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+
+ if (rf.r.x > res->width ||
+ rf.r.y > res->height ||
+ rf.r.width > res->width ||
+ rf.r.height > res->height ||
+ rf.r.x + rf.r.width > res->width ||
+ rf.r.y + rf.r.height > res->height) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: flush bounds outside resource"
+ " bounds for resource %d: %d %d %d %d vs %d %d\n",
+ __func__, rf.resource_id, rf.r.x, rf.r.y,
+ rf.r.width, rf.r.height, res->width, res->height);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+ return;
+ }
+
+ pixman_region_init_rect(&flush_region,
+ rf.r.x, rf.r.y, rf.r.width, rf.r.height);
+ for (i = 0; i < VIRTIO_GPU_MAX_SCANOUT; i++) {
+ struct virtio_gpu_scanout *scanout;
+ pixman_region16_t region, finalregion;
+ pixman_box16_t *extents;
+
+ if (!(res->scanout_bitmask & (1 << i))) {
+ continue;
+ }
+ scanout = &g->scanout[i];
+
+ pixman_region_init(&finalregion);
+ pixman_region_init_rect(&region, scanout->x, scanout->y,
+ scanout->width, scanout->height);
+
+ pixman_region_intersect(&finalregion, &flush_region, &region);
+ pixman_region_translate(&finalregion, -scanout->x, -scanout->y);
+ extents = pixman_region_extents(&finalregion);
+ /* work out the area we need to update for each console */
+ dpy_gfx_update(g->scanout[i].con,
+ extents->x1, extents->y1,
+ extents->x2 - extents->x1,
+ extents->y2 - extents->y1);
+
+ pixman_region_fini(&region);
+ pixman_region_fini(&finalregion);
+ }
+ pixman_region_fini(&flush_region);
+}
+
+static void virtio_gpu_set_scanout(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_simple_resource *res;
+ struct virtio_gpu_scanout *scanout;
+ pixman_format_code_t format;
+ uint32_t offset;
+ int bpp;
+ struct virtio_gpu_set_scanout ss;
+
+ VIRTIO_GPU_FILL_CMD(ss);
+ trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id,
+ ss.r.width, ss.r.height, ss.r.x, ss.r.y);
+
+ g->enable = 1;
+ if (ss.resource_id == 0) {
+ scanout = &g->scanout[ss.scanout_id];
+ if (scanout->resource_id) {
+ res = virtio_gpu_find_resource(g, scanout->resource_id);
+ if (res) {
+ res->scanout_bitmask &= ~(1 << ss.scanout_id);
+ }
+ }
+ if (ss.scanout_id == 0 ||
+ ss.scanout_id >= g->conf.max_outputs) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: illegal scanout id specified %d",
+ __func__, ss.scanout_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
+ return;
+ }
+ dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL);
+ scanout->ds = NULL;
+ scanout->width = 0;
+ scanout->height = 0;
+ return;
+ }
+
+ /* create a surface for this scanout */
+ if (ss.scanout_id >= VIRTIO_GPU_MAX_SCANOUT ||
+ ss.scanout_id >= g->conf.max_outputs) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %d",
+ __func__, ss.scanout_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
+ return;
+ }
+
+ res = virtio_gpu_find_resource(g, ss.resource_id);
+ if (!res) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
+ __func__, ss.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+
+ if (ss.r.x > res->width ||
+ ss.r.y > res->height ||
+ ss.r.width > res->width ||
+ ss.r.height > res->height ||
+ ss.r.x + ss.r.width > res->width ||
+ ss.r.y + ss.r.height > res->height) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for"
+ " resource %d, (%d,%d)+%d,%d vs %d %d\n",
+ __func__, ss.scanout_id, ss.resource_id, ss.r.x, ss.r.y,
+ ss.r.width, ss.r.height, res->width, res->height);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+ return;
+ }
+
+ scanout = &g->scanout[ss.scanout_id];
+
+ format = pixman_image_get_format(res->image);
+ bpp = (PIXMAN_FORMAT_BPP(format) + 7) / 8;
+ offset = (ss.r.x * bpp) + ss.r.y * pixman_image_get_stride(res->image);
+ if (!scanout->ds || surface_data(scanout->ds)
+ != ((uint8_t *)pixman_image_get_data(res->image) + offset) ||
+ scanout->width != ss.r.width ||
+ scanout->height != ss.r.height) {
+ /* realloc the surface ptr */
+ scanout->ds = qemu_create_displaysurface_from
+ (ss.r.width, ss.r.height, format,
+ pixman_image_get_stride(res->image),
+ (uint8_t *)pixman_image_get_data(res->image) + offset);
+ if (!scanout->ds) {
+ cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+ return;
+ }
+ dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, scanout->ds);
+ }
+
+ res->scanout_bitmask |= (1 << ss.scanout_id);
+ scanout->resource_id = ss.resource_id;
+ scanout->x = ss.r.x;
+ scanout->y = ss.r.y;
+ scanout->width = ss.r.width;
+ scanout->height = ss.r.height;
+}
+
+int virtio_gpu_create_mapping_iov(struct virtio_gpu_resource_attach_backing *ab,
+ struct virtio_gpu_ctrl_command *cmd,
+ struct iovec **iov)
+{
+ struct virtio_gpu_mem_entry *ents;
+ size_t esize, s;
+ int i;
+
+ if (ab->nr_entries > 16384) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: nr_entries is too big (%d > 16384)\n",
+ __func__, ab->nr_entries);
+ return -1;
+ }
+
+ esize = sizeof(*ents) * ab->nr_entries;
+ ents = g_malloc(esize);
+ s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num,
+ sizeof(*ab), ents, esize);
+ if (s != esize) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: command data size incorrect %zu vs %zu\n",
+ __func__, s, esize);
+ g_free(ents);
+ return -1;
+ }
+
+ *iov = g_malloc0(sizeof(struct iovec) * ab->nr_entries);
+ for (i = 0; i < ab->nr_entries; i++) {
+ hwaddr len = ents[i].length;
+ (*iov)[i].iov_len = ents[i].length;
+ (*iov)[i].iov_base = cpu_physical_memory_map(ents[i].addr, &len, 1);
+ if (!(*iov)[i].iov_base || len != ents[i].length) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to map MMIO memory for"
+ " resource %d element %d\n",
+ __func__, ab->resource_id, i);
+ virtio_gpu_cleanup_mapping_iov(*iov, i);
+ g_free(ents);
+ g_free(*iov);
+ *iov = NULL;
+ return -1;
+ }
+ }
+ g_free(ents);
+ return 0;
+}
+
+void virtio_gpu_cleanup_mapping_iov(struct iovec *iov, uint32_t count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ cpu_physical_memory_unmap(iov[i].iov_base, iov[i].iov_len, 1,
+ iov[i].iov_len);
+ }
+}
+
+static void virtio_gpu_cleanup_mapping(struct virtio_gpu_simple_resource *res)
+{
+ virtio_gpu_cleanup_mapping_iov(res->iov, res->iov_cnt);
+ g_free(res->iov);
+ res->iov = NULL;
+ res->iov_cnt = 0;
+}
+
+static void
+virtio_gpu_resource_attach_backing(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_simple_resource *res;
+ struct virtio_gpu_resource_attach_backing ab;
+ int ret;
+
+ VIRTIO_GPU_FILL_CMD(ab);
+ trace_virtio_gpu_cmd_res_back_attach(ab.resource_id);
+
+ res = virtio_gpu_find_resource(g, ab.resource_id);
+ if (!res) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
+ __func__, ab.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+
+ ret = virtio_gpu_create_mapping_iov(&ab, cmd, &res->iov);
+ if (ret != 0) {
+ cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+ return;
+ }
+
+ res->iov_cnt = ab.nr_entries;
+}
+
+static void
+virtio_gpu_resource_detach_backing(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_simple_resource *res;
+ struct virtio_gpu_resource_detach_backing detach;
+
+ VIRTIO_GPU_FILL_CMD(detach);
+ trace_virtio_gpu_cmd_res_back_detach(detach.resource_id);
+
+ res = virtio_gpu_find_resource(g, detach.resource_id);
+ if (!res || !res->iov) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
+ __func__, detach.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+ virtio_gpu_cleanup_mapping(res);
+}
+
+static void virtio_gpu_simple_process_cmd(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ VIRTIO_GPU_FILL_CMD(cmd->cmd_hdr);
+
+ switch (cmd->cmd_hdr.type) {
+ case VIRTIO_GPU_CMD_GET_DISPLAY_INFO:
+ virtio_gpu_get_display_info(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D:
+ virtio_gpu_resource_create_2d(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_UNREF:
+ virtio_gpu_resource_unref(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_FLUSH:
+ virtio_gpu_resource_flush(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D:
+ virtio_gpu_transfer_to_host_2d(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_SET_SCANOUT:
+ virtio_gpu_set_scanout(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING:
+ virtio_gpu_resource_attach_backing(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING:
+ virtio_gpu_resource_detach_backing(g, cmd);
+ break;
+ default:
+ cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+ break;
+ }
+ if (!cmd->finished) {
+ virtio_gpu_ctrl_response_nodata(g, cmd, cmd->error ? cmd->error :
+ VIRTIO_GPU_RESP_OK_NODATA);
+ }
+}
+
+static void virtio_gpu_handle_ctrl_cb(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+ qemu_bh_schedule(g->ctrl_bh);
+}
+
+static void virtio_gpu_handle_cursor_cb(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+ qemu_bh_schedule(g->cursor_bh);
+}
+
+static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+ struct virtio_gpu_ctrl_command *cmd;
+
+ if (!virtio_queue_ready(vq)) {
+ return;
+ }
+
+ cmd = g_new(struct virtio_gpu_ctrl_command, 1);
+ while (virtqueue_pop(vq, &cmd->elem)) {
+ cmd->vq = vq;
+ cmd->error = 0;
+ cmd->finished = false;
+ g->stats.requests++;
+
+ virtio_gpu_simple_process_cmd(g, cmd);
+ if (!cmd->finished) {
+ QTAILQ_INSERT_TAIL(&g->fenceq, cmd, next);
+ g->stats.inflight++;
+ if (g->stats.max_inflight < g->stats.inflight) {
+ g->stats.max_inflight = g->stats.inflight;
+ }
+ fprintf(stderr, "inflight: %3d (+)\r", g->stats.inflight);
+ cmd = g_new(struct virtio_gpu_ctrl_command, 1);
+ }
+ }
+ g_free(cmd);
+}
+
+static void virtio_gpu_ctrl_bh(void *opaque)
+{
+ VirtIOGPU *g = opaque;
+ virtio_gpu_handle_ctrl(&g->parent_obj, g->ctrl_vq);
+}
+
+static void virtio_gpu_handle_cursor(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+ VirtQueueElement elem;
+ size_t s;
+ struct virtio_gpu_update_cursor cursor_info;
+
+ if (!virtio_queue_ready(vq)) {
+ return;
+ }
+ while (virtqueue_pop(vq, &elem)) {
+ s = iov_to_buf(elem.out_sg, elem.out_num, 0,
+ &cursor_info, sizeof(cursor_info));
+ if (s != sizeof(cursor_info)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: cursor size incorrect %zu vs %zu\n",
+ __func__, s, sizeof(cursor_info));
+ } else {
+ update_cursor(g, &cursor_info);
+ }
+ virtqueue_push(vq, &elem, 0);
+ virtio_notify(vdev, vq);
+ }
+}
+
+static void virtio_gpu_cursor_bh(void *opaque)
+{
+ VirtIOGPU *g = opaque;
+ virtio_gpu_handle_cursor(&g->parent_obj, g->cursor_vq);
+}
+
+static void virtio_gpu_invalidate_display(void *opaque)
+{
+}
+
+static void virtio_gpu_update_display(void *opaque)
+{
+}
+
+static void virtio_gpu_text_update(void *opaque, console_ch_t *chardata)
+{
+}
+
+static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
+{
+ VirtIOGPU *g = opaque;
+
+ if (idx > g->conf.max_outputs) {
+ return -1;
+ }
+
+ g->req_state[idx].x = info->xoff;
+ g->req_state[idx].y = info->yoff;
+ g->req_state[idx].width = info->width;
+ g->req_state[idx].height = info->height;
+
+ if (info->width && info->height) {
+ g->enabled_output_bitmask |= (1 << idx);
+ } else {
+ g->enabled_output_bitmask &= ~(1 << idx);
+ }
+
+ /* send event to guest */
+ virtio_gpu_notify_event(g, VIRTIO_GPU_EVENT_DISPLAY);
+ return 0;
+}
+
+const GraphicHwOps virtio_gpu_ops = {
+ .invalidate = virtio_gpu_invalidate_display,
+ .gfx_update = virtio_gpu_update_display,
+ .text_update = virtio_gpu_text_update,
+ .ui_info = virtio_gpu_ui_info,
+};
+
+static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
+ VirtIOGPU *g = VIRTIO_GPU(qdev);
+ int i;
+
+ g->config_size = sizeof(struct virtio_gpu_config);
+ g->virtio_config.num_scanouts = g->conf.max_outputs;
+ virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU,
+ g->config_size);
+
+ g->req_state[0].width = 1024;
+ g->req_state[0].height = 768;
+
+ g->ctrl_vq = virtio_add_queue(vdev, 64, virtio_gpu_handle_ctrl_cb);
+ g->cursor_vq = virtio_add_queue(vdev, 16, virtio_gpu_handle_cursor_cb);
+
+ g->ctrl_bh = qemu_bh_new(virtio_gpu_ctrl_bh, g);
+ g->cursor_bh = qemu_bh_new(virtio_gpu_cursor_bh, g);
+ QTAILQ_INIT(&g->reslist);
+ QTAILQ_INIT(&g->fenceq);
+
+ g->enabled_output_bitmask = 1;
+ g->qdev = qdev;
+
+ for (i = 0; i < g->conf.max_outputs; i++) {
+ g->scanout[i].con =
+ graphic_console_init(DEVICE(g), i, &virtio_gpu_ops, g);
+ if (i > 0) {
+ dpy_gfx_replace_surface(g->scanout[i].con, NULL);
+ }
+ }
+}
+
+static void virtio_gpu_instance_init(Object *obj)
+{
+}
+
+static void virtio_gpu_reset(VirtIODevice *vdev)
+{
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+ struct virtio_gpu_simple_resource *res, *tmp;
+ int i;
+
+ g->enable = 0;
+
+ QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) {
+ virtio_gpu_resource_destroy(g, res);
+ }
+ for (i = 0; i < g->conf.max_outputs; i++) {
+#if 0
+ g->req_state[i].x = 0;
+ g->req_state[i].y = 0;
+ if (i == 0) {
+ g->req_state[0].width = 1024;
+ g->req_state[0].height = 768;
+ } else {
+ g->req_state[i].width = 0;
+ g->req_state[i].height = 0;
+ }
+#endif
+ g->scanout[i].resource_id = 0;
+ g->scanout[i].width = 0;
+ g->scanout[i].height = 0;
+ g->scanout[i].x = 0;
+ g->scanout[i].y = 0;
+ g->scanout[i].ds = NULL;
+ }
+ g->enabled_output_bitmask = 1;
+}
+
+static Property virtio_gpu_properties[] = {
+ DEFINE_PROP_UINT32("max_outputs", VirtIOGPU, conf.max_outputs, 1),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_gpu_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+
+ vdc->realize = virtio_gpu_device_realize;
+ vdc->get_config = virtio_gpu_get_config;
+ vdc->set_config = virtio_gpu_set_config;
+ vdc->get_features = virtio_gpu_get_features;
+
+ vdc->reset = virtio_gpu_reset;
+
+ dc->props = virtio_gpu_properties;
+}
+
+static const TypeInfo virtio_gpu_info = {
+ .name = TYPE_VIRTIO_GPU,
+ .parent = TYPE_VIRTIO_DEVICE,
+ .instance_size = sizeof(VirtIOGPU),
+ .instance_init = virtio_gpu_instance_init,
+ .class_init = virtio_gpu_class_init,
+};
+
+static void virtio_register_types(void)
+{
+ type_register_static(&virtio_gpu_info);
+}
+
+type_init(virtio_register_types)
+
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctrl_hdr) != 24);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_update_cursor) != 56);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_unref) != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_create_2d) != 40);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_set_scanout) != 48);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_flush) != 48);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_transfer_to_host_2d) != 56);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_mem_entry) != 16);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_attach_backing) != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_detach_backing) != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_display_info) != 408);
diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c
new file mode 100644
index 000000000..f7e539fe9
--- /dev/null
+++ b/hw/display/virtio-vga.c
@@ -0,0 +1,182 @@
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "ui/console.h"
+#include "vga_int.h"
+#include "hw/virtio/virtio-pci.h"
+
+/*
+ * virtio-vga: This extends VirtioPCIProxy.
+ */
+#define TYPE_VIRTIO_VGA "virtio-vga"
+#define VIRTIO_VGA(obj) \
+ OBJECT_CHECK(VirtIOVGA, (obj), TYPE_VIRTIO_VGA)
+
+typedef struct VirtIOVGA {
+ VirtIOPCIProxy parent_obj;
+ VirtIOGPU vdev;
+ VGACommonState vga;
+ MemoryRegion vga_mrs[3];
+} VirtIOVGA;
+
+static void virtio_vga_invalidate_display(void *opaque)
+{
+ VirtIOVGA *vvga = opaque;
+
+ if (vvga->vdev.enable) {
+ virtio_gpu_ops.invalidate(&vvga->vdev);
+ } else {
+ vvga->vga.hw_ops->invalidate(&vvga->vga);
+ }
+}
+
+static void virtio_vga_update_display(void *opaque)
+{
+ VirtIOVGA *vvga = opaque;
+
+ if (vvga->vdev.enable) {
+ virtio_gpu_ops.gfx_update(&vvga->vdev);
+ } else {
+ vvga->vga.hw_ops->gfx_update(&vvga->vga);
+ }
+}
+
+static void virtio_vga_text_update(void *opaque, console_ch_t *chardata)
+{
+ VirtIOVGA *vvga = opaque;
+
+ if (vvga->vdev.enable) {
+ if (virtio_gpu_ops.text_update) {
+ virtio_gpu_ops.text_update(&vvga->vdev, chardata);
+ }
+ } else {
+ if (vvga->vga.hw_ops->text_update) {
+ vvga->vga.hw_ops->text_update(&vvga->vga, chardata);
+ }
+ }
+}
+
+static int virtio_vga_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
+{
+ VirtIOVGA *vvga = opaque;
+
+ if (virtio_gpu_ops.ui_info) {
+ return virtio_gpu_ops.ui_info(&vvga->vdev, idx, info);
+ }
+ return -1;
+}
+
+static const GraphicHwOps virtio_vga_ops = {
+ .invalidate = virtio_vga_invalidate_display,
+ .gfx_update = virtio_vga_update_display,
+ .text_update = virtio_vga_text_update,
+ .ui_info = virtio_vga_ui_info,
+};
+
+/* VGA device wrapper around PCI device around virtio GPU */
+static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ VirtIOVGA *vvga = VIRTIO_VGA(vpci_dev);
+ VirtIOGPU *g = &vvga->vdev;
+ VGACommonState *vga = &vvga->vga;
+ uint32_t offset;
+ int i;
+
+ /* init vga compat bits */
+ vga->vram_size_mb = 8;
+ vga_common_init(vga, OBJECT(vpci_dev), false);
+ vga_init(vga, OBJECT(vpci_dev), pci_address_space(&vpci_dev->pci_dev),
+ pci_address_space_io(&vpci_dev->pci_dev), true);
+ pci_register_bar(&vpci_dev->pci_dev, 0,
+ PCI_BASE_ADDRESS_MEM_PREFETCH, &vga->vram);
+
+ /*
+ * Configure virtio bar and regions
+ *
+ * We use bar #2 for the mmio regions, to be compatible with stdvga.
+ * virtio regions are moved to the end of bar #2, to make room for
+ * the stdvga mmio registers at the start of bar #2.
+ */
+ vpci_dev->modern_mem_bar = 2;
+ vpci_dev->msix_bar = 4;
+ offset = memory_region_size(&vpci_dev->modern_bar);
+ offset -= vpci_dev->notify.size;
+ vpci_dev->notify.offset = offset;
+ offset -= vpci_dev->device.size;
+ vpci_dev->device.offset = offset;
+ offset -= vpci_dev->isr.size;
+ vpci_dev->isr.offset = offset;
+ offset -= vpci_dev->common.size;
+ vpci_dev->common.offset = offset;
+
+ /* init virtio bits */
+ qdev_set_parent_bus(DEVICE(g), BUS(&vpci_dev->bus));
+ /* force virtio-1.0 */
+ vpci_dev->flags &= ~VIRTIO_PCI_FLAG_DISABLE_MODERN;
+ vpci_dev->flags |= VIRTIO_PCI_FLAG_DISABLE_LEGACY;
+ object_property_set_bool(OBJECT(g), true, "realized", errp);
+
+ /* add stdvga mmio regions */
+ pci_std_vga_mmio_region_init(vga, &vpci_dev->modern_bar,
+ vvga->vga_mrs, true);
+
+ vga->con = g->scanout[0].con;
+ graphic_console_set_hwops(vga->con, &virtio_vga_ops, vvga);
+
+ for (i = 0; i < g->conf.max_outputs; i++) {
+ object_property_set_link(OBJECT(g->scanout[i].con),
+ OBJECT(vpci_dev),
+ "device", errp);
+ }
+}
+
+static void virtio_vga_reset(DeviceState *dev)
+{
+ VirtIOVGA *vvga = VIRTIO_VGA(dev);
+ vvga->vdev.enable = 0;
+
+ vga_dirty_log_start(&vvga->vga);
+}
+
+static Property virtio_vga_properties[] = {
+ DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_vga_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
+ dc->props = virtio_vga_properties;
+ dc->reset = virtio_vga_reset;
+ dc->hotpluggable = false;
+
+ k->realize = virtio_vga_realize;
+ pcidev_k->romfile = "vgabios-virtio.bin";
+ pcidev_k->class_id = PCI_CLASS_DISPLAY_VGA;
+}
+
+static void virtio_vga_inst_initfn(Object *obj)
+{
+ VirtIOVGA *dev = VIRTIO_VGA(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VIRTIO_GPU);
+}
+
+static TypeInfo virtio_vga_info = {
+ .name = TYPE_VIRTIO_VGA,
+ .parent = TYPE_VIRTIO_PCI,
+ .instance_size = sizeof(struct VirtIOVGA),
+ .instance_init = virtio_vga_inst_initfn,
+ .class_init = virtio_vga_class_init,
+};
+
+static void virtio_vga_register_types(void)
+{
+ type_register_static(&virtio_vga_info);
+}
+
+type_init(virtio_vga_register_types)
diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c
index c17ddd1fc..7f397d3c2 100644
--- a/hw/display/vmware_vga.c
+++ b/hw/display/vmware_vga.c
@@ -1124,7 +1124,7 @@ static void vmsvga_update_display(void *opaque)
* Is it more efficient to look at vram VGA-dirty bits or wait
* for the driver to issue SVGA_CMD_UPDATE?
*/
- if (memory_region_is_logging(&s->vga.vram)) {
+ if (memory_region_is_logging(&s->vga.vram, DIRTY_MEMORY_VGA)) {
vga_sync_dirty_bitmap(&s->vga);
dirty = memory_region_get_dirty(&s->vga.vram, 0,
surface_stride(surface) * surface_height(surface),
diff --git a/hw/dma/pl080.c b/hw/dma/pl080.c
index 741dd20d3..b89b4744f 100644
--- a/hw/dma/pl080.c
+++ b/hw/dma/pl080.c
@@ -205,10 +205,22 @@ again:
if (size == 0) {
/* Transfer complete. */
if (ch->lli) {
- ch->src = ldl_le_phys(&address_space_memory, ch->lli);
- ch->dest = ldl_le_phys(&address_space_memory, ch->lli + 4);
- ch->ctrl = ldl_le_phys(&address_space_memory, ch->lli + 12);
- ch->lli = ldl_le_phys(&address_space_memory, ch->lli + 8);
+ ch->src = address_space_ldl_le(&address_space_memory,
+ ch->lli,
+ MEMTXATTRS_UNSPECIFIED,
+ NULL);
+ ch->dest = address_space_ldl_le(&address_space_memory,
+ ch->lli + 4,
+ MEMTXATTRS_UNSPECIFIED,
+ NULL);
+ ch->ctrl = address_space_ldl_le(&address_space_memory,
+ ch->lli + 12,
+ MEMTXATTRS_UNSPECIFIED,
+ NULL);
+ ch->lli = address_space_ldl_le(&address_space_memory,
+ ch->lli + 8,
+ MEMTXATTRS_UNSPECIFIED,
+ NULL);
} else {
ch->conf &= ~PL080_CCONF_E;
}
diff --git a/hw/dma/rc4030.c b/hw/dma/rc4030.c
index af2663256..3efa6de35 100644
--- a/hw/dma/rc4030.c
+++ b/hw/dma/rc4030.c
@@ -1,7 +1,7 @@
/*
* QEMU JAZZ RC4030 chipset
*
- * Copyright (c) 2007-2009 Herve Poussineau
+ * Copyright (c) 2007-2013 Hervé Poussineau
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,29 +24,16 @@
#include "hw/hw.h"
#include "hw/mips/mips.h"
+#include "hw/sysbus.h"
#include "qemu/timer.h"
-
-/********************************************************/
-/* debug rc4030 */
-
-//#define DEBUG_RC4030
-//#define DEBUG_RC4030_DMA
-
-#ifdef DEBUG_RC4030
-#define DPRINTF(fmt, ...) \
-do { printf("rc4030: " fmt , ## __VA_ARGS__); } while (0)
-static const char* irq_names[] = { "parallel", "floppy", "sound", "video",
- "network", "scsi", "keyboard", "mouse", "serial0", "serial1" };
-#else
-#define DPRINTF(fmt, ...)
-#endif
-
-#define RC4030_ERROR(fmt, ...) \
-do { fprintf(stderr, "rc4030 ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
+#include "exec/address-spaces.h"
+#include "trace.h"
/********************************************************/
/* rc4030 emulation */
+#define MAX_TL_ENTRIES 512
+
typedef struct dma_pagetable_entry {
int32_t frame;
int32_t owner;
@@ -63,8 +50,14 @@ typedef struct dma_pagetable_entry {
#define DMA_FLAG_MEM_INTR 0x0200
#define DMA_FLAG_ADDR_INTR 0x0400
+#define TYPE_RC4030 "rc4030"
+#define RC4030(obj) \
+ OBJECT_CHECK(rc4030State, (obj), TYPE_RC4030)
+
typedef struct rc4030State
{
+ SysBusDevice parent;
+
uint32_t config; /* 0x0000: RC4030 config register */
uint32_t revision; /* 0x0008: RC4030 Revision register */
uint32_t invalid_address_register; /* 0x0010: Invalid Address register */
@@ -83,7 +76,7 @@ typedef struct rc4030State
uint32_t cache_bmask; /* 0x0058: I/O Cache Byte Mask */
uint32_t nmi_interrupt; /* 0x0200: interrupt source */
- uint32_t offset210;
+ uint32_t memory_refresh_rate; /* 0x0210: memory refresh rate */
uint32_t nvram_protect; /* 0x0220: NV ram protect register */
uint32_t rem_speed[16];
uint32_t imr_jazz; /* Local bus int enable mask */
@@ -96,6 +89,16 @@ typedef struct rc4030State
qemu_irq timer_irq;
qemu_irq jazz_bus_irq;
+ /* biggest translation table */
+ MemoryRegion dma_tt;
+ /* translation table memory region alias, added to system RAM */
+ MemoryRegion dma_tt_alias;
+ /* whole DMA memory region, root of DMA address space */
+ MemoryRegion dma_mr;
+ /* translation table entry aliases, added to DMA memory region */
+ MemoryRegion dma_mrs[MAX_TL_ENTRIES];
+ AddressSpace dma_as;
+
MemoryRegion iomem_chipset;
MemoryRegion iomem_jazzio;
} rc4030State;
@@ -112,7 +115,7 @@ static void set_next_tick(rc4030State *s)
}
/* called for accesses to rc4030 */
-static uint32_t rc4030_readl(void *opaque, hwaddr addr)
+static uint64_t rc4030_read(void *opaque, hwaddr addr, unsigned int size)
{
rc4030State *s = opaque;
uint32_t val;
@@ -220,9 +223,9 @@ static uint32_t rc4030_readl(void *opaque, hwaddr addr)
case 0x0208:
val = 0;
break;
- /* Offset 0x0210 */
+ /* Memory refresh rate */
case 0x0210:
- val = s->offset210;
+ val = s->memory_refresh_rate;
break;
/* NV ram protect register */
case 0x0220:
@@ -238,39 +241,117 @@ static uint32_t rc4030_readl(void *opaque, hwaddr addr)
val = 7; /* FIXME: should be read from EISA controller */
break;
default:
- RC4030_ERROR("invalid read [" TARGET_FMT_plx "]\n", addr);
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "rc4030: invalid read at 0x%x", (int)addr);
val = 0;
break;
}
if ((addr & ~3) != 0x230) {
- DPRINTF("read 0x%02x at " TARGET_FMT_plx "\n", val, addr);
+ trace_rc4030_read(addr, val);
}
return val;
}
-static uint32_t rc4030_readw(void *opaque, hwaddr addr)
+static void rc4030_dma_as_update_one(rc4030State *s, int index, uint32_t frame)
{
- uint32_t v = rc4030_readl(opaque, addr & ~0x3);
- if (addr & 0x2)
- return v >> 16;
- else
- return v & 0xffff;
+ if (index < MAX_TL_ENTRIES) {
+ memory_region_set_enabled(&s->dma_mrs[index], false);
+ }
+
+ if (!frame) {
+ return;
+ }
+
+ if (index >= MAX_TL_ENTRIES) {
+ qemu_log_mask(LOG_UNIMP,
+ "rc4030: trying to use too high "
+ "translation table entry %d (max allowed=%d)",
+ index, MAX_TL_ENTRIES);
+ return;
+ }
+ memory_region_set_alias_offset(&s->dma_mrs[index], frame);
+ memory_region_set_enabled(&s->dma_mrs[index], true);
}
-static uint32_t rc4030_readb(void *opaque, hwaddr addr)
+static void rc4030_dma_tt_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned int size)
{
- uint32_t v = rc4030_readl(opaque, addr & ~0x3);
- return (v >> (8 * (addr & 0x3))) & 0xff;
+ rc4030State *s = opaque;
+
+ /* write memory */
+ memcpy(memory_region_get_ram_ptr(&s->dma_tt) + addr, &data, size);
+
+ /* update dma address space (only if frame field has been written) */
+ if (addr % sizeof(dma_pagetable_entry) == 0) {
+ int index = addr / sizeof(dma_pagetable_entry);
+ memory_region_transaction_begin();
+ rc4030_dma_as_update_one(s, index, (uint32_t)data);
+ memory_region_transaction_commit();
+ }
}
-static void rc4030_writel(void *opaque, hwaddr addr, uint32_t val)
+static const MemoryRegionOps rc4030_dma_tt_ops = {
+ .write = rc4030_dma_tt_write,
+ .impl.min_access_size = 4,
+ .impl.max_access_size = 4,
+};
+
+static void rc4030_dma_tt_update(rc4030State *s, uint32_t new_tl_base,
+ uint32_t new_tl_limit)
+{
+ int entries, i;
+ dma_pagetable_entry *dma_tl_contents;
+
+ if (s->dma_tl_limit) {
+ /* write old dma tl table to physical memory */
+ memory_region_del_subregion(get_system_memory(), &s->dma_tt_alias);
+ cpu_physical_memory_write(s->dma_tl_limit & 0x7fffffff,
+ memory_region_get_ram_ptr(&s->dma_tt),
+ memory_region_size(&s->dma_tt_alias));
+ }
+ object_unparent(OBJECT(&s->dma_tt_alias));
+
+ s->dma_tl_base = new_tl_base;
+ s->dma_tl_limit = new_tl_limit;
+ new_tl_base &= 0x7fffffff;
+
+ if (s->dma_tl_limit) {
+ uint64_t dma_tt_size;
+ if (s->dma_tl_limit <= memory_region_size(&s->dma_tt)) {
+ dma_tt_size = s->dma_tl_limit;
+ } else {
+ dma_tt_size = memory_region_size(&s->dma_tt);
+ }
+ memory_region_init_alias(&s->dma_tt_alias, OBJECT(s),
+ "dma-table-alias",
+ &s->dma_tt, 0, dma_tt_size);
+ dma_tl_contents = memory_region_get_ram_ptr(&s->dma_tt);
+ cpu_physical_memory_read(new_tl_base, dma_tl_contents, dma_tt_size);
+
+ memory_region_transaction_begin();
+ entries = dma_tt_size / sizeof(dma_pagetable_entry);
+ for (i = 0; i < entries; i++) {
+ rc4030_dma_as_update_one(s, i, dma_tl_contents[i].frame);
+ }
+ memory_region_add_subregion(get_system_memory(), new_tl_base,
+ &s->dma_tt_alias);
+ memory_region_transaction_commit();
+ } else {
+ memory_region_init(&s->dma_tt_alias, OBJECT(s),
+ "dma-table-alias", 0);
+ }
+}
+
+static void rc4030_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned int size)
{
rc4030State *s = opaque;
+ uint32_t val = data;
addr &= 0x3fff;
- DPRINTF("write 0x%02x at " TARGET_FMT_plx "\n", val, addr);
+ trace_rc4030_write(addr, val);
switch (addr & ~0x3) {
/* Global config register */
@@ -279,11 +360,11 @@ static void rc4030_writel(void *opaque, hwaddr addr, uint32_t val)
break;
/* DMA transl. table base */
case 0x0018:
- s->dma_tl_base = val;
+ rc4030_dma_tt_update(s, val, s->dma_tl_limit);
break;
/* DMA transl. table limit */
case 0x0020:
- s->dma_tl_limit = val;
+ rc4030_dma_tt_update(s, s->dma_tl_base, val);
break;
/* DMA transl. table invalidated */
case 0x0028:
@@ -371,9 +452,9 @@ static void rc4030_writel(void *opaque, hwaddr addr, uint32_t val)
s->dma_regs[entry][idx] = val;
}
break;
- /* Offset 0x0210 */
+ /* Memory refresh rate */
case 0x0210:
- s->offset210 = val;
+ s->memory_refresh_rate = val;
break;
/* Interval timer reload */
case 0x0228:
@@ -385,48 +466,18 @@ static void rc4030_writel(void *opaque, hwaddr addr, uint32_t val)
case 0x0238:
break;
default:
- RC4030_ERROR("invalid write of 0x%02x at [" TARGET_FMT_plx "]\n", val, addr);
- break;
- }
-}
-
-static void rc4030_writew(void *opaque, hwaddr addr, uint32_t val)
-{
- uint32_t old_val = rc4030_readl(opaque, addr & ~0x3);
-
- if (addr & 0x2)
- val = (val << 16) | (old_val & 0x0000ffff);
- else
- val = val | (old_val & 0xffff0000);
- rc4030_writel(opaque, addr & ~0x3, val);
-}
-
-static void rc4030_writeb(void *opaque, hwaddr addr, uint32_t val)
-{
- uint32_t old_val = rc4030_readl(opaque, addr & ~0x3);
-
- switch (addr & 3) {
- case 0:
- val = val | (old_val & 0xffffff00);
- break;
- case 1:
- val = (val << 8) | (old_val & 0xffff00ff);
- break;
- case 2:
- val = (val << 16) | (old_val & 0xff00ffff);
- break;
- case 3:
- val = (val << 24) | (old_val & 0x00ffffff);
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "rc4030: invalid write of 0x%02x at 0x%x",
+ val, (int)addr);
break;
}
- rc4030_writel(opaque, addr & ~0x3, val);
}
static const MemoryRegionOps rc4030_ops = {
- .old_mmio = {
- .read = { rc4030_readb, rc4030_readw, rc4030_readl, },
- .write = { rc4030_writeb, rc4030_writew, rc4030_writel, },
- },
+ .read = rc4030_read,
+ .write = rc4030_write,
+ .impl.min_access_size = 4,
+ .impl.max_access_size = 4,
.endianness = DEVICE_NATIVE_ENDIAN,
};
@@ -436,22 +487,6 @@ static void update_jazz_irq(rc4030State *s)
pending = s->isr_jazz & s->imr_jazz;
-#ifdef DEBUG_RC4030
- if (s->isr_jazz != 0) {
- uint32_t irq = 0;
- DPRINTF("pending irqs:");
- for (irq = 0; irq < ARRAY_SIZE(irq_names); irq++) {
- if (s->isr_jazz & (1 << irq)) {
- printf(" %s", irq_names[irq]);
- if (!(s->imr_jazz & (1 << irq))) {
- printf("(ignored)");
- }
- }
- }
- printf("\n");
- }
-#endif
-
if (pending != 0)
qemu_irq_raise(s->jazz_bus_irq);
else
@@ -479,7 +514,7 @@ static void rc4030_periodic_timer(void *opaque)
qemu_irq_raise(s->timer_irq);
}
-static uint32_t jazzio_readw(void *opaque, hwaddr addr)
+static uint64_t jazzio_read(void *opaque, hwaddr addr, unsigned int size)
{
rc4030State *s = opaque;
uint32_t val;
@@ -494,7 +529,6 @@ static uint32_t jazzio_readw(void *opaque, hwaddr addr)
irq = 0;
while (pending) {
if (pending & 1) {
- DPRINTF("returning irq %s\n", irq_names[irq]);
val = (irq + 1) << 2;
break;
}
@@ -508,36 +542,25 @@ static uint32_t jazzio_readw(void *opaque, hwaddr addr)
val = s->imr_jazz;
break;
default:
- RC4030_ERROR("(jazz io controller) invalid read [" TARGET_FMT_plx "]\n", addr);
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "rc4030/jazzio: invalid read at 0x%x", (int)addr);
val = 0;
+ break;
}
- DPRINTF("(jazz io controller) read 0x%04x at " TARGET_FMT_plx "\n", val, addr);
+ trace_jazzio_read(addr, val);
return val;
}
-static uint32_t jazzio_readb(void *opaque, hwaddr addr)
-{
- uint32_t v;
- v = jazzio_readw(opaque, addr & ~0x1);
- return (v >> (8 * (addr & 0x1))) & 0xff;
-}
-
-static uint32_t jazzio_readl(void *opaque, hwaddr addr)
-{
- uint32_t v;
- v = jazzio_readw(opaque, addr);
- v |= jazzio_readw(opaque, addr + 2) << 16;
- return v;
-}
-
-static void jazzio_writew(void *opaque, hwaddr addr, uint32_t val)
+static void jazzio_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned int size)
{
rc4030State *s = opaque;
+ uint32_t val = data;
addr &= 0xfff;
- DPRINTF("(jazz io controller) write 0x%04x at " TARGET_FMT_plx "\n", val, addr);
+ trace_jazzio_write(addr, val);
switch (addr) {
/* Local bus int enable mask */
@@ -546,43 +569,24 @@ static void jazzio_writew(void *opaque, hwaddr addr, uint32_t val)
update_jazz_irq(s);
break;
default:
- RC4030_ERROR("(jazz io controller) invalid write of 0x%04x at [" TARGET_FMT_plx "]\n", val, addr);
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "rc4030/jazzio: invalid write of 0x%02x at 0x%x",
+ val, (int)addr);
break;
}
}
-static void jazzio_writeb(void *opaque, hwaddr addr, uint32_t val)
-{
- uint32_t old_val = jazzio_readw(opaque, addr & ~0x1);
-
- switch (addr & 1) {
- case 0:
- val = val | (old_val & 0xff00);
- break;
- case 1:
- val = (val << 8) | (old_val & 0x00ff);
- break;
- }
- jazzio_writew(opaque, addr & ~0x1, val);
-}
-
-static void jazzio_writel(void *opaque, hwaddr addr, uint32_t val)
-{
- jazzio_writew(opaque, addr, val & 0xffff);
- jazzio_writew(opaque, addr + 2, (val >> 16) & 0xffff);
-}
-
static const MemoryRegionOps jazzio_ops = {
- .old_mmio = {
- .read = { jazzio_readb, jazzio_readw, jazzio_readl, },
- .write = { jazzio_writeb, jazzio_writew, jazzio_writel, },
- },
+ .read = jazzio_read,
+ .write = jazzio_write,
+ .impl.min_access_size = 2,
+ .impl.max_access_size = 2,
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static void rc4030_reset(void *opaque)
+static void rc4030_reset(DeviceState *dev)
{
- rc4030State *s = opaque;
+ rc4030State *s = RC4030(dev);
int i;
s->config = 0x410; /* some boards seem to accept 0x104 too */
@@ -590,14 +594,14 @@ static void rc4030_reset(void *opaque)
s->invalid_address_register = 0;
memset(s->dma_regs, 0, sizeof(s->dma_regs));
- s->dma_tl_base = s->dma_tl_limit = 0;
+ rc4030_dma_tt_update(s, 0, 0);
s->remote_failed_address = s->memory_failed_address = 0;
s->cache_maint = 0;
s->cache_ptag = s->cache_ltag = 0;
s->cache_bmask = 0;
- s->offset210 = 0x18186;
+ s->memory_refresh_rate = 0x18186;
s->nvram_protect = 7;
for (i = 0; i < 15; i++)
s->rem_speed[i] = 7;
@@ -631,7 +635,7 @@ static int rc4030_load(QEMUFile *f, void *opaque, int version_id)
s->cache_ptag = qemu_get_be32(f);
s->cache_ltag = qemu_get_be32(f);
s->cache_bmask = qemu_get_be32(f);
- s->offset210 = qemu_get_be32(f);
+ s->memory_refresh_rate = qemu_get_be32(f);
s->nvram_protect = qemu_get_be32(f);
for (i = 0; i < 15; i++)
s->rem_speed[i] = qemu_get_be32(f);
@@ -663,7 +667,7 @@ static void rc4030_save(QEMUFile *f, void *opaque)
qemu_put_be32(f, s->cache_ptag);
qemu_put_be32(f, s->cache_ltag);
qemu_put_be32(f, s->cache_bmask);
- qemu_put_be32(f, s->offset210);
+ qemu_put_be32(f, s->memory_refresh_rate);
qemu_put_be32(f, s->nvram_protect);
for (i = 0; i < 15; i++)
qemu_put_be32(f, s->rem_speed[i]);
@@ -672,44 +676,6 @@ static void rc4030_save(QEMUFile *f, void *opaque)
qemu_put_be32(f, s->itr);
}
-void rc4030_dma_memory_rw(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write)
-{
- rc4030State *s = opaque;
- hwaddr entry_addr;
- hwaddr phys_addr;
- dma_pagetable_entry entry;
- int index;
- int ncpy, i;
-
- i = 0;
- for (;;) {
- if (i == len) {
- break;
- }
-
- ncpy = DMA_PAGESIZE - (addr & (DMA_PAGESIZE - 1));
- if (ncpy > len - i)
- ncpy = len - i;
-
- /* Get DMA translation table entry */
- index = addr / DMA_PAGESIZE;
- if (index >= s->dma_tl_limit / sizeof(dma_pagetable_entry)) {
- break;
- }
- entry_addr = s->dma_tl_base + index * sizeof(dma_pagetable_entry);
- /* XXX: not sure. should we really use only lowest bits? */
- entry_addr &= 0x7fffffff;
- cpu_physical_memory_read(entry_addr, &entry, sizeof(entry));
-
- /* Read/write data at right place */
- phys_addr = entry.frame + (addr & (DMA_PAGESIZE - 1));
- cpu_physical_memory_rw(phys_addr, &buf[i], ncpy, is_write);
-
- i += ncpy;
- addr += ncpy;
- }
-}
-
static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_write)
{
rc4030State *s = opaque;
@@ -733,32 +699,11 @@ static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_wri
dma_addr = s->dma_regs[n][DMA_REG_ADDRESS];
/* Read/write data at right place */
- rc4030_dma_memory_rw(opaque, dma_addr, buf, len, is_write);
+ address_space_rw(&s->dma_as, dma_addr, MEMTXATTRS_UNSPECIFIED,
+ buf, len, is_write);
s->dma_regs[n][DMA_REG_ENABLE] |= DMA_FLAG_TC_INTR;
s->dma_regs[n][DMA_REG_COUNT] -= len;
-
-#ifdef DEBUG_RC4030_DMA
- {
- int i, j;
- printf("rc4030 dma: Copying %d bytes %s host %p\n",
- len, is_write ? "from" : "to", buf);
- for (i = 0; i < len; i += 16) {
- int n = 16;
- if (n > len - i) {
- n = len - i;
- }
- for (j = 0; j < n; j++)
- printf("%02x ", buf[i + j]);
- while (j++ < 16)
- printf(" ");
- printf("| ");
- for (j = 0; j < n; j++)
- printf("%c", isprint(buf[i + j]) ? buf[i + j] : '.');
- printf("\n");
- }
- }
-#endif
}
struct rc4030DMAState {
@@ -795,31 +740,102 @@ static rc4030_dma *rc4030_allocate_dmas(void *opaque, int n)
return s;
}
-void *rc4030_init(qemu_irq timer, qemu_irq jazz_bus,
- qemu_irq **irqs, rc4030_dma **dmas,
- MemoryRegion *sysmem)
+static void rc4030_initfn(Object *obj)
{
- rc4030State *s;
+ DeviceState *dev = DEVICE(obj);
+ rc4030State *s = RC4030(obj);
+ SysBusDevice *sysbus = SYS_BUS_DEVICE(obj);
- s = g_malloc0(sizeof(rc4030State));
+ qdev_init_gpio_in(dev, rc4030_irq_jazz_request, 16);
- *irqs = qemu_allocate_irqs(rc4030_irq_jazz_request, s, 16);
- *dmas = rc4030_allocate_dmas(s, 4);
+ sysbus_init_irq(sysbus, &s->timer_irq);
+ sysbus_init_irq(sysbus, &s->jazz_bus_irq);
- s->periodic_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, rc4030_periodic_timer, s);
- s->timer_irq = timer;
- s->jazz_bus_irq = jazz_bus;
-
- qemu_register_reset(rc4030_reset, s);
register_savevm(NULL, "rc4030", 0, 2, rc4030_save, rc4030_load, s);
- rc4030_reset(s);
+
+ sysbus_init_mmio(sysbus, &s->iomem_chipset);
+ sysbus_init_mmio(sysbus, &s->iomem_jazzio);
+}
+
+static void rc4030_realize(DeviceState *dev, Error **errp)
+{
+ rc4030State *s = RC4030(dev);
+ Object *o = OBJECT(dev);
+ int i;
+
+ s->periodic_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ rc4030_periodic_timer, s);
memory_region_init_io(&s->iomem_chipset, NULL, &rc4030_ops, s,
"rc4030.chipset", 0x300);
- memory_region_add_subregion(sysmem, 0x80000000, &s->iomem_chipset);
memory_region_init_io(&s->iomem_jazzio, NULL, &jazzio_ops, s,
"rc4030.jazzio", 0x00001000);
- memory_region_add_subregion(sysmem, 0xf0000000, &s->iomem_jazzio);
- return s;
+ memory_region_init_rom_device(&s->dma_tt, o,
+ &rc4030_dma_tt_ops, s, "dma-table",
+ MAX_TL_ENTRIES * sizeof(dma_pagetable_entry),
+ NULL);
+ memory_region_init(&s->dma_tt_alias, o, "dma-table-alias", 0);
+ memory_region_init(&s->dma_mr, o, "dma", INT32_MAX);
+ for (i = 0; i < MAX_TL_ENTRIES; ++i) {
+ memory_region_init_alias(&s->dma_mrs[i], o, "dma-alias",
+ get_system_memory(), 0, DMA_PAGESIZE);
+ memory_region_set_enabled(&s->dma_mrs[i], false);
+ memory_region_add_subregion(&s->dma_mr, i * DMA_PAGESIZE,
+ &s->dma_mrs[i]);
+ }
+ address_space_init(&s->dma_as, &s->dma_mr, "rc4030-dma");
+}
+
+static void rc4030_unrealize(DeviceState *dev, Error **errp)
+{
+ rc4030State *s = RC4030(dev);
+ int i;
+
+ timer_free(s->periodic_timer);
+
+ address_space_destroy(&s->dma_as);
+ object_unparent(OBJECT(&s->dma_tt));
+ object_unparent(OBJECT(&s->dma_tt_alias));
+ object_unparent(OBJECT(&s->dma_mr));
+ for (i = 0; i < MAX_TL_ENTRIES; ++i) {
+ memory_region_del_subregion(&s->dma_mr, &s->dma_mrs[i]);
+ object_unparent(OBJECT(&s->dma_mrs[i]));
+ }
+}
+
+static void rc4030_class_init(ObjectClass *klass, void *class_data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = rc4030_realize;
+ dc->unrealize = rc4030_unrealize;
+ dc->reset = rc4030_reset;
+}
+
+static const TypeInfo rc4030_info = {
+ .name = TYPE_RC4030,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(rc4030State),
+ .instance_init = rc4030_initfn,
+ .class_init = rc4030_class_init,
+};
+
+static void rc4030_register_types(void)
+{
+ type_register_static(&rc4030_info);
+}
+
+type_init(rc4030_register_types)
+
+DeviceState *rc4030_init(rc4030_dma **dmas, MemoryRegion **dma_mr)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, TYPE_RC4030);
+ qdev_init_nofail(dev);
+
+ *dmas = rc4030_allocate_dmas(dev, 4);
+ *dma_mr = &RC4030(dev)->dma_mr;
+ return dev;
}
diff --git a/hw/dma/sun4m_iommu.c b/hw/dma/sun4m_iommu.c
index ec7c2efcd..9a488bc9b 100644
--- a/hw/dma/sun4m_iommu.c
+++ b/hw/dma/sun4m_iommu.c
@@ -263,7 +263,8 @@ static uint32_t iommu_page_get_flags(IOMMUState *s, hwaddr addr)
iopte = s->regs[IOMMU_BASE] << 4;
addr &= ~s->iostart;
iopte += (addr >> (IOMMU_PAGE_SHIFT - 2)) & ~3;
- ret = ldl_be_phys(&address_space_memory, iopte);
+ ret = address_space_ldl_be(&address_space_memory, iopte,
+ MEMTXATTRS_UNSPECIFIED, NULL);
trace_sun4m_iommu_page_get_flags(pa, iopte, ret);
return ret;
}
diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c
index d06002dde..cf842a3cc 100644
--- a/hw/dma/xilinx_axidma.c
+++ b/hw/dma/xilinx_axidma.c
@@ -26,7 +26,6 @@
#include "qemu/timer.h"
#include "hw/ptimer.h"
#include "qemu/log.h"
-#include "qapi/qmp/qerror.h"
#include "qemu/main-loop.h"
#include "hw/stream.h"
diff --git a/hw/gpio/max7310.c b/hw/gpio/max7310.c
index 7fbf313ce..2f59b134e 100644
--- a/hw/gpio/max7310.c
+++ b/hw/gpio/max7310.c
@@ -96,7 +96,7 @@ static int max7310_tx(I2CSlave *i2c, uint8_t data)
case 0x01: /* Output port */
for (diff = (data ^ s->level) & ~s->direction; diff;
diff &= ~(1 << line)) {
- line = ffs(diff) - 1;
+ line = ctz32(diff);
if (s->handler[line])
qemu_set_irq(s->handler[line], (data >> line) & 1);
}
diff --git a/hw/gpio/omap_gpio.c b/hw/gpio/omap_gpio.c
index 9a4348689..d92f8cfba 100644
--- a/hw/gpio/omap_gpio.c
+++ b/hw/gpio/omap_gpio.c
@@ -125,8 +125,7 @@ static void omap_gpio_write(void *opaque, hwaddr addr,
case 0x04: /* DATA_OUTPUT */
diff = (s->outputs ^ value) & ~s->dir;
s->outputs = value;
- while ((ln = ffs(diff))) {
- ln --;
+ while ((ln = ctz32(diff)) != 32) {
if (s->handler[ln])
qemu_set_irq(s->handler[ln], (value >> ln) & 1);
diff &= ~(1 << ln);
@@ -138,8 +137,7 @@ static void omap_gpio_write(void *opaque, hwaddr addr,
s->dir = value;
value = s->outputs & ~s->dir;
- while ((ln = ffs(diff))) {
- ln --;
+ while ((ln = ctz32(diff)) != 32) {
if (s->handler[ln])
qemu_set_irq(s->handler[ln], (value >> ln) & 1);
diff &= ~(1 << ln);
@@ -253,8 +251,7 @@ static inline void omap2_gpio_module_out_update(struct omap2_gpio_s *s,
s->outputs ^= diff;
diff &= ~s->dir;
- while ((ln = ffs(diff))) {
- ln --;
+ while ((ln = ctz32(diff)) != 32) {
qemu_set_irq(s->handler[ln], (s->outputs >> ln) & 1);
diff &= ~(1 << ln);
}
@@ -442,8 +439,8 @@ static void omap2_gpio_module_write(void *opaque, hwaddr addr,
s->dir = value;
value = s->outputs & ~s->dir;
- while ((ln = ffs(diff))) {
- diff &= ~(1 <<-- ln);
+ while ((ln = ctz32(diff)) != 32) {
+ diff &= ~(1 << ln);
qemu_set_irq(s->handler[ln], (value >> ln) & 1);
}
diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c
index bd03e9997..4ba730b47 100644
--- a/hw/gpio/pl061.c
+++ b/hw/gpio/pl061.c
@@ -173,7 +173,7 @@ static uint64_t pl061_read(void *opaque, hwaddr offset,
case 0x414: /* Raw interrupt status */
return s->istate;
case 0x418: /* Masked interrupt status */
- return s->istate | s->im;
+ return s->istate & s->im;
case 0x420: /* Alternate function select */
return s->afsel;
case 0x500: /* 2mA drive */
diff --git a/hw/gpio/zaurus.c b/hw/gpio/zaurus.c
index 94083424f..24a77272d 100644
--- a/hw/gpio/zaurus.c
+++ b/hw/gpio/zaurus.c
@@ -65,7 +65,7 @@ static inline void scoop_gpio_handler_update(ScoopInfo *s) {
level = s->gpio_level & s->gpio_dir;
for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
- bit = ffs(diff) - 1;
+ bit = ctz32(diff);
qemu_set_irq(s->handler[bit], (level >> bit) & 1);
}
diff --git a/hw/i2c/Makefile.objs b/hw/i2c/Makefile.objs
index 648278e7c..0f130608c 100644
--- a/hw/i2c/Makefile.objs
+++ b/hw/i2c/Makefile.objs
@@ -1,6 +1,6 @@
common-obj-y += core.o smbus.o smbus_eeprom.o
common-obj-$(CONFIG_VERSATILE_I2C) += versatile_i2c.o
-common-obj-$(CONFIG_ACPI) += smbus_ich9.o
+common-obj-$(CONFIG_ACPI_X86) += smbus_ich9.o
common-obj-$(CONFIG_APM) += pm_smbus.o
common-obj-$(CONFIG_BITBANG_I2C) += bitbang_i2c.o
common-obj-$(CONFIG_EXYNOS4) += exynos4210_i2c.o
diff --git a/hw/i2c/omap_i2c.c b/hw/i2c/omap_i2c.c
index d63278dbd..b6f544a22 100644
--- a/hw/i2c/omap_i2c.c
+++ b/hw/i2c/omap_i2c.c
@@ -171,9 +171,13 @@ static uint32_t omap_i2c_read(void *opaque, hwaddr addr)
case 0x0c: /* I2C_IV */
if (s->revision >= OMAP2_INTR_REV)
break;
- ret = ffs(s->stat & s->mask);
- if (ret)
- s->stat ^= 1 << (ret - 1);
+ ret = ctz32(s->stat & s->mask);
+ if (ret != 32) {
+ s->stat ^= 1 << ret;
+ ret++;
+ } else {
+ ret = 0;
+ }
omap_i2c_interrupts_update(s);
return ret;
diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs
index e058a3914..bd4f147f9 100644
--- a/hw/i386/Makefile.objs
+++ b/hw/i386/Makefile.objs
@@ -8,8 +8,7 @@ obj-$(CONFIG_XEN) += ../xenpv/ xen/
obj-y += kvmvapic.o
obj-y += acpi-build.o
hw/i386/acpi-build.o: hw/i386/acpi-build.c \
- hw/i386/acpi-dsdt.hex hw/i386/q35-acpi-dsdt.hex \
- hw/i386/ssdt-tpm.hex
+ hw/i386/acpi-dsdt.hex hw/i386/q35-acpi-dsdt.hex
iasl-option=$(shell if test -z "`$(1) $(2) 2>&1 > /dev/null`" \
; then echo "$(2)"; else echo "$(3)"; fi ;)
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index e761005ef..46eddb8e4 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -26,14 +26,13 @@
#include "qemu-common.h"
#include "qemu/bitmap.h"
#include "qemu/osdep.h"
-#include "qemu/range.h"
#include "qemu/error-report.h"
#include "hw/pci/pci.h"
#include "qom/cpu.h"
#include "hw/i386/pc.h"
#include "target-i386/cpu.h"
#include "hw/timer/hpet.h"
-#include "hw/i386/acpi-defs.h"
+#include "hw/acpi/acpi-defs.h"
#include "hw/acpi/acpi.h"
#include "hw/nvram/fw_cfg.h"
#include "hw/acpi/bios-linker-loader.h"
@@ -42,6 +41,7 @@
#include "hw/acpi/memory_hotplug.h"
#include "sysemu/tpm.h"
#include "hw/acpi/tpm.h"
+#include "sysemu/tpm_backend.h"
/* Supported chipsets: */
#include "hw/acpi/piix4.h"
@@ -58,7 +58,6 @@
#include "qapi/qmp/qint.h"
#include "qom/qom-qobject.h"
-#include "exec/ram_addr.h"
/* These are used to size the ACPI tables for -M pc-i440fx-1.7 and
* -M pc-i440fx-2.0. Even if the actual amount of AML generated grows
@@ -70,9 +69,6 @@
#define ACPI_BUILD_TABLE_SIZE 0x20000
-/* Reserve RAM space for tables: add another order of magnitude. */
-#define ACPI_BUILD_TABLE_MAX_SIZE 0x200000
-
/* #define DEBUG_ACPI_BUILD */
#ifdef DEBUG_ACPI_BUILD
#define ACPI_BUILD_DPRINTF(fmt, ...) \
@@ -111,7 +107,7 @@ typedef struct AcpiPmInfo {
typedef struct AcpiMiscInfo {
bool has_hpet;
- bool has_tpm;
+ TPMVersion tpm_version;
const unsigned char *dsdt_code;
unsigned dsdt_size;
uint16_t pvpanic_port;
@@ -239,18 +235,37 @@ static void acpi_get_pm_info(AcpiPmInfo *pm)
static void acpi_get_misc_info(AcpiMiscInfo *info)
{
info->has_hpet = hpet_find();
- info->has_tpm = tpm_find();
+ info->tpm_version = tpm_get_version();
info->pvpanic_port = pvpanic_port();
info->applesmc_io_base = applesmc_port();
}
+/*
+ * Because of the PXB hosts we cannot simply query TYPE_PCI_HOST_BRIDGE.
+ * On i386 arch we only have two pci hosts, so we can look only for them.
+ */
+static Object *acpi_get_i386_pci_host(void)
+{
+ PCIHostState *host;
+
+ host = OBJECT_CHECK(PCIHostState,
+ object_resolve_path("/machine/i440fx", NULL),
+ TYPE_PCI_HOST_BRIDGE);
+ if (!host) {
+ host = OBJECT_CHECK(PCIHostState,
+ object_resolve_path("/machine/q35", NULL),
+ TYPE_PCI_HOST_BRIDGE);
+ }
+
+ return OBJECT(host);
+}
+
static void acpi_get_pci_info(PcPciInfo *info)
{
Object *pci_host;
- bool ambiguous;
- pci_host = object_resolve_path_type("", TYPE_PCI_HOST_BRIDGE, &ambiguous);
- g_assert(!ambiguous);
+
+ pci_host = acpi_get_i386_pci_host();
g_assert(pci_host);
info->w32.begin = object_property_get_int(pci_host,
@@ -267,51 +282,8 @@ static void acpi_get_pci_info(PcPciInfo *info)
NULL);
}
-#define ACPI_BUILD_APPNAME "Bochs"
-#define ACPI_BUILD_APPNAME6 "BOCHS "
-#define ACPI_BUILD_APPNAME4 "BXPC"
-
-#define ACPI_BUILD_TABLE_FILE "etc/acpi/tables"
-#define ACPI_BUILD_RSDP_FILE "etc/acpi/rsdp"
-#define ACPI_BUILD_TPMLOG_FILE "etc/tpm/log"
-
-static void
-build_header(GArray *linker, GArray *table_data,
- AcpiTableHeader *h, const char *sig, int len, uint8_t rev)
-{
- memcpy(&h->signature, sig, 4);
- h->length = cpu_to_le32(len);
- h->revision = rev;
- memcpy(h->oem_id, ACPI_BUILD_APPNAME6, 6);
- memcpy(h->oem_table_id, ACPI_BUILD_APPNAME4, 4);
- memcpy(h->oem_table_id + 4, sig, 4);
- h->oem_revision = cpu_to_le32(1);
- memcpy(h->asl_compiler_id, ACPI_BUILD_APPNAME4, 4);
- h->asl_compiler_revision = cpu_to_le32(1);
- h->checksum = 0;
- /* Checksum to be filled in by Guest linker */
- bios_linker_loader_add_checksum(linker, ACPI_BUILD_TABLE_FILE,
- table_data->data, h, len, &h->checksum);
-}
-
-/* End here */
#define ACPI_PORT_SMI_CMD 0x00b2 /* TODO: this is APM_CNT_IOPORT */
-static inline void *acpi_data_push(GArray *table_data, unsigned size)
-{
- unsigned off = table_data->len;
- g_array_set_size(table_data, off + size);
- return table_data->data + off;
-}
-
-static unsigned acpi_data_len(GArray *table)
-{
-#if GLIB_CHECK_VERSION(2, 22, 0)
- assert(g_array_get_element_size(table) == 1);
-#endif
- return table->len;
-}
-
static void acpi_align_size(GArray *blob, unsigned align)
{
/* Align size to multiple of given size. This reduces the chance
@@ -320,12 +292,6 @@ static void acpi_align_size(GArray *blob, unsigned align)
g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align));
}
-static inline void acpi_add_table(GArray *table_offsets, GArray *table_data)
-{
- uint32_t offset = cpu_to_le32(table_data->len);
- g_array_append_val(table_offsets, offset);
-}
-
/* FACS */
static void
build_facs(GArray *table_data, GArray *linker, PcGuestInfo *guest_info)
@@ -467,8 +433,6 @@ build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu,
table_data->len - madt_start, 1);
}
-#include "hw/i386/ssdt-tpm.hex"
-
/* Assign BSEL property to all buses. In the future, this can be changed
* to only assign to buses that support hotplug.
*/
@@ -648,6 +612,291 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
}
}
aml_append(parent_scope, method);
+ qobject_decref(bsel);
+}
+
+/*
+ * initialize_route - Initialize the interrupt routing rule
+ * through a specific LINK:
+ * if (lnk_idx == idx)
+ * route using link 'link_name'
+ */
+static Aml *initialize_route(Aml *route, const char *link_name,
+ Aml *lnk_idx, int idx)
+{
+ Aml *if_ctx = aml_if(aml_equal(lnk_idx, aml_int(idx)));
+ Aml *pkg = aml_package(4);
+
+ aml_append(pkg, aml_int(0));
+ aml_append(pkg, aml_int(0));
+ aml_append(pkg, aml_name("%s", link_name));
+ aml_append(pkg, aml_int(0));
+ aml_append(if_ctx, aml_store(pkg, route));
+
+ return if_ctx;
+}
+
+/*
+ * build_prt - Define interrupt rounting rules
+ *
+ * Returns an array of 128 routes, one for each device,
+ * based on device location.
+ * The main goal is to equaly distribute the interrupts
+ * over the 4 existing ACPI links (works only for i440fx).
+ * The hash function is (slot + pin) & 3 -> "LNK[D|A|B|C]".
+ *
+ */
+static Aml *build_prt(void)
+{
+ Aml *method, *while_ctx, *pin, *res;
+
+ method = aml_method("_PRT", 0);
+ res = aml_local(0);
+ pin = aml_local(1);
+ aml_append(method, aml_store(aml_package(128), res));
+ aml_append(method, aml_store(aml_int(0), pin));
+
+ /* while (pin < 128) */
+ while_ctx = aml_while(aml_lless(pin, aml_int(128)));
+ {
+ Aml *slot = aml_local(2);
+ Aml *lnk_idx = aml_local(3);
+ Aml *route = aml_local(4);
+
+ /* slot = pin >> 2 */
+ aml_append(while_ctx,
+ aml_store(aml_shiftright(pin, aml_int(2)), slot));
+ /* lnk_idx = (slot + pin) & 3 */
+ aml_append(while_ctx,
+ aml_store(aml_and(aml_add(pin, slot), aml_int(3)), lnk_idx));
+
+ /* route[2] = "LNK[D|A|B|C]", selection based on pin % 3 */
+ aml_append(while_ctx, initialize_route(route, "LNKD", lnk_idx, 0));
+ aml_append(while_ctx, initialize_route(route, "LNKA", lnk_idx, 1));
+ aml_append(while_ctx, initialize_route(route, "LNKB", lnk_idx, 2));
+ aml_append(while_ctx, initialize_route(route, "LNKC", lnk_idx, 3));
+
+ /* route[0] = 0x[slot]FFFF */
+ aml_append(while_ctx,
+ aml_store(aml_or(aml_shiftleft(slot, aml_int(16)), aml_int(0xFFFF)),
+ aml_index(route, aml_int(0))));
+ /* route[1] = pin & 3 */
+ aml_append(while_ctx,
+ aml_store(aml_and(pin, aml_int(3)), aml_index(route, aml_int(1))));
+ /* res[pin] = route */
+ aml_append(while_ctx, aml_store(route, aml_index(res, pin)));
+ /* pin++ */
+ aml_append(while_ctx, aml_increment(pin));
+ }
+ aml_append(method, while_ctx);
+ /* return res*/
+ aml_append(method, aml_return(res));
+
+ return method;
+}
+
+typedef struct CrsRangeEntry {
+ uint64_t base;
+ uint64_t limit;
+} CrsRangeEntry;
+
+static void crs_range_insert(GPtrArray *ranges, uint64_t base, uint64_t limit)
+{
+ CrsRangeEntry *entry;
+
+ entry = g_malloc(sizeof(*entry));
+ entry->base = base;
+ entry->limit = limit;
+
+ g_ptr_array_add(ranges, entry);
+}
+
+static void crs_range_free(gpointer data)
+{
+ CrsRangeEntry *entry = (CrsRangeEntry *)data;
+ g_free(entry);
+}
+
+static gint crs_range_compare(gconstpointer a, gconstpointer b)
+{
+ CrsRangeEntry *entry_a = *(CrsRangeEntry **)a;
+ CrsRangeEntry *entry_b = *(CrsRangeEntry **)b;
+
+ return (int64_t)entry_a->base - (int64_t)entry_b->base;
+}
+
+/*
+ * crs_replace_with_free_ranges - given the 'used' ranges within [start - end]
+ * interval, computes the 'free' ranges from the same interval.
+ * Example: If the input array is { [a1 - a2],[b1 - b2] }, the function
+ * will return { [base - a1], [a2 - b1], [b2 - limit] }.
+ */
+static void crs_replace_with_free_ranges(GPtrArray *ranges,
+ uint64_t start, uint64_t end)
+{
+ GPtrArray *free_ranges = g_ptr_array_new_with_free_func(crs_range_free);
+ uint64_t free_base = start;
+ int i;
+
+ g_ptr_array_sort(ranges, crs_range_compare);
+ for (i = 0; i < ranges->len; i++) {
+ CrsRangeEntry *used = g_ptr_array_index(ranges, i);
+
+ if (free_base < used->base) {
+ crs_range_insert(free_ranges, free_base, used->base - 1);
+ }
+
+ free_base = used->limit + 1;
+ }
+
+ if (free_base < end) {
+ crs_range_insert(free_ranges, free_base, end);
+ }
+
+ g_ptr_array_set_size(ranges, 0);
+ for (i = 0; i < free_ranges->len; i++) {
+ g_ptr_array_add(ranges, g_ptr_array_index(free_ranges, i));
+ }
+
+ g_ptr_array_free(free_ranges, false);
+}
+
+static Aml *build_crs(PCIHostState *host,
+ GPtrArray *io_ranges, GPtrArray *mem_ranges)
+{
+ Aml *crs = aml_resource_template();
+ uint8_t max_bus = pci_bus_num(host->bus);
+ uint8_t type;
+ int devfn;
+
+ for (devfn = 0; devfn < ARRAY_SIZE(host->bus->devices); devfn++) {
+ int i;
+ uint64_t range_base, range_limit;
+ PCIDevice *dev = host->bus->devices[devfn];
+
+ if (!dev) {
+ continue;
+ }
+
+ for (i = 0; i < PCI_NUM_REGIONS; i++) {
+ PCIIORegion *r = &dev->io_regions[i];
+
+ range_base = r->addr;
+ range_limit = r->addr + r->size - 1;
+
+ /*
+ * Work-around for old bioses
+ * that do not support multiple root buses
+ */
+ if (!range_base || range_base > range_limit) {
+ continue;
+ }
+
+ if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
+ aml_append(crs,
+ aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED,
+ AML_POS_DECODE, AML_ENTIRE_RANGE,
+ 0,
+ range_base,
+ range_limit,
+ 0,
+ range_limit - range_base + 1));
+ crs_range_insert(io_ranges, range_base, range_limit);
+ } else { /* "memory" */
+ aml_append(crs,
+ aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
+ AML_MAX_FIXED, AML_NON_CACHEABLE,
+ AML_READ_WRITE,
+ 0,
+ range_base,
+ range_limit,
+ 0,
+ range_limit - range_base + 1));
+ crs_range_insert(mem_ranges, range_base, range_limit);
+ }
+ }
+
+ type = dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION;
+ if (type == PCI_HEADER_TYPE_BRIDGE) {
+ uint8_t subordinate = dev->config[PCI_SUBORDINATE_BUS];
+ if (subordinate > max_bus) {
+ max_bus = subordinate;
+ }
+
+ range_base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO);
+ range_limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO);
+
+ /*
+ * Work-around for old bioses
+ * that do not support multiple root buses
+ */
+ if (range_base && range_base <= range_limit) {
+ aml_append(crs,
+ aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED,
+ AML_POS_DECODE, AML_ENTIRE_RANGE,
+ 0,
+ range_base,
+ range_limit,
+ 0,
+ range_limit - range_base + 1));
+ crs_range_insert(io_ranges, range_base, range_limit);
+ }
+
+ range_base =
+ pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
+ range_limit =
+ pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
+
+ /*
+ * Work-around for old bioses
+ * that do not support multiple root buses
+ */
+ if (range_base && range_base <= range_limit) {
+ aml_append(crs,
+ aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
+ AML_MAX_FIXED, AML_NON_CACHEABLE,
+ AML_READ_WRITE,
+ 0,
+ range_base,
+ range_limit,
+ 0,
+ range_limit - range_base + 1));
+ crs_range_insert(mem_ranges, range_base, range_limit);
+ }
+
+ range_base =
+ pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
+ range_limit =
+ pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
+
+ /*
+ * Work-around for old bioses
+ * that do not support multiple root buses
+ */
+ if (range_base && range_base <= range_limit) {
+ aml_append(crs,
+ aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
+ AML_MAX_FIXED, AML_NON_CACHEABLE,
+ AML_READ_WRITE,
+ 0,
+ range_base,
+ range_limit,
+ 0,
+ range_limit - range_base + 1));
+ crs_range_insert(mem_ranges, range_base, range_limit);
+ }
+ }
+ }
+
+ aml_append(crs,
+ aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE,
+ 0,
+ pci_bus_num(host->bus),
+ max_bus,
+ 0,
+ max_bus - pci_bus_num(host->bus) + 1));
+
+ return crs;
}
static void
@@ -659,6 +908,11 @@ build_ssdt(GArray *table_data, GArray *linker,
uint32_t nr_mem = machine->ram_slots;
unsigned acpi_cpus = guest_info->apic_id_limit;
Aml *ssdt, *sb_scope, *scope, *pkg, *dev, *method, *crs, *field, *ifctx;
+ PCIBus *bus = NULL;
+ GPtrArray *io_ranges = g_ptr_array_new_with_free_func(crs_range_free);
+ GPtrArray *mem_ranges = g_ptr_array_new_with_free_func(crs_range_free);
+ CrsRangeEntry *entry;
+ int root_bus_limit = 0xFF;
int i;
ssdt = init_aml_allocator();
@@ -670,35 +924,84 @@ build_ssdt(GArray *table_data, GArray *linker,
/* Reserve space for header */
acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader));
+ /* Extra PCI root buses are implemented only for i440fx */
+ bus = find_i440fx();
+ if (bus) {
+ QLIST_FOREACH(bus, &bus->child, sibling) {
+ uint8_t bus_num = pci_bus_num(bus);
+ uint8_t numa_node = pci_bus_numa_node(bus);
+
+ /* look only for expander root buses */
+ if (!pci_bus_is_root(bus)) {
+ continue;
+ }
+
+ if (bus_num < root_bus_limit) {
+ root_bus_limit = bus_num - 1;
+ }
+
+ scope = aml_scope("\\_SB");
+ dev = aml_device("PC%.02X", bus_num);
+ aml_append(dev, aml_name_decl("_UID", aml_int(bus_num)));
+ aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A03")));
+ aml_append(dev, aml_name_decl("_BBN", aml_int(bus_num)));
+
+ if (numa_node != NUMA_NODE_UNASSIGNED) {
+ aml_append(dev, aml_name_decl("_PXM", aml_int(numa_node)));
+ }
+
+ aml_append(dev, build_prt());
+ crs = build_crs(PCI_HOST_BRIDGE(BUS(bus)->parent),
+ io_ranges, mem_ranges);
+ aml_append(dev, aml_name_decl("_CRS", crs));
+ aml_append(scope, dev);
+ aml_append(ssdt, scope);
+ }
+ }
+
scope = aml_scope("\\_SB.PCI0");
/* build PCI0._CRS */
crs = aml_resource_template();
aml_append(crs,
- aml_word_bus_number(aml_min_fixed, aml_max_fixed, aml_pos_decode,
- 0x0000, 0x0000, 0x00FF, 0x0000, 0x0100));
- aml_append(crs, aml_io(aml_decode16, 0x0CF8, 0x0CF8, 0x01, 0x08));
+ aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE,
+ 0x0000, 0x0, root_bus_limit,
+ 0x0000, root_bus_limit + 1));
+ aml_append(crs, aml_io(AML_DECODE16, 0x0CF8, 0x0CF8, 0x01, 0x08));
aml_append(crs,
- aml_word_io(aml_min_fixed, aml_max_fixed,
- aml_pos_decode, aml_entire_range,
+ aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED,
+ AML_POS_DECODE, AML_ENTIRE_RANGE,
0x0000, 0x0000, 0x0CF7, 0x0000, 0x0CF8));
+
+ crs_replace_with_free_ranges(io_ranges, 0x0D00, 0xFFFF);
+ for (i = 0; i < io_ranges->len; i++) {
+ entry = g_ptr_array_index(io_ranges, i);
+ aml_append(crs,
+ aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED,
+ AML_POS_DECODE, AML_ENTIRE_RANGE,
+ 0x0000, entry->base, entry->limit,
+ 0x0000, entry->limit - entry->base + 1));
+ }
+
aml_append(crs,
- aml_word_io(aml_min_fixed, aml_max_fixed,
- aml_pos_decode, aml_entire_range,
- 0x0000, 0x0D00, 0xFFFF, 0x0000, 0xF300));
- aml_append(crs,
- aml_dword_memory(aml_pos_decode, aml_min_fixed, aml_max_fixed,
- aml_cacheable, aml_ReadWrite,
+ aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
+ AML_CACHEABLE, AML_READ_WRITE,
0, 0x000A0000, 0x000BFFFF, 0, 0x00020000));
- aml_append(crs,
- aml_dword_memory(aml_pos_decode, aml_min_fixed, aml_max_fixed,
- aml_non_cacheable, aml_ReadWrite,
- 0, pci->w32.begin, pci->w32.end - 1, 0,
- pci->w32.end - pci->w32.begin));
+
+ crs_replace_with_free_ranges(mem_ranges, pci->w32.begin, pci->w32.end - 1);
+ for (i = 0; i < mem_ranges->len; i++) {
+ entry = g_ptr_array_index(mem_ranges, i);
+ aml_append(crs,
+ aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
+ AML_NON_CACHEABLE, AML_READ_WRITE,
+ 0, entry->base, entry->limit,
+ 0, entry->limit - entry->base + 1));
+ }
+
if (pci->w64.begin) {
aml_append(crs,
- aml_qword_memory(aml_pos_decode, aml_min_fixed, aml_max_fixed,
- aml_cacheable, aml_ReadWrite,
+ aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
+ AML_CACHEABLE, AML_READ_WRITE,
0, pci->w64.begin, pci->w64.end - 1, 0,
pci->w64.end - pci->w64.begin));
}
@@ -712,11 +1015,14 @@ build_ssdt(GArray *table_data, GArray *linker,
aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
crs = aml_resource_template();
aml_append(crs,
- aml_io(aml_decode16, pm->gpe0_blk, pm->gpe0_blk, 1, pm->gpe0_blk_len)
+ aml_io(AML_DECODE16, pm->gpe0_blk, pm->gpe0_blk, 1, pm->gpe0_blk_len)
);
aml_append(dev, aml_name_decl("_CRS", crs));
aml_append(scope, dev);
+ g_ptr_array_free(io_ranges, true);
+ g_ptr_array_free(mem_ranges, true);
+
/* reserve PCIHP resources */
if (pm->pcihp_io_len) {
dev = aml_device("PHPR");
@@ -727,7 +1033,7 @@ build_ssdt(GArray *table_data, GArray *linker,
aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
crs = aml_resource_template();
aml_append(crs,
- aml_io(aml_decode16, pm->pcihp_io_base, pm->pcihp_io_base, 1,
+ aml_io(AML_DECODE16, pm->pcihp_io_base, pm->pcihp_io_base, 1,
pm->pcihp_io_len)
);
aml_append(dev, aml_name_decl("_CRS", crs));
@@ -774,7 +1080,7 @@ build_ssdt(GArray *table_data, GArray *linker,
crs = aml_resource_template();
aml_append(crs,
- aml_io(aml_decode16, misc->applesmc_io_base, misc->applesmc_io_base,
+ aml_io(AML_DECODE16, misc->applesmc_io_base, misc->applesmc_io_base,
0x01, APPLESMC_MAX_DATA_LENGTH)
);
aml_append(crs, aml_irq_no_flags(6));
@@ -787,21 +1093,24 @@ build_ssdt(GArray *table_data, GArray *linker,
if (misc->pvpanic_port) {
scope = aml_scope("\\_SB.PCI0.ISA");
- dev = aml_device("PEVR");
+ dev = aml_device("PEVT");
aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0001")));
crs = aml_resource_template();
aml_append(crs,
- aml_io(aml_decode16, misc->pvpanic_port, misc->pvpanic_port, 1, 1)
+ aml_io(AML_DECODE16, misc->pvpanic_port, misc->pvpanic_port, 1, 1)
);
aml_append(dev, aml_name_decl("_CRS", crs));
- aml_append(dev, aml_operation_region("PEOR", aml_system_io,
+ aml_append(dev, aml_operation_region("PEOR", AML_SYSTEM_IO,
misc->pvpanic_port, 1));
- field = aml_field("PEOR", aml_byte_acc);
+ field = aml_field("PEOR", AML_BYTE_ACC, AML_PRESERVE);
aml_append(field, aml_named_field("PEPT", 8));
aml_append(dev, field);
+ /* device present, functioning, decoding, shown in UI */
+ aml_append(dev, aml_name_decl("_STA", aml_int(0xF)));
+
method = aml_method("RDPT", 0);
aml_append(method, aml_store(aml_name("PEPT"), aml_local(0)));
aml_append(method, aml_return(aml_local(0)));
@@ -815,7 +1124,7 @@ build_ssdt(GArray *table_data, GArray *linker,
aml_append(ssdt, scope);
}
- sb_scope = aml_scope("_SB");
+ sb_scope = aml_scope("\\_SB");
{
/* create PCI0.PRES device and its _CRS to reserve CPU hotplug MMIO */
dev = aml_device("PCI0." stringify(CPU_HOTPLUG_RESOURCE_DEVICE));
@@ -827,15 +1136,15 @@ build_ssdt(GArray *table_data, GArray *linker,
aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
crs = aml_resource_template();
aml_append(crs,
- aml_io(aml_decode16, pm->cpu_hp_io_base, pm->cpu_hp_io_base, 1,
+ aml_io(AML_DECODE16, pm->cpu_hp_io_base, pm->cpu_hp_io_base, 1,
pm->cpu_hp_io_len)
);
aml_append(dev, aml_name_decl("_CRS", crs));
aml_append(sb_scope, dev);
/* declare CPU hotplug MMIO region and PRS field to access it */
aml_append(sb_scope, aml_operation_region(
- "PRST", aml_system_io, pm->cpu_hp_io_base, pm->cpu_hp_io_len));
- field = aml_field("PRST", aml_byte_acc);
+ "PRST", AML_SYSTEM_IO, pm->cpu_hp_io_base, pm->cpu_hp_io_len));
+ field = aml_field("PRST", AML_BYTE_ACC, AML_PRESERVE);
aml_append(field, aml_named_field("PRS", 256));
aml_append(sb_scope, field);
@@ -899,17 +1208,18 @@ build_ssdt(GArray *table_data, GArray *linker,
crs = aml_resource_template();
aml_append(crs,
- aml_io(aml_decode16, pm->mem_hp_io_base, pm->mem_hp_io_base, 0,
+ aml_io(AML_DECODE16, pm->mem_hp_io_base, pm->mem_hp_io_base, 0,
pm->mem_hp_io_len)
);
aml_append(scope, aml_name_decl("_CRS", crs));
aml_append(scope, aml_operation_region(
- stringify(MEMORY_HOTPLUG_IO_REGION), aml_system_io,
+ stringify(MEMORY_HOTPLUG_IO_REGION), AML_SYSTEM_IO,
pm->mem_hp_io_base, pm->mem_hp_io_len)
);
- field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), aml_dword_acc);
+ field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), AML_DWORD_ACC,
+ AML_PRESERVE);
aml_append(field, /* read only */
aml_named_field(stringify(MEMORY_SLOT_ADDR_LOW), 32));
aml_append(field, /* read only */
@@ -922,16 +1232,24 @@ build_ssdt(GArray *table_data, GArray *linker,
aml_named_field(stringify(MEMORY_SLOT_PROXIMITY), 32));
aml_append(scope, field);
- field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), aml_byte_acc);
+ field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), AML_BYTE_ACC,
+ AML_WRITE_AS_ZEROS);
aml_append(field, aml_reserved_field(160 /* bits, Offset(20) */));
aml_append(field, /* 1 if enabled, read only */
aml_named_field(stringify(MEMORY_SLOT_ENABLED), 1));
aml_append(field,
/*(read) 1 if has a insert event. (write) 1 to clear event */
aml_named_field(stringify(MEMORY_SLOT_INSERT_EVENT), 1));
+ aml_append(field,
+ /* (read) 1 if has a remove event. (write) 1 to clear event */
+ aml_named_field(stringify(MEMORY_SLOT_REMOVE_EVENT), 1));
+ aml_append(field,
+ /* initiates device eject, write only */
+ aml_named_field(stringify(MEMORY_SLOT_EJECT), 1));
aml_append(scope, field);
- field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), aml_dword_acc);
+ field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), AML_DWORD_ACC,
+ AML_PRESERVE);
aml_append(field, /* DIMM selector, write only */
aml_named_field(stringify(MEMORY_SLOT_SLECTOR), 32));
aml_append(field, /* _OST event code, write only */
@@ -972,11 +1290,17 @@ build_ssdt(GArray *table_data, GArray *linker,
)));
aml_append(dev, method);
+ method = aml_method("_EJ0", 1);
+ s = BASEPATH stringify(MEMORY_SLOT_EJECT_METHOD);
+ aml_append(method, aml_return(aml_call2(
+ s, aml_name("_UID"), aml_arg(0))));
+ aml_append(dev, method);
+
aml_append(sb_scope, dev);
}
/* build Method(MEMORY_SLOT_NOTIFY_METHOD, 2) {
- * If (LEqual(Arg0, 0x00)) {Notify(MP00, Arg1)} ...
+ * If (LEqual(Arg0, 0x00)) {Notify(MP00, Arg1)} ... }
*/
method = aml_method(stringify(MEMORY_SLOT_NOTIFY_METHOD), 2);
for (i = 0; i < nr_mem; i++) {
@@ -991,10 +1315,9 @@ build_ssdt(GArray *table_data, GArray *linker,
{
Object *pci_host;
PCIBus *bus = NULL;
- bool ambiguous;
- pci_host = object_resolve_path_type("", TYPE_PCI_HOST_BRIDGE, &ambiguous);
- if (!ambiguous && pci_host) {
+ pci_host = acpi_get_i386_pci_host();
+ if (pci_host) {
bus = PCI_HOST_BRIDGE(pci_host)->bus;
}
@@ -1002,6 +1325,19 @@ build_ssdt(GArray *table_data, GArray *linker,
Aml *scope = aml_scope("PCI0");
/* Scan all PCI buses. Generate tables to support hotplug. */
build_append_pci_bus_devices(scope, bus, pm->pcihp_bridge_en);
+
+ if (misc->tpm_version != TPM_VERSION_UNSPEC) {
+ dev = aml_device("ISA.TPM");
+ aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C31")));
+ aml_append(dev, aml_name_decl("_STA", aml_int(0xF)));
+ crs = aml_resource_template();
+ aml_append(crs, aml_memory32_fixed(TPM_TIS_ADDR_BASE,
+ TPM_TIS_ADDR_SIZE, AML_READ_WRITE));
+ aml_append(crs, aml_irq_no_flags(TPM_TIS_IRQ));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+ aml_append(scope, dev);
+ }
+
aml_append(sb_scope, scope);
}
}
@@ -1057,12 +1393,18 @@ build_tpm_tcpa(GArray *table_data, GArray *linker, GArray *tcpalog)
}
static void
-build_tpm_ssdt(GArray *table_data, GArray *linker)
+build_tpm2(GArray *table_data, GArray *linker)
{
- void *tpm_ptr;
+ Acpi20TPM2 *tpm2_ptr;
+
+ tpm2_ptr = acpi_data_push(table_data, sizeof *tpm2_ptr);
- tpm_ptr = acpi_data_push(table_data, sizeof(ssdt_tpm_aml));
- memcpy(tpm_ptr, ssdt_tpm_aml, sizeof(ssdt_tpm_aml));
+ tpm2_ptr->platform_class = cpu_to_le16(TPM2_ACPI_CLASS_CLIENT);
+ tpm2_ptr->control_area_address = cpu_to_le64(0);
+ tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_MMIO);
+
+ build_header(linker, table_data,
+ (void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4);
}
typedef enum {
@@ -1167,7 +1509,7 @@ build_srat(GArray *table_data, GArray *linker, PcGuestInfo *guest_info)
*/
if (hotplugabble_address_space_size) {
numamem = acpi_data_push(table_data, sizeof *numamem);
- acpi_build_srat_memory(numamem, pcms->hotplug_memory_base,
+ acpi_build_srat_memory(numamem, pcms->hotplug_memory.base,
hotplugabble_address_space_size, 0,
MEM_AFFINITY_HOTPLUGGABLE |
MEM_AFFINITY_ENABLED);
@@ -1247,30 +1589,6 @@ build_dsdt(GArray *table_data, GArray *linker, AcpiMiscInfo *misc)
misc->dsdt_size, 1);
}
-/* Build final rsdt table */
-static void
-build_rsdt(GArray *table_data, GArray *linker, GArray *table_offsets)
-{
- AcpiRsdtDescriptorRev1 *rsdt;
- size_t rsdt_len;
- int i;
-
- rsdt_len = sizeof(*rsdt) + sizeof(uint32_t) * table_offsets->len;
- rsdt = acpi_data_push(table_data, rsdt_len);
- memcpy(rsdt->table_offset_entry, table_offsets->data,
- sizeof(uint32_t) * table_offsets->len);
- for (i = 0; i < table_offsets->len; ++i) {
- /* rsdt->table_offset_entry to be filled by Guest linker */
- bios_linker_loader_add_pointer(linker,
- ACPI_BUILD_TABLE_FILE,
- ACPI_BUILD_TABLE_FILE,
- table_data, &rsdt->table_offset_entry[i],
- sizeof(uint32_t));
- }
- build_header(linker, table_data,
- (void *)rsdt, "RSDT", rsdt_len, 1);
-}
-
static GArray *
build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt)
{
@@ -1296,50 +1614,23 @@ build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt)
}
typedef
-struct AcpiBuildTables {
- GArray *table_data;
- GArray *rsdp;
- GArray *tcpalog;
- GArray *linker;
-} AcpiBuildTables;
-
-static inline void acpi_build_tables_init(AcpiBuildTables *tables)
-{
- tables->rsdp = g_array_new(false, true /* clear */, 1);
- tables->table_data = g_array_new(false, true /* clear */, 1);
- tables->tcpalog = g_array_new(false, true /* clear */, 1);
- tables->linker = bios_linker_loader_init();
-}
-
-static inline void acpi_build_tables_cleanup(AcpiBuildTables *tables, bool mfre)
-{
- void *linker_data = bios_linker_loader_cleanup(tables->linker);
- g_free(linker_data);
- g_array_free(tables->rsdp, true);
- g_array_free(tables->table_data, true);
- g_array_free(tables->tcpalog, mfre);
-}
-
-typedef
struct AcpiBuildState {
/* Copy of table in RAM (for patching). */
- ram_addr_t table_ram;
+ MemoryRegion *table_mr;
/* Is table patched? */
uint8_t patched;
PcGuestInfo *guest_info;
void *rsdp;
- ram_addr_t rsdp_ram;
- ram_addr_t linker_ram;
+ MemoryRegion *rsdp_mr;
+ MemoryRegion *linker_mr;
} AcpiBuildState;
static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg)
{
Object *pci_host;
QObject *o;
- bool ambiguous;
- pci_host = object_resolve_path_type("", TYPE_PCI_HOST_BRIDGE, &ambiguous);
- g_assert(!ambiguous);
+ pci_host = acpi_get_i386_pci_host();
g_assert(pci_host);
o = object_property_get_qobject(pci_host, PCIE_HOST_MCFG_BASE, NULL);
@@ -1428,12 +1719,14 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables)
acpi_add_table(table_offsets, tables_blob);
build_hpet(tables_blob, tables->linker);
}
- if (misc.has_tpm) {
+ if (misc.tpm_version != TPM_VERSION_UNSPEC) {
acpi_add_table(table_offsets, tables_blob);
build_tpm_tcpa(tables_blob, tables->linker, tables->tcpalog);
- acpi_add_table(table_offsets, tables_blob);
- build_tpm_ssdt(tables_blob, tables->linker);
+ if (misc.tpm_version == TPM_VERSION_2_0) {
+ acpi_add_table(table_offsets, tables_blob);
+ build_tpm2(tables_blob, tables->linker);
+ }
}
if (guest_info->numa_nodes) {
acpi_add_table(table_offsets, tables_blob);
@@ -1513,15 +1806,15 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables)
g_array_free(table_offsets, true);
}
-static void acpi_ram_update(ram_addr_t ram, GArray *data)
+static void acpi_ram_update(MemoryRegion *mr, GArray *data)
{
uint32_t size = acpi_data_len(data);
/* Make sure RAM size is correct - in case it got changed e.g. by migration */
- qemu_ram_resize(ram, size, &error_abort);
+ memory_region_ram_resize(mr, size, &error_abort);
- memcpy(qemu_get_ram_ptr(ram), data->data, size);
- cpu_physical_memory_set_dirty_range_nocode(ram, size);
+ memcpy(memory_region_get_ram_ptr(mr), data->data, size);
+ memory_region_set_dirty(mr, 0, size);
}
static void acpi_build_update(void *build_opaque, uint32_t offset)
@@ -1539,15 +1832,15 @@ static void acpi_build_update(void *build_opaque, uint32_t offset)
acpi_build(build_state->guest_info, &tables);
- acpi_ram_update(build_state->table_ram, tables.table_data);
+ acpi_ram_update(build_state->table_mr, tables.table_data);
if (build_state->rsdp) {
memcpy(build_state->rsdp, tables.rsdp->data, acpi_data_len(tables.rsdp));
} else {
- acpi_ram_update(build_state->rsdp_ram, tables.rsdp);
+ acpi_ram_update(build_state->rsdp_mr, tables.rsdp);
}
- acpi_ram_update(build_state->linker_ram, tables.linker);
+ acpi_ram_update(build_state->linker_mr, tables.linker);
acpi_build_tables_cleanup(&tables, true);
}
@@ -1557,8 +1850,9 @@ static void acpi_build_reset(void *build_opaque)
build_state->patched = 0;
}
-static ram_addr_t acpi_add_rom_blob(AcpiBuildState *build_state, GArray *blob,
- const char *name, uint64_t max_size)
+static MemoryRegion *acpi_add_rom_blob(AcpiBuildState *build_state,
+ GArray *blob, const char *name,
+ uint64_t max_size)
{
return rom_add_blob(name, blob->data, acpi_data_len(blob), max_size, -1,
name, acpi_build_update, build_state);
@@ -1604,12 +1898,12 @@ void acpi_setup(PcGuestInfo *guest_info)
acpi_build(build_state->guest_info, &tables);
/* Now expose it all to Guest */
- build_state->table_ram = acpi_add_rom_blob(build_state, tables.table_data,
+ build_state->table_mr = acpi_add_rom_blob(build_state, tables.table_data,
ACPI_BUILD_TABLE_FILE,
ACPI_BUILD_TABLE_MAX_SIZE);
- assert(build_state->table_ram != RAM_ADDR_MAX);
+ assert(build_state->table_mr != NULL);
- build_state->linker_ram =
+ build_state->linker_mr =
acpi_add_rom_blob(build_state, tables.linker, "etc/table-loader", 0);
fw_cfg_add_file(guest_info->fw_cfg, ACPI_BUILD_TPMLOG_FILE,
@@ -1627,10 +1921,10 @@ void acpi_setup(PcGuestInfo *guest_info)
fw_cfg_add_file_callback(guest_info->fw_cfg, ACPI_BUILD_RSDP_FILE,
acpi_build_update, build_state,
build_state->rsdp, rsdp_size);
- build_state->rsdp_ram = (ram_addr_t)-1;
+ build_state->rsdp_mr = NULL;
} else {
build_state->rsdp = NULL;
- build_state->rsdp_ram = acpi_add_rom_blob(build_state, tables.rsdp,
+ build_state->rsdp_mr = acpi_add_rom_blob(build_state, tables.rsdp,
ACPI_BUILD_RSDP_FILE, 0);
}
diff --git a/hw/i386/acpi-defs.h b/hw/i386/acpi-defs.h
deleted file mode 100644
index c4468f84e..000000000
--- a/hw/i386/acpi-defs.h
+++ /dev/null
@@ -1,368 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-#ifndef QEMU_ACPI_DEFS_H
-#define QEMU_ACPI_DEFS_H
-
-enum {
- ACPI_FADT_F_WBINVD,
- ACPI_FADT_F_WBINVD_FLUSH,
- ACPI_FADT_F_PROC_C1,
- ACPI_FADT_F_P_LVL2_UP,
- ACPI_FADT_F_PWR_BUTTON,
- ACPI_FADT_F_SLP_BUTTON,
- ACPI_FADT_F_FIX_RTC,
- ACPI_FADT_F_RTC_S4,
- ACPI_FADT_F_TMR_VAL_EXT,
- ACPI_FADT_F_DCK_CAP,
- ACPI_FADT_F_RESET_REG_SUP,
- ACPI_FADT_F_SEALED_CASE,
- ACPI_FADT_F_HEADLESS,
- ACPI_FADT_F_CPU_SW_SLP,
- ACPI_FADT_F_PCI_EXP_WAK,
- ACPI_FADT_F_USE_PLATFORM_CLOCK,
- ACPI_FADT_F_S4_RTC_STS_VALID,
- ACPI_FADT_F_REMOTE_POWER_ON_CAPABLE,
- ACPI_FADT_F_FORCE_APIC_CLUSTER_MODEL,
- ACPI_FADT_F_FORCE_APIC_PHYSICAL_DESTINATION_MODE,
- ACPI_FADT_F_HW_REDUCED_ACPI,
- ACPI_FADT_F_LOW_POWER_S0_IDLE_CAPABLE,
-};
-
-/*
- * ACPI 2.0 Generic Address Space definition.
- */
-struct Acpi20GenericAddress {
- uint8_t address_space_id;
- uint8_t register_bit_width;
- uint8_t register_bit_offset;
- uint8_t reserved;
- uint64_t address;
-} QEMU_PACKED;
-typedef struct Acpi20GenericAddress Acpi20GenericAddress;
-
-struct AcpiRsdpDescriptor { /* Root System Descriptor Pointer */
- uint64_t signature; /* ACPI signature, contains "RSD PTR " */
- uint8_t checksum; /* To make sum of struct == 0 */
- uint8_t oem_id [6]; /* OEM identification */
- uint8_t revision; /* Must be 0 for 1.0, 2 for 2.0 */
- uint32_t rsdt_physical_address; /* 32-bit physical address of RSDT */
- uint32_t length; /* XSDT Length in bytes including hdr */
- uint64_t xsdt_physical_address; /* 64-bit physical address of XSDT */
- uint8_t extended_checksum; /* Checksum of entire table */
- uint8_t reserved [3]; /* Reserved field must be 0 */
-} QEMU_PACKED;
-typedef struct AcpiRsdpDescriptor AcpiRsdpDescriptor;
-
-/* Table structure from Linux kernel (the ACPI tables are under the
- BSD license) */
-
-
-#define ACPI_TABLE_HEADER_DEF /* ACPI common table header */ \
- uint32_t signature; /* ACPI signature (4 ASCII characters) */ \
- uint32_t length; /* Length of table, in bytes, including header */ \
- uint8_t revision; /* ACPI Specification minor version # */ \
- uint8_t checksum; /* To make sum of entire table == 0 */ \
- uint8_t oem_id [6]; /* OEM identification */ \
- uint8_t oem_table_id [8]; /* OEM table identification */ \
- uint32_t oem_revision; /* OEM revision number */ \
- uint8_t asl_compiler_id [4]; /* ASL compiler vendor ID */ \
- uint32_t asl_compiler_revision; /* ASL compiler revision number */
-
-
-struct AcpiTableHeader /* ACPI common table header */
-{
- ACPI_TABLE_HEADER_DEF
-} QEMU_PACKED;
-typedef struct AcpiTableHeader AcpiTableHeader;
-
-/*
- * ACPI 1.0 Fixed ACPI Description Table (FADT)
- */
-struct AcpiFadtDescriptorRev1
-{
- ACPI_TABLE_HEADER_DEF /* ACPI common table header */
- uint32_t firmware_ctrl; /* Physical address of FACS */
- uint32_t dsdt; /* Physical address of DSDT */
- uint8_t model; /* System Interrupt Model */
- uint8_t reserved1; /* Reserved */
- uint16_t sci_int; /* System vector of SCI interrupt */
- uint32_t smi_cmd; /* Port address of SMI command port */
- uint8_t acpi_enable; /* Value to write to smi_cmd to enable ACPI */
- uint8_t acpi_disable; /* Value to write to smi_cmd to disable ACPI */
- uint8_t S4bios_req; /* Value to write to SMI CMD to enter S4BIOS state */
- uint8_t reserved2; /* Reserved - must be zero */
- uint32_t pm1a_evt_blk; /* Port address of Power Mgt 1a acpi_event Reg Blk */
- uint32_t pm1b_evt_blk; /* Port address of Power Mgt 1b acpi_event Reg Blk */
- uint32_t pm1a_cnt_blk; /* Port address of Power Mgt 1a Control Reg Blk */
- uint32_t pm1b_cnt_blk; /* Port address of Power Mgt 1b Control Reg Blk */
- uint32_t pm2_cnt_blk; /* Port address of Power Mgt 2 Control Reg Blk */
- uint32_t pm_tmr_blk; /* Port address of Power Mgt Timer Ctrl Reg Blk */
- uint32_t gpe0_blk; /* Port addr of General Purpose acpi_event 0 Reg Blk */
- uint32_t gpe1_blk; /* Port addr of General Purpose acpi_event 1 Reg Blk */
- uint8_t pm1_evt_len; /* Byte length of ports at pm1_x_evt_blk */
- uint8_t pm1_cnt_len; /* Byte length of ports at pm1_x_cnt_blk */
- uint8_t pm2_cnt_len; /* Byte Length of ports at pm2_cnt_blk */
- uint8_t pm_tmr_len; /* Byte Length of ports at pm_tm_blk */
- uint8_t gpe0_blk_len; /* Byte Length of ports at gpe0_blk */
- uint8_t gpe1_blk_len; /* Byte Length of ports at gpe1_blk */
- uint8_t gpe1_base; /* Offset in gpe model where gpe1 events start */
- uint8_t reserved3; /* Reserved */
- uint16_t plvl2_lat; /* Worst case HW latency to enter/exit C2 state */
- uint16_t plvl3_lat; /* Worst case HW latency to enter/exit C3 state */
- uint16_t flush_size; /* Size of area read to flush caches */
- uint16_t flush_stride; /* Stride used in flushing caches */
- uint8_t duty_offset; /* Bit location of duty cycle field in p_cnt reg */
- uint8_t duty_width; /* Bit width of duty cycle field in p_cnt reg */
- uint8_t day_alrm; /* Index to day-of-month alarm in RTC CMOS RAM */
- uint8_t mon_alrm; /* Index to month-of-year alarm in RTC CMOS RAM */
- uint8_t century; /* Index to century in RTC CMOS RAM */
- uint8_t reserved4; /* Reserved */
- uint8_t reserved4a; /* Reserved */
- uint8_t reserved4b; /* Reserved */
- uint32_t flags;
-} QEMU_PACKED;
-typedef struct AcpiFadtDescriptorRev1 AcpiFadtDescriptorRev1;
-
-/*
- * ACPI 1.0 Root System Description Table (RSDT)
- */
-struct AcpiRsdtDescriptorRev1
-{
- ACPI_TABLE_HEADER_DEF /* ACPI common table header */
- uint32_t table_offset_entry[0]; /* Array of pointers to other */
- /* ACPI tables */
-} QEMU_PACKED;
-typedef struct AcpiRsdtDescriptorRev1 AcpiRsdtDescriptorRev1;
-
-/*
- * ACPI 1.0 Firmware ACPI Control Structure (FACS)
- */
-struct AcpiFacsDescriptorRev1
-{
- uint32_t signature; /* ACPI Signature */
- uint32_t length; /* Length of structure, in bytes */
- uint32_t hardware_signature; /* Hardware configuration signature */
- uint32_t firmware_waking_vector; /* ACPI OS waking vector */
- uint32_t global_lock; /* Global Lock */
- uint32_t flags;
- uint8_t resverved3 [40]; /* Reserved - must be zero */
-} QEMU_PACKED;
-typedef struct AcpiFacsDescriptorRev1 AcpiFacsDescriptorRev1;
-
-/*
- * Differentiated System Description Table (DSDT)
- */
-
-/*
- * MADT values and structures
- */
-
-/* Values for MADT PCATCompat */
-
-#define ACPI_DUAL_PIC 0
-#define ACPI_MULTIPLE_APIC 1
-
-/* Master MADT */
-
-struct AcpiMultipleApicTable
-{
- ACPI_TABLE_HEADER_DEF /* ACPI common table header */
- uint32_t local_apic_address; /* Physical address of local APIC */
- uint32_t flags;
-} QEMU_PACKED;
-typedef struct AcpiMultipleApicTable AcpiMultipleApicTable;
-
-/* Values for Type in APIC sub-headers */
-
-#define ACPI_APIC_PROCESSOR 0
-#define ACPI_APIC_IO 1
-#define ACPI_APIC_XRUPT_OVERRIDE 2
-#define ACPI_APIC_NMI 3
-#define ACPI_APIC_LOCAL_NMI 4
-#define ACPI_APIC_ADDRESS_OVERRIDE 5
-#define ACPI_APIC_IO_SAPIC 6
-#define ACPI_APIC_LOCAL_SAPIC 7
-#define ACPI_APIC_XRUPT_SOURCE 8
-#define ACPI_APIC_RESERVED 9 /* 9 and greater are reserved */
-
-/*
- * MADT sub-structures (Follow MULTIPLE_APIC_DESCRIPTION_TABLE)
- */
-#define ACPI_SUB_HEADER_DEF /* Common ACPI sub-structure header */\
- uint8_t type; \
- uint8_t length;
-
-/* Sub-structures for MADT */
-
-struct AcpiMadtProcessorApic
-{
- ACPI_SUB_HEADER_DEF
- uint8_t processor_id; /* ACPI processor id */
- uint8_t local_apic_id; /* Processor's local APIC id */
- uint32_t flags;
-} QEMU_PACKED;
-typedef struct AcpiMadtProcessorApic AcpiMadtProcessorApic;
-
-struct AcpiMadtIoApic
-{
- ACPI_SUB_HEADER_DEF
- uint8_t io_apic_id; /* I/O APIC ID */
- uint8_t reserved; /* Reserved - must be zero */
- uint32_t address; /* APIC physical address */
- uint32_t interrupt; /* Global system interrupt where INTI
- * lines start */
-} QEMU_PACKED;
-typedef struct AcpiMadtIoApic AcpiMadtIoApic;
-
-struct AcpiMadtIntsrcovr {
- ACPI_SUB_HEADER_DEF
- uint8_t bus;
- uint8_t source;
- uint32_t gsi;
- uint16_t flags;
-} QEMU_PACKED;
-typedef struct AcpiMadtIntsrcovr AcpiMadtIntsrcovr;
-
-struct AcpiMadtLocalNmi {
- ACPI_SUB_HEADER_DEF
- uint8_t processor_id; /* ACPI processor id */
- uint16_t flags; /* MPS INTI flags */
- uint8_t lint; /* Local APIC LINT# */
-} QEMU_PACKED;
-typedef struct AcpiMadtLocalNmi AcpiMadtLocalNmi;
-
-/*
- * HPET Description Table
- */
-struct Acpi20Hpet {
- ACPI_TABLE_HEADER_DEF /* ACPI common table header */
- uint32_t timer_block_id;
- Acpi20GenericAddress addr;
- uint8_t hpet_number;
- uint16_t min_tick;
- uint8_t page_protect;
-} QEMU_PACKED;
-typedef struct Acpi20Hpet Acpi20Hpet;
-
-/*
- * SRAT (NUMA topology description) table
- */
-
-struct AcpiSystemResourceAffinityTable
-{
- ACPI_TABLE_HEADER_DEF
- uint32_t reserved1;
- uint32_t reserved2[2];
-} QEMU_PACKED;
-typedef struct AcpiSystemResourceAffinityTable AcpiSystemResourceAffinityTable;
-
-#define ACPI_SRAT_PROCESSOR 0
-#define ACPI_SRAT_MEMORY 1
-
-struct AcpiSratProcessorAffinity
-{
- ACPI_SUB_HEADER_DEF
- uint8_t proximity_lo;
- uint8_t local_apic_id;
- uint32_t flags;
- uint8_t local_sapic_eid;
- uint8_t proximity_hi[3];
- uint32_t reserved;
-} QEMU_PACKED;
-typedef struct AcpiSratProcessorAffinity AcpiSratProcessorAffinity;
-
-struct AcpiSratMemoryAffinity
-{
- ACPI_SUB_HEADER_DEF
- uint8_t proximity[4];
- uint16_t reserved1;
- uint64_t base_addr;
- uint64_t range_length;
- uint32_t reserved2;
- uint32_t flags;
- uint32_t reserved3[2];
-} QEMU_PACKED;
-typedef struct AcpiSratMemoryAffinity AcpiSratMemoryAffinity;
-
-/* PCI fw r3.0 MCFG table. */
-/* Subtable */
-struct AcpiMcfgAllocation {
- uint64_t address; /* Base address, processor-relative */
- uint16_t pci_segment; /* PCI segment group number */
- uint8_t start_bus_number; /* Starting PCI Bus number */
- uint8_t end_bus_number; /* Final PCI Bus number */
- uint32_t reserved;
-} QEMU_PACKED;
-typedef struct AcpiMcfgAllocation AcpiMcfgAllocation;
-
-struct AcpiTableMcfg {
- ACPI_TABLE_HEADER_DEF;
- uint8_t reserved[8];
- AcpiMcfgAllocation allocation[0];
-} QEMU_PACKED;
-typedef struct AcpiTableMcfg AcpiTableMcfg;
-
-/*
- * TCPA Description Table
- */
-struct Acpi20Tcpa {
- ACPI_TABLE_HEADER_DEF /* ACPI common table header */
- uint16_t platform_class;
- uint32_t log_area_minimum_length;
- uint64_t log_area_start_address;
-} QEMU_PACKED;
-typedef struct Acpi20Tcpa Acpi20Tcpa;
-
-/* DMAR - DMA Remapping table r2.2 */
-struct AcpiTableDmar {
- ACPI_TABLE_HEADER_DEF
- uint8_t host_address_width; /* Maximum DMA physical addressability */
- uint8_t flags;
- uint8_t reserved[10];
-} QEMU_PACKED;
-typedef struct AcpiTableDmar AcpiTableDmar;
-
-/* Masks for Flags field above */
-#define ACPI_DMAR_INTR_REMAP 1
-#define ACPI_DMAR_X2APIC_OPT_OUT (1 << 1)
-
-/* Values for sub-structure type for DMAR */
-enum {
- ACPI_DMAR_TYPE_HARDWARE_UNIT = 0, /* DRHD */
- ACPI_DMAR_TYPE_RESERVED_MEMORY = 1, /* RMRR */
- ACPI_DMAR_TYPE_ATSR = 2, /* ATSR */
- ACPI_DMAR_TYPE_HARDWARE_AFFINITY = 3, /* RHSR */
- ACPI_DMAR_TYPE_ANDD = 4, /* ANDD */
- ACPI_DMAR_TYPE_RESERVED = 5 /* Reserved for furture use */
-};
-
-/*
- * Sub-structures for DMAR
- */
-/* Type 0: Hardware Unit Definition */
-struct AcpiDmarHardwareUnit {
- uint16_t type;
- uint16_t length;
- uint8_t flags;
- uint8_t reserved;
- uint16_t pci_segment; /* The PCI Segment associated with this unit */
- uint64_t address; /* Base address of remapping hardware register-set */
-} QEMU_PACKED;
-typedef struct AcpiDmarHardwareUnit AcpiDmarHardwareUnit;
-
-/* Masks for Flags field above */
-#define ACPI_DMAR_INCLUDE_PCI_ALL 1
-
-#endif
diff --git a/hw/i386/acpi-dsdt-mem-hotplug.dsl b/hw/i386/acpi-dsdt-mem-hotplug.dsl
index 1e9ec3927..c2bb6a160 100644
--- a/hw/i386/acpi-dsdt-mem-hotplug.dsl
+++ b/hw/i386/acpi-dsdt-mem-hotplug.dsl
@@ -29,6 +29,8 @@
External(MEMORY_SLOT_PROXIMITY, FieldUnitObj) // read only
External(MEMORY_SLOT_ENABLED, FieldUnitObj) // 1 if enabled, read only
External(MEMORY_SLOT_INSERT_EVENT, FieldUnitObj) // (read) 1 if has a insert event. (write) 1 to clear event
+ External(MEMORY_SLOT_REMOVE_EVENT, FieldUnitObj) // (read) 1 if has a remove event. (write) 1 to clear event
+ External(MEMORY_SLOT_EJECT, FieldUnitObj) // initiates device eject, write only
External(MEMORY_SLOT_SLECTOR, FieldUnitObj) // DIMM selector, write only
External(MEMORY_SLOT_OST_EVENT, FieldUnitObj) // _OST event code, write only
External(MEMORY_SLOT_OST_STATUS, FieldUnitObj) // _OST status code, write only
@@ -55,8 +57,10 @@
If (LEqual(MEMORY_SLOT_INSERT_EVENT, One)) { // Memory device needs check
MEMORY_SLOT_NOTIFY_METHOD(Local0, 1)
Store(1, MEMORY_SLOT_INSERT_EVENT)
+ } Elseif (LEqual(MEMORY_SLOT_REMOVE_EVENT, One)) { // Ejection request
+ MEMORY_SLOT_NOTIFY_METHOD(Local0, 3)
+ Store(1, MEMORY_SLOT_REMOVE_EVENT)
}
- // TODO: handle memory eject request
Add(Local0, One, Local0) // goto next DIMM
}
Release(MEMORY_SLOT_LOCK)
@@ -156,5 +160,12 @@
Store(Arg2, MEMORY_SLOT_OST_STATUS)
Release(MEMORY_SLOT_LOCK)
}
+
+ Method(MEMORY_SLOT_EJECT_METHOD, 2) {
+ Acquire(MEMORY_SLOT_LOCK, 0xFFFF)
+ Store(ToInteger(Arg0), MEMORY_SLOT_SLECTOR) // select DIMM
+ Store(1, MEMORY_SLOT_EJECT)
+ Release(MEMORY_SLOT_LOCK)
+ }
} // Device()
} // Scope()
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index 7da70ff34..08055a8d8 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -246,7 +246,8 @@ static void vtd_generate_interrupt(IntelIOMMUState *s, hwaddr mesg_addr_reg,
data = vtd_get_long_raw(s, mesg_data_reg);
VTD_DPRINTF(FLOG, "msi: addr 0x%"PRIx64 " data 0x%"PRIx32, addr, data);
- stl_le_phys(&address_space_memory, addr, data);
+ address_space_stl_le(&address_space_memory, addr, data,
+ MEMTXATTRS_UNSPECIFIED, NULL);
}
/* Generate a fault event to software via MSI if conditions are met.
diff --git a/hw/i386/kvm/pci-assign.c b/hw/i386/kvm/pci-assign.c
index 9db7c7760..74d22f4fd 100644
--- a/hw/i386/kvm/pci-assign.c
+++ b/hw/i386/kvm/pci-assign.c
@@ -141,6 +141,9 @@ typedef struct AssignedDevice {
int32_t bootindex;
} AssignedDevice;
+#define TYPE_PCI_ASSIGN "kvm-pci-assign"
+#define PCI_ASSIGN(obj) OBJECT_CHECK(AssignedDevice, (obj), TYPE_PCI_ASSIGN)
+
static void assigned_dev_update_irq_routing(PCIDevice *dev);
static void assigned_dev_load_option_rom(AssignedDevice *dev);
@@ -257,7 +260,7 @@ static const MemoryRegionOps slow_bar_ops = {
static void assigned_dev_iomem_setup(PCIDevice *pci_dev, int region_num,
pcibus_t e_size)
{
- AssignedDevice *r_dev = DO_UPCAST(AssignedDevice, dev, pci_dev);
+ AssignedDevice *r_dev = PCI_ASSIGN(pci_dev);
AssignedDevRegion *region = &r_dev->v_addrs[region_num];
PCIRegion *real_region = &r_dev->real_device.regions[region_num];
@@ -289,7 +292,7 @@ static const MemoryRegionOps assigned_dev_ioport_ops = {
static void assigned_dev_ioport_setup(PCIDevice *pci_dev, int region_num,
pcibus_t size)
{
- AssignedDevice *r_dev = DO_UPCAST(AssignedDevice, dev, pci_dev);
+ AssignedDevice *r_dev = PCI_ASSIGN(pci_dev);
AssignedDevRegion *region = &r_dev->v_addrs[region_num];
region->e_size = size;
@@ -303,7 +306,7 @@ static void assigned_dev_ioport_setup(PCIDevice *pci_dev, int region_num,
static uint32_t assigned_dev_pci_read(PCIDevice *d, int pos, int len)
{
- AssignedDevice *pci_dev = DO_UPCAST(AssignedDevice, dev, d);
+ AssignedDevice *pci_dev = PCI_ASSIGN(d);
uint32_t val;
ssize_t ret;
int fd = pci_dev->real_device.config_fd;
@@ -328,7 +331,7 @@ static uint8_t assigned_dev_pci_read_byte(PCIDevice *d, int pos)
static void assigned_dev_pci_write(PCIDevice *d, int pos, uint32_t val, int len)
{
- AssignedDevice *pci_dev = DO_UPCAST(AssignedDevice, dev, d);
+ AssignedDevice *pci_dev = PCI_ASSIGN(d);
ssize_t ret;
int fd = pci_dev->real_device.config_fd;
@@ -946,7 +949,7 @@ static void deassign_device(AssignedDevice *dev)
*/
static void assigned_dev_update_irq_routing(PCIDevice *dev)
{
- AssignedDevice *assigned_dev = DO_UPCAST(AssignedDevice, dev, dev);
+ AssignedDevice *assigned_dev = PCI_ASSIGN(dev);
Error *err = NULL;
int r;
@@ -961,7 +964,7 @@ static void assigned_dev_update_irq_routing(PCIDevice *dev)
static void assigned_dev_update_msi(PCIDevice *pci_dev)
{
- AssignedDevice *assigned_dev = DO_UPCAST(AssignedDevice, dev, pci_dev);
+ AssignedDevice *assigned_dev = PCI_ASSIGN(pci_dev);
uint8_t ctrl_byte = pci_get_byte(pci_dev->config + pci_dev->msi_cap +
PCI_MSI_FLAGS);
int r;
@@ -1015,7 +1018,7 @@ static void assigned_dev_update_msi(PCIDevice *pci_dev)
static void assigned_dev_update_msi_msg(PCIDevice *pci_dev)
{
- AssignedDevice *assigned_dev = DO_UPCAST(AssignedDevice, dev, pci_dev);
+ AssignedDevice *assigned_dev = PCI_ASSIGN(pci_dev);
uint8_t ctrl_byte = pci_get_byte(pci_dev->config + pci_dev->msi_cap +
PCI_MSI_FLAGS);
@@ -1048,7 +1051,7 @@ static bool assigned_dev_msix_skipped(MSIXTableEntry *entry)
static int assigned_dev_update_msix_mmio(PCIDevice *pci_dev)
{
- AssignedDevice *adev = DO_UPCAST(AssignedDevice, dev, pci_dev);
+ AssignedDevice *adev = PCI_ASSIGN(pci_dev);
uint16_t entries_nr = 0;
int i, r = 0;
MSIXTableEntry *entry = adev->msix_table;
@@ -1113,7 +1116,7 @@ static int assigned_dev_update_msix_mmio(PCIDevice *pci_dev)
static void assigned_dev_update_msix(PCIDevice *pci_dev)
{
- AssignedDevice *assigned_dev = DO_UPCAST(AssignedDevice, dev, pci_dev);
+ AssignedDevice *assigned_dev = PCI_ASSIGN(pci_dev);
uint16_t ctrl_word = pci_get_word(pci_dev->config + pci_dev->msix_cap +
PCI_MSIX_FLAGS);
int r;
@@ -1163,7 +1166,7 @@ static void assigned_dev_update_msix(PCIDevice *pci_dev)
static uint32_t assigned_dev_pci_read_config(PCIDevice *pci_dev,
uint32_t address, int len)
{
- AssignedDevice *assigned_dev = DO_UPCAST(AssignedDevice, dev, pci_dev);
+ AssignedDevice *assigned_dev = PCI_ASSIGN(pci_dev);
uint32_t virt_val = pci_default_read_config(pci_dev, address, len);
uint32_t real_val, emulate_mask, full_emulation_mask;
@@ -1184,7 +1187,7 @@ static uint32_t assigned_dev_pci_read_config(PCIDevice *pci_dev,
static void assigned_dev_pci_write_config(PCIDevice *pci_dev, uint32_t address,
uint32_t val, int len)
{
- AssignedDevice *assigned_dev = DO_UPCAST(AssignedDevice, dev, pci_dev);
+ AssignedDevice *assigned_dev = PCI_ASSIGN(pci_dev);
uint16_t old_cmd = pci_get_word(pci_dev->config + PCI_COMMAND);
uint32_t emulate_mask, full_emulation_mask;
int ret;
@@ -1244,7 +1247,7 @@ static void assigned_dev_setup_cap_read(AssignedDevice *dev, uint32_t offset,
static int assigned_device_pci_cap_init(PCIDevice *pci_dev, Error **errp)
{
- AssignedDevice *dev = DO_UPCAST(AssignedDevice, dev, pci_dev);
+ AssignedDevice *dev = PCI_ASSIGN(pci_dev);
PCIRegion *pci_region = dev->real_device.regions;
int ret, pos;
Error *local_err = NULL;
@@ -1684,8 +1687,8 @@ static const VMStateDescription vmstate_assigned_device = {
static void reset_assigned_device(DeviceState *dev)
{
- PCIDevice *pci_dev = DO_UPCAST(PCIDevice, qdev, dev);
- AssignedDevice *adev = DO_UPCAST(AssignedDevice, dev, pci_dev);
+ PCIDevice *pci_dev = PCI_DEVICE(dev);
+ AssignedDevice *adev = PCI_ASSIGN(pci_dev);
char reset_file[64];
const char reset[] = "1";
int fd, ret;
@@ -1740,7 +1743,7 @@ static void reset_assigned_device(DeviceState *dev)
static void assigned_realize(struct PCIDevice *pci_dev, Error **errp)
{
- AssignedDevice *dev = DO_UPCAST(AssignedDevice, dev, pci_dev);
+ AssignedDevice *dev = PCI_ASSIGN(pci_dev);
uint8_t e_intx;
int r;
Error *local_err = NULL;
@@ -1836,7 +1839,7 @@ exit_with_error:
static void assigned_exitfn(struct PCIDevice *pci_dev)
{
- AssignedDevice *dev = DO_UPCAST(AssignedDevice, dev, pci_dev);
+ AssignedDevice *dev = PCI_ASSIGN(pci_dev);
deassign_device(dev);
free_assigned_device(dev);
@@ -1845,7 +1848,7 @@ static void assigned_exitfn(struct PCIDevice *pci_dev)
static void assigned_dev_instance_init(Object *obj)
{
PCIDevice *pci_dev = PCI_DEVICE(obj);
- AssignedDevice *d = DO_UPCAST(AssignedDevice, dev, PCI_DEVICE(obj));
+ AssignedDevice *d = PCI_ASSIGN(pci_dev);
device_add_bootindex_property(obj, &d->bootindex,
"bootindex", NULL,
@@ -1879,7 +1882,7 @@ static void assign_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo assign_info = {
- .name = "kvm-pci-assign",
+ .name = TYPE_PCI_ASSIGN,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(AssignedDevice),
.class_init = assign_class_init,
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index a8e6be14e..7661ea9cd 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -30,7 +30,7 @@
#include "hw/block/fdc.h"
#include "hw/ide.h"
#include "hw/pci/pci.h"
-#include "monitor/monitor.h"
+#include "hw/pci/pci_bus.h"
#include "hw/nvram/fw_cfg.h"
#include "hw/timer/hpet.h"
#include "hw/i386/smbios.h"
@@ -56,6 +56,7 @@
#include "sysemu/arch_init.h"
#include "qemu/bitmap.h"
#include "qemu/config-file.h"
+#include "qemu/error-report.h"
#include "hw/acpi/acpi.h"
#include "hw/acpi/cpu_hotplug.h"
#include "hw/cpu/icc_bus.h"
@@ -63,7 +64,6 @@
#include "hw/pci/pci_host.h"
#include "acpi-build.h"
#include "hw/mem/pc-dimm.h"
-#include "trace.h"
#include "qapi/visitor.h"
#include "qapi-visit.h"
@@ -163,27 +163,6 @@ uint64_t cpu_get_tsc(CPUX86State *env)
return cpu_get_ticks();
}
-/* SMM support */
-
-static cpu_set_smm_t smm_set;
-static void *smm_arg;
-
-void cpu_smm_register(cpu_set_smm_t callback, void *arg)
-{
- assert(smm_set == NULL);
- assert(smm_arg == NULL);
- smm_set = callback;
- smm_arg = arg;
-}
-
-void cpu_smm_update(CPUX86State *env)
-{
- if (smm_set && smm_arg && CPU(x86_env_get_cpu(env)) == first_cpu) {
- smm_set(!!(env->hflags & HF_SMM_MASK), smm_arg);
- }
-}
-
-
/* IRQ handling */
int cpu_get_pic_interrupt(CPUX86State *env)
{
@@ -314,11 +293,82 @@ static void pc_boot_set(void *opaque, const char *boot_device, Error **errp)
set_boot_dev(opaque, boot_device, errp);
}
+static void pc_cmos_init_floppy(ISADevice *rtc_state, ISADevice *floppy)
+{
+ int val, nb, i;
+ FDriveType fd_type[2] = { FDRIVE_DRV_NONE, FDRIVE_DRV_NONE };
+
+ /* floppy type */
+ if (floppy) {
+ for (i = 0; i < 2; i++) {
+ fd_type[i] = isa_fdc_get_drive_type(floppy, i);
+ }
+ }
+ val = (cmos_get_fd_drive_type(fd_type[0]) << 4) |
+ cmos_get_fd_drive_type(fd_type[1]);
+ rtc_set_memory(rtc_state, 0x10, val);
+
+ val = rtc_get_memory(rtc_state, REG_EQUIPMENT_BYTE);
+ nb = 0;
+ if (fd_type[0] < FDRIVE_DRV_NONE) {
+ nb++;
+ }
+ if (fd_type[1] < FDRIVE_DRV_NONE) {
+ nb++;
+ }
+ switch (nb) {
+ case 0:
+ break;
+ case 1:
+ val |= 0x01; /* 1 drive, ready for boot */
+ break;
+ case 2:
+ val |= 0x41; /* 2 drives, ready for boot */
+ break;
+ }
+ rtc_set_memory(rtc_state, REG_EQUIPMENT_BYTE, val);
+}
+
typedef struct pc_cmos_init_late_arg {
ISADevice *rtc_state;
BusState *idebus[2];
} pc_cmos_init_late_arg;
+typedef struct check_fdc_state {
+ ISADevice *floppy;
+ bool multiple;
+} CheckFdcState;
+
+static int check_fdc(Object *obj, void *opaque)
+{
+ CheckFdcState *state = opaque;
+ Object *fdc;
+ uint32_t iobase;
+ Error *local_err = NULL;
+
+ fdc = object_dynamic_cast(obj, TYPE_ISA_FDC);
+ if (!fdc) {
+ return 0;
+ }
+
+ iobase = object_property_get_int(obj, "iobase", &local_err);
+ if (local_err || iobase != 0x3f0) {
+ error_free(local_err);
+ return 0;
+ }
+
+ if (state->floppy) {
+ state->multiple = true;
+ } else {
+ state->floppy = ISA_DEVICE(obj);
+ }
+ return 0;
+}
+
+static const char * const fdc_container_path[] = {
+ "/unattached", "/peripheral", "/peripheral-anon"
+};
+
static void pc_cmos_init_late(void *opaque)
{
pc_cmos_init_late_arg *arg = opaque;
@@ -327,6 +377,8 @@ static void pc_cmos_init_late(void *opaque)
int8_t heads, sectors;
int val;
int i, trans;
+ Object *container;
+ CheckFdcState state = { 0 };
val = 0;
if (ide_get_geometry(arg->idebus[0], 0,
@@ -356,16 +408,32 @@ static void pc_cmos_init_late(void *opaque)
}
rtc_set_memory(s, 0x39, val);
+ /*
+ * Locate the FDC at IO address 0x3f0, and configure the CMOS registers
+ * accordingly.
+ */
+ for (i = 0; i < ARRAY_SIZE(fdc_container_path); i++) {
+ container = container_get(qdev_get_machine(), fdc_container_path[i]);
+ object_child_foreach(container, check_fdc, &state);
+ }
+
+ if (state.multiple) {
+ error_report("warning: multiple floppy disk controllers with "
+ "iobase=0x3f0 have been found;\n"
+ "the one being picked for CMOS setup might not reflect "
+ "your intent");
+ }
+ pc_cmos_init_floppy(s, state.floppy);
+
qemu_unregister_reset(pc_cmos_init_late, opaque);
}
void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
const char *boot_device, MachineState *machine,
- ISADevice *floppy, BusState *idebus0, BusState *idebus1,
+ BusState *idebus0, BusState *idebus1,
ISADevice *s)
{
- int val, nb, i;
- FDriveType fd_type[2] = { FDRIVE_DRV_NONE, FDRIVE_DRV_NONE };
+ int val;
static pc_cmos_init_late_arg arg;
PCMachineState *pc_machine = PC_MACHINE(machine);
Error *local_err = NULL;
@@ -422,39 +490,12 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
exit(1);
}
- /* floppy type */
- if (floppy) {
- for (i = 0; i < 2; i++) {
- fd_type[i] = isa_fdc_get_drive_type(floppy, i);
- }
- }
- val = (cmos_get_fd_drive_type(fd_type[0]) << 4) |
- cmos_get_fd_drive_type(fd_type[1]);
- rtc_set_memory(s, 0x10, val);
-
val = 0;
- nb = 0;
- if (fd_type[0] < FDRIVE_DRV_NONE) {
- nb++;
- }
- if (fd_type[1] < FDRIVE_DRV_NONE) {
- nb++;
- }
- switch (nb) {
- case 0:
- break;
- case 1:
- val |= 0x01; /* 1 drive, ready for boot */
- break;
- case 2:
- val |= 0x41; /* 2 drives, ready for boot */
- break;
- }
val |= 0x02; /* FPU is there */
val |= 0x04; /* PS/2 mouse installed */
rtc_set_memory(s, REG_EQUIPMENT_BYTE, val);
- /* hard drives */
+ /* hard drives and FDC */
arg.rtc_state = s;
arg.idebus[0] = idebus0;
arg.idebus[1] = idebus1;
@@ -1006,7 +1047,6 @@ static X86CPU *pc_new_cpu(const char *cpu_model, int64_t apic_id,
}
qdev_set_parent_bus(DEVICE(cpu), qdev_get_child_bus(icc_bridge, "icc"));
- object_unref(OBJECT(cpu));
object_property_set_int(OBJECT(cpu), apic_id, "apic-id", &local_err);
object_property_set_bool(OBJECT(cpu), true, "realized", &local_err);
@@ -1025,7 +1065,9 @@ static const char *current_cpu_model;
void pc_hot_add_cpu(const int64_t id, Error **errp)
{
DeviceState *icc_bridge;
+ X86CPU *cpu;
int64_t apic_id = x86_cpu_apic_id_from_index(id);
+ Error *local_err = NULL;
if (id < 0) {
error_setg(errp, "Invalid CPU id: %" PRIi64, id);
@@ -1053,7 +1095,12 @@ void pc_hot_add_cpu(const int64_t id, Error **errp)
icc_bridge = DEVICE(object_resolve_path_type("icc-bridge",
TYPE_ICC_BRIDGE, NULL));
- pc_new_cpu(current_cpu_model, apic_id, icc_bridge, errp);
+ cpu = pc_new_cpu(current_cpu_model, apic_id, icc_bridge, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ object_unref(OBJECT(cpu));
}
void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge)
@@ -1087,6 +1134,7 @@ void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge)
error_report_err(error);
exit(1);
}
+ object_unref(OBJECT(cpu));
}
/* map APIC MMIO area if CPU has APIC */
@@ -1119,6 +1167,25 @@ void pc_guest_info_machine_done(Notifier *notifier, void *data)
PcGuestInfoState *guest_info_state = container_of(notifier,
PcGuestInfoState,
machine_done);
+ PCIBus *bus = find_i440fx();
+
+ if (bus) {
+ int extra_hosts = 0;
+
+ QLIST_FOREACH(bus, &bus->child, sibling) {
+ /* look for expander root buses */
+ if (pci_bus_is_root(bus)) {
+ extra_hosts++;
+ }
+ }
+ if (extra_hosts && guest_info_state->info.fw_cfg) {
+ uint64_t *val = g_malloc(sizeof(*val));
+ *val = cpu_to_le64(extra_hosts);
+ fw_cfg_add_file(guest_info_state->info.fw_cfg,
+ "etc/extra-pci-roots", val, sizeof(*val));
+ }
+ }
+
acpi_setup(&guest_info_state->info);
}
@@ -1291,7 +1358,7 @@ FWCfgState *pc_memory_init(MachineState *machine,
exit(EXIT_FAILURE);
}
- pcms->hotplug_memory_base =
+ pcms->hotplug_memory.base =
ROUND_UP(0x100000000ULL + above_4g_mem_size, 1ULL << 30);
if (pcms->enforce_aligned_dimm) {
@@ -1299,17 +1366,17 @@ FWCfgState *pc_memory_init(MachineState *machine,
hotplug_mem_size += (1ULL << 30) * machine->ram_slots;
}
- if ((pcms->hotplug_memory_base + hotplug_mem_size) <
+ if ((pcms->hotplug_memory.base + hotplug_mem_size) <
hotplug_mem_size) {
error_report("unsupported amount of maximum memory: " RAM_ADDR_FMT,
machine->maxram_size);
exit(EXIT_FAILURE);
}
- memory_region_init(&pcms->hotplug_memory, OBJECT(pcms),
+ memory_region_init(&pcms->hotplug_memory.mr, OBJECT(pcms),
"hotplug-memory", hotplug_mem_size);
- memory_region_add_subregion(system_memory, pcms->hotplug_memory_base,
- &pcms->hotplug_memory);
+ memory_region_add_subregion(system_memory, pcms->hotplug_memory.base,
+ &pcms->hotplug_memory.mr);
}
/* Initialize PC system firmware */
@@ -1327,9 +1394,9 @@ FWCfgState *pc_memory_init(MachineState *machine,
fw_cfg = bochs_bios_init();
rom_set_fw(fw_cfg);
- if (guest_info->has_reserved_memory && pcms->hotplug_memory_base) {
+ if (guest_info->has_reserved_memory && pcms->hotplug_memory.base) {
uint64_t *val = g_malloc(sizeof(*val));
- *val = cpu_to_le64(ROUND_UP(pcms->hotplug_memory_base, 0x1ULL << 30));
+ *val = cpu_to_le64(ROUND_UP(pcms->hotplug_memory.base, 0x1ULL << 30));
fw_cfg_add_file(fw_cfg, "etc/reserved-memory-end", val, sizeof(*val));
}
@@ -1345,9 +1412,9 @@ FWCfgState *pc_memory_init(MachineState *machine,
return fw_cfg;
}
-qemu_irq *pc_allocate_cpu_irq(void)
+qemu_irq pc_allocate_cpu_irq(void)
{
- return qemu_allocate_irqs(pic_irq_request, NULL, 1);
+ return qemu_allocate_irq(pic_irq_request, NULL, 0);
}
DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus)
@@ -1395,7 +1462,7 @@ static const MemoryRegionOps ioportF0_io_ops = {
void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
ISADevice **rtc_state,
- ISADevice **floppy,
+ bool create_fdctrl,
bool no_vmport,
uint32 hpet_irqs)
{
@@ -1489,8 +1556,11 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
for(i = 0; i < MAX_FD; i++) {
fd[i] = drive_get(IF_FLOPPY, 0, i);
+ create_fdctrl |= !!fd[i];
+ }
+ if (create_fdctrl) {
+ fdctrl_init_isa(isa_bus, fd);
}
- *floppy = fdctrl_init_isa(isa_bus, fd);
}
void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
@@ -1543,118 +1613,44 @@ void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name)
}
}
-static void pc_generic_machine_class_init(ObjectClass *oc, void *data)
-{
- MachineClass *mc = MACHINE_CLASS(oc);
- QEMUMachine *qm = data;
-
- mc->family = qm->family;
- mc->name = qm->name;
- mc->alias = qm->alias;
- mc->desc = qm->desc;
- mc->init = qm->init;
- mc->reset = qm->reset;
- mc->hot_add_cpu = qm->hot_add_cpu;
- mc->kvm_type = qm->kvm_type;
- mc->block_default_type = qm->block_default_type;
- mc->units_per_default_bus = qm->units_per_default_bus;
- mc->max_cpus = qm->max_cpus;
- mc->no_serial = qm->no_serial;
- mc->no_parallel = qm->no_parallel;
- mc->use_virtcon = qm->use_virtcon;
- mc->use_sclp = qm->use_sclp;
- mc->no_floppy = qm->no_floppy;
- mc->no_cdrom = qm->no_cdrom;
- mc->no_sdcard = qm->no_sdcard;
- mc->is_default = qm->is_default;
- mc->default_machine_opts = qm->default_machine_opts;
- mc->default_boot_order = qm->default_boot_order;
- mc->default_display = qm->default_display;
- mc->compat_props = qm->compat_props;
- mc->hw_version = qm->hw_version;
-}
-
-void qemu_register_pc_machine(QEMUMachine *m)
-{
- char *name = g_strconcat(m->name, TYPE_MACHINE_SUFFIX, NULL);
- TypeInfo ti = {
- .name = name,
- .parent = TYPE_PC_MACHINE,
- .class_init = pc_generic_machine_class_init,
- .class_data = (void *)m,
- };
-
- type_register(&ti);
- g_free(name);
-}
-
static void pc_dimm_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
- int slot;
HotplugHandlerClass *hhc;
Error *local_err = NULL;
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
- MachineState *machine = MACHINE(hotplug_dev);
PCDIMMDevice *dimm = PC_DIMM(dev);
PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
MemoryRegion *mr = ddc->get_memory_region(dimm);
- uint64_t existing_dimms_capacity = 0;
uint64_t align = TARGET_PAGE_SIZE;
- uint64_t addr;
-
- addr = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP, &local_err);
- if (local_err) {
- goto out;
- }
if (memory_region_get_alignment(mr) && pcms->enforce_aligned_dimm) {
align = memory_region_get_alignment(mr);
}
- addr = pc_dimm_get_free_addr(pcms->hotplug_memory_base,
- memory_region_size(&pcms->hotplug_memory),
- !addr ? NULL : &addr, align,
- memory_region_size(mr), &local_err);
- if (local_err) {
- goto out;
- }
-
- existing_dimms_capacity = pc_existing_dimms_capacity(&local_err);
- if (local_err) {
- goto out;
- }
-
- if (existing_dimms_capacity + memory_region_size(mr) >
- machine->maxram_size - machine->ram_size) {
- error_setg(&local_err, "not enough space, currently 0x%" PRIx64
- " in use of total hot pluggable 0x" RAM_ADDR_FMT,
- existing_dimms_capacity,
- machine->maxram_size - machine->ram_size);
+ if (!pcms->acpi_dev) {
+ error_setg(&local_err,
+ "memory hotplug is not enabled: missing acpi device");
goto out;
}
- object_property_set_int(OBJECT(dev), addr, PC_DIMM_ADDR_PROP, &local_err);
+ pc_dimm_memory_plug(dev, &pcms->hotplug_memory, mr, align, &local_err);
if (local_err) {
goto out;
}
- trace_mhp_pc_dimm_assigned_address(addr);
- slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, &local_err);
- if (local_err) {
- goto out;
- }
+ hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev);
+ hhc->plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &error_abort);
+out:
+ error_propagate(errp, local_err);
+}
- slot = pc_dimm_get_free_slot(slot == PC_DIMM_UNASSIGNED_SLOT ? NULL : &slot,
- machine->ram_slots, &local_err);
- if (local_err) {
- goto out;
- }
- object_property_set_int(OBJECT(dev), slot, PC_DIMM_SLOT_PROP, &local_err);
- if (local_err) {
- goto out;
- }
- trace_mhp_pc_dimm_assigned_slot(slot);
+static void pc_dimm_unplug_request(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ HotplugHandlerClass *hhc;
+ Error *local_err = NULL;
+ PCMachineState *pcms = PC_MACHINE(hotplug_dev);
if (!pcms->acpi_dev) {
error_setg(&local_err,
@@ -1662,18 +1658,34 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev,
goto out;
}
- if (kvm_enabled() && !kvm_has_free_slot(machine)) {
- error_setg(&local_err, "hypervisor has no free memory slots left");
+ hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev);
+ hhc->unplug_request(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err);
+
+out:
+ error_propagate(errp, local_err);
+}
+
+static void pc_dimm_unplug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ PCMachineState *pcms = PC_MACHINE(hotplug_dev);
+ PCDIMMDevice *dimm = PC_DIMM(dev);
+ PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
+ MemoryRegion *mr = ddc->get_memory_region(dimm);
+ HotplugHandlerClass *hhc;
+ Error *local_err = NULL;
+
+ hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev);
+ hhc->unplug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err);
+
+ if (local_err) {
goto out;
}
- memory_region_add_subregion(&pcms->hotplug_memory,
- addr - pcms->hotplug_memory_base, mr);
- vmstate_register_ram(mr, dev);
+ pc_dimm_memory_unplug(dev, &pcms->hotplug_memory, mr);
+ object_unparent(OBJECT(dev));
- hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev);
- hhc->plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err);
-out:
+ out:
error_propagate(errp, local_err);
}
@@ -1719,15 +1731,23 @@ static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev,
static void pc_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
- error_setg(errp, "acpi: device unplug request for not supported device"
- " type: %s", object_get_typename(OBJECT(dev)));
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ pc_dimm_unplug_request(hotplug_dev, dev, errp);
+ } else {
+ error_setg(errp, "acpi: device unplug request for not supported device"
+ " type: %s", object_get_typename(OBJECT(dev)));
+ }
}
static void pc_machine_device_unplug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
- error_setg(errp, "acpi: device unplug for not supported device"
- " type: %s", object_get_typename(OBJECT(dev)));
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ pc_dimm_unplug(hotplug_dev, dev, errp);
+ } else {
+ error_setg(errp, "acpi: device unplug for not supported device"
+ " type: %s", object_get_typename(OBJECT(dev)));
+ }
}
static HotplugHandler *pc_get_hotpug_handler(MachineState *machine,
@@ -1749,7 +1769,7 @@ pc_machine_get_hotplug_memory_region_size(Object *obj, Visitor *v, void *opaque,
const char *name, Error **errp)
{
PCMachineState *pcms = PC_MACHINE(obj);
- int64_t value = memory_region_size(&pcms->hotplug_memory);
+ int64_t value = memory_region_size(&pcms->hotplug_memory.mr);
visit_type_int(v, &value, name, errp);
}
@@ -1811,6 +1831,48 @@ static void pc_machine_set_vmport(Object *obj, Visitor *v, void *opaque,
visit_type_OnOffAuto(v, &pcms->vmport, name, errp);
}
+bool pc_machine_is_smm_enabled(PCMachineState *pcms)
+{
+ bool smm_available = false;
+
+ if (pcms->smm == ON_OFF_AUTO_OFF) {
+ return false;
+ }
+
+ if (tcg_enabled() || qtest_enabled()) {
+ smm_available = true;
+ } else if (kvm_enabled()) {
+ smm_available = kvm_has_smm();
+ }
+
+ if (smm_available) {
+ return true;
+ }
+
+ if (pcms->smm == ON_OFF_AUTO_ON) {
+ error_report("System Management Mode not supported by this hypervisor.");
+ exit(1);
+ }
+ return false;
+}
+
+static void pc_machine_get_smm(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ PCMachineState *pcms = PC_MACHINE(obj);
+ OnOffAuto smm = pcms->smm;
+
+ visit_type_OnOffAuto(v, &smm, name, errp);
+}
+
+static void pc_machine_set_smm(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ PCMachineState *pcms = PC_MACHINE(obj);
+
+ visit_type_OnOffAuto(v, &pcms->smm, name, errp);
+}
+
static bool pc_machine_get_aligned_dimm(Object *obj, Error **errp)
{
PCMachineState *pcms = PC_MACHINE(obj);
@@ -1835,6 +1897,15 @@ static void pc_machine_initfn(Object *obj)
"Maximum ram below the 4G boundary (32bit boundary)",
NULL);
+ pcms->smm = ON_OFF_AUTO_AUTO;
+ object_property_add(obj, PC_MACHINE_SMM, "OnOffAuto",
+ pc_machine_get_smm,
+ pc_machine_set_smm,
+ NULL, NULL, NULL);
+ object_property_set_description(obj, PC_MACHINE_SMM,
+ "Enable SMM (pc & q35)",
+ NULL);
+
pcms->vmport = ON_OFF_AUTO_AUTO;
object_property_add(obj, PC_MACHINE_VMPORT, "OnOffAuto",
pc_machine_get_vmport,
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 1fe7bfb29..a896624f8 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -52,6 +52,7 @@
#ifdef CONFIG_XEN
# include <xen/hvm/hvm_info_table.h>
#endif
+#include "migration/migration.h"
#define MAX_IDE_BUS 2
@@ -59,6 +60,7 @@ static const int ide_iobase[MAX_IDE_BUS] = { 0x1f0, 0x170 };
static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 };
static const int ide_irq[MAX_IDE_BUS] = { 14, 15 };
+static bool pci_enabled = true;
static bool has_acpi_build = true;
static bool rsdp_in_ram = true;
static int legacy_acpi_table_size;
@@ -71,11 +73,10 @@ static bool smbios_uuid_encoded = true;
*/
static bool gigabyte_align = true;
static bool has_reserved_memory = true;
+static bool kvmclock_enabled = true;
/* PC hardware initialisation */
-static void pc_init1(MachineState *machine,
- int pci_enabled,
- int kvmclock_enabled)
+static void pc_init1(MachineState *machine)
{
PCMachineState *pc_machine = PC_MACHINE(machine);
MemoryRegion *system_memory = get_system_memory();
@@ -86,20 +87,17 @@ static void pc_init1(MachineState *machine,
ISABus *isa_bus;
PCII440FXState *i440fx_state;
int piix3_devfn = -1;
- qemu_irq *cpu_irq;
qemu_irq *gsi;
qemu_irq *i8259;
- qemu_irq *smi_irq;
+ qemu_irq smi_irq;
GSIState *gsi_state;
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
BusState *idebus[MAX_IDE_BUS];
ISADevice *rtc_state;
- ISADevice *floppy;
MemoryRegion *ram_memory;
MemoryRegion *pci_memory;
MemoryRegion *rom_memory;
DeviceState *icc_bridge;
- FWCfgState *fw_cfg = NULL;
PcGuestInfo *guest_info;
ram_addr_t lowmem;
@@ -180,16 +178,16 @@ static void pc_init1(MachineState *machine,
/* allocate ram and load rom/bios */
if (!xen_enabled()) {
- fw_cfg = pc_memory_init(machine, system_memory,
- below_4g_mem_size, above_4g_mem_size,
- rom_memory, &ram_memory, guest_info);
+ pc_memory_init(machine, system_memory,
+ below_4g_mem_size, above_4g_mem_size,
+ rom_memory, &ram_memory, guest_info);
} else if (machine->kernel_filename != NULL) {
/* For xen HVM direct kernel boot, load linux here */
- fw_cfg = xen_load_linux(machine->kernel_filename,
- machine->kernel_cmdline,
- machine->initrd_filename,
- below_4g_mem_size,
- guest_info);
+ xen_load_linux(machine->kernel_filename,
+ machine->kernel_cmdline,
+ machine->initrd_filename,
+ below_4g_mem_size,
+ guest_info);
}
gsi_state = g_malloc0(sizeof(*gsi_state));
@@ -220,13 +218,13 @@ static void pc_init1(MachineState *machine,
} else if (xen_enabled()) {
i8259 = xen_interrupt_controller_init();
} else {
- cpu_irq = pc_allocate_cpu_irq();
- i8259 = i8259_init(isa_bus, cpu_irq[0]);
+ i8259 = i8259_init(isa_bus, pc_allocate_cpu_irq());
}
for (i = 0; i < ISA_NUM_IRQS; i++) {
gsi_state->i8259_irq[i] = i8259[i];
}
+ g_free(i8259);
if (pci_enabled) {
ioapic_init_gsi(gsi_state, "i440fx");
}
@@ -242,7 +240,7 @@ static void pc_init1(MachineState *machine,
}
/* init basic PC hardware */
- pc_basic_device_init(isa_bus, gsi, &rtc_state, &floppy,
+ pc_basic_device_init(isa_bus, gsi, &rtc_state, true,
(pc_machine->vmport != ON_OFF_AUTO_ON), 0x4);
pc_nic_init(isa_bus, pci_bus);
@@ -274,7 +272,7 @@ static void pc_init1(MachineState *machine,
}
pc_cmos_init(below_4g_mem_size, above_4g_mem_size, machine->boot_order,
- machine, floppy, idebus[0], idebus[1], rtc_state);
+ machine, idebus[0], idebus[1], rtc_state);
if (pci_enabled && usb_enabled()) {
pci_create_simple(pci_bus, piix3_devfn + 2, "piix3-usb-uhci");
@@ -284,11 +282,12 @@ static void pc_init1(MachineState *machine,
DeviceState *piix4_pm;
I2CBus *smbus;
- smi_irq = qemu_allocate_irqs(pc_acpi_smi_interrupt, first_cpu, 1);
+ smi_irq = qemu_allocate_irq(pc_acpi_smi_interrupt, first_cpu, 0);
/* TODO: Populate SPD eeprom data. */
smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100,
- gsi[9], *smi_irq,
- kvm_enabled(), fw_cfg, &piix4_pm);
+ gsi[9], smi_irq,
+ pc_machine_is_smm_enabled(pc_machine),
+ &piix4_pm);
smbus_eeprom_init(smbus, 8, NULL, 0);
object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP,
@@ -305,13 +304,20 @@ static void pc_init1(MachineState *machine,
}
}
-static void pc_init_pci(MachineState *machine)
+static void pc_compat_2_3(MachineState *machine)
{
- pc_init1(machine, 1, 1);
+ PCMachineState *pcms = PC_MACHINE(machine);
+ savevm_skip_section_footers();
+ if (kvm_enabled()) {
+ pcms->smm = ON_OFF_AUTO_OFF;
+ }
+ global_state_set_optional();
+ savevm_skip_configuration();
}
static void pc_compat_2_2(MachineState *machine)
{
+ pc_compat_2_3(machine);
rsdp_in_ram = false;
x86_cpu_compat_set_features("kvm64", FEAT_1_EDX, 0, CPUID_VME);
x86_cpu_compat_set_features("kvm32", FEAT_1_EDX, 0, CPUID_VME);
@@ -413,202 +419,165 @@ static void pc_compat_1_2(MachineState *machine)
x86_cpu_compat_kvm_no_autoenable(FEAT_KVM, 1 << KVM_FEATURE_PV_EOI);
}
-static void pc_init_pci_2_2(MachineState *machine)
+/* PC compat function for pc-0.10 to pc-0.13 */
+static void pc_compat_0_13(MachineState *machine)
{
- pc_compat_2_2(machine);
- pc_init_pci(machine);
+ pc_compat_1_2(machine);
+ kvmclock_enabled = false;
}
-static void pc_init_pci_2_1(MachineState *machine)
+static void pc_init_isa(MachineState *machine)
{
- pc_compat_2_1(machine);
- pc_init_pci(machine);
+ pci_enabled = false;
+ has_acpi_build = false;
+ smbios_defaults = false;
+ gigabyte_align = false;
+ smbios_legacy_mode = true;
+ has_reserved_memory = false;
+ option_rom_has_mr = true;
+ rom_file_has_mr = false;
+ if (!machine->cpu_model) {
+ machine->cpu_model = "486";
+ }
+ x86_cpu_compat_kvm_no_autoenable(FEAT_KVM, 1 << KVM_FEATURE_PV_EOI);
+ enable_compat_apic_id_mode();
+ pc_init1(machine);
}
-static void pc_init_pci_2_0(MachineState *machine)
+#ifdef CONFIG_XEN
+static void pc_xen_hvm_init(MachineState *machine)
{
- pc_compat_2_0(machine);
- pc_init_pci(machine);
-}
+ PCIBus *bus;
-static void pc_init_pci_1_7(MachineState *machine)
-{
- pc_compat_1_7(machine);
- pc_init_pci(machine);
+ pc_init1(machine);
+
+ bus = pci_find_primary_bus();
+ if (bus != NULL) {
+ pci_create_simple(bus, -1, "xen-platform");
+ }
}
+#endif
-static void pc_init_pci_1_6(MachineState *machine)
+#define DEFINE_I440FX_MACHINE(suffix, name, compatfn, optionfn) \
+ static void pc_init_##suffix(MachineState *machine) \
+ { \
+ void (*compat)(MachineState *m) = (compatfn); \
+ if (compat) { \
+ compat(machine); \
+ } \
+ pc_init1(machine); \
+ } \
+ DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn)
+
+static void pc_i440fx_machine_options(MachineClass *m)
{
- pc_compat_1_6(machine);
- pc_init_pci(machine);
+ pc_default_machine_options(m);
+ m->family = "pc_piix";
+ m->desc = "Standard PC (i440FX + PIIX, 1996)";
+ m->hot_add_cpu = pc_hot_add_cpu;
}
-static void pc_init_pci_1_5(MachineState *machine)
+static void pc_i440fx_2_4_machine_options(MachineClass *m)
{
- pc_compat_1_5(machine);
- pc_init_pci(machine);
+ pc_i440fx_machine_options(m);
+ m->default_machine_opts = "firmware=bios-256k.bin";
+ m->default_display = "std";
+ m->alias = "pc";
+ m->is_default = 1;
}
-static void pc_init_pci_1_4(MachineState *machine)
+DEFINE_I440FX_MACHINE(v2_4, "pc-i440fx-2.4", NULL,
+ pc_i440fx_2_4_machine_options)
+
+
+static void pc_i440fx_2_3_machine_options(MachineClass *m)
{
- pc_compat_1_4(machine);
- pc_init_pci(machine);
+ pc_i440fx_2_4_machine_options(m);
+ m->alias = NULL;
+ m->is_default = 0;
+ SET_MACHINE_COMPAT(m, PC_COMPAT_2_3);
}
-static void pc_init_pci_1_3(MachineState *machine)
+DEFINE_I440FX_MACHINE(v2_3, "pc-i440fx-2.3", pc_compat_2_3,
+ pc_i440fx_2_3_machine_options);
+
+
+static void pc_i440fx_2_2_machine_options(MachineClass *m)
{
- pc_compat_1_3(machine);
- pc_init_pci(machine);
+ pc_i440fx_2_3_machine_options(m);
+ SET_MACHINE_COMPAT(m, PC_COMPAT_2_2);
}
-/* PC machine init function for pc-0.14 to pc-1.2 */
-static void pc_init_pci_1_2(MachineState *machine)
+DEFINE_I440FX_MACHINE(v2_2, "pc-i440fx-2.2", pc_compat_2_2,
+ pc_i440fx_2_2_machine_options);
+
+
+static void pc_i440fx_2_1_machine_options(MachineClass *m)
{
- pc_compat_1_2(machine);
- pc_init_pci(machine);
+ pc_i440fx_2_2_machine_options(m);
+ m->default_display = NULL;
+ SET_MACHINE_COMPAT(m, PC_COMPAT_2_1);
}
-/* PC init function for pc-0.10 to pc-0.13 */
-static void pc_init_pci_no_kvmclock(MachineState *machine)
+DEFINE_I440FX_MACHINE(v2_1, "pc-i440fx-2.1", pc_compat_2_1,
+ pc_i440fx_2_1_machine_options);
+
+
+
+static void pc_i440fx_2_0_machine_options(MachineClass *m)
{
- pc_compat_1_2(machine);
- pc_init1(machine, 1, 0);
+ pc_i440fx_2_1_machine_options(m);
+ SET_MACHINE_COMPAT(m, PC_COMPAT_2_0);
}
-static void pc_init_isa(MachineState *machine)
+DEFINE_I440FX_MACHINE(v2_0, "pc-i440fx-2.0", pc_compat_2_0,
+ pc_i440fx_2_0_machine_options);
+
+
+static void pc_i440fx_1_7_machine_options(MachineClass *m)
{
- has_acpi_build = false;
- smbios_defaults = false;
- gigabyte_align = false;
- smbios_legacy_mode = true;
- has_reserved_memory = false;
- option_rom_has_mr = true;
- rom_file_has_mr = false;
- if (!machine->cpu_model) {
- machine->cpu_model = "486";
- }
- x86_cpu_compat_kvm_no_autoenable(FEAT_KVM, 1 << KVM_FEATURE_PV_EOI);
- enable_compat_apic_id_mode();
- pc_init1(machine, 0, 1);
+ pc_i440fx_2_0_machine_options(m);
+ m->default_machine_opts = NULL;
+ SET_MACHINE_COMPAT(m, PC_COMPAT_1_7);
}
-#ifdef CONFIG_XEN
-static void pc_xen_hvm_init(MachineState *machine)
-{
- PCIBus *bus;
+DEFINE_I440FX_MACHINE(v1_7, "pc-i440fx-1.7", pc_compat_1_7,
+ pc_i440fx_1_7_machine_options);
- pc_init_pci(machine);
- bus = pci_find_primary_bus();
- if (bus != NULL) {
- pci_create_simple(bus, -1, "xen-platform");
- }
+static void pc_i440fx_1_6_machine_options(MachineClass *m)
+{
+ pc_i440fx_1_7_machine_options(m);
+ SET_MACHINE_COMPAT(m, PC_COMPAT_1_6);
}
-#endif
-#define PC_I440FX_MACHINE_OPTIONS \
- PC_DEFAULT_MACHINE_OPTIONS, \
- .family = "pc_piix", \
- .desc = "Standard PC (i440FX + PIIX, 1996)", \
- .hot_add_cpu = pc_hot_add_cpu
-
-#define PC_I440FX_2_3_MACHINE_OPTIONS \
- PC_I440FX_MACHINE_OPTIONS, \
- .default_machine_opts = "firmware=bios-256k.bin", \
- .default_display = "std"
-
-static QEMUMachine pc_i440fx_machine_v2_3 = {
- PC_I440FX_2_3_MACHINE_OPTIONS,
- .name = "pc-i440fx-2.3",
- .alias = "pc",
- .init = pc_init_pci,
- .is_default = 1,
-};
-
-#define PC_I440FX_2_2_MACHINE_OPTIONS PC_I440FX_2_3_MACHINE_OPTIONS
-
-static QEMUMachine pc_i440fx_machine_v2_2 = {
- PC_I440FX_2_2_MACHINE_OPTIONS,
- .name = "pc-i440fx-2.2",
- .init = pc_init_pci_2_2,
-};
-
-#define PC_I440FX_2_1_MACHINE_OPTIONS \
- PC_I440FX_MACHINE_OPTIONS, \
- .default_machine_opts = "firmware=bios-256k.bin"
-
-static QEMUMachine pc_i440fx_machine_v2_1 = {
- PC_I440FX_2_1_MACHINE_OPTIONS,
- .name = "pc-i440fx-2.1",
- .init = pc_init_pci_2_1,
- .compat_props = (GlobalProperty[]) {
- HW_COMPAT_2_1,
- { /* end of list */ }
- },
-};
+DEFINE_I440FX_MACHINE(v1_6, "pc-i440fx-1.6", pc_compat_1_6,
+ pc_i440fx_1_6_machine_options);
-#define PC_I440FX_2_0_MACHINE_OPTIONS PC_I440FX_2_1_MACHINE_OPTIONS
-static QEMUMachine pc_i440fx_machine_v2_0 = {
- PC_I440FX_2_0_MACHINE_OPTIONS,
- .name = "pc-i440fx-2.0",
- .init = pc_init_pci_2_0,
- .compat_props = (GlobalProperty[]) {
- PC_COMPAT_2_0,
- { /* end of list */ }
- },
-};
+static void pc_i440fx_1_5_machine_options(MachineClass *m)
+{
+ pc_i440fx_1_6_machine_options(m);
+ SET_MACHINE_COMPAT(m, PC_COMPAT_1_5);
+}
-#define PC_I440FX_1_7_MACHINE_OPTIONS PC_I440FX_MACHINE_OPTIONS
+DEFINE_I440FX_MACHINE(v1_5, "pc-i440fx-1.5", pc_compat_1_5,
+ pc_i440fx_1_5_machine_options);
-static QEMUMachine pc_i440fx_machine_v1_7 = {
- PC_I440FX_1_7_MACHINE_OPTIONS,
- .name = "pc-i440fx-1.7",
- .init = pc_init_pci_1_7,
- .compat_props = (GlobalProperty[]) {
- PC_COMPAT_1_7,
- { /* end of list */ }
- },
-};
-#define PC_I440FX_1_6_MACHINE_OPTIONS PC_I440FX_MACHINE_OPTIONS
+static void pc_i440fx_1_4_machine_options(MachineClass *m)
+{
+ pc_i440fx_1_5_machine_options(m);
+ m->hot_add_cpu = NULL;
+ SET_MACHINE_COMPAT(m, PC_COMPAT_1_4);
+}
+
+DEFINE_I440FX_MACHINE(v1_4, "pc-i440fx-1.4", pc_compat_1_4,
+ pc_i440fx_1_4_machine_options);
-static QEMUMachine pc_i440fx_machine_v1_6 = {
- PC_I440FX_1_6_MACHINE_OPTIONS,
- .name = "pc-i440fx-1.6",
- .init = pc_init_pci_1_6,
- .compat_props = (GlobalProperty[]) {
- PC_COMPAT_1_6,
- { /* end of list */ }
- },
-};
-
-static QEMUMachine pc_i440fx_machine_v1_5 = {
- PC_I440FX_1_6_MACHINE_OPTIONS,
- .name = "pc-i440fx-1.5",
- .init = pc_init_pci_1_5,
- .compat_props = (GlobalProperty[]) {
- PC_COMPAT_1_5,
- { /* end of list */ }
- },
-};
-
-#define PC_I440FX_1_4_MACHINE_OPTIONS \
- PC_I440FX_1_6_MACHINE_OPTIONS, \
- .hot_add_cpu = NULL
-
-static QEMUMachine pc_i440fx_machine_v1_4 = {
- PC_I440FX_1_4_MACHINE_OPTIONS,
- .name = "pc-i440fx-1.4",
- .init = pc_init_pci_1_4,
- .compat_props = (GlobalProperty[]) {
- PC_COMPAT_1_4,
- { /* end of list */ }
- },
-};
#define PC_COMPAT_1_3 \
- PC_COMPAT_1_4, \
+ PC_COMPAT_1_4 \
{\
.driver = "usb-tablet",\
.property = "usb_version",\
@@ -625,20 +594,21 @@ static QEMUMachine pc_i440fx_machine_v1_4 = {
.driver = "e1000",\
.property = "autonegotiation",\
.value = "off",\
- }
+ },
+
+
+static void pc_i440fx_1_3_machine_options(MachineClass *m)
+{
+ pc_i440fx_1_4_machine_options(m);
+ SET_MACHINE_COMPAT(m, PC_COMPAT_1_3);
+}
+
+DEFINE_I440FX_MACHINE(v1_3, "pc-1.3", pc_compat_1_3,
+ pc_i440fx_1_3_machine_options);
-static QEMUMachine pc_machine_v1_3 = {
- PC_I440FX_1_4_MACHINE_OPTIONS,
- .name = "pc-1.3",
- .init = pc_init_pci_1_3,
- .compat_props = (GlobalProperty[]) {
- PC_COMPAT_1_3,
- { /* end of list */ }
- },
-};
#define PC_COMPAT_1_2 \
- PC_COMPAT_1_3,\
+ PC_COMPAT_1_3 \
{\
.driver = "nec-usb-xhci",\
.property = "msi",\
@@ -663,23 +633,20 @@ static QEMUMachine pc_machine_v1_3 = {
.driver = "VGA",\
.property = "mmio",\
.value = "off",\
- }
+ },
+
+static void pc_i440fx_1_2_machine_options(MachineClass *m)
+{
+ pc_i440fx_1_3_machine_options(m);
+ SET_MACHINE_COMPAT(m, PC_COMPAT_1_2);
+}
-#define PC_I440FX_1_2_MACHINE_OPTIONS \
- PC_I440FX_1_4_MACHINE_OPTIONS, \
- .init = pc_init_pci_1_2
+DEFINE_I440FX_MACHINE(v1_2, "pc-1.2", pc_compat_1_2,
+ pc_i440fx_1_2_machine_options);
-static QEMUMachine pc_machine_v1_2 = {
- PC_I440FX_1_2_MACHINE_OPTIONS,
- .name = "pc-1.2",
- .compat_props = (GlobalProperty[]) {
- PC_COMPAT_1_2,
- { /* end of list */ }
- },
-};
#define PC_COMPAT_1_1 \
- PC_COMPAT_1_2,\
+ PC_COMPAT_1_2 \
{\
.driver = "virtio-scsi-pci",\
.property = "hotplug",\
@@ -708,19 +675,20 @@ static QEMUMachine pc_machine_v1_2 = {
.driver = "virtio-blk-pci",\
.property = "config-wce",\
.value = "off",\
- }
+ },
+
+static void pc_i440fx_1_1_machine_options(MachineClass *m)
+{
+ pc_i440fx_1_2_machine_options(m);
+ SET_MACHINE_COMPAT(m, PC_COMPAT_1_1);
+}
+
+DEFINE_I440FX_MACHINE(v1_1, "pc-1.1", pc_compat_1_2,
+ pc_i440fx_1_1_machine_options);
-static QEMUMachine pc_machine_v1_1 = {
- PC_I440FX_1_2_MACHINE_OPTIONS,
- .name = "pc-1.1",
- .compat_props = (GlobalProperty[]) {
- PC_COMPAT_1_1,
- { /* end of list */ }
- },
-};
#define PC_COMPAT_1_0 \
- PC_COMPAT_1_1,\
+ PC_COMPAT_1_1 \
{\
.driver = TYPE_ISA_FDC,\
.property = "check_media_rate",\
@@ -737,33 +705,35 @@ static QEMUMachine pc_machine_v1_1 = {
.driver = TYPE_USB_DEVICE,\
.property = "full-path",\
.value = "no",\
- }
+ },
+
+static void pc_i440fx_1_0_machine_options(MachineClass *m)
+{
+ pc_i440fx_1_1_machine_options(m);
+ m->hw_version = "1.0";
+ SET_MACHINE_COMPAT(m, PC_COMPAT_1_0);
+}
+
+DEFINE_I440FX_MACHINE(v1_0, "pc-1.0", pc_compat_1_2,
+ pc_i440fx_1_0_machine_options);
-static QEMUMachine pc_machine_v1_0 = {
- PC_I440FX_1_2_MACHINE_OPTIONS,
- .name = "pc-1.0",
- .compat_props = (GlobalProperty[]) {
- PC_COMPAT_1_0,
- { /* end of list */ }
- },
- .hw_version = "1.0",
-};
#define PC_COMPAT_0_15 \
PC_COMPAT_1_0
-static QEMUMachine pc_machine_v0_15 = {
- PC_I440FX_1_2_MACHINE_OPTIONS,
- .name = "pc-0.15",
- .compat_props = (GlobalProperty[]) {
- PC_COMPAT_0_15,
- { /* end of list */ }
- },
- .hw_version = "0.15",
-};
+static void pc_i440fx_0_15_machine_options(MachineClass *m)
+{
+ pc_i440fx_1_0_machine_options(m);
+ m->hw_version = "0.15";
+ SET_MACHINE_COMPAT(m, PC_COMPAT_0_15);
+}
+
+DEFINE_I440FX_MACHINE(v0_15, "pc-0.15", pc_compat_1_2,
+ pc_i440fx_0_15_machine_options);
+
#define PC_COMPAT_0_14 \
- PC_COMPAT_0_15,\
+ PC_COMPAT_0_15 \
{\
.driver = "virtio-blk-pci",\
.property = "event_idx",\
@@ -780,29 +750,29 @@ static QEMUMachine pc_machine_v0_15 = {
.driver = "virtio-balloon-pci",\
.property = "event_idx",\
.value = "off",\
- }
-
-static QEMUMachine pc_machine_v0_14 = {
- PC_I440FX_1_2_MACHINE_OPTIONS,
- .name = "pc-0.14",
- .compat_props = (GlobalProperty[]) {
- PC_COMPAT_0_14,
- {
- .driver = "qxl",
- .property = "revision",
- .value = stringify(2),
- },{
- .driver = "qxl-vga",
- .property = "revision",
- .value = stringify(2),
+ },{\
+ .driver = "qxl",\
+ .property = "revision",\
+ .value = stringify(2),\
+ },{\
+ .driver = "qxl-vga",\
+ .property = "revision",\
+ .value = stringify(2),\
},
- { /* end of list */ }
- },
- .hw_version = "0.14",
-};
+
+static void pc_i440fx_0_14_machine_options(MachineClass *m)
+{
+ pc_i440fx_0_15_machine_options(m);
+ m->hw_version = "0.14";
+ SET_MACHINE_COMPAT(m, PC_COMPAT_0_14);
+}
+
+DEFINE_I440FX_MACHINE(v0_14, "pc-0.14", pc_compat_1_2,
+ pc_i440fx_0_14_machine_options);
+
#define PC_COMPAT_0_13 \
- PC_COMPAT_0_14,\
+ PC_COMPAT_0_14 \
{\
.driver = TYPE_PCI_DEVICE,\
.property = "command_serr_enable",\
@@ -811,37 +781,33 @@ static QEMUMachine pc_machine_v0_14 = {
.driver = "AC97",\
.property = "use_broken_id",\
.value = stringify(1),\
- }
-
-#define PC_I440FX_0_13_MACHINE_OPTIONS \
- PC_I440FX_1_2_MACHINE_OPTIONS, \
- .init = pc_init_pci_no_kvmclock
-
-static QEMUMachine pc_machine_v0_13 = {
- PC_I440FX_0_13_MACHINE_OPTIONS,
- .name = "pc-0.13",
- .compat_props = (GlobalProperty[]) {
- PC_COMPAT_0_13,
- {
- .driver = "virtio-9p-pci",
- .property = "vectors",
- .value = stringify(0),
- },{
- .driver = "VGA",
- .property = "rombar",
- .value = stringify(0),
- },{
- .driver = "vmware-svga",
- .property = "rombar",
- .value = stringify(0),
+ },{\
+ .driver = "virtio-9p-pci",\
+ .property = "vectors",\
+ .value = stringify(0),\
+ },{\
+ .driver = "VGA",\
+ .property = "rombar",\
+ .value = stringify(0),\
+ },{\
+ .driver = "vmware-svga",\
+ .property = "rombar",\
+ .value = stringify(0),\
},
- { /* end of list */ }
- },
- .hw_version = "0.13",
-};
+
+static void pc_i440fx_0_13_machine_options(MachineClass *m)
+{
+ pc_i440fx_0_14_machine_options(m);
+ m->hw_version = "0.13";
+ SET_MACHINE_COMPAT(m, PC_COMPAT_0_13);
+}
+
+DEFINE_I440FX_MACHINE(v0_13, "pc-0.13", pc_compat_0_13,
+ pc_i440fx_0_13_machine_options);
+
#define PC_COMPAT_0_12 \
- PC_COMPAT_0_13,\
+ PC_COMPAT_0_13 \
{\
.driver = "virtio-serial-pci",\
.property = "max_ports",\
@@ -862,29 +828,21 @@ static QEMUMachine pc_machine_v0_13 = {
.driver = "usb-kbd",\
.property = "serial",\
.value = "1",\
- }
-
-static QEMUMachine pc_machine_v0_12 = {
- PC_I440FX_0_13_MACHINE_OPTIONS,
- .name = "pc-0.12",
- .compat_props = (GlobalProperty[]) {
- PC_COMPAT_0_12,
- {
- .driver = "VGA",
- .property = "rombar",
- .value = stringify(0),
- },{
- .driver = "vmware-svga",
- .property = "rombar",
- .value = stringify(0),
},
- { /* end of list */ }
- },
- .hw_version = "0.12",
-};
+
+static void pc_i440fx_0_12_machine_options(MachineClass *m)
+{
+ pc_i440fx_0_13_machine_options(m);
+ m->hw_version = "0.12";
+ SET_MACHINE_COMPAT(m, PC_COMPAT_0_12);
+}
+
+DEFINE_I440FX_MACHINE(v0_12, "pc-0.12", pc_compat_0_13,
+ pc_i440fx_0_12_machine_options);
+
#define PC_COMPAT_0_11 \
- PC_COMPAT_0_12,\
+ PC_COMPAT_0_12 \
{\
.driver = "virtio-blk-pci",\
.property = "vectors",\
@@ -893,105 +851,83 @@ static QEMUMachine pc_machine_v0_12 = {
.driver = TYPE_PCI_DEVICE,\
.property = "rombar",\
.value = stringify(0),\
- }
-
-static QEMUMachine pc_machine_v0_11 = {
- PC_I440FX_0_13_MACHINE_OPTIONS,
- .name = "pc-0.11",
- .compat_props = (GlobalProperty[]) {
- PC_COMPAT_0_11,
- {
- .driver = "ide-drive",
- .property = "ver",
- .value = "0.11",
- },{
- .driver = "scsi-disk",
- .property = "ver",
- .value = "0.11",
- },
- { /* end of list */ }
- },
- .hw_version = "0.11",
-};
-
-static QEMUMachine pc_machine_v0_10 = {
- PC_I440FX_0_13_MACHINE_OPTIONS,
- .name = "pc-0.10",
- .compat_props = (GlobalProperty[]) {
- PC_COMPAT_0_11,
- {
- .driver = "virtio-blk-pci",
- .property = "class",
- .value = stringify(PCI_CLASS_STORAGE_OTHER),
- },{
- .driver = "virtio-serial-pci",
- .property = "class",
- .value = stringify(PCI_CLASS_DISPLAY_OTHER),
- },{
- .driver = "virtio-net-pci",
- .property = "vectors",
- .value = stringify(0),
- },{
- .driver = "ide-drive",
- .property = "ver",
- .value = "0.10",
- },{
- .driver = "scsi-disk",
- .property = "ver",
- .value = "0.10",
+ },{\
+ .driver = "ide-drive",\
+ .property = "ver",\
+ .value = "0.11",\
+ },{\
+ .driver = "scsi-disk",\
+ .property = "ver",\
+ .value = "0.11",\
},
- { /* end of list */ }
- },
- .hw_version = "0.10",
-};
-
-static QEMUMachine isapc_machine = {
- PC_COMMON_MACHINE_OPTIONS,
- .name = "isapc",
- .desc = "ISA-only PC",
- .init = pc_init_isa,
- .max_cpus = 1,
- .compat_props = (GlobalProperty[]) {
- { /* end of list */ }
+
+static void pc_i440fx_0_11_machine_options(MachineClass *m)
+{
+ pc_i440fx_0_12_machine_options(m);
+ m->hw_version = "0.11";
+ SET_MACHINE_COMPAT(m, PC_COMPAT_0_11);
+}
+
+DEFINE_I440FX_MACHINE(v0_11, "pc-0.11", pc_compat_0_13,
+ pc_i440fx_0_11_machine_options);
+
+
+#define PC_COMPAT_0_10 \
+ PC_COMPAT_0_11 \
+ {\
+ .driver = "virtio-blk-pci",\
+ .property = "class",\
+ .value = stringify(PCI_CLASS_STORAGE_OTHER),\
+ },{\
+ .driver = "virtio-serial-pci",\
+ .property = "class",\
+ .value = stringify(PCI_CLASS_DISPLAY_OTHER),\
+ },{\
+ .driver = "virtio-net-pci",\
+ .property = "vectors",\
+ .value = stringify(0),\
+ },{\
+ .driver = "ide-drive",\
+ .property = "ver",\
+ .value = "0.10",\
+ },{\
+ .driver = "scsi-disk",\
+ .property = "ver",\
+ .value = "0.10",\
},
-};
-#ifdef CONFIG_XEN
-static QEMUMachine xenfv_machine = {
- PC_COMMON_MACHINE_OPTIONS,
- .name = "xenfv",
- .desc = "Xen Fully-virtualized PC",
- .init = pc_xen_hvm_init,
- .max_cpus = HVM_MAX_VCPUS,
- .default_machine_opts = "accel=xen",
- .hot_add_cpu = pc_hot_add_cpu,
-};
-#endif
+static void pc_i440fx_0_10_machine_options(MachineClass *m)
+{
+ pc_i440fx_0_11_machine_options(m);
+ m->hw_version = "0.10";
+ SET_MACHINE_COMPAT(m, PC_COMPAT_0_10);
+}
+
+DEFINE_I440FX_MACHINE(v0_10, "pc-0.10", pc_compat_0_13,
+ pc_i440fx_0_10_machine_options);
+
+
+static void isapc_machine_options(MachineClass *m)
+{
+ pc_common_machine_options(m);
+ m->desc = "ISA-only PC";
+ m->max_cpus = 1;
+}
+
+DEFINE_PC_MACHINE(isapc, "isapc", pc_init_isa,
+ isapc_machine_options);
+
-static void pc_machine_init(void)
-{
- qemu_register_pc_machine(&pc_i440fx_machine_v2_3);
- qemu_register_pc_machine(&pc_i440fx_machine_v2_2);
- qemu_register_pc_machine(&pc_i440fx_machine_v2_1);
- qemu_register_pc_machine(&pc_i440fx_machine_v2_0);
- qemu_register_pc_machine(&pc_i440fx_machine_v1_7);
- qemu_register_pc_machine(&pc_i440fx_machine_v1_6);
- qemu_register_pc_machine(&pc_i440fx_machine_v1_5);
- qemu_register_pc_machine(&pc_i440fx_machine_v1_4);
- qemu_register_pc_machine(&pc_machine_v1_3);
- qemu_register_pc_machine(&pc_machine_v1_2);
- qemu_register_pc_machine(&pc_machine_v1_1);
- qemu_register_pc_machine(&pc_machine_v1_0);
- qemu_register_pc_machine(&pc_machine_v0_15);
- qemu_register_pc_machine(&pc_machine_v0_14);
- qemu_register_pc_machine(&pc_machine_v0_13);
- qemu_register_pc_machine(&pc_machine_v0_12);
- qemu_register_pc_machine(&pc_machine_v0_11);
- qemu_register_pc_machine(&pc_machine_v0_10);
- qemu_register_pc_machine(&isapc_machine);
#ifdef CONFIG_XEN
- qemu_register_pc_machine(&xenfv_machine);
-#endif
+static void xenfv_machine_options(MachineClass *m)
+{
+ pc_common_machine_options(m);
+ m->desc = "Xen Fully-virtualized PC";
+ m->max_cpus = HVM_MAX_VCPUS;
+ m->default_machine_opts = "accel=xen";
+ m->hot_add_cpu = pc_hot_add_cpu;
}
-machine_init(pc_machine_init);
+DEFINE_PC_MACHINE(xenfv, "xenfv", pc_xen_hvm_init,
+ xenfv_machine_options);
+#endif
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index dcc17c074..974aead5a 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -45,6 +45,7 @@
#include "hw/usb.h"
#include "hw/cpu/icc_bus.h"
#include "qemu/error-report.h"
+#include "migration/migration.h"
/* ICH9 AHCI has 6 ports */
#define MAX_SATA_PORTS 6
@@ -72,14 +73,12 @@ static void pc_q35_init(MachineState *machine)
PCIDevice *lpc;
BusState *idebus[MAX_SATA_PORTS];
ISADevice *rtc_state;
- ISADevice *floppy;
MemoryRegion *pci_memory;
MemoryRegion *rom_memory;
MemoryRegion *ram_memory;
GSIState *gsi_state;
ISABus *isa_bus;
int pci_enabled = 1;
- qemu_irq *cpu_irq;
qemu_irq *gsi;
qemu_irq *i8259;
int i;
@@ -89,6 +88,7 @@ static void pc_q35_init(MachineState *machine)
PcGuestInfo *guest_info;
ram_addr_t lowmem;
DriveInfo *hd[MAX_SATA_PORTS];
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
/* Check whether RAM fits below 4G (leaving 1/2 GByte for IO memory
* and 256 Mbytes for PCI Express Enhanced Configuration Access Mapping
@@ -163,7 +163,6 @@ static void pc_q35_init(MachineState *machine)
guest_info->legacy_acpi_table_size = 0;
if (smbios_defaults) {
- MachineClass *mc = MACHINE_GET_CLASS(machine);
/* These values are guest ABI, do not change */
smbios_set_defaults("QEMU", "Standard PC (Q35 + ICH9, 2009)",
mc->name, smbios_legacy_mode, smbios_uuid_encoded);
@@ -230,8 +229,7 @@ static void pc_q35_init(MachineState *machine)
} else if (xen_enabled()) {
i8259 = xen_interrupt_controller_init();
} else {
- cpu_irq = pc_allocate_cpu_irq();
- i8259 = i8259_init(isa_bus, cpu_irq[0]);
+ i8259 = i8259_init(isa_bus, pc_allocate_cpu_irq());
}
for (i = 0; i < ISA_NUM_IRQS; i++) {
@@ -250,11 +248,11 @@ static void pc_q35_init(MachineState *machine)
}
/* init basic PC hardware */
- pc_basic_device_init(isa_bus, gsi, &rtc_state, &floppy,
+ pc_basic_device_init(isa_bus, gsi, &rtc_state, !mc->no_floppy,
(pc_machine->vmport != ON_OFF_AUTO_ON), 0xff0104);
/* connect pm stuff to lpc */
- ich9_lpc_pm_init(lpc);
+ ich9_lpc_pm_init(lpc, pc_machine_is_smm_enabled(pc_machine), !mc->no_tco);
/* ahci and SATA device, for q35 1 ahci controller is built-in */
ahci = pci_create_simple_multifunction(host_bus,
@@ -279,7 +277,7 @@ static void pc_q35_init(MachineState *machine)
8, NULL, 0);
pc_cmos_init(below_4g_mem_size, above_4g_mem_size, machine->boot_order,
- machine, floppy, idebus[0], idebus[1], rtc_state);
+ machine, idebus[0], idebus[1], rtc_state);
/* the rest devices to which pci devfn is automatically assigned */
pc_vga_init(isa_bus, host_bus);
@@ -289,8 +287,20 @@ static void pc_q35_init(MachineState *machine)
}
}
+static void pc_compat_2_3(MachineState *machine)
+{
+ PCMachineState *pcms = PC_MACHINE(machine);
+ savevm_skip_section_footers();
+ if (kvm_enabled()) {
+ pcms->smm = ON_OFF_AUTO_OFF;
+ }
+ global_state_set_optional();
+ savevm_skip_configuration();
+}
+
static void pc_compat_2_2(MachineState *machine)
{
+ pc_compat_2_3(machine);
rsdp_in_ram = false;
x86_cpu_compat_set_features("kvm64", FEAT_1_EDX, 0, CPUID_VME);
x86_cpu_compat_set_features("kvm32", FEAT_1_EDX, 0, CPUID_VME);
@@ -361,159 +371,122 @@ static void pc_compat_1_4(MachineState *machine)
x86_cpu_compat_set_features("Westmere", FEAT_1_ECX, 0, CPUID_EXT_PCLMULQDQ);
}
-static void pc_q35_init_2_2(MachineState *machine)
+#define DEFINE_Q35_MACHINE(suffix, name, compatfn, optionfn) \
+ static void pc_init_##suffix(MachineState *machine) \
+ { \
+ void (*compat)(MachineState *m) = (compatfn); \
+ if (compat) { \
+ compat(machine); \
+ } \
+ pc_q35_init(machine); \
+ } \
+ DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn)
+
+
+static void pc_q35_machine_options(MachineClass *m)
{
- pc_compat_2_2(machine);
- pc_q35_init(machine);
+ pc_default_machine_options(m);
+ m->family = "pc_q35";
+ m->desc = "Standard PC (Q35 + ICH9, 2009)";
+ m->hot_add_cpu = pc_hot_add_cpu;
+ m->units_per_default_bus = 1;
}
-static void pc_q35_init_2_1(MachineState *machine)
+static void pc_q35_2_4_machine_options(MachineClass *m)
{
- pc_compat_2_1(machine);
- pc_q35_init(machine);
+ pc_q35_machine_options(m);
+ m->default_machine_opts = "firmware=bios-256k.bin";
+ m->default_display = "std";
+ m->no_floppy = 1;
+ m->no_tco = 0;
+ m->alias = "q35";
}
-static void pc_q35_init_2_0(MachineState *machine)
+DEFINE_Q35_MACHINE(v2_4, "pc-q35-2.4", NULL,
+ pc_q35_2_4_machine_options);
+
+
+static void pc_q35_2_3_machine_options(MachineClass *m)
{
- pc_compat_2_0(machine);
- pc_q35_init(machine);
+ pc_q35_2_4_machine_options(m);
+ m->no_floppy = 0;
+ m->no_tco = 1;
+ m->alias = NULL;
+ SET_MACHINE_COMPAT(m, PC_COMPAT_2_3);
}
-static void pc_q35_init_1_7(MachineState *machine)
+DEFINE_Q35_MACHINE(v2_3, "pc-q35-2.3", pc_compat_2_3,
+ pc_q35_2_3_machine_options);
+
+
+static void pc_q35_2_2_machine_options(MachineClass *m)
{
- pc_compat_1_7(machine);
- pc_q35_init(machine);
+ pc_q35_2_3_machine_options(m);
+ SET_MACHINE_COMPAT(m, PC_COMPAT_2_2);
}
-static void pc_q35_init_1_6(MachineState *machine)
+DEFINE_Q35_MACHINE(v2_2, "pc-q35-2.2", pc_compat_2_2,
+ pc_q35_2_2_machine_options);
+
+
+static void pc_q35_2_1_machine_options(MachineClass *m)
{
- pc_compat_1_6(machine);
- pc_q35_init(machine);
+ pc_q35_2_2_machine_options(m);
+ m->default_display = NULL;
+ SET_MACHINE_COMPAT(m, PC_COMPAT_2_1);
}
-static void pc_q35_init_1_5(MachineState *machine)
+DEFINE_Q35_MACHINE(v2_1, "pc-q35-2.1", pc_compat_2_1,
+ pc_q35_2_1_machine_options);
+
+
+static void pc_q35_2_0_machine_options(MachineClass *m)
{
- pc_compat_1_5(machine);
- pc_q35_init(machine);
+ pc_q35_2_1_machine_options(m);
+ SET_MACHINE_COMPAT(m, PC_COMPAT_2_0);
}
-static void pc_q35_init_1_4(MachineState *machine)
+DEFINE_Q35_MACHINE(v2_0, "pc-q35-2.0", pc_compat_2_0,
+ pc_q35_2_0_machine_options);
+
+
+static void pc_q35_1_7_machine_options(MachineClass *m)
{
- pc_compat_1_4(machine);
- pc_q35_init(machine);
+ pc_q35_2_0_machine_options(m);
+ m->default_machine_opts = NULL;
+ SET_MACHINE_COMPAT(m, PC_COMPAT_1_7);
}
-#define PC_Q35_MACHINE_OPTIONS \
- PC_DEFAULT_MACHINE_OPTIONS, \
- .family = "pc_q35", \
- .desc = "Standard PC (Q35 + ICH9, 2009)", \
- .hot_add_cpu = pc_hot_add_cpu, \
- .units_per_default_bus = 1
-
-#define PC_Q35_2_3_MACHINE_OPTIONS \
- PC_Q35_MACHINE_OPTIONS, \
- .default_machine_opts = "firmware=bios-256k.bin", \
- .default_display = "std"
-
-static QEMUMachine pc_q35_machine_v2_3 = {
- PC_Q35_2_3_MACHINE_OPTIONS,
- .name = "pc-q35-2.3",
- .alias = "q35",
- .init = pc_q35_init,
-};
-
-#define PC_Q35_2_2_MACHINE_OPTIONS PC_Q35_2_3_MACHINE_OPTIONS
-
-static QEMUMachine pc_q35_machine_v2_2 = {
- PC_Q35_2_2_MACHINE_OPTIONS,
- .name = "pc-q35-2.2",
- .init = pc_q35_init_2_2,
-};
-
-#define PC_Q35_2_1_MACHINE_OPTIONS \
- PC_Q35_MACHINE_OPTIONS, \
- .default_machine_opts = "firmware=bios-256k.bin"
-
-static QEMUMachine pc_q35_machine_v2_1 = {
- PC_Q35_2_1_MACHINE_OPTIONS,
- .name = "pc-q35-2.1",
- .init = pc_q35_init_2_1,
- .compat_props = (GlobalProperty[]) {
- HW_COMPAT_2_1,
- { /* end of list */ }
- },
-};
-
-#define PC_Q35_2_0_MACHINE_OPTIONS PC_Q35_2_1_MACHINE_OPTIONS
-
-static QEMUMachine pc_q35_machine_v2_0 = {
- PC_Q35_2_0_MACHINE_OPTIONS,
- .name = "pc-q35-2.0",
- .init = pc_q35_init_2_0,
- .compat_props = (GlobalProperty[]) {
- PC_COMPAT_2_0,
- { /* end of list */ }
- },
-};
-
-#define PC_Q35_1_7_MACHINE_OPTIONS PC_Q35_MACHINE_OPTIONS
-
-static QEMUMachine pc_q35_machine_v1_7 = {
- PC_Q35_1_7_MACHINE_OPTIONS,
- .name = "pc-q35-1.7",
- .init = pc_q35_init_1_7,
- .compat_props = (GlobalProperty[]) {
- PC_COMPAT_1_7,
- { /* end of list */ }
- },
-};
-
-#define PC_Q35_1_6_MACHINE_OPTIONS PC_Q35_MACHINE_OPTIONS
-
-static QEMUMachine pc_q35_machine_v1_6 = {
- PC_Q35_1_6_MACHINE_OPTIONS,
- .name = "pc-q35-1.6",
- .init = pc_q35_init_1_6,
- .compat_props = (GlobalProperty[]) {
- PC_COMPAT_1_6,
- { /* end of list */ }
- },
-};
-
-static QEMUMachine pc_q35_machine_v1_5 = {
- PC_Q35_1_6_MACHINE_OPTIONS,
- .name = "pc-q35-1.5",
- .init = pc_q35_init_1_5,
- .compat_props = (GlobalProperty[]) {
- PC_COMPAT_1_5,
- { /* end of list */ }
- },
-};
-
-#define PC_Q35_1_4_MACHINE_OPTIONS \
- PC_Q35_1_6_MACHINE_OPTIONS, \
- .hot_add_cpu = NULL
-
-static QEMUMachine pc_q35_machine_v1_4 = {
- PC_Q35_1_4_MACHINE_OPTIONS,
- .name = "pc-q35-1.4",
- .init = pc_q35_init_1_4,
- .compat_props = (GlobalProperty[]) {
- PC_COMPAT_1_4,
- { /* end of list */ }
- },
-};
-
-static void pc_q35_machine_init(void)
+DEFINE_Q35_MACHINE(v1_7, "pc-q35-1.7", pc_compat_1_7,
+ pc_q35_1_7_machine_options);
+
+
+static void pc_q35_1_6_machine_options(MachineClass *m)
+{
+ pc_q35_machine_options(m);
+ SET_MACHINE_COMPAT(m, PC_COMPAT_1_6);
+}
+
+DEFINE_Q35_MACHINE(v1_6, "pc-q35-1.6", pc_compat_1_6,
+ pc_q35_1_6_machine_options);
+
+
+static void pc_q35_1_5_machine_options(MachineClass *m)
+{
+ pc_q35_1_6_machine_options(m);
+ SET_MACHINE_COMPAT(m, PC_COMPAT_1_5);
+}
+
+DEFINE_Q35_MACHINE(v1_5, "pc-q35-1.5", pc_compat_1_5,
+ pc_q35_1_5_machine_options);
+
+
+static void pc_q35_1_4_machine_options(MachineClass *m)
{
- qemu_register_pc_machine(&pc_q35_machine_v2_3);
- qemu_register_pc_machine(&pc_q35_machine_v2_2);
- qemu_register_pc_machine(&pc_q35_machine_v2_1);
- qemu_register_pc_machine(&pc_q35_machine_v2_0);
- qemu_register_pc_machine(&pc_q35_machine_v1_7);
- qemu_register_pc_machine(&pc_q35_machine_v1_6);
- qemu_register_pc_machine(&pc_q35_machine_v1_5);
- qemu_register_pc_machine(&pc_q35_machine_v1_4);
+ pc_q35_1_5_machine_options(m);
+ m->hot_add_cpu = NULL;
+ SET_MACHINE_COMPAT(m, PC_COMPAT_1_4);
}
-machine_init(pc_q35_machine_init);
+DEFINE_Q35_MACHINE(v1_4, "pc-q35-1.4", pc_compat_1_4,
+ pc_q35_1_4_machine_options);
diff --git a/hw/i386/ssdt-tpm.dsl b/hw/i386/ssdt-tpm.dsl
deleted file mode 100644
index 75d96910b..000000000
--- a/hw/i386/ssdt-tpm.dsl
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-#include "hw/acpi/tpm.h"
-
-ACPI_EXTRACT_ALL_CODE ssdt_tpm_aml
-
-DefinitionBlock (
- "ssdt-tpm.aml", // Output Filename
- "SSDT", // Signature
- 0x01, // SSDT Compliance Revision
- "BXPC", // OEMID
- "BXSSDT", // TABLE ID
- 0x1 // OEM Revision
- )
-{
- Scope(\_SB) {
- /* TPM with emulated TPM TIS interface */
- Device (TPM) {
- Name (_HID, EisaID ("PNP0C31"))
- Name (_CRS, ResourceTemplate ()
- {
- Memory32Fixed (ReadWrite, TPM_TIS_ADDR_BASE, TPM_TIS_ADDR_SIZE)
- // older Linux tpm_tis drivers do not work with IRQ
- //IRQNoFlags () {TPM_TIS_IRQ}
- })
- Method (_STA, 0, NotSerialized) {
- Return (0x0F)
- }
- }
- }
-}
diff --git a/hw/i386/ssdt-tpm.hex.generated b/hw/i386/ssdt-tpm.hex.generated
deleted file mode 100644
index e84dc6cfc..000000000
--- a/hw/i386/ssdt-tpm.hex.generated
+++ /dev/null
@@ -1,95 +0,0 @@
-static unsigned char ssdt_tpm_aml[] = {
-0x53,
-0x53,
-0x44,
-0x54,
-0x5d,
-0x0,
-0x0,
-0x0,
-0x1,
-0x1c,
-0x42,
-0x58,
-0x50,
-0x43,
-0x0,
-0x0,
-0x42,
-0x58,
-0x53,
-0x53,
-0x44,
-0x54,
-0x0,
-0x0,
-0x1,
-0x0,
-0x0,
-0x0,
-0x49,
-0x4e,
-0x54,
-0x4c,
-0x7,
-0x11,
-0x14,
-0x20,
-0x10,
-0x38,
-0x5c,
-0x5f,
-0x53,
-0x42,
-0x5f,
-0x5b,
-0x82,
-0x30,
-0x54,
-0x50,
-0x4d,
-0x5f,
-0x8,
-0x5f,
-0x48,
-0x49,
-0x44,
-0xc,
-0x41,
-0xd0,
-0xc,
-0x31,
-0x8,
-0x5f,
-0x43,
-0x52,
-0x53,
-0x11,
-0x11,
-0xa,
-0xe,
-0x86,
-0x9,
-0x0,
-0x1,
-0x0,
-0x0,
-0xd4,
-0xfe,
-0x0,
-0x50,
-0x0,
-0x0,
-0x79,
-0x0,
-0x14,
-0x9,
-0x5f,
-0x53,
-0x54,
-0x41,
-0x0,
-0xa4,
-0xa,
-0xf
-};
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index 833fd45fa..378ad60c3 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -27,7 +27,7 @@
#include <hw/pci/pci.h>
#include <hw/sysbus.h>
-#include "monitor/monitor.h"
+#include "qemu/error-report.h"
#include "sysemu/block-backend.h"
#include "sysemu/dma.h"
#include "internal.h"
@@ -45,12 +45,11 @@ do { \
} while (0)
static void check_cmd(AHCIState *s, int port);
-static int handle_cmd(AHCIState *s,int port,int slot);
+static int handle_cmd(AHCIState *s, int port, uint8_t slot);
static void ahci_reset_port(AHCIState *s, int port);
static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis);
static void ahci_init_d2h(AHCIDevice *ad);
-static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write);
-static void ahci_commit_buf(IDEDMA *dma, uint32_t tx_bytes);
+static int ahci_dma_prepare_buf(IDEDMA *dma, int32_t limit);
static bool ahci_map_clb_address(AHCIDevice *ad);
static bool ahci_map_fis_address(AHCIDevice *ad);
static void ahci_unmap_clb_address(AHCIDevice *ad);
@@ -106,8 +105,6 @@ static uint32_t ahci_port_read(AHCIState *s, int port, int offset)
val = pr->scr_err;
break;
case PORT_SCR_ACT:
- pr->scr_act &= ~s->dev[port].finished;
- s->dev[port].finished = 0;
val = pr->scr_act;
break;
case PORT_CMD_ISSUE:
@@ -198,6 +195,61 @@ static void map_page(AddressSpace *as, uint8_t **ptr, uint64_t addr,
}
}
+/**
+ * Check the cmd register to see if we should start or stop
+ * the DMA or FIS RX engines.
+ *
+ * @ad: Device to engage.
+ * @allow_stop: Allow device to transition from started to stopped?
+ * 'no' is useful for migration post_load, which does not expect a transition.
+ *
+ * @return 0 on success, -1 on error.
+ */
+static int ahci_cond_start_engines(AHCIDevice *ad, bool allow_stop)
+{
+ AHCIPortRegs *pr = &ad->port_regs;
+
+ if (pr->cmd & PORT_CMD_START) {
+ if (ahci_map_clb_address(ad)) {
+ pr->cmd |= PORT_CMD_LIST_ON;
+ } else {
+ error_report("AHCI: Failed to start DMA engine: "
+ "bad command list buffer address");
+ return -1;
+ }
+ } else if (pr->cmd & PORT_CMD_LIST_ON) {
+ if (allow_stop) {
+ ahci_unmap_clb_address(ad);
+ pr->cmd = pr->cmd & ~(PORT_CMD_LIST_ON);
+ } else {
+ error_report("AHCI: DMA engine should be off, "
+ "but appears to still be running");
+ return -1;
+ }
+ }
+
+ if (pr->cmd & PORT_CMD_FIS_RX) {
+ if (ahci_map_fis_address(ad)) {
+ pr->cmd |= PORT_CMD_FIS_ON;
+ } else {
+ error_report("AHCI: Failed to start FIS receive engine: "
+ "bad FIS receive buffer address");
+ return -1;
+ }
+ } else if (pr->cmd & PORT_CMD_FIS_ON) {
+ if (allow_stop) {
+ ahci_unmap_fis_address(ad);
+ pr->cmd = pr->cmd & ~(PORT_CMD_FIS_ON);
+ } else {
+ error_report("AHCI: FIS receive engine should be off, "
+ "but appears to still be running");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val)
{
AHCIPortRegs *pr = &s->dev[port].port_regs;
@@ -226,32 +278,16 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val)
break;
case PORT_CMD:
/* Block any Read-only fields from being set;
- * including LIST_ON and FIS_ON. */
- pr->cmd = (pr->cmd & PORT_CMD_RO_MASK) | (val & ~PORT_CMD_RO_MASK);
-
- if (pr->cmd & PORT_CMD_START) {
- if (ahci_map_clb_address(&s->dev[port])) {
- pr->cmd |= PORT_CMD_LIST_ON;
- } else {
- error_report("AHCI: Failed to start DMA engine: "
- "bad command list buffer address");
- }
- } else if (pr->cmd & PORT_CMD_LIST_ON) {
- ahci_unmap_clb_address(&s->dev[port]);
- pr->cmd = pr->cmd & ~(PORT_CMD_LIST_ON);
- }
+ * including LIST_ON and FIS_ON.
+ * The spec requires to set ICC bits to zero after the ICC change
+ * is done. We don't support ICC state changes, therefore always
+ * force the ICC bits to zero.
+ */
+ pr->cmd = (pr->cmd & PORT_CMD_RO_MASK) |
+ (val & ~(PORT_CMD_RO_MASK|PORT_CMD_ICC_MASK));
- if (pr->cmd & PORT_CMD_FIS_RX) {
- if (ahci_map_fis_address(&s->dev[port])) {
- pr->cmd |= PORT_CMD_FIS_ON;
- } else {
- error_report("AHCI: Failed to start FIS receive engine: "
- "bad FIS receive buffer address");
- }
- } else if (pr->cmd & PORT_CMD_FIS_ON) {
- ahci_unmap_fis_address(&s->dev[port]);
- pr->cmd = pr->cmd & ~(PORT_CMD_FIS_ON);
- }
+ /* Check FIS RX and CLB engines, allow transition to false: */
+ ahci_cond_start_engines(&s->dev[port], true);
/* XXX usually the FIS would be pending on the bus here and
issuing deferred until the OS enables FIS receival.
@@ -297,8 +333,7 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val)
}
}
-static uint64_t ahci_mem_read(void *opaque, hwaddr addr,
- unsigned size)
+static uint64_t ahci_mem_read_32(void *opaque, hwaddr addr)
{
AHCIState *s = opaque;
uint32_t val = 0;
@@ -334,6 +369,30 @@ static uint64_t ahci_mem_read(void *opaque, hwaddr addr,
}
+/**
+ * AHCI 1.3 section 3 ("HBA Memory Registers")
+ * Support unaligned 8/16/32 bit reads, and 64 bit aligned reads.
+ * Caller is responsible for masking unwanted higher order bytes.
+ */
+static uint64_t ahci_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+ hwaddr aligned = addr & ~0x3;
+ int ofst = addr - aligned;
+ uint64_t lo = ahci_mem_read_32(opaque, aligned);
+ uint64_t hi;
+
+ /* if < 8 byte read does not cross 4 byte boundary */
+ if (ofst + size <= 4) {
+ return lo >> (ofst * 8);
+ }
+ g_assert_cmpint(size, >, 1);
+
+ /* If the 64bit read is unaligned, we will produce undefined
+ * results. AHCI does not support unaligned 64bit reads. */
+ hi = ahci_mem_read_32(opaque, aligned + 4);
+ return (hi << 32 | lo) >> (ofst * 8);
+}
+
static void ahci_mem_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
@@ -449,7 +508,7 @@ static void ahci_reg_init(AHCIState *s)
static void check_cmd(AHCIState *s, int port)
{
AHCIPortRegs *pr = &s->dev[port].port_regs;
- int slot;
+ uint8_t slot;
if ((pr->cmd & PORT_CMD_START) && pr->cmd_issue) {
for (slot = 0; (slot < 32) && pr->cmd_issue; slot++) {
@@ -524,6 +583,7 @@ static void ahci_reset_port(AHCIState *s, int port)
/* reset ncq queue */
for (i = 0; i < AHCI_MAX_CMDS; i++) {
NCQTransferState *ncq_tfs = &s->dev[port].ncq_tfs[i];
+ ncq_tfs->halt = false;
if (!ncq_tfs->used) {
continue;
}
@@ -608,14 +668,14 @@ static void ahci_unmap_clb_address(AHCIDevice *ad)
ad->lst = NULL;
}
-static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished)
+static void ahci_write_fis_sdb(AHCIState *s, NCQTransferState *ncq_tfs)
{
- AHCIDevice *ad = &s->dev[port];
+ AHCIDevice *ad = ncq_tfs->drive;
AHCIPortRegs *pr = &ad->port_regs;
IDEState *ide_state;
SDBFIS *sdb_fis;
- if (!s->dev[port].res_fis ||
+ if (!ad->res_fis ||
!(pr->cmd & PORT_CMD_FIS_RX)) {
return;
}
@@ -625,53 +685,35 @@ static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished)
sdb_fis->type = SATA_FIS_TYPE_SDB;
/* Interrupt pending & Notification bit */
- sdb_fis->flags = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0);
+ sdb_fis->flags = 0x40; /* Interrupt bit, always 1 for NCQ */
sdb_fis->status = ide_state->status & 0x77;
sdb_fis->error = ide_state->error;
/* update SAct field in SDB_FIS */
- s->dev[port].finished |= finished;
sdb_fis->payload = cpu_to_le32(ad->finished);
/* Update shadow registers (except BSY 0x80 and DRQ 0x08) */
pr->tfdata = (ad->port.ifs[0].error << 8) |
(ad->port.ifs[0].status & 0x77) |
(pr->tfdata & 0x88);
+ pr->scr_act &= ~ad->finished;
+ ad->finished = 0;
- ahci_trigger_irq(s, ad, PORT_IRQ_SDB_FIS);
+ /* Trigger IRQ if interrupt bit is set (which currently, it always is) */
+ if (sdb_fis->flags & 0x40) {
+ ahci_trigger_irq(s, ad, PORT_IRQ_SDB_FIS);
+ }
}
static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len)
{
AHCIPortRegs *pr = &ad->port_regs;
- uint8_t *pio_fis, *cmd_fis;
- uint64_t tbl_addr;
- dma_addr_t cmd_len = 0x80;
+ uint8_t *pio_fis;
IDEState *s = &ad->port.ifs[0];
if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) {
return;
}
- /* map cmd_fis */
- tbl_addr = le64_to_cpu(ad->cur_cmd->tbl_addr);
- cmd_fis = dma_memory_map(ad->hba->as, tbl_addr, &cmd_len,
- DMA_DIRECTION_TO_DEVICE);
-
- if (cmd_fis == NULL) {
- DPRINTF(ad->port_no, "dma_memory_map failed in ahci_write_fis_pio");
- ahci_trigger_irq(ad->hba, ad, PORT_IRQ_HBUS_ERR);
- return;
- }
-
- if (cmd_len != 0x80) {
- DPRINTF(ad->port_no,
- "dma_memory_map mapped too few bytes in ahci_write_fis_pio");
- dma_memory_unmap(ad->hba->as, cmd_fis, cmd_len,
- DMA_DIRECTION_TO_DEVICE, cmd_len);
- ahci_trigger_irq(ad->hba, ad, PORT_IRQ_HBUS_ERR);
- return;
- }
-
pio_fis = &ad->res_fis[RES_FIS_PSFIS];
pio_fis[0] = SATA_FIS_TYPE_PIO_SETUP;
@@ -687,8 +729,8 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len)
pio_fis[9] = s->hob_lcyl;
pio_fis[10] = s->hob_hcyl;
pio_fis[11] = 0;
- pio_fis[12] = cmd_fis[12];
- pio_fis[13] = cmd_fis[13];
+ pio_fis[12] = s->nsector & 0xFF;
+ pio_fis[13] = (s->nsector >> 8) & 0xFF;
pio_fis[14] = 0;
pio_fis[15] = s->status;
pio_fis[16] = len & 255;
@@ -705,9 +747,6 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len)
}
ahci_trigger_irq(ad->hba, ad, PORT_IRQ_PIOS_FIS);
-
- dma_memory_unmap(ad->hba->as, cmd_fis, cmd_len,
- DMA_DIRECTION_TO_DEVICE, cmd_len);
}
static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
@@ -715,22 +754,12 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
AHCIPortRegs *pr = &ad->port_regs;
uint8_t *d2h_fis;
int i;
- dma_addr_t cmd_len = 0x80;
- int cmd_mapped = 0;
IDEState *s = &ad->port.ifs[0];
if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) {
return;
}
- if (!cmd_fis) {
- /* map cmd_fis */
- uint64_t tbl_addr = le64_to_cpu(ad->cur_cmd->tbl_addr);
- cmd_fis = dma_memory_map(ad->hba->as, tbl_addr, &cmd_len,
- DMA_DIRECTION_TO_DEVICE);
- cmd_mapped = 1;
- }
-
d2h_fis = &ad->res_fis[RES_FIS_RFIS];
d2h_fis[0] = SATA_FIS_TYPE_REGISTER_D2H;
@@ -746,8 +775,8 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
d2h_fis[9] = s->hob_lcyl;
d2h_fis[10] = s->hob_hcyl;
d2h_fis[11] = 0;
- d2h_fis[12] = cmd_fis[12];
- d2h_fis[13] = cmd_fis[13];
+ d2h_fis[12] = s->nsector & 0xFF;
+ d2h_fis[13] = (s->nsector >> 8) & 0xFF;
for (i = 14; i < 20; i++) {
d2h_fis[i] = 0;
}
@@ -761,26 +790,22 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
}
ahci_trigger_irq(ad->hba, ad, PORT_IRQ_D2H_REG_FIS);
-
- if (cmd_mapped) {
- dma_memory_unmap(ad->hba->as, cmd_fis, cmd_len,
- DMA_DIRECTION_TO_DEVICE, cmd_len);
- }
}
static int prdt_tbl_entry_size(const AHCI_SG *tbl)
{
+ /* flags_size is zero-based */
return (le32_to_cpu(tbl->flags_size) & AHCI_PRDT_SIZE_MASK) + 1;
}
static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist,
- int32_t offset)
+ AHCICmdHdr *cmd, int64_t limit, int32_t offset)
{
- AHCICmdHdr *cmd = ad->cur_cmd;
- uint32_t opts = le32_to_cpu(cmd->opts);
- uint64_t prdt_addr = le64_to_cpu(cmd->tbl_addr) + 0x80;
- int sglist_alloc_hint = opts >> AHCI_CMD_HDR_PRDT_LEN;
- dma_addr_t prdt_len = (sglist_alloc_hint * sizeof(AHCI_SG));
+ uint16_t opts = le16_to_cpu(cmd->opts);
+ uint16_t prdtl = le16_to_cpu(cmd->prdtl);
+ uint64_t cfis_addr = le64_to_cpu(cmd->tbl_addr);
+ uint64_t prdt_addr = cfis_addr + 0x80;
+ dma_addr_t prdt_len = (prdtl * sizeof(AHCI_SG));
dma_addr_t real_prdt_len = prdt_len;
uint8_t *prdt;
int i;
@@ -800,7 +825,7 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist,
* request for sector sizes up to 32K.
*/
- if (!sglist_alloc_hint) {
+ if (!prdtl) {
DPRINTF(ad->port_no, "no sg list given by guest: 0x%08x\n", opts);
return -1;
}
@@ -819,13 +844,12 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist,
}
/* Get entries in the PRDT, init a qemu sglist accordingly */
- if (sglist_alloc_hint > 0) {
+ if (prdtl > 0) {
AHCI_SG *tbl = (AHCI_SG *)prdt;
sum = 0;
- for (i = 0; i < sglist_alloc_hint; i++) {
- /* flags_size is zero-based */
+ for (i = 0; i < prdtl; i++) {
tbl_entry_size = prdt_tbl_entry_size(&tbl[i]);
- if (offset <= (sum + tbl_entry_size)) {
+ if (offset < (sum + tbl_entry_size)) {
off_idx = i;
off_pos = offset - sum;
break;
@@ -840,15 +864,16 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist,
goto out;
}
- qemu_sglist_init(sglist, qbus->parent, (sglist_alloc_hint - off_idx),
+ qemu_sglist_init(sglist, qbus->parent, (prdtl - off_idx),
ad->hba->as);
qemu_sglist_add(sglist, le64_to_cpu(tbl[off_idx].addr) + off_pos,
- prdt_tbl_entry_size(&tbl[off_idx]) - off_pos);
+ MIN(prdt_tbl_entry_size(&tbl[off_idx]) - off_pos,
+ limit));
- for (i = off_idx + 1; i < sglist_alloc_hint; i++) {
- /* flags_size is zero-based */
+ for (i = off_idx + 1; i < prdtl && sglist->size < limit; i++) {
qemu_sglist_add(sglist, le64_to_cpu(tbl[i].addr),
- prdt_tbl_entry_size(&tbl[i]));
+ MIN(prdt_tbl_entry_size(&tbl[i]),
+ limit - sglist->size));
if (sglist->size > INT32_MAX) {
error_report("AHCI Physical Region Descriptor Table describes "
"more than 2 GiB.\n");
@@ -865,28 +890,25 @@ out:
return r;
}
-static void ncq_cb(void *opaque, int ret)
+static void ncq_err(NCQTransferState *ncq_tfs)
{
- NCQTransferState *ncq_tfs = (NCQTransferState *)opaque;
IDEState *ide_state = &ncq_tfs->drive->port.ifs[0];
- if (ret == -ECANCELED) {
- return;
- }
- /* Clear bit for this tag in SActive */
- ncq_tfs->drive->port_regs.scr_act &= ~(1 << ncq_tfs->tag);
+ ide_state->error = ABRT_ERR;
+ ide_state->status = READY_STAT | ERR_STAT;
+ ncq_tfs->drive->port_regs.scr_err |= (1 << ncq_tfs->tag);
+}
- if (ret < 0) {
- /* error */
- ide_state->error = ABRT_ERR;
- ide_state->status = READY_STAT | ERR_STAT;
- ncq_tfs->drive->port_regs.scr_err |= (1 << ncq_tfs->tag);
- } else {
- ide_state->status = READY_STAT | SEEK_STAT;
+static void ncq_finish(NCQTransferState *ncq_tfs)
+{
+ /* If we didn't error out, set our finished bit. Errored commands
+ * do not get a bit set for the SDB FIS ACT register, nor do they
+ * clear the outstanding bit in scr_act (PxSACT). */
+ if (!(ncq_tfs->drive->port_regs.scr_err & (1 << ncq_tfs->tag))) {
+ ncq_tfs->drive->finished |= (1 << ncq_tfs->tag);
}
- ahci_write_fis_sdb(ncq_tfs->drive->hba, ncq_tfs->drive->port_no,
- (1 << ncq_tfs->tag));
+ ahci_write_fis_sdb(ncq_tfs->drive->hba, ncq_tfs);
DPRINTF(ncq_tfs->drive->port_no, "NCQ transfer tag %d finished\n",
ncq_tfs->tag);
@@ -897,6 +919,35 @@ static void ncq_cb(void *opaque, int ret)
ncq_tfs->used = 0;
}
+static void ncq_cb(void *opaque, int ret)
+{
+ NCQTransferState *ncq_tfs = (NCQTransferState *)opaque;
+ IDEState *ide_state = &ncq_tfs->drive->port.ifs[0];
+
+ if (ret == -ECANCELED) {
+ return;
+ }
+
+ if (ret < 0) {
+ bool is_read = ncq_tfs->cmd == READ_FPDMA_QUEUED;
+ BlockErrorAction action = blk_get_error_action(ide_state->blk,
+ is_read, -ret);
+ if (action == BLOCK_ERROR_ACTION_STOP) {
+ ncq_tfs->halt = true;
+ ide_state->bus->error_status = IDE_RETRY_HBA;
+ } else if (action == BLOCK_ERROR_ACTION_REPORT) {
+ ncq_err(ncq_tfs);
+ }
+ blk_error_action(ide_state->blk, action, is_read, -ret);
+ } else {
+ ide_state->status = READY_STAT | SEEK_STAT;
+ }
+
+ if (!ncq_tfs->halt) {
+ ncq_finish(ncq_tfs);
+ }
+}
+
static int is_ncq(uint8_t ata_cmd)
{
/* Based on SATA 3.2 section 13.6.3.2 */
@@ -912,13 +963,60 @@ static int is_ncq(uint8_t ata_cmd)
}
}
+static void execute_ncq_command(NCQTransferState *ncq_tfs)
+{
+ AHCIDevice *ad = ncq_tfs->drive;
+ IDEState *ide_state = &ad->port.ifs[0];
+ int port = ad->port_no;
+
+ g_assert(is_ncq(ncq_tfs->cmd));
+ ncq_tfs->halt = false;
+
+ switch (ncq_tfs->cmd) {
+ case READ_FPDMA_QUEUED:
+ DPRINTF(port, "NCQ reading %d sectors from LBA %"PRId64", tag %d\n",
+ ncq_tfs->sector_count, ncq_tfs->lba, ncq_tfs->tag);
+
+ DPRINTF(port, "tag %d aio read %"PRId64"\n",
+ ncq_tfs->tag, ncq_tfs->lba);
+
+ dma_acct_start(ide_state->blk, &ncq_tfs->acct,
+ &ncq_tfs->sglist, BLOCK_ACCT_READ);
+ ncq_tfs->aiocb = dma_blk_read(ide_state->blk, &ncq_tfs->sglist,
+ ncq_tfs->lba, ncq_cb, ncq_tfs);
+ break;
+ case WRITE_FPDMA_QUEUED:
+ DPRINTF(port, "NCQ writing %d sectors to LBA %"PRId64", tag %d\n",
+ ncq_tfs->sector_count, ncq_tfs->lba, ncq_tfs->tag);
+
+ DPRINTF(port, "tag %d aio write %"PRId64"\n",
+ ncq_tfs->tag, ncq_tfs->lba);
+
+ dma_acct_start(ide_state->blk, &ncq_tfs->acct,
+ &ncq_tfs->sglist, BLOCK_ACCT_WRITE);
+ ncq_tfs->aiocb = dma_blk_write(ide_state->blk, &ncq_tfs->sglist,
+ ncq_tfs->lba, ncq_cb, ncq_tfs);
+ break;
+ default:
+ DPRINTF(port, "error: unsupported NCQ command (0x%02x) received\n",
+ ncq_tfs->cmd);
+ qemu_sglist_destroy(&ncq_tfs->sglist);
+ ncq_err(ncq_tfs);
+ }
+}
+
+
static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis,
- int slot)
+ uint8_t slot)
{
+ AHCIDevice *ad = &s->dev[port];
+ IDEState *ide_state = &ad->port.ifs[0];
NCQFrame *ncq_fis = (NCQFrame*)cmd_fis;
uint8_t tag = ncq_fis->tag >> 3;
- NCQTransferState *ncq_tfs = &s->dev[port].ncq_tfs[tag];
+ NCQTransferState *ncq_tfs = &ad->ncq_tfs[tag];
+ size_t size;
+ g_assert(is_ncq(ncq_fis->command));
if (ncq_tfs->used) {
/* error - already in use */
fprintf(stderr, "%s: tag %d already used\n", __FUNCTION__, tag);
@@ -926,75 +1024,82 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis,
}
ncq_tfs->used = 1;
- ncq_tfs->drive = &s->dev[port];
+ ncq_tfs->drive = ad;
ncq_tfs->slot = slot;
+ ncq_tfs->cmdh = &((AHCICmdHdr *)ad->lst)[slot];
+ ncq_tfs->cmd = ncq_fis->command;
ncq_tfs->lba = ((uint64_t)ncq_fis->lba5 << 40) |
((uint64_t)ncq_fis->lba4 << 32) |
((uint64_t)ncq_fis->lba3 << 24) |
((uint64_t)ncq_fis->lba2 << 16) |
((uint64_t)ncq_fis->lba1 << 8) |
(uint64_t)ncq_fis->lba0;
+ ncq_tfs->tag = tag;
- /* Note: We calculate the sector count, but don't currently rely on it.
- * The total size of the DMA buffer tells us the transfer size instead. */
- ncq_tfs->sector_count = ((uint16_t)ncq_fis->sector_count_high << 8) |
- ncq_fis->sector_count_low;
+ /* Sanity-check the NCQ packet */
+ if (tag != slot) {
+ DPRINTF(port, "Warn: NCQ slot (%d) did not match the given tag (%d)\n",
+ slot, tag);
+ }
- DPRINTF(port, "NCQ transfer LBA from %"PRId64" to %"PRId64", "
- "drive max %"PRId64"\n",
- ncq_tfs->lba, ncq_tfs->lba + ncq_tfs->sector_count - 2,
- s->dev[port].port.ifs[0].nb_sectors - 1);
+ if (ncq_fis->aux0 || ncq_fis->aux1 || ncq_fis->aux2 || ncq_fis->aux3) {
+ DPRINTF(port, "Warn: Attempt to use NCQ auxiliary fields.\n");
+ }
+ if (ncq_fis->prio || ncq_fis->icc) {
+ DPRINTF(port, "Warn: Unsupported attempt to use PRIO/ICC fields\n");
+ }
+ if (ncq_fis->fua & NCQ_FIS_FUA_MASK) {
+ DPRINTF(port, "Warn: Unsupported attempt to use Force Unit Access\n");
+ }
+ if (ncq_fis->tag & NCQ_FIS_RARC_MASK) {
+ DPRINTF(port, "Warn: Unsupported attempt to use Rebuild Assist\n");
+ }
- ahci_populate_sglist(&s->dev[port], &ncq_tfs->sglist, 0);
- ncq_tfs->tag = tag;
+ ncq_tfs->sector_count = ((ncq_fis->sector_count_high << 8) |
+ ncq_fis->sector_count_low);
+ if (!ncq_tfs->sector_count) {
+ ncq_tfs->sector_count = 0x10000;
+ }
+ size = ncq_tfs->sector_count * 512;
+ ahci_populate_sglist(ad, &ncq_tfs->sglist, ncq_tfs->cmdh, size, 0);
- switch(ncq_fis->command) {
- case READ_FPDMA_QUEUED:
- DPRINTF(port, "NCQ reading %d sectors from LBA %"PRId64", "
- "tag %d\n",
- ncq_tfs->sector_count-1, ncq_tfs->lba, ncq_tfs->tag);
+ if (ncq_tfs->sglist.size < size) {
+ error_report("ahci: PRDT length for NCQ command (0x%zx) "
+ "is smaller than the requested size (0x%zx)",
+ ncq_tfs->sglist.size, size);
+ qemu_sglist_destroy(&ncq_tfs->sglist);
+ ncq_err(ncq_tfs);
+ ahci_trigger_irq(ad->hba, ad, PORT_IRQ_OVERFLOW);
+ return;
+ } else if (ncq_tfs->sglist.size != size) {
+ DPRINTF(port, "Warn: PRDTL (0x%zx)"
+ " does not match requested size (0x%zx)",
+ ncq_tfs->sglist.size, size);
+ }
- DPRINTF(port, "tag %d aio read %"PRId64"\n",
- ncq_tfs->tag, ncq_tfs->lba);
+ DPRINTF(port, "NCQ transfer LBA from %"PRId64" to %"PRId64", "
+ "drive max %"PRId64"\n",
+ ncq_tfs->lba, ncq_tfs->lba + ncq_tfs->sector_count - 1,
+ ide_state->nb_sectors - 1);
- dma_acct_start(ncq_tfs->drive->port.ifs[0].blk, &ncq_tfs->acct,
- &ncq_tfs->sglist, BLOCK_ACCT_READ);
- ncq_tfs->aiocb = dma_blk_read(ncq_tfs->drive->port.ifs[0].blk,
- &ncq_tfs->sglist, ncq_tfs->lba,
- ncq_cb, ncq_tfs);
- break;
- case WRITE_FPDMA_QUEUED:
- DPRINTF(port, "NCQ writing %d sectors to LBA %"PRId64", tag %d\n",
- ncq_tfs->sector_count-1, ncq_tfs->lba, ncq_tfs->tag);
-
- DPRINTF(port, "tag %d aio write %"PRId64"\n",
- ncq_tfs->tag, ncq_tfs->lba);
-
- dma_acct_start(ncq_tfs->drive->port.ifs[0].blk, &ncq_tfs->acct,
- &ncq_tfs->sglist, BLOCK_ACCT_WRITE);
- ncq_tfs->aiocb = dma_blk_write(ncq_tfs->drive->port.ifs[0].blk,
- &ncq_tfs->sglist, ncq_tfs->lba,
- ncq_cb, ncq_tfs);
- break;
- default:
- if (is_ncq(cmd_fis[2])) {
- DPRINTF(port,
- "error: unsupported NCQ command (0x%02x) received\n",
- cmd_fis[2]);
- } else {
- DPRINTF(port,
- "error: tried to process non-NCQ command as NCQ\n");
- }
- qemu_sglist_destroy(&ncq_tfs->sglist);
+ execute_ncq_command(ncq_tfs);
+}
+
+static AHCICmdHdr *get_cmd_header(AHCIState *s, uint8_t port, uint8_t slot)
+{
+ if (port >= s->ports || slot >= AHCI_MAX_CMDS) {
+ return NULL;
}
+
+ return s->dev[port].lst ? &((AHCICmdHdr *)s->dev[port].lst)[slot] : NULL;
}
static void handle_reg_h2d_fis(AHCIState *s, int port,
- int slot, uint8_t *cmd_fis)
+ uint8_t slot, uint8_t *cmd_fis)
{
IDEState *ide_state = &s->dev[port].port.ifs[0];
- AHCICmdHdr *cmd = s->dev[port].cur_cmd;
- uint32_t opts = le32_to_cpu(cmd->opts);
+ AHCICmdHdr *cmd = get_cmd_header(s, port, slot);
+ uint16_t opts = le16_to_cpu(cmd->opts);
if (cmd_fis[1] & 0x0F) {
DPRINTF(port, "Port Multiplier not supported."
@@ -1074,7 +1179,7 @@ static void handle_reg_h2d_fis(AHCIState *s, int port,
ide_exec_cmd(&s->dev[port].port, cmd_fis[2]);
}
-static int handle_cmd(AHCIState *s, int port, int slot)
+static int handle_cmd(AHCIState *s, int port, uint8_t slot)
{
IDEState *ide_state;
uint64_t tbl_addr;
@@ -1092,7 +1197,7 @@ static int handle_cmd(AHCIState *s, int port, int slot)
DPRINTF(port, "error: lst not given but cmd handled");
return -1;
}
- cmd = &((AHCICmdHdr *)s->dev[port].lst)[slot];
+ cmd = get_cmd_header(s, port, slot);
/* remember current slot handle for later */
s->dev[port].cur_cmd = cmd;
@@ -1151,7 +1256,7 @@ static void ahci_start_transfer(IDEDMA *dma)
IDEState *s = &ad->port.ifs[0];
uint32_t size = (uint32_t)(s->data_end - s->data_ptr);
/* write == ram -> device */
- uint32_t opts = le32_to_cpu(ad->cur_cmd->opts);
+ uint16_t opts = le16_to_cpu(ad->cur_cmd->opts);
int is_write = opts & AHCI_CMD_WRITE;
int is_atapi = opts & AHCI_CMD_ATAPI;
int has_sglist = 0;
@@ -1163,7 +1268,7 @@ static void ahci_start_transfer(IDEDMA *dma)
goto out;
}
- if (ahci_dma_prepare_buf(dma, is_write)) {
+ if (ahci_dma_prepare_buf(dma, size)) {
has_sglist = 1;
}
@@ -1184,7 +1289,7 @@ out:
s->data_ptr = s->data_end;
/* Update number of transferred bytes, destroy sglist */
- ahci_commit_buf(dma, size);
+ dma_buf_commit(s, size);
s->end_transfer_func(s);
@@ -1209,16 +1314,33 @@ static void ahci_restart_dma(IDEDMA *dma)
}
/**
- * Called in DMA R/W chains to read the PRDT, utilizing ahci_populate_sglist.
- * Not currently invoked by PIO R/W chains,
- * which invoke ahci_populate_sglist via ahci_start_transfer.
+ * IDE/PIO restarts are handled by the core layer, but NCQ commands
+ * need an extra kick from the AHCI HBA.
+ */
+static void ahci_restart(IDEDMA *dma)
+{
+ AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
+ int i;
+
+ for (i = 0; i < AHCI_MAX_CMDS; i++) {
+ NCQTransferState *ncq_tfs = &ad->ncq_tfs[i];
+ if (ncq_tfs->halt) {
+ execute_ncq_command(ncq_tfs);
+ }
+ }
+}
+
+/**
+ * Called in DMA and PIO R/W chains to read the PRDT.
+ * Not shared with NCQ pathways.
*/
-static int32_t ahci_dma_prepare_buf(IDEDMA *dma, int is_write)
+static int32_t ahci_dma_prepare_buf(IDEDMA *dma, int32_t limit)
{
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
IDEState *s = &ad->port.ifs[0];
- if (ahci_populate_sglist(ad, &s->sg, s->io_buffer_offset) == -1) {
+ if (ahci_populate_sglist(ad, &s->sg, ad->cur_cmd,
+ limit, s->io_buffer_offset) == -1) {
DPRINTF(ad->port_no, "ahci_dma_prepare_buf failed.\n");
return -1;
}
@@ -1229,21 +1351,16 @@ static int32_t ahci_dma_prepare_buf(IDEDMA *dma, int is_write)
}
/**
- * Destroys the scatter-gather list,
- * and updates the command header with a bytes-read value.
- * called explicitly via ahci_dma_rw_buf (ATAPI DMA),
- * and ahci_start_transfer (PIO R/W),
- * and called via callback from ide_dma_cb for DMA R/W paths.
+ * Updates the command header with a bytes-read value.
+ * Called via dma_buf_commit, for both DMA and PIO paths.
+ * sglist destruction is handled within dma_buf_commit.
*/
static void ahci_commit_buf(IDEDMA *dma, uint32_t tx_bytes)
{
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
- IDEState *s = &ad->port.ifs[0];
tx_bytes += le32_to_cpu(ad->cur_cmd->status);
ad->cur_cmd->status = cpu_to_le32(tx_bytes);
-
- qemu_sglist_destroy(&s->sg);
}
static int ahci_dma_rw_buf(IDEDMA *dma, int is_write)
@@ -1253,7 +1370,7 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write)
uint8_t *p = s->io_buffer + s->io_buffer_index;
int l = s->io_buffer_size - s->io_buffer_index;
- if (ahci_populate_sglist(ad, &s->sg, s->io_buffer_offset)) {
+ if (ahci_populate_sglist(ad, &s->sg, ad->cur_cmd, l, s->io_buffer_offset)) {
return 0;
}
@@ -1264,10 +1381,9 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write)
}
/* free sglist, update byte count */
- ahci_commit_buf(dma, l);
+ dma_buf_commit(s, l);
s->io_buffer_index += l;
- s->io_buffer_offset += l;
DPRINTF(ad->port_no, "len=%#x\n", l);
@@ -1296,6 +1412,7 @@ static void ahci_irq_set(void *opaque, int n, int level)
static const IDEDMAOps ahci_dma_ops = {
.start_dma = ahci_start_dma,
+ .restart = ahci_restart,
.restart_dma = ahci_restart_dma,
.start_transfer = ahci_start_transfer,
.prepare_buf = ahci_dma_prepare_buf,
@@ -1366,6 +1483,21 @@ void ahci_reset(AHCIState *s)
}
}
+static const VMStateDescription vmstate_ncq_tfs = {
+ .name = "ncq state",
+ .version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(sector_count, NCQTransferState),
+ VMSTATE_UINT64(lba, NCQTransferState),
+ VMSTATE_UINT8(tag, NCQTransferState),
+ VMSTATE_UINT8(cmd, NCQTransferState),
+ VMSTATE_UINT8(slot, NCQTransferState),
+ VMSTATE_BOOL(used, NCQTransferState),
+ VMSTATE_BOOL(halt, NCQTransferState),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
static const VMStateDescription vmstate_ahci_device = {
.name = "ahci port",
.version_id = 1,
@@ -1391,21 +1523,59 @@ static const VMStateDescription vmstate_ahci_device = {
VMSTATE_BOOL(done_atapi_packet, AHCIDevice),
VMSTATE_INT32(busy_slot, AHCIDevice),
VMSTATE_BOOL(init_d2h_sent, AHCIDevice),
+ VMSTATE_STRUCT_ARRAY(ncq_tfs, AHCIDevice, AHCI_MAX_CMDS,
+ 1, vmstate_ncq_tfs, NCQTransferState),
VMSTATE_END_OF_LIST()
},
};
static int ahci_state_post_load(void *opaque, int version_id)
{
- int i;
+ int i, j;
struct AHCIDevice *ad;
+ NCQTransferState *ncq_tfs;
AHCIState *s = opaque;
for (i = 0; i < s->ports; i++) {
ad = &s->dev[i];
- ahci_map_clb_address(ad);
- ahci_map_fis_address(ad);
+ /* Only remap the CLB address if appropriate, disallowing a state
+ * transition from 'on' to 'off' it should be consistent here. */
+ if (ahci_cond_start_engines(ad, false) != 0) {
+ return -1;
+ }
+
+ for (j = 0; j < AHCI_MAX_CMDS; j++) {
+ ncq_tfs = &ad->ncq_tfs[j];
+ ncq_tfs->drive = ad;
+
+ if (ncq_tfs->used != ncq_tfs->halt) {
+ return -1;
+ }
+ if (!ncq_tfs->halt) {
+ continue;
+ }
+ if (!is_ncq(ncq_tfs->cmd)) {
+ return -1;
+ }
+ if (ncq_tfs->slot != ncq_tfs->tag) {
+ return -1;
+ }
+ /* If ncq_tfs->halt is justly set, the engine should be engaged,
+ * and the command list buffer should be mapped. */
+ ncq_tfs->cmdh = get_cmd_header(s, i, ncq_tfs->slot);
+ if (!ncq_tfs->cmdh) {
+ return -1;
+ }
+ ahci_populate_sglist(ncq_tfs->drive, &ncq_tfs->sglist,
+ ncq_tfs->cmdh, ncq_tfs->sector_count * 512,
+ 0);
+ if (ncq_tfs->sector_count != ncq_tfs->sglist.size >> 9) {
+ return -1;
+ }
+ }
+
+
/*
* If an error is present, ad->busy_slot will be valid and not -1.
* In this case, an operation is waiting to resume and will re-check
@@ -1422,7 +1592,7 @@ static int ahci_state_post_load(void *opaque, int version_id)
if (ad->busy_slot < 0 || ad->busy_slot >= AHCI_MAX_CMDS) {
return -1;
}
- ad->cur_cmd = &((AHCICmdHdr *)ad->lst)[ad->busy_slot];
+ ad->cur_cmd = get_cmd_header(s, i, ad->busy_slot);
}
}
@@ -1461,7 +1631,6 @@ typedef struct SysbusAHCIState {
static const VMStateDescription vmstate_sysbus_ahci = {
.name = "sysbus-ahci",
- .unmigratable = 1, /* Still buggy under I/O load */
.fields = (VMStateField[]) {
VMSTATE_AHCI(ahci, SysbusAHCIState),
VMSTATE_END_OF_LIST()
diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h
index 501c002c3..79a463d93 100644
--- a/hw/ide/ahci.h
+++ b/hw/ide/ahci.h
@@ -127,7 +127,7 @@
#define PORT_CMD_SPIN_UP (1 << 1) /* Spin up device */
#define PORT_CMD_START (1 << 0) /* Enable port DMA engine */
-#define PORT_CMD_ICC_MASK (0xf << 28) /* i/f ICC state mask */
+#define PORT_CMD_ICC_MASK (0xfU << 28) /* i/f ICC state mask */
#define PORT_CMD_ICC_ACTIVE (0x1 << 28) /* Put i/f in active state */
#define PORT_CMD_ICC_PARTIAL (0x2 << 28) /* Put i/f in partial state */
#define PORT_CMD_ICC_SLUMBER (0x6 << 28) /* Put i/f in slumber state */
@@ -166,7 +166,7 @@
#define AHCI_CMD_HDR_CMD_FIS_LEN 0x1f
#define AHCI_CMD_HDR_PRDT_LEN 16
-#define SATA_SIGNATURE_CDROM 0xeb140000
+#define SATA_SIGNATURE_CDROM 0xeb140101
#define SATA_SIGNATURE_DISK 0x00000101
#define AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR 0x20
@@ -195,6 +195,9 @@
#define RECEIVE_FPDMA_QUEUED 0x65
#define SEND_FPDMA_QUEUED 0x64
+#define NCQ_FIS_FUA_MASK 0x80
+#define NCQ_FIS_RARC_MASK 0x01
+
#define RES_FIS_DSFIS 0x00
#define RES_FIS_PSFIS 0x20
#define RES_FIS_RFIS 0x40
@@ -233,7 +236,8 @@ typedef struct AHCIPortRegs {
} AHCIPortRegs;
typedef struct AHCICmdHdr {
- uint32_t opts;
+ uint16_t opts;
+ uint16_t prdtl;
uint32_t status;
uint64_t tbl_addr;
uint32_t reserved[4];
@@ -250,13 +254,16 @@ typedef struct AHCIDevice AHCIDevice;
typedef struct NCQTransferState {
AHCIDevice *drive;
BlockAIOCB *aiocb;
+ AHCICmdHdr *cmdh;
QEMUSGList sglist;
BlockAcctCookie acct;
- uint16_t sector_count;
+ uint32_t sector_count;
uint64_t lba;
uint8_t tag;
- int slot;
- int used;
+ uint8_t cmd;
+ uint8_t slot;
+ bool used;
+ bool halt;
} NCQTransferState;
struct AHCIDevice {
@@ -312,27 +319,39 @@ extern const VMStateDescription vmstate_ahci;
.offset = vmstate_offset_value(_state, _field, AHCIState), \
}
+/**
+ * NCQFrame is the same as a Register H2D FIS (described in SATA 3.2),
+ * but some fields have been re-mapped and re-purposed, as seen in
+ * SATA 3.2 section 13.6.4.1 ("READ FPDMA QUEUED")
+ *
+ * cmd_fis[3], feature 7:0, becomes sector count 7:0.
+ * cmd_fis[7], device 7:0, uses bit 7 as the Force Unit Access bit.
+ * cmd_fis[11], feature 15:8, becomes sector count 15:8.
+ * cmd_fis[12], count 7:0, becomes the NCQ TAG (7:3) and RARC bit (0)
+ * cmd_fis[13], count 15:8, becomes the priority value (7:6)
+ * bytes 16-19 become an le32 "auxiliary" field.
+ */
typedef struct NCQFrame {
uint8_t fis_type;
uint8_t c;
uint8_t command;
- uint8_t sector_count_low;
+ uint8_t sector_count_low; /* (feature 7:0) */
uint8_t lba0;
uint8_t lba1;
uint8_t lba2;
- uint8_t fua;
+ uint8_t fua; /* (device 7:0) */
uint8_t lba3;
uint8_t lba4;
uint8_t lba5;
- uint8_t sector_count_high;
- uint8_t tag;
- uint8_t reserved5;
- uint8_t reserved6;
+ uint8_t sector_count_high; /* (feature 15:8) */
+ uint8_t tag; /* (count 0:7) */
+ uint8_t prio; /* (count 15:8) */
+ uint8_t icc;
uint8_t control;
- uint8_t reserved7;
- uint8_t reserved8;
- uint8_t reserved9;
- uint8_t reserved10;
+ uint8_t aux0;
+ uint8_t aux1;
+ uint8_t aux2;
+ uint8_t aux3;
} QEMU_PACKED NCQFrame;
typedef struct SDBFIS {
diff --git a/hw/ide/core.c b/hw/ide/core.c
index 822519bde..1cc6945d8 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -591,7 +591,6 @@ static void ide_sector_read_cb(void *opaque, int ret)
s->nsector -= n;
/* Allow the guest to read the io_buffer */
ide_transfer_start(s, s->io_buffer, n * BDRV_SECTOR_SIZE, ide_sector_read);
- s->io_buffer_offset += 512 * n;
ide_set_irq(s->bus);
}
@@ -635,11 +634,12 @@ static void ide_sector_read(IDEState *s)
ide_sector_read_cb, s);
}
-static void dma_buf_commit(IDEState *s, uint32_t tx_bytes)
+void dma_buf_commit(IDEState *s, uint32_t tx_bytes)
{
if (s->bus->dma->ops->commit_buf) {
s->bus->dma->ops->commit_buf(s->bus->dma, tx_bytes);
}
+ s->io_buffer_offset += tx_bytes;
qemu_sglist_destroy(&s->sg);
}
@@ -716,8 +716,8 @@ static void ide_dma_cb(void *opaque, int ret)
sector_num = ide_get_sector(s);
if (n > 0) {
- assert(s->io_buffer_size == s->sg.size);
- dma_buf_commit(s, s->io_buffer_size);
+ assert(n * 512 == s->sg.size);
+ dma_buf_commit(s, s->sg.size);
sector_num += n;
ide_set_sector(s, sector_num);
s->nsector -= n;
@@ -734,7 +734,7 @@ static void ide_dma_cb(void *opaque, int ret)
n = s->nsector;
s->io_buffer_index = 0;
s->io_buffer_size = n * 512;
- if (s->bus->dma->ops->prepare_buf(s->bus->dma, ide_cmd_is_read(s)) < 512) {
+ if (s->bus->dma->ops->prepare_buf(s->bus->dma, s->io_buffer_size) < 512) {
/* The PRDs were too short. Reset the Active bit, but don't raise an
* interrupt. */
s->status = READY_STAT | SEEK_STAT;
@@ -842,7 +842,6 @@ static void ide_sector_write_cb(void *opaque, int ret)
n = s->req_nb_sectors;
}
s->nsector -= n;
- s->io_buffer_offset += 512 * n;
ide_set_sector(s, ide_get_sector(s) + n);
if (s->nsector == 0) {
@@ -1747,11 +1746,11 @@ static const struct {
} ide_cmd_table[0x100] = {
/* NOP not implemented, mandatory for CD */
[CFA_REQ_EXT_ERROR_CODE] = { cmd_cfa_req_ext_error_code, CFA_OK },
- [WIN_DSM] = { cmd_data_set_management, ALL_OK },
+ [WIN_DSM] = { cmd_data_set_management, HD_CFA_OK },
[WIN_DEVICE_RESET] = { cmd_device_reset, CD_OK },
[WIN_RECAL] = { cmd_nop, HD_CFA_OK | SET_DSC},
[WIN_READ] = { cmd_read_pio, ALL_OK },
- [WIN_READ_ONCE] = { cmd_read_pio, ALL_OK },
+ [WIN_READ_ONCE] = { cmd_read_pio, HD_CFA_OK },
[WIN_READ_EXT] = { cmd_read_pio, HD_CFA_OK },
[WIN_READDMA_EXT] = { cmd_read_dma, HD_CFA_OK },
[WIN_READ_NATIVE_MAX_EXT] = { cmd_read_native_max, HD_CFA_OK | SET_DSC },
@@ -1770,12 +1769,12 @@ static const struct {
[CFA_TRANSLATE_SECTOR] = { cmd_cfa_translate_sector, CFA_OK },
[WIN_DIAGNOSE] = { cmd_exec_dev_diagnostic, ALL_OK },
[WIN_SPECIFY] = { cmd_nop, HD_CFA_OK | SET_DSC },
- [WIN_STANDBYNOW2] = { cmd_nop, ALL_OK },
- [WIN_IDLEIMMEDIATE2] = { cmd_nop, ALL_OK },
- [WIN_STANDBY2] = { cmd_nop, ALL_OK },
- [WIN_SETIDLE2] = { cmd_nop, ALL_OK },
- [WIN_CHECKPOWERMODE2] = { cmd_check_power_mode, ALL_OK | SET_DSC },
- [WIN_SLEEPNOW2] = { cmd_nop, ALL_OK },
+ [WIN_STANDBYNOW2] = { cmd_nop, HD_CFA_OK },
+ [WIN_IDLEIMMEDIATE2] = { cmd_nop, HD_CFA_OK },
+ [WIN_STANDBY2] = { cmd_nop, HD_CFA_OK },
+ [WIN_SETIDLE2] = { cmd_nop, HD_CFA_OK },
+ [WIN_CHECKPOWERMODE2] = { cmd_check_power_mode, HD_CFA_OK | SET_DSC },
+ [WIN_SLEEPNOW2] = { cmd_nop, HD_CFA_OK },
[WIN_PACKETCMD] = { cmd_packet, CD_OK },
[WIN_PIDENTIFY] = { cmd_identify_packet, CD_OK },
[WIN_SMART] = { cmd_smart, HD_CFA_OK | SET_DSC },
@@ -1789,19 +1788,19 @@ static const struct {
[WIN_WRITEDMA] = { cmd_write_dma, HD_CFA_OK },
[WIN_WRITEDMA_ONCE] = { cmd_write_dma, HD_CFA_OK },
[CFA_WRITE_MULTI_WO_ERASE] = { cmd_write_multiple, CFA_OK },
- [WIN_STANDBYNOW1] = { cmd_nop, ALL_OK },
- [WIN_IDLEIMMEDIATE] = { cmd_nop, ALL_OK },
- [WIN_STANDBY] = { cmd_nop, ALL_OK },
- [WIN_SETIDLE1] = { cmd_nop, ALL_OK },
- [WIN_CHECKPOWERMODE1] = { cmd_check_power_mode, ALL_OK | SET_DSC },
- [WIN_SLEEPNOW1] = { cmd_nop, ALL_OK },
+ [WIN_STANDBYNOW1] = { cmd_nop, HD_CFA_OK },
+ [WIN_IDLEIMMEDIATE] = { cmd_nop, HD_CFA_OK },
+ [WIN_STANDBY] = { cmd_nop, HD_CFA_OK },
+ [WIN_SETIDLE1] = { cmd_nop, HD_CFA_OK },
+ [WIN_CHECKPOWERMODE1] = { cmd_check_power_mode, HD_CFA_OK | SET_DSC },
+ [WIN_SLEEPNOW1] = { cmd_nop, HD_CFA_OK },
[WIN_FLUSH_CACHE] = { cmd_flush_cache, ALL_OK },
[WIN_FLUSH_CACHE_EXT] = { cmd_flush_cache, HD_CFA_OK },
[WIN_IDENTIFY] = { cmd_identify, ALL_OK },
[WIN_SETFEATURES] = { cmd_set_features, ALL_OK | SET_DSC },
[IBM_SENSE_CONDITION] = { cmd_ibm_sense_condition, CFA_OK | SET_DSC },
[CFA_WEAR_LEVEL] = { cmd_cfa_erase_sectors, HD_CFA_OK | SET_DSC },
- [WIN_READ_NATIVE_MAX] = { cmd_read_native_max, ALL_OK | SET_DSC },
+ [WIN_READ_NATIVE_MAX] = { cmd_read_native_max, HD_CFA_OK | SET_DSC },
};
static bool ide_cmd_permitted(IDEState *s, uint32_t cmd)
@@ -2350,7 +2349,7 @@ static void ide_nop(IDEDMA *dma)
{
}
-static int32_t ide_nop_int32(IDEDMA *dma, int x)
+static int32_t ide_nop_int32(IDEDMA *dma, int32_t l)
{
return 0;
}
@@ -2395,6 +2394,13 @@ static void ide_restart_bh(void *opaque)
* called function can set a new error status. */
bus->error_status = 0;
+ /* The HBA has generically asked to be kicked on retry */
+ if (error_status & IDE_RETRY_HBA) {
+ if (s->bus->dma->ops->restart) {
+ s->bus->dma->ops->restart(s->bus->dma);
+ }
+ }
+
if (error_status & IDE_RETRY_DMA) {
if (error_status & IDE_RETRY_TRIM) {
ide_restart_dma(s, IDE_DMA_TRIM);
@@ -2460,8 +2466,8 @@ void ide_init2(IDEBus *bus, qemu_irq irq)
static const MemoryRegionPortio ide_portio_list[] = {
{ 0, 8, 1, .read = ide_ioport_read, .write = ide_ioport_write },
- { 0, 2, 2, .read = ide_data_readw, .write = ide_data_writew },
- { 0, 4, 4, .read = ide_data_readl, .write = ide_data_writel },
+ { 0, 1, 2, .read = ide_data_readw, .write = ide_data_writew },
+ { 0, 1, 4, .read = ide_data_readl, .write = ide_data_writel },
PORTIO_END_OF_LIST(),
};
@@ -2585,6 +2591,7 @@ static const VMStateDescription vmstate_ide_atapi_gesn_state = {
.name ="ide_drive/atapi/gesn_state",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = ide_atapi_gesn_needed,
.fields = (VMStateField[]) {
VMSTATE_BOOL(events.new_media, IDEState),
VMSTATE_BOOL(events.eject_request, IDEState),
@@ -2596,6 +2603,7 @@ static const VMStateDescription vmstate_ide_tray_state = {
.name = "ide_drive/tray_state",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = ide_tray_state_needed,
.fields = (VMStateField[]) {
VMSTATE_BOOL(tray_open, IDEState),
VMSTATE_BOOL(tray_locked, IDEState),
@@ -2609,6 +2617,7 @@ static const VMStateDescription vmstate_ide_drive_pio_state = {
.minimum_version_id = 1,
.pre_save = ide_drive_pio_pre_save,
.post_load = ide_drive_pio_post_load,
+ .needed = ide_drive_pio_state_needed,
.fields = (VMStateField[]) {
VMSTATE_INT32(req_nb_sectors, IDEState),
VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1,
@@ -2650,19 +2659,11 @@ const VMStateDescription vmstate_ide_drive = {
VMSTATE_UINT8_V(cdrom_changed, IDEState, 3),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection []) {
- {
- .vmsd = &vmstate_ide_drive_pio_state,
- .needed = ide_drive_pio_state_needed,
- }, {
- .vmsd = &vmstate_ide_tray_state,
- .needed = ide_tray_state_needed,
- }, {
- .vmsd = &vmstate_ide_atapi_gesn_state,
- .needed = ide_atapi_gesn_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_ide_drive_pio_state,
+ &vmstate_ide_tray_state,
+ &vmstate_ide_atapi_gesn_state,
+ NULL
}
};
@@ -2670,6 +2671,7 @@ static const VMStateDescription vmstate_ide_error_status = {
.name ="ide_bus/error",
.version_id = 2,
.minimum_version_id = 1,
+ .needed = ide_error_needed,
.fields = (VMStateField[]) {
VMSTATE_INT32(error_status, IDEBus),
VMSTATE_INT64_V(retry_sector_num, IDEBus, 2),
@@ -2688,13 +2690,9 @@ const VMStateDescription vmstate_ide_bus = {
VMSTATE_UINT8(unit, IDEBus),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection []) {
- {
- .vmsd = &vmstate_ide_error_status,
- .needed = ide_error_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_ide_error_status,
+ NULL
}
};
diff --git a/hw/ide/ich.c b/hw/ide/ich.c
index b1d887467..350c7f1c7 100644
--- a/hw/ide/ich.c
+++ b/hw/ide/ich.c
@@ -82,7 +82,6 @@
static const VMStateDescription vmstate_ich9_ahci = {
.name = "ich9_ahci",
- .unmigratable = 1, /* Still buggy under I/O load */
.version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(parent_obj, AHCIPCIState),
diff --git a/hw/ide/internal.h b/hw/ide/internal.h
index 965cc55cb..7288a677a 100644
--- a/hw/ide/internal.h
+++ b/hw/ide/internal.h
@@ -324,7 +324,7 @@ typedef void EndTransferFunc(IDEState *);
typedef void DMAStartFunc(IDEDMA *, IDEState *, BlockCompletionFunc *);
typedef void DMAVoidFunc(IDEDMA *);
typedef int DMAIntFunc(IDEDMA *, int);
-typedef int32_t DMAInt32Func(IDEDMA *, int);
+typedef int32_t DMAInt32Func(IDEDMA *, int32_t len);
typedef void DMAu32Func(IDEDMA *, uint32_t);
typedef void DMAStopFunc(IDEDMA *, bool);
typedef void DMARestartFunc(void *, int, RunState);
@@ -436,6 +436,7 @@ struct IDEDMAOps {
DMAInt32Func *prepare_buf;
DMAu32Func *commit_buf;
DMAIntFunc *rw_buf;
+ DMAVoidFunc *restart;
DMAVoidFunc *restart_dma;
DMAStopFunc *set_inactive;
DMAVoidFunc *cmd_done;
@@ -499,6 +500,7 @@ struct IDEDevice {
#define IDE_RETRY_READ 0x20
#define IDE_RETRY_FLUSH 0x40
#define IDE_RETRY_TRIM 0x80
+#define IDE_RETRY_HBA 0x100
static inline IDEState *idebus_active_if(IDEBus *bus)
{
@@ -534,6 +536,7 @@ int64_t ide_get_sector(IDEState *s);
void ide_set_sector(IDEState *s, int64_t sector_num);
void ide_start_dma(IDEState *s, BlockCompletionFunc *cb);
+void dma_buf_commit(IDEState *s, uint32_t tx_bytes);
void ide_dma_error(IDEState *s);
void ide_atapi_cmd_ok(IDEState *s);
diff --git a/hw/ide/macio.c b/hw/ide/macio.c
index a009674f4..66ac2baa9 100644
--- a/hw/ide/macio.c
+++ b/hw/ide/macio.c
@@ -51,128 +51,245 @@ static const int debug_macio = 0;
#define MACIO_PAGE_SIZE 4096
-static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
+/*
+ * Unaligned DMA read/write access functions required for OS X/Darwin which
+ * don't perform DMA transactions on sector boundaries. These functions are
+ * modelled on bdrv_co_do_preadv()/bdrv_co_do_pwritev() and so should be
+ * easy to remove if the unaligned block APIs are ever exposed.
+ */
+
+static void pmac_dma_read(BlockBackend *blk,
+ int64_t offset, unsigned int bytes,
+ void (*cb)(void *opaque, int ret), void *opaque)
{
DBDMA_io *io = opaque;
MACIOIDEState *m = io->opaque;
IDEState *s = idebus_active_if(&m->bus);
- int unaligned;
+ dma_addr_t dma_addr, dma_len;
+ void *mem;
+ int64_t sector_num;
+ int nsector;
+ uint64_t align = BDRV_SECTOR_SIZE;
+ size_t head_bytes, tail_bytes;
- if (ret < 0) {
- m->aiocb = NULL;
- qemu_sglist_destroy(&s->sg);
- ide_atapi_io_error(s, ret);
- io->remainder_len = 0;
- goto done;
- }
+ qemu_iovec_destroy(&io->iov);
+ qemu_iovec_init(&io->iov, io->len / MACIO_PAGE_SIZE + 1);
- if (!m->dma_active) {
- MACIO_DPRINTF("waiting for data (%#x - %#x - %x)\n",
- s->nsector, io->len, s->status);
- /* data not ready yet, wait for the channel to get restarted */
- io->processing = false;
- return;
+ sector_num = (offset >> 9);
+ nsector = (io->len >> 9);
+
+ MACIO_DPRINTF("--- DMA read transfer (0x%" HWADDR_PRIx ",0x%x): "
+ "sector_num: %" PRId64 ", nsector: %d\n", io->addr, io->len,
+ sector_num, nsector);
+
+ dma_addr = io->addr;
+ dma_len = io->len;
+ mem = dma_memory_map(&address_space_memory, dma_addr, &dma_len,
+ DMA_DIRECTION_FROM_DEVICE);
+
+ if (offset & (align - 1)) {
+ head_bytes = offset & (align - 1);
+
+ MACIO_DPRINTF("--- DMA unaligned head: sector %" PRId64 ", "
+ "discarding %zu bytes\n", sector_num, head_bytes);
+
+ qemu_iovec_add(&io->iov, &io->head_remainder, head_bytes);
+
+ bytes += offset & (align - 1);
+ offset = offset & ~(align - 1);
}
- MACIO_DPRINTF("io_buffer_size = %#x\n", s->io_buffer_size);
+ qemu_iovec_add(&io->iov, mem, io->len);
- if (s->io_buffer_size > 0) {
- m->aiocb = NULL;
- qemu_sglist_destroy(&s->sg);
+ if ((offset + bytes) & (align - 1)) {
+ tail_bytes = (offset + bytes) & (align - 1);
- s->packet_transfer_size -= s->io_buffer_size;
+ MACIO_DPRINTF("--- DMA unaligned tail: sector %" PRId64 ", "
+ "discarding bytes %zu\n", sector_num, tail_bytes);
- s->io_buffer_index += s->io_buffer_size;
- s->lba += s->io_buffer_index >> 11;
- s->io_buffer_index &= 0x7ff;
+ qemu_iovec_add(&io->iov, &io->tail_remainder, align - tail_bytes);
+ bytes = ROUND_UP(bytes, align);
}
- s->io_buffer_size = MIN(io->len, s->packet_transfer_size);
+ s->io_buffer_size -= io->len;
+ s->io_buffer_index += io->len;
- MACIO_DPRINTF("remainder: %d io->len: %d size: %d\n", io->remainder_len,
- io->len, s->packet_transfer_size);
- if (io->remainder_len && io->len) {
- /* guest wants the rest of its previous transfer */
- int remainder_len = MIN(io->remainder_len, io->len);
+ io->len = 0;
- MACIO_DPRINTF("copying remainder %d bytes\n", remainder_len);
+ MACIO_DPRINTF("--- Block read transfer - sector_num: %" PRIx64 " "
+ "nsector: %x\n", (offset >> 9), (bytes >> 9));
- cpu_physical_memory_write(io->addr, io->remainder + 0x200 -
- remainder_len, remainder_len);
+ m->aiocb = blk_aio_readv(blk, (offset >> 9), &io->iov, (bytes >> 9),
+ cb, io);
+}
- io->addr += remainder_len;
- io->len -= remainder_len;
- s->io_buffer_size = remainder_len;
- io->remainder_len -= remainder_len;
- /* treat remainder as individual transfer, start again */
- qemu_sglist_init(&s->sg, DEVICE(m), io->len / MACIO_PAGE_SIZE + 1,
- &address_space_memory);
- pmac_ide_atapi_transfer_cb(opaque, 0);
- return;
+static void pmac_dma_write(BlockBackend *blk,
+ int64_t offset, int bytes,
+ void (*cb)(void *opaque, int ret), void *opaque)
+{
+ DBDMA_io *io = opaque;
+ MACIOIDEState *m = io->opaque;
+ IDEState *s = idebus_active_if(&m->bus);
+ dma_addr_t dma_addr, dma_len;
+ void *mem;
+ int64_t sector_num;
+ int nsector;
+ uint64_t align = BDRV_SECTOR_SIZE;
+ size_t head_bytes, tail_bytes;
+ bool unaligned_head = false, unaligned_tail = false;
+
+ qemu_iovec_destroy(&io->iov);
+ qemu_iovec_init(&io->iov, io->len / MACIO_PAGE_SIZE + 1);
+
+ sector_num = (offset >> 9);
+ nsector = (io->len >> 9);
+
+ MACIO_DPRINTF("--- DMA write transfer (0x%" HWADDR_PRIx ",0x%x): "
+ "sector_num: %" PRId64 ", nsector: %d\n", io->addr, io->len,
+ sector_num, nsector);
+
+ dma_addr = io->addr;
+ dma_len = io->len;
+ mem = dma_memory_map(&address_space_memory, dma_addr, &dma_len,
+ DMA_DIRECTION_TO_DEVICE);
+
+ if (offset & (align - 1)) {
+ head_bytes = offset & (align - 1);
+ sector_num = ((offset & ~(align - 1)) >> 9);
+
+ MACIO_DPRINTF("--- DMA unaligned head: pre-reading head sector %"
+ PRId64 "\n", sector_num);
+
+ blk_pread(s->blk, (sector_num << 9), &io->head_remainder, align);
+
+ qemu_iovec_add(&io->iov, &io->head_remainder, head_bytes);
+ qemu_iovec_add(&io->iov, mem, io->len);
+
+ bytes += offset & (align - 1);
+ offset = offset & ~(align - 1);
+
+ unaligned_head = true;
}
- if (!s->packet_transfer_size) {
- MACIO_DPRINTF("end of transfer\n");
- ide_atapi_cmd_ok(s);
- m->dma_active = false;
+ if ((offset + bytes) & (align - 1)) {
+ tail_bytes = (offset + bytes) & (align - 1);
+ sector_num = (((offset + bytes) & ~(align - 1)) >> 9);
+
+ MACIO_DPRINTF("--- DMA unaligned tail: pre-reading tail sector %"
+ PRId64 "\n", sector_num);
+
+ blk_pread(s->blk, (sector_num << 9), &io->tail_remainder, align);
+
+ if (!unaligned_head) {
+ qemu_iovec_add(&io->iov, mem, io->len);
+ }
+
+ qemu_iovec_add(&io->iov, &io->tail_remainder + tail_bytes,
+ align - tail_bytes);
+
+ bytes = ROUND_UP(bytes, align);
+
+ unaligned_tail = true;
}
- if (io->len == 0) {
- MACIO_DPRINTF("end of DMA\n");
- goto done;
+ if (!unaligned_head && !unaligned_tail) {
+ qemu_iovec_add(&io->iov, mem, io->len);
}
- /* launch next transfer */
+ s->io_buffer_size -= io->len;
+ s->io_buffer_index += io->len;
- /* handle unaligned accesses first, get them over with and only do the
- remaining bulk transfer using our async DMA helpers */
- unaligned = io->len & 0x1ff;
- if (unaligned) {
- int sector_num = (s->lba << 2) + (s->io_buffer_index >> 9);
- int nsector = io->len >> 9;
+ io->len = 0;
- MACIO_DPRINTF("precopying unaligned %d bytes to %#" HWADDR_PRIx "\n",
- unaligned, io->addr + io->len - unaligned);
+ MACIO_DPRINTF("--- Block write transfer - sector_num: %" PRIx64 " "
+ "nsector: %x\n", (offset >> 9), (bytes >> 9));
- blk_read(s->blk, sector_num + nsector, io->remainder, 1);
- cpu_physical_memory_write(io->addr + io->len - unaligned,
- io->remainder, unaligned);
+ m->aiocb = blk_aio_writev(blk, (offset >> 9), &io->iov, (bytes >> 9),
+ cb, io);
+}
- io->len -= unaligned;
- }
+static void pmac_dma_trim(BlockBackend *blk,
+ int64_t offset, int bytes,
+ void (*cb)(void *opaque, int ret), void *opaque)
+{
+ DBDMA_io *io = opaque;
+ MACIOIDEState *m = io->opaque;
+ IDEState *s = idebus_active_if(&m->bus);
+ dma_addr_t dma_addr, dma_len;
+ void *mem;
+
+ qemu_iovec_destroy(&io->iov);
+ qemu_iovec_init(&io->iov, io->len / MACIO_PAGE_SIZE + 1);
+
+ dma_addr = io->addr;
+ dma_len = io->len;
+ mem = dma_memory_map(&address_space_memory, dma_addr, &dma_len,
+ DMA_DIRECTION_TO_DEVICE);
+
+ qemu_iovec_add(&io->iov, mem, io->len);
+ s->io_buffer_size -= io->len;
+ s->io_buffer_index += io->len;
+ io->len = 0;
+
+ m->aiocb = ide_issue_trim(blk, (offset >> 9), &io->iov, (bytes >> 9),
+ cb, io);
+}
+
+static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
+{
+ DBDMA_io *io = opaque;
+ MACIOIDEState *m = io->opaque;
+ IDEState *s = idebus_active_if(&m->bus);
+ int64_t offset;
- MACIO_DPRINTF("io->len = %#x\n", io->len);
+ MACIO_DPRINTF("pmac_ide_atapi_transfer_cb\n");
- qemu_sglist_init(&s->sg, DEVICE(m), io->len / MACIO_PAGE_SIZE + 1,
- &address_space_memory);
- qemu_sglist_add(&s->sg, io->addr, io->len);
- io->addr += s->io_buffer_size;
- io->remainder_len = MIN(s->packet_transfer_size - s->io_buffer_size,
- (0x200 - unaligned) & 0x1ff);
- MACIO_DPRINTF("set remainder to: %d\n", io->remainder_len);
+ if (ret < 0) {
+ MACIO_DPRINTF("DMA error: %d\n", ret);
+ ide_atapi_io_error(s, ret);
+ goto done;
+ }
- /* We would read no data from the block layer, thus not get a callback.
- Just fake completion manually. */
- if (!io->len) {
- pmac_ide_atapi_transfer_cb(opaque, 0);
+ if (!m->dma_active) {
+ MACIO_DPRINTF("waiting for data (%#x - %#x - %x)\n",
+ s->nsector, io->len, s->status);
+ /* data not ready yet, wait for the channel to get restarted */
+ io->processing = false;
return;
}
- io->len = 0;
+ if (s->io_buffer_size <= 0) {
+ MACIO_DPRINTF("End of IDE transfer\n");
+ ide_atapi_cmd_ok(s);
+ m->dma_active = false;
+ goto done;
+ }
- MACIO_DPRINTF("sector_num=%d size=%d, cmd_cmd=%d\n",
- (s->lba << 2) + (s->io_buffer_index >> 9),
- s->packet_transfer_size, s->dma_cmd);
+ if (io->len == 0) {
+ MACIO_DPRINTF("End of DMA transfer\n");
+ goto done;
+ }
- m->aiocb = dma_blk_read(s->blk, &s->sg,
- (int64_t)(s->lba << 2) + (s->io_buffer_index >> 9),
- pmac_ide_atapi_transfer_cb, io);
+ if (s->lba == -1) {
+ /* Non-block ATAPI transfer - just copy to RAM */
+ s->io_buffer_size = MIN(s->io_buffer_size, io->len);
+ cpu_physical_memory_write(io->addr, s->io_buffer, s->io_buffer_size);
+ ide_atapi_cmd_ok(s);
+ m->dma_active = false;
+ goto done;
+ }
+
+ /* Calculate current offset */
+ offset = (int64_t)(s->lba << 11) + s->io_buffer_index;
+
+ pmac_dma_read(s->blk, offset, io->len, pmac_ide_atapi_transfer_cb, io);
return;
done:
- MACIO_DPRINTF("done DMA\n");
block_acct_done(blk_get_stats(s->blk), &s->acct);
io->dma_end(opaque);
+
+ return;
}
static void pmac_ide_transfer_cb(void *opaque, int ret)
@@ -180,24 +297,17 @@ static void pmac_ide_transfer_cb(void *opaque, int ret)
DBDMA_io *io = opaque;
MACIOIDEState *m = io->opaque;
IDEState *s = idebus_active_if(&m->bus);
- int n = 0;
- int64_t sector_num;
- int unaligned;
+ int64_t offset;
+
+ MACIO_DPRINTF("pmac_ide_transfer_cb\n");
if (ret < 0) {
- MACIO_DPRINTF("DMA error\n");
+ MACIO_DPRINTF("DMA error: %d\n", ret);
m->aiocb = NULL;
- qemu_sglist_destroy(&s->sg);
ide_dma_error(s);
- io->remainder_len = 0;
goto done;
}
- if (--io->requests) {
- /* More requests still in flight */
- return;
- }
-
if (!m->dma_active) {
MACIO_DPRINTF("waiting for data (%#x - %#x - %x)\n",
s->nsector, io->len, s->status);
@@ -206,155 +316,41 @@ static void pmac_ide_transfer_cb(void *opaque, int ret)
return;
}
- sector_num = ide_get_sector(s);
- MACIO_DPRINTF("io_buffer_size = %#x\n", s->io_buffer_size);
- if (s->io_buffer_size > 0) {
- m->aiocb = NULL;
- qemu_sglist_destroy(&s->sg);
- n = (s->io_buffer_size + 0x1ff) >> 9;
- sector_num += n;
- ide_set_sector(s, sector_num);
- s->nsector -= n;
- }
-
- if (io->finish_remain_read) {
- /* Finish a stale read from the last iteration */
- io->finish_remain_read = false;
- cpu_physical_memory_write(io->finish_addr, io->remainder,
- io->finish_len);
- }
-
- MACIO_DPRINTF("remainder: %d io->len: %d nsector: %d "
- "sector_num: %" PRId64 "\n",
- io->remainder_len, io->len, s->nsector, sector_num);
- if (io->remainder_len && io->len) {
- /* guest wants the rest of its previous transfer */
- int remainder_len = MIN(io->remainder_len, io->len);
- uint8_t *p = &io->remainder[0x200 - remainder_len];
-
- MACIO_DPRINTF("copying remainder %d bytes at %#" HWADDR_PRIx "\n",
- remainder_len, io->addr);
-
- switch (s->dma_cmd) {
- case IDE_DMA_READ:
- cpu_physical_memory_write(io->addr, p, remainder_len);
- break;
- case IDE_DMA_WRITE:
- cpu_physical_memory_read(io->addr, p, remainder_len);
- break;
- case IDE_DMA_TRIM:
- break;
- }
- io->addr += remainder_len;
- io->len -= remainder_len;
- io->remainder_len -= remainder_len;
-
- if (s->dma_cmd == IDE_DMA_WRITE && !io->remainder_len) {
- io->requests++;
- qemu_iovec_reset(&io->iov);
- qemu_iovec_add(&io->iov, io->remainder, 0x200);
-
- m->aiocb = blk_aio_writev(s->blk, sector_num - 1, &io->iov, 1,
- pmac_ide_transfer_cb, io);
- }
- }
-
- if (s->nsector == 0 && !io->remainder_len) {
- MACIO_DPRINTF("end of transfer\n");
+ if (s->io_buffer_size <= 0) {
+ MACIO_DPRINTF("End of IDE transfer\n");
s->status = READY_STAT | SEEK_STAT;
ide_set_irq(s->bus);
m->dma_active = false;
+ goto done;
}
if (io->len == 0) {
- MACIO_DPRINTF("end of DMA\n");
+ MACIO_DPRINTF("End of DMA transfer\n");
goto done;
}
- /* launch next transfer */
-
- s->io_buffer_index = 0;
- s->io_buffer_size = MIN(io->len, s->nsector * 512);
-
- /* handle unaligned accesses first, get them over with and only do the
- remaining bulk transfer using our async DMA helpers */
- unaligned = io->len & 0x1ff;
- if (unaligned) {
- int nsector = io->len >> 9;
-
- MACIO_DPRINTF("precopying unaligned %d bytes to %#" HWADDR_PRIx "\n",
- unaligned, io->addr + io->len - unaligned);
-
- switch (s->dma_cmd) {
- case IDE_DMA_READ:
- io->requests++;
- io->finish_addr = io->addr + io->len - unaligned;
- io->finish_len = unaligned;
- io->finish_remain_read = true;
- qemu_iovec_reset(&io->iov);
- qemu_iovec_add(&io->iov, io->remainder, 0x200);
-
- m->aiocb = blk_aio_readv(s->blk, sector_num + nsector, &io->iov, 1,
- pmac_ide_transfer_cb, io);
- break;
- case IDE_DMA_WRITE:
- /* cache the contents in our io struct */
- cpu_physical_memory_read(io->addr + io->len - unaligned,
- io->remainder + io->remainder_len,
- unaligned);
- break;
- case IDE_DMA_TRIM:
- break;
- }
- }
-
- MACIO_DPRINTF("io->len = %#x\n", io->len);
-
- qemu_sglist_init(&s->sg, DEVICE(m), io->len / MACIO_PAGE_SIZE + 1,
- &address_space_memory);
- qemu_sglist_add(&s->sg, io->addr, io->len);
- io->addr += io->len + unaligned;
- io->remainder_len = (0x200 - unaligned) & 0x1ff;
- MACIO_DPRINTF("set remainder to: %d\n", io->remainder_len);
-
- /* Only subsector reads happening */
- if (!io->len) {
- if (!io->requests) {
- io->requests++;
- pmac_ide_transfer_cb(opaque, ret);
- }
- return;
- }
-
- io->len = 0;
-
- MACIO_DPRINTF("sector_num=%" PRId64 " n=%d, nsector=%d, cmd_cmd=%d\n",
- sector_num, n, s->nsector, s->dma_cmd);
+ /* Calculate number of sectors */
+ offset = (ide_get_sector(s) << 9) + s->io_buffer_index;
switch (s->dma_cmd) {
case IDE_DMA_READ:
- m->aiocb = dma_blk_read(s->blk, &s->sg, sector_num,
- pmac_ide_transfer_cb, io);
+ pmac_dma_read(s->blk, offset, io->len, pmac_ide_transfer_cb, io);
break;
case IDE_DMA_WRITE:
- m->aiocb = dma_blk_write(s->blk, &s->sg, sector_num,
- pmac_ide_transfer_cb, io);
+ pmac_dma_write(s->blk, offset, io->len, pmac_ide_transfer_cb, io);
break;
case IDE_DMA_TRIM:
- m->aiocb = dma_blk_io(s->blk, &s->sg, sector_num,
- ide_issue_trim, pmac_ide_transfer_cb, io,
- DMA_DIRECTION_TO_DEVICE);
+ pmac_dma_trim(s->blk, offset, io->len, pmac_ide_transfer_cb, io);
break;
}
- io->requests++;
return;
done:
if (s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) {
block_acct_done(blk_get_stats(s->blk), &s->acct);
}
- io->dma_end(io);
+ io->dma_end(opaque);
}
static void pmac_ide_transfer(DBDMA_io *io)
@@ -364,31 +360,10 @@ static void pmac_ide_transfer(DBDMA_io *io)
MACIO_DPRINTF("\n");
- s->io_buffer_size = 0;
if (s->drive_kind == IDE_CD) {
-
- /* Handle non-block ATAPI DMA transfers */
- if (s->lba == -1) {
- s->io_buffer_size = MIN(io->len, s->packet_transfer_size);
- block_acct_start(blk_get_stats(s->blk), &s->acct, s->io_buffer_size,
- BLOCK_ACCT_READ);
- MACIO_DPRINTF("non-block ATAPI DMA transfer size: %d\n",
- s->io_buffer_size);
-
- /* Copy ATAPI buffer directly to RAM and finish */
- cpu_physical_memory_write(io->addr, s->io_buffer,
- s->io_buffer_size);
- ide_atapi_cmd_ok(s);
- m->dma_active = false;
-
- MACIO_DPRINTF("end of non-block ATAPI DMA transfer\n");
- block_acct_done(blk_get_stats(s->blk), &s->acct);
- io->dma_end(io);
- return;
- }
-
block_acct_start(blk_get_stats(s->blk), &s->acct, io->len,
BLOCK_ACCT_READ);
+
pmac_ide_atapi_transfer_cb(io, 0);
return;
}
@@ -406,7 +381,6 @@ static void pmac_ide_transfer(DBDMA_io *io)
break;
}
- io->requests++;
pmac_ide_transfer_cb(io, 0);
}
@@ -553,7 +527,7 @@ static int ide_nop_int(IDEDMA *dma, int x)
return 0;
}
-static int32_t ide_nop_int32(IDEDMA *dma, int x)
+static int32_t ide_nop_int32(IDEDMA *dma, int32_t l)
{
return 0;
}
@@ -563,7 +537,19 @@ static void ide_dbdma_start(IDEDMA *dma, IDEState *s,
{
MACIOIDEState *m = container_of(dma, MACIOIDEState, dma);
- MACIO_DPRINTF("\n");
+ s->io_buffer_index = 0;
+ if (s->drive_kind == IDE_CD) {
+ s->io_buffer_size = s->packet_transfer_size;
+ } else {
+ s->io_buffer_size = s->nsector * BDRV_SECTOR_SIZE;
+ }
+
+ MACIO_DPRINTF("\n\n------------ IDE transfer\n");
+ MACIO_DPRINTF("buffer_size: %x buffer_index: %x\n",
+ s->io_buffer_size, s->io_buffer_index);
+ MACIO_DPRINTF("lba: %x size: %x\n", s->lba, s->io_buffer_size);
+ MACIO_DPRINTF("-------------------------\n");
+
m->dma_active = true;
DBDMA_kick(m->dbdma);
}
diff --git a/hw/ide/pci.c b/hw/ide/pci.c
index 1b3d1c12a..d31ff885b 100644
--- a/hw/ide/pci.c
+++ b/hw/ide/pci.c
@@ -53,10 +53,14 @@ static void bmdma_start_dma(IDEDMA *dma, IDEState *s,
}
/**
- * Return the number of bytes successfully prepared.
- * -1 on error.
+ * Prepare an sglist based on available PRDs.
+ * @limit: How many bytes to prepare total.
+ *
+ * Returns the number of bytes prepared, -1 on error.
+ * IDEState.io_buffer_size will contain the number of bytes described
+ * by the PRDs, whether or not we added them to the sglist.
*/
-static int32_t bmdma_prepare_buf(IDEDMA *dma, int is_write)
+static int32_t bmdma_prepare_buf(IDEDMA *dma, int32_t limit)
{
BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma);
IDEState *s = bmdma_active_if(bm);
@@ -75,7 +79,7 @@ static int32_t bmdma_prepare_buf(IDEDMA *dma, int is_write)
/* end of table (with a fail safe of one page) */
if (bm->cur_prd_last ||
(bm->cur_addr - bm->addr) >= BMDMA_PAGE_SIZE) {
- return s->io_buffer_size;
+ return s->sg.size;
}
pci_dma_read(pci_dev, bm->cur_addr, &prd, 8);
bm->cur_addr += 8;
@@ -90,7 +94,14 @@ static int32_t bmdma_prepare_buf(IDEDMA *dma, int is_write)
}
l = bm->cur_prd_len;
if (l > 0) {
- qemu_sglist_add(&s->sg, bm->cur_prd_addr, l);
+ uint64_t sg_len;
+
+ /* Don't add extra bytes to the SGList; consume any remaining
+ * PRDs from the guest, but ignore them. */
+ sg_len = MIN(limit - s->sg.size, bm->cur_prd_len);
+ if (sg_len) {
+ qemu_sglist_add(&s->sg, bm->cur_prd_addr, sg_len);
+ }
/* Note: We limit the max transfer to be 2GiB.
* This should accommodate the largest ATA transaction
@@ -350,6 +361,7 @@ static const VMStateDescription vmstate_bmdma_current = {
.name = "ide bmdma_current",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = ide_bmdma_current_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT32(cur_addr, BMDMAState),
VMSTATE_UINT32(cur_prd_last, BMDMAState),
@@ -363,6 +375,7 @@ static const VMStateDescription vmstate_bmdma_status = {
.name ="ide bmdma/status",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = ide_bmdma_status_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8(status, BMDMAState),
VMSTATE_END_OF_LIST()
@@ -383,16 +396,10 @@ static const VMStateDescription vmstate_bmdma = {
VMSTATE_UINT8(migration_retry_unit, BMDMAState),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection []) {
- {
- .vmsd = &vmstate_bmdma_current,
- .needed = ide_bmdma_current_needed,
- }, {
- .vmsd = &vmstate_bmdma_status,
- .needed = ide_bmdma_status_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_bmdma_current,
+ &vmstate_bmdma_status,
+ NULL
}
};
@@ -452,8 +459,6 @@ static const struct IDEDMAOps bmdma_ops = {
void bmdma_init(IDEBus *bus, BMDMAState *bm, PCIIDEState *d)
{
- qemu_irq *irq;
-
if (bus->dma == &bm->dma) {
return;
}
@@ -461,8 +466,7 @@ void bmdma_init(IDEBus *bus, BMDMAState *bm, PCIIDEState *d)
bm->dma.ops = &bmdma_ops;
bus->dma = &bm->dma;
bm->irq = bus->irq;
- irq = qemu_allocate_irqs(bmdma_irq, bm, 1);
- bus->irq = *irq;
+ bus->irq = qemu_allocate_irq(bmdma_irq, bm, 0);
bm->pci_dev = d;
}
diff --git a/hw/input/Makefile.objs b/hw/input/Makefile.objs
index e8c80b9de..624ba7ea4 100644
--- a/hw/input/Makefile.objs
+++ b/hw/input/Makefile.objs
@@ -8,6 +8,12 @@ common-obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o
common-obj-$(CONFIG_TSC2005) += tsc2005.o
common-obj-$(CONFIG_VMMOUSE) += vmmouse.o
+ifeq ($(CONFIG_LINUX),y)
+common-obj-$(CONFIG_VIRTIO) += virtio-input.o
+common-obj-$(CONFIG_VIRTIO) += virtio-input-hid.o
+common-obj-$(CONFIG_VIRTIO) += virtio-input-host.o
+endif
+
obj-$(CONFIG_MILKYMIST) += milkymist-softusb.o
obj-$(CONFIG_PXA2XX) += pxa2xx_keypad.o
obj-$(CONFIG_TSC210X) += tsc210x.o
diff --git a/hw/input/hid.c b/hw/input/hid.c
index 6841cb864..21ebd9e71 100644
--- a/hw/input/hid.c
+++ b/hw/input/hid.c
@@ -239,7 +239,7 @@ static void hid_keyboard_event(DeviceState *dev, QemuConsole *src,
static void hid_keyboard_process_keycode(HIDState *hs)
{
- uint8_t hid_code, key;
+ uint8_t hid_code, index, key;
int i, keycode, slot;
if (hs->n == 0) {
@@ -249,7 +249,8 @@ static void hid_keyboard_process_keycode(HIDState *hs)
keycode = hs->kbd.keycodes[slot];
key = keycode & 0x7f;
- hid_code = hid_usage_keys[key | ((hs->kbd.modifiers >> 1) & (1 << 7))];
+ index = key | ((hs->kbd.modifiers & (1 << 8)) >> 1);
+ hid_code = hid_usage_keys[index];
hs->kbd.modifiers &= ~(1 << 8);
switch (hid_code) {
@@ -257,18 +258,41 @@ static void hid_keyboard_process_keycode(HIDState *hs)
return;
case 0xe0:
+ assert(key == 0x1d);
if (hs->kbd.modifiers & (1 << 9)) {
- hs->kbd.modifiers ^= 3 << 8;
+ /* The hid_codes for the 0xe1/0x1d scancode sequence are 0xe9/0xe0.
+ * Here we're processing the second hid_code. By dropping bit 9
+ * and setting bit 8, the scancode after 0x1d will access the
+ * second half of the table.
+ */
+ hs->kbd.modifiers ^= (1 << 8) | (1 << 9);
return;
}
+ /* fall through to process Ctrl_L */
case 0xe1 ... 0xe7:
+ /* Ctrl_L/Ctrl_R, Shift_L/Shift_R, Alt_L/Alt_R, Win_L/Win_R.
+ * Handle releases here, or fall through to process presses.
+ */
if (keycode & (1 << 7)) {
hs->kbd.modifiers &= ~(1 << (hid_code & 0x0f));
return;
}
- case 0xe8 ... 0xef:
+ /* fall through */
+ case 0xe8 ... 0xe9:
+ /* USB modifiers are just 1 byte long. Bits 8 and 9 of
+ * hs->kbd.modifiers implement a state machine that detects the
+ * 0xe0 and 0xe1/0x1d sequences. These bits do not follow the
+ * usual rules where bit 7 marks released keys; they are cleared
+ * elsewhere in the function as the state machine dictates.
+ */
hs->kbd.modifiers |= 1 << (hid_code & 0x0f);
return;
+
+ case 0xea ... 0xef:
+ abort();
+
+ default:
+ break;
}
if (keycode & (1 << 7)) {
diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c
index 9b9a7d7a8..ddac69df6 100644
--- a/hw/input/pckbd.c
+++ b/hw/input/pckbd.c
@@ -391,23 +391,24 @@ static int kbd_outport_post_load(void *opaque, int version_id)
return 0;
}
+static bool kbd_outport_needed(void *opaque)
+{
+ KBDState *s = opaque;
+ return s->outport != kbd_outport_default(s);
+}
+
static const VMStateDescription vmstate_kbd_outport = {
.name = "pckbd_outport",
.version_id = 1,
.minimum_version_id = 1,
.post_load = kbd_outport_post_load,
+ .needed = kbd_outport_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8(outport, KBDState),
VMSTATE_END_OF_LIST()
}
};
-static bool kbd_outport_needed(void *opaque)
-{
- KBDState *s = opaque;
- return s->outport != kbd_outport_default(s);
-}
-
static int kbd_post_load(void *opaque, int version_id)
{
KBDState *s = opaque;
@@ -430,12 +431,9 @@ static const VMStateDescription vmstate_kbd = {
VMSTATE_UINT8(pending, KBDState),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_kbd_outport,
- .needed = kbd_outport_needed,
- },
- VMSTATE_END_OF_LIST()
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_kbd_outport,
+ NULL
}
};
diff --git a/hw/input/ps2.c b/hw/input/ps2.c
index 4baeea2b5..fdbe565e6 100644
--- a/hw/input/ps2.c
+++ b/hw/input/ps2.c
@@ -677,6 +677,7 @@ static const VMStateDescription vmstate_ps2_keyboard_ledstate = {
.version_id = 3,
.minimum_version_id = 2,
.post_load = ps2_kbd_ledstate_post_load,
+ .needed = ps2_keyboard_ledstate_needed,
.fields = (VMStateField[]) {
VMSTATE_INT32(ledstate, PS2KbdState),
VMSTATE_END_OF_LIST()
@@ -717,13 +718,9 @@ static const VMStateDescription vmstate_ps2_keyboard = {
VMSTATE_INT32_V(scancode_set, PS2KbdState,3),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection []) {
- {
- .vmsd = &vmstate_ps2_keyboard_ledstate,
- .needed = ps2_keyboard_ledstate_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_ps2_keyboard_ledstate,
+ NULL
}
};
diff --git a/hw/input/virtio-input-hid.c b/hw/input/virtio-input-hid.c
new file mode 100644
index 000000000..4d85dad4d
--- /dev/null
+++ b/hw/input/virtio-input-hid.c
@@ -0,0 +1,514 @@
+/*
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version. See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "qemu/iov.h"
+
+#include "hw/qdev.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-input.h"
+
+#undef CONFIG_CURSES
+#include "ui/console.h"
+
+#include "standard-headers/linux/input.h"
+
+#define VIRTIO_ID_NAME_KEYBOARD "QEMU Virtio Keyboard"
+#define VIRTIO_ID_NAME_MOUSE "QEMU Virtio Mouse"
+#define VIRTIO_ID_NAME_TABLET "QEMU Virtio Tablet"
+
+/* ----------------------------------------------------------------- */
+
+static const unsigned int keymap_qcode[Q_KEY_CODE_MAX] = {
+ [Q_KEY_CODE_ESC] = KEY_ESC,
+ [Q_KEY_CODE_1] = KEY_1,
+ [Q_KEY_CODE_2] = KEY_2,
+ [Q_KEY_CODE_3] = KEY_3,
+ [Q_KEY_CODE_4] = KEY_4,
+ [Q_KEY_CODE_5] = KEY_5,
+ [Q_KEY_CODE_6] = KEY_6,
+ [Q_KEY_CODE_7] = KEY_7,
+ [Q_KEY_CODE_8] = KEY_8,
+ [Q_KEY_CODE_9] = KEY_9,
+ [Q_KEY_CODE_0] = KEY_0,
+ [Q_KEY_CODE_MINUS] = KEY_MINUS,
+ [Q_KEY_CODE_EQUAL] = KEY_EQUAL,
+ [Q_KEY_CODE_BACKSPACE] = KEY_BACKSPACE,
+
+ [Q_KEY_CODE_TAB] = KEY_TAB,
+ [Q_KEY_CODE_Q] = KEY_Q,
+ [Q_KEY_CODE_W] = KEY_W,
+ [Q_KEY_CODE_E] = KEY_E,
+ [Q_KEY_CODE_R] = KEY_R,
+ [Q_KEY_CODE_T] = KEY_T,
+ [Q_KEY_CODE_Y] = KEY_Y,
+ [Q_KEY_CODE_U] = KEY_U,
+ [Q_KEY_CODE_I] = KEY_I,
+ [Q_KEY_CODE_O] = KEY_O,
+ [Q_KEY_CODE_P] = KEY_P,
+ [Q_KEY_CODE_BRACKET_LEFT] = KEY_LEFTBRACE,
+ [Q_KEY_CODE_BRACKET_RIGHT] = KEY_RIGHTBRACE,
+ [Q_KEY_CODE_RET] = KEY_ENTER,
+
+ [Q_KEY_CODE_CTRL] = KEY_LEFTCTRL,
+ [Q_KEY_CODE_A] = KEY_A,
+ [Q_KEY_CODE_S] = KEY_S,
+ [Q_KEY_CODE_D] = KEY_D,
+ [Q_KEY_CODE_F] = KEY_F,
+ [Q_KEY_CODE_G] = KEY_G,
+ [Q_KEY_CODE_H] = KEY_H,
+ [Q_KEY_CODE_J] = KEY_J,
+ [Q_KEY_CODE_K] = KEY_K,
+ [Q_KEY_CODE_L] = KEY_L,
+ [Q_KEY_CODE_SEMICOLON] = KEY_SEMICOLON,
+ [Q_KEY_CODE_APOSTROPHE] = KEY_APOSTROPHE,
+ [Q_KEY_CODE_GRAVE_ACCENT] = KEY_GRAVE,
+
+ [Q_KEY_CODE_SHIFT] = KEY_LEFTSHIFT,
+ [Q_KEY_CODE_BACKSLASH] = KEY_BACKSLASH,
+ [Q_KEY_CODE_LESS] = KEY_102ND,
+ [Q_KEY_CODE_Z] = KEY_Z,
+ [Q_KEY_CODE_X] = KEY_X,
+ [Q_KEY_CODE_C] = KEY_C,
+ [Q_KEY_CODE_V] = KEY_V,
+ [Q_KEY_CODE_B] = KEY_B,
+ [Q_KEY_CODE_N] = KEY_N,
+ [Q_KEY_CODE_M] = KEY_M,
+ [Q_KEY_CODE_COMMA] = KEY_COMMA,
+ [Q_KEY_CODE_DOT] = KEY_DOT,
+ [Q_KEY_CODE_SLASH] = KEY_SLASH,
+ [Q_KEY_CODE_SHIFT_R] = KEY_RIGHTSHIFT,
+
+ [Q_KEY_CODE_ALT] = KEY_LEFTALT,
+ [Q_KEY_CODE_SPC] = KEY_SPACE,
+ [Q_KEY_CODE_CAPS_LOCK] = KEY_CAPSLOCK,
+
+ [Q_KEY_CODE_F1] = KEY_F1,
+ [Q_KEY_CODE_F2] = KEY_F2,
+ [Q_KEY_CODE_F3] = KEY_F3,
+ [Q_KEY_CODE_F4] = KEY_F4,
+ [Q_KEY_CODE_F5] = KEY_F5,
+ [Q_KEY_CODE_F6] = KEY_F6,
+ [Q_KEY_CODE_F7] = KEY_F7,
+ [Q_KEY_CODE_F8] = KEY_F8,
+ [Q_KEY_CODE_F9] = KEY_F9,
+ [Q_KEY_CODE_F10] = KEY_F10,
+ [Q_KEY_CODE_NUM_LOCK] = KEY_NUMLOCK,
+ [Q_KEY_CODE_SCROLL_LOCK] = KEY_SCROLLLOCK,
+
+ [Q_KEY_CODE_KP_0] = KEY_KP0,
+ [Q_KEY_CODE_KP_1] = KEY_KP1,
+ [Q_KEY_CODE_KP_2] = KEY_KP2,
+ [Q_KEY_CODE_KP_3] = KEY_KP3,
+ [Q_KEY_CODE_KP_4] = KEY_KP4,
+ [Q_KEY_CODE_KP_5] = KEY_KP5,
+ [Q_KEY_CODE_KP_6] = KEY_KP6,
+ [Q_KEY_CODE_KP_7] = KEY_KP7,
+ [Q_KEY_CODE_KP_8] = KEY_KP8,
+ [Q_KEY_CODE_KP_9] = KEY_KP9,
+ [Q_KEY_CODE_KP_SUBTRACT] = KEY_KPMINUS,
+ [Q_KEY_CODE_KP_ADD] = KEY_KPPLUS,
+ [Q_KEY_CODE_KP_DECIMAL] = KEY_KPDOT,
+ [Q_KEY_CODE_KP_ENTER] = KEY_KPENTER,
+ [Q_KEY_CODE_KP_DIVIDE] = KEY_KPSLASH,
+ [Q_KEY_CODE_KP_MULTIPLY] = KEY_KPASTERISK,
+
+ [Q_KEY_CODE_F11] = KEY_F11,
+ [Q_KEY_CODE_F12] = KEY_F12,
+
+ [Q_KEY_CODE_CTRL_R] = KEY_RIGHTCTRL,
+ [Q_KEY_CODE_SYSRQ] = KEY_SYSRQ,
+ [Q_KEY_CODE_ALT_R] = KEY_RIGHTALT,
+
+ [Q_KEY_CODE_HOME] = KEY_HOME,
+ [Q_KEY_CODE_UP] = KEY_UP,
+ [Q_KEY_CODE_PGUP] = KEY_PAGEUP,
+ [Q_KEY_CODE_LEFT] = KEY_LEFT,
+ [Q_KEY_CODE_RIGHT] = KEY_RIGHT,
+ [Q_KEY_CODE_END] = KEY_END,
+ [Q_KEY_CODE_DOWN] = KEY_DOWN,
+ [Q_KEY_CODE_PGDN] = KEY_PAGEDOWN,
+ [Q_KEY_CODE_INSERT] = KEY_INSERT,
+ [Q_KEY_CODE_DELETE] = KEY_DELETE,
+
+ [Q_KEY_CODE_META_L] = KEY_LEFTMETA,
+ [Q_KEY_CODE_META_R] = KEY_RIGHTMETA,
+ [Q_KEY_CODE_MENU] = KEY_MENU,
+};
+
+static const unsigned int keymap_button[INPUT_BUTTON_MAX] = {
+ [INPUT_BUTTON_LEFT] = BTN_LEFT,
+ [INPUT_BUTTON_RIGHT] = BTN_RIGHT,
+ [INPUT_BUTTON_MIDDLE] = BTN_MIDDLE,
+ [INPUT_BUTTON_WHEEL_UP] = BTN_GEAR_UP,
+ [INPUT_BUTTON_WHEEL_DOWN] = BTN_GEAR_DOWN,
+};
+
+static const unsigned int axismap_rel[INPUT_AXIS_MAX] = {
+ [INPUT_AXIS_X] = REL_X,
+ [INPUT_AXIS_Y] = REL_Y,
+};
+
+static const unsigned int axismap_abs[INPUT_AXIS_MAX] = {
+ [INPUT_AXIS_X] = ABS_X,
+ [INPUT_AXIS_Y] = ABS_Y,
+};
+
+/* ----------------------------------------------------------------- */
+
+static void virtio_input_key_config(VirtIOInput *vinput,
+ const unsigned int *keymap,
+ size_t mapsize)
+{
+ virtio_input_config keys;
+ int i, bit, byte, bmax = 0;
+
+ memset(&keys, 0, sizeof(keys));
+ for (i = 0; i < mapsize; i++) {
+ bit = keymap[i];
+ if (!bit) {
+ continue;
+ }
+ byte = bit / 8;
+ bit = bit % 8;
+ keys.u.bitmap[byte] |= (1 << bit);
+ if (bmax < byte+1) {
+ bmax = byte+1;
+ }
+ }
+ keys.select = VIRTIO_INPUT_CFG_EV_BITS;
+ keys.subsel = EV_KEY;
+ keys.size = bmax;
+ virtio_input_add_config(vinput, &keys);
+}
+
+static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src,
+ InputEvent *evt)
+{
+ VirtIOInput *vinput = VIRTIO_INPUT(dev);
+ virtio_input_event event;
+ int qcode;
+
+ switch (evt->kind) {
+ case INPUT_EVENT_KIND_KEY:
+ qcode = qemu_input_key_value_to_qcode(evt->key->key);
+ if (qcode && keymap_qcode[qcode]) {
+ event.type = cpu_to_le16(EV_KEY);
+ event.code = cpu_to_le16(keymap_qcode[qcode]);
+ event.value = cpu_to_le32(evt->key->down ? 1 : 0);
+ virtio_input_send(vinput, &event);
+ } else {
+ if (evt->key->down) {
+ fprintf(stderr, "%s: unmapped key: %d [%s]\n", __func__,
+ qcode, QKeyCode_lookup[qcode]);
+ }
+ }
+ break;
+ case INPUT_EVENT_KIND_BTN:
+ if (keymap_button[evt->btn->button]) {
+ event.type = cpu_to_le16(EV_KEY);
+ event.code = cpu_to_le16(keymap_button[evt->btn->button]);
+ event.value = cpu_to_le32(evt->btn->down ? 1 : 0);
+ virtio_input_send(vinput, &event);
+ } else {
+ if (evt->btn->down) {
+ fprintf(stderr, "%s: unmapped button: %d [%s]\n", __func__,
+ evt->btn->button, InputButton_lookup[evt->btn->button]);
+ }
+ }
+ break;
+ case INPUT_EVENT_KIND_REL:
+ event.type = cpu_to_le16(EV_REL);
+ event.code = cpu_to_le16(axismap_rel[evt->rel->axis]);
+ event.value = cpu_to_le32(evt->rel->value);
+ virtio_input_send(vinput, &event);
+ break;
+ case INPUT_EVENT_KIND_ABS:
+ event.type = cpu_to_le16(EV_ABS);
+ event.code = cpu_to_le16(axismap_abs[evt->abs->axis]);
+ event.value = cpu_to_le32(evt->abs->value);
+ virtio_input_send(vinput, &event);
+ break;
+ default:
+ /* keep gcc happy */
+ break;
+ }
+}
+
+static void virtio_input_handle_sync(DeviceState *dev)
+{
+ VirtIOInput *vinput = VIRTIO_INPUT(dev);
+ virtio_input_event event = {
+ .type = cpu_to_le16(EV_SYN),
+ .code = cpu_to_le16(SYN_REPORT),
+ .value = 0,
+ };
+
+ virtio_input_send(vinput, &event);
+}
+
+static void virtio_input_hid_realize(DeviceState *dev, Error **errp)
+{
+ VirtIOInputHID *vhid = VIRTIO_INPUT_HID(dev);
+
+ vhid->hs = qemu_input_handler_register(dev, vhid->handler);
+ if (vhid->display && vhid->hs) {
+ qemu_input_handler_bind(vhid->hs, vhid->display, vhid->head, NULL);
+ }
+}
+
+static void virtio_input_hid_unrealize(DeviceState *dev, Error **errp)
+{
+ VirtIOInputHID *vhid = VIRTIO_INPUT_HID(dev);
+ qemu_input_handler_unregister(vhid->hs);
+}
+
+static void virtio_input_hid_change_active(VirtIOInput *vinput)
+{
+ VirtIOInputHID *vhid = VIRTIO_INPUT_HID(vinput);
+
+ if (vinput->active) {
+ qemu_input_handler_activate(vhid->hs);
+ } else {
+ qemu_input_handler_deactivate(vhid->hs);
+ }
+}
+
+static void virtio_input_hid_handle_status(VirtIOInput *vinput,
+ virtio_input_event *event)
+{
+ VirtIOInputHID *vhid = VIRTIO_INPUT_HID(vinput);
+ int ledbit = 0;
+
+ switch (le16_to_cpu(event->type)) {
+ case EV_LED:
+ if (event->code == LED_NUML) {
+ ledbit = QEMU_NUM_LOCK_LED;
+ } else if (event->code == LED_CAPSL) {
+ ledbit = QEMU_CAPS_LOCK_LED;
+ } else if (event->code == LED_SCROLLL) {
+ ledbit = QEMU_SCROLL_LOCK_LED;
+ }
+ if (event->value) {
+ vhid->ledstate |= ledbit;
+ } else {
+ vhid->ledstate &= ~ledbit;
+ }
+ kbd_put_ledstate(vhid->ledstate);
+ break;
+ default:
+ fprintf(stderr, "%s: unknown type %d\n", __func__,
+ le16_to_cpu(event->type));
+ break;
+ }
+}
+
+static Property virtio_input_hid_properties[] = {
+ DEFINE_PROP_STRING("display", VirtIOInputHID, display),
+ DEFINE_PROP_UINT32("head", VirtIOInputHID, head, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_input_hid_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtIOInputClass *vic = VIRTIO_INPUT_CLASS(klass);
+
+ dc->props = virtio_input_hid_properties;
+ vic->realize = virtio_input_hid_realize;
+ vic->unrealize = virtio_input_hid_unrealize;
+ vic->change_active = virtio_input_hid_change_active;
+ vic->handle_status = virtio_input_hid_handle_status;
+}
+
+static const TypeInfo virtio_input_hid_info = {
+ .name = TYPE_VIRTIO_INPUT_HID,
+ .parent = TYPE_VIRTIO_INPUT,
+ .instance_size = sizeof(VirtIOInputHID),
+ .class_init = virtio_input_hid_class_init,
+ .abstract = true,
+};
+
+/* ----------------------------------------------------------------- */
+
+static QemuInputHandler virtio_keyboard_handler = {
+ .name = VIRTIO_ID_NAME_KEYBOARD,
+ .mask = INPUT_EVENT_MASK_KEY,
+ .event = virtio_input_handle_event,
+ .sync = virtio_input_handle_sync,
+};
+
+static struct virtio_input_config virtio_keyboard_config[] = {
+ {
+ .select = VIRTIO_INPUT_CFG_ID_NAME,
+ .size = sizeof(VIRTIO_ID_NAME_KEYBOARD),
+ .u.string = VIRTIO_ID_NAME_KEYBOARD,
+ },{
+ .select = VIRTIO_INPUT_CFG_ID_DEVIDS,
+ .size = sizeof(struct virtio_input_devids),
+ .u.ids = {
+ .bustype = const_le16(BUS_VIRTUAL),
+ .vendor = const_le16(0x0627), /* same we use for usb hid devices */
+ .product = const_le16(0x0001),
+ .version = const_le16(0x0001),
+ },
+ },{
+ .select = VIRTIO_INPUT_CFG_EV_BITS,
+ .subsel = EV_REP,
+ .size = 1,
+ },{
+ .select = VIRTIO_INPUT_CFG_EV_BITS,
+ .subsel = EV_LED,
+ .size = 1,
+ .u.bitmap = {
+ (1 << LED_NUML) | (1 << LED_CAPSL) | (1 << LED_SCROLLL),
+ },
+ },
+ { /* end of list */ },
+};
+
+static void virtio_keyboard_init(Object *obj)
+{
+ VirtIOInputHID *vhid = VIRTIO_INPUT_HID(obj);
+ VirtIOInput *vinput = VIRTIO_INPUT(obj);
+
+ vhid->handler = &virtio_keyboard_handler;
+ virtio_input_init_config(vinput, virtio_keyboard_config);
+ virtio_input_key_config(vinput, keymap_qcode,
+ ARRAY_SIZE(keymap_qcode));
+}
+
+static const TypeInfo virtio_keyboard_info = {
+ .name = TYPE_VIRTIO_KEYBOARD,
+ .parent = TYPE_VIRTIO_INPUT_HID,
+ .instance_size = sizeof(VirtIOInputHID),
+ .instance_init = virtio_keyboard_init,
+};
+
+/* ----------------------------------------------------------------- */
+
+static QemuInputHandler virtio_mouse_handler = {
+ .name = VIRTIO_ID_NAME_MOUSE,
+ .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
+ .event = virtio_input_handle_event,
+ .sync = virtio_input_handle_sync,
+};
+
+static struct virtio_input_config virtio_mouse_config[] = {
+ {
+ .select = VIRTIO_INPUT_CFG_ID_NAME,
+ .size = sizeof(VIRTIO_ID_NAME_MOUSE),
+ .u.string = VIRTIO_ID_NAME_MOUSE,
+ },{
+ .select = VIRTIO_INPUT_CFG_ID_DEVIDS,
+ .size = sizeof(struct virtio_input_devids),
+ .u.ids = {
+ .bustype = const_le16(BUS_VIRTUAL),
+ .vendor = const_le16(0x0627), /* same we use for usb hid devices */
+ .product = const_le16(0x0002),
+ .version = const_le16(0x0001),
+ },
+ },{
+ .select = VIRTIO_INPUT_CFG_EV_BITS,
+ .subsel = EV_REL,
+ .size = 1,
+ .u.bitmap = {
+ (1 << REL_X) | (1 << REL_Y),
+ },
+ },
+ { /* end of list */ },
+};
+
+static void virtio_mouse_init(Object *obj)
+{
+ VirtIOInputHID *vhid = VIRTIO_INPUT_HID(obj);
+ VirtIOInput *vinput = VIRTIO_INPUT(obj);
+
+ vhid->handler = &virtio_mouse_handler;
+ virtio_input_init_config(vinput, virtio_mouse_config);
+ virtio_input_key_config(vinput, keymap_button,
+ ARRAY_SIZE(keymap_button));
+}
+
+static const TypeInfo virtio_mouse_info = {
+ .name = TYPE_VIRTIO_MOUSE,
+ .parent = TYPE_VIRTIO_INPUT_HID,
+ .instance_size = sizeof(VirtIOInputHID),
+ .instance_init = virtio_mouse_init,
+};
+
+/* ----------------------------------------------------------------- */
+
+static QemuInputHandler virtio_tablet_handler = {
+ .name = VIRTIO_ID_NAME_TABLET,
+ .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS,
+ .event = virtio_input_handle_event,
+ .sync = virtio_input_handle_sync,
+};
+
+static struct virtio_input_config virtio_tablet_config[] = {
+ {
+ .select = VIRTIO_INPUT_CFG_ID_NAME,
+ .size = sizeof(VIRTIO_ID_NAME_TABLET),
+ .u.string = VIRTIO_ID_NAME_TABLET,
+ },{
+ .select = VIRTIO_INPUT_CFG_ID_DEVIDS,
+ .size = sizeof(struct virtio_input_devids),
+ .u.ids = {
+ .bustype = const_le16(BUS_VIRTUAL),
+ .vendor = const_le16(0x0627), /* same we use for usb hid devices */
+ .product = const_le16(0x0003),
+ .version = const_le16(0x0001),
+ },
+ },{
+ .select = VIRTIO_INPUT_CFG_EV_BITS,
+ .subsel = EV_ABS,
+ .size = 1,
+ .u.bitmap = {
+ (1 << ABS_X) | (1 << ABS_Y),
+ },
+ },{
+ .select = VIRTIO_INPUT_CFG_ABS_INFO,
+ .subsel = ABS_X,
+ .size = sizeof(virtio_input_absinfo),
+ .u.abs.max = const_le32(INPUT_EVENT_ABS_SIZE),
+ },{
+ .select = VIRTIO_INPUT_CFG_ABS_INFO,
+ .subsel = ABS_Y,
+ .size = sizeof(virtio_input_absinfo),
+ .u.abs.max = const_le32(INPUT_EVENT_ABS_SIZE),
+ },
+ { /* end of list */ },
+};
+
+static void virtio_tablet_init(Object *obj)
+{
+ VirtIOInputHID *vhid = VIRTIO_INPUT_HID(obj);
+ VirtIOInput *vinput = VIRTIO_INPUT(obj);
+
+ vhid->handler = &virtio_tablet_handler;
+ virtio_input_init_config(vinput, virtio_tablet_config);
+ virtio_input_key_config(vinput, keymap_button,
+ ARRAY_SIZE(keymap_button));
+}
+
+static const TypeInfo virtio_tablet_info = {
+ .name = TYPE_VIRTIO_TABLET,
+ .parent = TYPE_VIRTIO_INPUT_HID,
+ .instance_size = sizeof(VirtIOInputHID),
+ .instance_init = virtio_tablet_init,
+};
+
+/* ----------------------------------------------------------------- */
+
+static void virtio_register_types(void)
+{
+ type_register_static(&virtio_input_hid_info);
+ type_register_static(&virtio_keyboard_info);
+ type_register_static(&virtio_mouse_info);
+ type_register_static(&virtio_tablet_info);
+}
+
+type_init(virtio_register_types)
diff --git a/hw/input/virtio-input-host.c b/hw/input/virtio-input-host.c
new file mode 100644
index 000000000..8978f16ba
--- /dev/null
+++ b/hw/input/virtio-input-host.c
@@ -0,0 +1,189 @@
+/*
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version. See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "qemu-common.h"
+#include "qemu/sockets.h"
+
+#include "hw/qdev.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-input.h"
+
+#include <sys/ioctl.h>
+#include "standard-headers/linux/input.h"
+
+/* ----------------------------------------------------------------- */
+
+static struct virtio_input_config virtio_input_host_config[] = {
+ { /* empty list */ },
+};
+
+static void virtio_input_host_event(void *opaque)
+{
+ VirtIOInputHost *vih = opaque;
+ VirtIOInput *vinput = VIRTIO_INPUT(vih);
+ struct virtio_input_event virtio;
+ struct input_event evdev;
+ int rc;
+
+ for (;;) {
+ rc = read(vih->fd, &evdev, sizeof(evdev));
+ if (rc != sizeof(evdev)) {
+ break;
+ }
+
+ virtio.type = cpu_to_le16(evdev.type);
+ virtio.code = cpu_to_le16(evdev.code);
+ virtio.value = cpu_to_le32(evdev.value);
+ virtio_input_send(vinput, &virtio);
+ }
+}
+
+static void virtio_input_bits_config(VirtIOInputHost *vih,
+ int type, int count)
+{
+ virtio_input_config bits;
+ int rc, i, size = 0;
+
+ memset(&bits, 0, sizeof(bits));
+ rc = ioctl(vih->fd, EVIOCGBIT(type, count/8), bits.u.bitmap);
+ if (rc < 0) {
+ return;
+ }
+
+ for (i = 0; i < count/8; i++) {
+ if (bits.u.bitmap[i]) {
+ size = i+1;
+ }
+ }
+ if (size == 0) {
+ return;
+ }
+
+ bits.select = VIRTIO_INPUT_CFG_EV_BITS;
+ bits.subsel = type;
+ bits.size = size;
+ virtio_input_add_config(VIRTIO_INPUT(vih), &bits);
+}
+
+static void virtio_input_host_realize(DeviceState *dev, Error **errp)
+{
+ VirtIOInputHost *vih = VIRTIO_INPUT_HOST(dev);
+ VirtIOInput *vinput = VIRTIO_INPUT(dev);
+ virtio_input_config id;
+ struct input_id ids;
+ int rc, ver;
+
+ if (!vih->evdev) {
+ error_setg(errp, "evdev property is required");
+ return;
+ }
+
+ vih->fd = open(vih->evdev, O_RDWR);
+ if (vih->fd < 0) {
+ error_setg_file_open(errp, errno, vih->evdev);
+ return;
+ }
+ qemu_set_nonblock(vih->fd);
+
+ rc = ioctl(vih->fd, EVIOCGVERSION, &ver);
+ if (rc < 0) {
+ error_setg(errp, "%s: is not an evdev device", vih->evdev);
+ goto err_close;
+ }
+
+ rc = ioctl(vih->fd, EVIOCGRAB, 1);
+ if (rc < 0) {
+ error_setg_errno(errp, errno, "%s: failed to get exclusive access",
+ vih->evdev);
+ goto err_close;
+ }
+
+ memset(&id, 0, sizeof(id));
+ ioctl(vih->fd, EVIOCGNAME(sizeof(id.u.string)-1), id.u.string);
+ id.select = VIRTIO_INPUT_CFG_ID_NAME;
+ id.size = strlen(id.u.string);
+ virtio_input_add_config(vinput, &id);
+
+ if (ioctl(vih->fd, EVIOCGID, &ids) == 0) {
+ memset(&id, 0, sizeof(id));
+ id.select = VIRTIO_INPUT_CFG_ID_DEVIDS;
+ id.size = sizeof(struct virtio_input_devids);
+ id.u.ids.bustype = cpu_to_le16(ids.bustype);
+ id.u.ids.vendor = cpu_to_le16(ids.vendor);
+ id.u.ids.product = cpu_to_le16(ids.product);
+ id.u.ids.version = cpu_to_le16(ids.version);
+ virtio_input_add_config(vinput, &id);
+ }
+
+ virtio_input_bits_config(vih, EV_KEY, KEY_CNT);
+ virtio_input_bits_config(vih, EV_REL, REL_CNT);
+ virtio_input_bits_config(vih, EV_ABS, ABS_CNT);
+ virtio_input_bits_config(vih, EV_MSC, MSC_CNT);
+ virtio_input_bits_config(vih, EV_SW, SW_CNT);
+
+ qemu_set_fd_handler(vih->fd, virtio_input_host_event, NULL, vih);
+ return;
+
+err_close:
+ close(vih->fd);
+ vih->fd = -1;
+ return;
+}
+
+static void virtio_input_host_unrealize(DeviceState *dev, Error **errp)
+{
+ VirtIOInputHost *vih = VIRTIO_INPUT_HOST(dev);
+
+ if (vih->fd > 0) {
+ qemu_set_fd_handler(vih->fd, NULL, NULL, NULL);
+ close(vih->fd);
+ }
+}
+
+static const VMStateDescription vmstate_virtio_input_host = {
+ .name = "virtio-input-host",
+ .unmigratable = 1,
+};
+
+static Property virtio_input_host_properties[] = {
+ DEFINE_PROP_STRING("evdev", VirtIOInputHost, evdev),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_input_host_class_init(ObjectClass *klass, void *data)
+{
+ VirtIOInputClass *vic = VIRTIO_INPUT_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &vmstate_virtio_input_host;
+ dc->props = virtio_input_host_properties;
+ vic->realize = virtio_input_host_realize;
+ vic->unrealize = virtio_input_host_unrealize;
+}
+
+static void virtio_input_host_init(Object *obj)
+{
+ VirtIOInput *vinput = VIRTIO_INPUT(obj);
+
+ virtio_input_init_config(vinput, virtio_input_host_config);
+}
+
+static const TypeInfo virtio_input_host_info = {
+ .name = TYPE_VIRTIO_INPUT_HOST,
+ .parent = TYPE_VIRTIO_INPUT,
+ .instance_size = sizeof(VirtIOInputHost),
+ .instance_init = virtio_input_host_init,
+ .class_init = virtio_input_host_class_init,
+};
+
+/* ----------------------------------------------------------------- */
+
+static void virtio_register_types(void)
+{
+ type_register_static(&virtio_input_host_info);
+}
+
+type_init(virtio_register_types)
diff --git a/hw/input/virtio-input.c b/hw/input/virtio-input.c
new file mode 100644
index 000000000..1f5a40de3
--- /dev/null
+++ b/hw/input/virtio-input.c
@@ -0,0 +1,293 @@
+/*
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version. See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "qemu/iov.h"
+
+#include "hw/qdev.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-input.h"
+
+#include "standard-headers/linux/input.h"
+
+/* ----------------------------------------------------------------- */
+
+void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event)
+{
+ VirtQueueElement elem;
+ unsigned have, need;
+ int i, len;
+
+ if (!vinput->active) {
+ return;
+ }
+
+ /* queue up events ... */
+ if (vinput->qindex == vinput->qsize) {
+ vinput->qsize++;
+ vinput->queue = realloc(vinput->queue, vinput->qsize *
+ sizeof(virtio_input_event));
+ }
+ vinput->queue[vinput->qindex++] = *event;
+
+ /* ... until we see a report sync ... */
+ if (event->type != cpu_to_le16(EV_SYN) ||
+ event->code != cpu_to_le16(SYN_REPORT)) {
+ return;
+ }
+
+ /* ... then check available space ... */
+ need = sizeof(virtio_input_event) * vinput->qindex;
+ virtqueue_get_avail_bytes(vinput->evt, &have, NULL, need, 0);
+ if (have < need) {
+ vinput->qindex = 0;
+ fprintf(stderr, "%s: ENOSPC in vq, dropping events\n", __func__);
+ return;
+ }
+
+ /* ... and finally pass them to the guest */
+ for (i = 0; i < vinput->qindex; i++) {
+ if (!virtqueue_pop(vinput->evt, &elem)) {
+ /* should not happen, we've checked for space beforehand */
+ fprintf(stderr, "%s: Huh? No vq elem available ...\n", __func__);
+ return;
+ }
+ len = iov_from_buf(elem.in_sg, elem.in_num,
+ 0, vinput->queue+i, sizeof(virtio_input_event));
+ virtqueue_push(vinput->evt, &elem, len);
+ }
+ virtio_notify(VIRTIO_DEVICE(vinput), vinput->evt);
+ vinput->qindex = 0;
+}
+
+static void virtio_input_handle_evt(VirtIODevice *vdev, VirtQueue *vq)
+{
+ /* nothing */
+}
+
+static void virtio_input_handle_sts(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev);
+ VirtIOInput *vinput = VIRTIO_INPUT(vdev);
+ virtio_input_event event;
+ VirtQueueElement elem;
+ int len;
+
+ while (virtqueue_pop(vinput->sts, &elem)) {
+ memset(&event, 0, sizeof(event));
+ len = iov_to_buf(elem.out_sg, elem.out_num,
+ 0, &event, sizeof(event));
+ if (vic->handle_status) {
+ vic->handle_status(vinput, &event);
+ }
+ virtqueue_push(vinput->sts, &elem, len);
+ }
+ virtio_notify(vdev, vinput->sts);
+}
+
+static virtio_input_config *virtio_input_find_config(VirtIOInput *vinput,
+ uint8_t select,
+ uint8_t subsel)
+{
+ VirtIOInputConfig *cfg;
+
+ QTAILQ_FOREACH(cfg, &vinput->cfg_list, node) {
+ if (select == cfg->config.select &&
+ subsel == cfg->config.subsel) {
+ return &cfg->config;
+ }
+ }
+ return NULL;
+}
+
+void virtio_input_add_config(VirtIOInput *vinput,
+ virtio_input_config *config)
+{
+ VirtIOInputConfig *cfg;
+
+ if (virtio_input_find_config(vinput, config->select, config->subsel)) {
+ /* should not happen */
+ fprintf(stderr, "%s: duplicate config: %d/%d\n",
+ __func__, config->select, config->subsel);
+ abort();
+ }
+
+ cfg = g_new0(VirtIOInputConfig, 1);
+ cfg->config = *config;
+ QTAILQ_INSERT_TAIL(&vinput->cfg_list, cfg, node);
+}
+
+void virtio_input_init_config(VirtIOInput *vinput,
+ virtio_input_config *config)
+{
+ int i = 0;
+
+ QTAILQ_INIT(&vinput->cfg_list);
+ while (config[i].select) {
+ virtio_input_add_config(vinput, config + i);
+ i++;
+ }
+}
+
+void virtio_input_idstr_config(VirtIOInput *vinput,
+ uint8_t select, const char *string)
+{
+ virtio_input_config id;
+
+ if (!string) {
+ return;
+ }
+ memset(&id, 0, sizeof(id));
+ id.select = select;
+ id.size = snprintf(id.u.string, sizeof(id.u.string), "%s", string);
+ virtio_input_add_config(vinput, &id);
+}
+
+static void virtio_input_get_config(VirtIODevice *vdev, uint8_t *config_data)
+{
+ VirtIOInput *vinput = VIRTIO_INPUT(vdev);
+ virtio_input_config *config;
+
+ config = virtio_input_find_config(vinput, vinput->cfg_select,
+ vinput->cfg_subsel);
+ if (config) {
+ memcpy(config_data, config, vinput->cfg_size);
+ } else {
+ memset(config_data, 0, vinput->cfg_size);
+ }
+}
+
+static void virtio_input_set_config(VirtIODevice *vdev,
+ const uint8_t *config_data)
+{
+ VirtIOInput *vinput = VIRTIO_INPUT(vdev);
+ virtio_input_config *config = (virtio_input_config *)config_data;
+
+ vinput->cfg_select = config->select;
+ vinput->cfg_subsel = config->subsel;
+ virtio_notify_config(vdev);
+}
+
+static uint64_t virtio_input_get_features(VirtIODevice *vdev, uint64_t f,
+ Error **errp)
+{
+ return f;
+}
+
+static void virtio_input_set_status(VirtIODevice *vdev, uint8_t val)
+{
+ VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev);
+ VirtIOInput *vinput = VIRTIO_INPUT(vdev);
+
+ if (val & VIRTIO_CONFIG_S_DRIVER_OK) {
+ if (!vinput->active) {
+ vinput->active = true;
+ if (vic->change_active) {
+ vic->change_active(vinput);
+ }
+ }
+ }
+}
+
+static void virtio_input_reset(VirtIODevice *vdev)
+{
+ VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev);
+ VirtIOInput *vinput = VIRTIO_INPUT(vdev);
+
+ if (vinput->active) {
+ vinput->active = false;
+ if (vic->change_active) {
+ vic->change_active(vinput);
+ }
+ }
+}
+
+static void virtio_input_device_realize(DeviceState *dev, Error **errp)
+{
+ VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIOInput *vinput = VIRTIO_INPUT(dev);
+ VirtIOInputConfig *cfg;
+ Error *local_err = NULL;
+
+ if (vic->realize) {
+ vic->realize(dev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
+
+ virtio_input_idstr_config(vinput, VIRTIO_INPUT_CFG_ID_SERIAL,
+ vinput->serial);
+
+ QTAILQ_FOREACH(cfg, &vinput->cfg_list, node) {
+ if (vinput->cfg_size < cfg->config.size) {
+ vinput->cfg_size = cfg->config.size;
+ }
+ }
+ vinput->cfg_size += 8;
+ assert(vinput->cfg_size <= sizeof(virtio_input_config));
+
+ virtio_init(vdev, "virtio-input", VIRTIO_ID_INPUT,
+ vinput->cfg_size);
+ vinput->evt = virtio_add_queue(vdev, 64, virtio_input_handle_evt);
+ vinput->sts = virtio_add_queue(vdev, 64, virtio_input_handle_sts);
+}
+
+static void virtio_input_device_unrealize(DeviceState *dev, Error **errp)
+{
+ VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ Error *local_err = NULL;
+
+ if (vic->unrealize) {
+ vic->unrealize(dev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
+ virtio_cleanup(vdev);
+}
+
+static Property virtio_input_properties[] = {
+ DEFINE_PROP_STRING("serial", VirtIOInput, serial),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_input_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+
+ dc->props = virtio_input_properties;
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
+ vdc->realize = virtio_input_device_realize;
+ vdc->unrealize = virtio_input_device_unrealize;
+ vdc->get_config = virtio_input_get_config;
+ vdc->set_config = virtio_input_set_config;
+ vdc->get_features = virtio_input_get_features;
+ vdc->set_status = virtio_input_set_status;
+ vdc->reset = virtio_input_reset;
+}
+
+static const TypeInfo virtio_input_info = {
+ .name = TYPE_VIRTIO_INPUT,
+ .parent = TYPE_VIRTIO_DEVICE,
+ .instance_size = sizeof(VirtIOInput),
+ .class_size = sizeof(VirtIOInputClass),
+ .class_init = virtio_input_class_init,
+ .abstract = true,
+};
+
+/* ----------------------------------------------------------------- */
+
+static void virtio_register_types(void)
+{
+ type_register_static(&virtio_input_info);
+}
+
+type_init(virtio_register_types)
diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
index 843864a3e..092d8a80a 100644
--- a/hw/intc/Makefile.objs
+++ b/hw/intc/Makefile.objs
@@ -11,6 +11,7 @@ common-obj-$(CONFIG_SLAVIO) += slavio_intctl.o
common-obj-$(CONFIG_IOAPIC) += ioapic_common.o
common-obj-$(CONFIG_ARM_GIC) += arm_gic_common.o
common-obj-$(CONFIG_ARM_GIC) += arm_gic.o
+common-obj-$(CONFIG_ARM_GIC) += arm_gicv2m.o
common-obj-$(CONFIG_OPENPIC) += openpic.o
obj-$(CONFIG_APIC) += apic.o apic_common.o
diff --git a/hw/intc/allwinner-a10-pic.c b/hw/intc/allwinner-a10-pic.c
index de820b972..eed7621f1 100644
--- a/hw/intc/allwinner-a10-pic.c
+++ b/hw/intc/allwinner-a10-pic.c
@@ -23,7 +23,7 @@
static void aw_a10_pic_update(AwA10PICState *s)
{
uint8_t i;
- int irq = 0, fiq = 0, pending;
+ int irq = 0, fiq = 0, zeroes;
s->vector = 0;
@@ -32,9 +32,9 @@ static void aw_a10_pic_update(AwA10PICState *s)
fiq |= s->select[i] & s->irq_pending[i] & ~s->mask[i];
if (!s->vector) {
- pending = ffs(s->irq_pending[i] & ~s->mask[i]);
- if (pending) {
- s->vector = (i * 32 + pending - 1) * 4;
+ zeroes = ctz32(s->irq_pending[i] & ~s->mask[i]);
+ if (zeroes != 32) {
+ s->vector = (i * 32 + zeroes) * 4;
}
}
}
diff --git a/hw/intc/apic.c b/hw/intc/apic.c
index 0f97b4792..77b639cce 100644
--- a/hw/intc/apic.c
+++ b/hw/intc/apic.c
@@ -370,13 +370,14 @@ static int apic_irq_pending(APICCommonState *s)
static void apic_update_irq(APICCommonState *s)
{
CPUState *cpu;
+ DeviceState *dev = (DeviceState *)s;
cpu = CPU(s->cpu);
if (!qemu_cpu_is_self(cpu)) {
cpu_interrupt(cpu, CPU_INTERRUPT_POLL);
} else if (apic_irq_pending(s) > 0) {
cpu_interrupt(cpu, CPU_INTERRUPT_HARD);
- } else if (!apic_accept_pic_intr(&s->busdev.qdev) || !pic_get_output(isa_pic)) {
+ } else if (!apic_accept_pic_intr(dev) || !pic_get_output(isa_pic)) {
cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD);
}
}
@@ -549,10 +550,12 @@ static void apic_deliver(DeviceState *dev, uint8_t dest, uint8_t dest_mode,
static bool apic_check_pic(APICCommonState *s)
{
- if (!apic_accept_pic_intr(&s->busdev.qdev) || !pic_get_output(isa_pic)) {
+ DeviceState *dev = (DeviceState *)s;
+
+ if (!apic_accept_pic_intr(dev) || !pic_get_output(isa_pic)) {
return false;
}
- apic_deliver_pic_intr(&s->busdev.qdev, 1);
+ apic_deliver_pic_intr(dev, 1);
return true;
}
diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c
index 042e960f4..0032b97c5 100644
--- a/hw/intc/apic_common.c
+++ b/hw/intc/apic_common.c
@@ -233,25 +233,15 @@ static void apic_reset_common(DeviceState *dev)
{
APICCommonState *s = APIC_COMMON(dev);
APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
- bool bsp;
+ uint32_t bsp;
- bsp = cpu_is_bsp(s->cpu);
- s->apicbase = APIC_DEFAULT_ADDRESS |
- (bsp ? MSR_IA32_APICBASE_BSP : 0) | MSR_IA32_APICBASE_ENABLE;
+ bsp = s->apicbase & MSR_IA32_APICBASE_BSP;
+ s->apicbase = APIC_DEFAULT_ADDRESS | bsp | MSR_IA32_APICBASE_ENABLE;
s->vapic_paddr = 0;
info->vapic_base_update(s);
apic_init_reset(dev);
-
- if (bsp) {
- /*
- * LINT0 delivery mode on CPU #0 is set to ExtInt at initialization
- * time typically by BIOS, so PIC interrupt can be delivered to the
- * processor when local APIC is enabled.
- */
- s->lvt[APIC_LVT_LINT0] = 0x700;
- }
}
/* This function is only used for old state version 1 and 2 */
@@ -379,6 +369,7 @@ static const VMStateDescription vmstate_apic_common_sipi = {
.name = "apic_sipi",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = apic_common_sipi_needed,
.fields = (VMStateField[]) {
VMSTATE_INT32(sipi_vector, APICCommonState),
VMSTATE_INT32(wait_for_sipi, APICCommonState),
@@ -418,12 +409,9 @@ static const VMStateDescription vmstate_apic_common = {
APICCommonState), /* open-coded timer state */
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_apic_common_sipi,
- .needed = apic_common_sipi_needed,
- },
- VMSTATE_END_OF_LIST()
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_apic_common_sipi,
+ NULL
}
};
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index a04c82283..454bfd7df 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -45,6 +45,14 @@ static inline int gic_get_current_cpu(GICState *s)
return 0;
}
+/* Return true if this GIC config has interrupt groups, which is
+ * true if we're a GICv2, or a GICv1 with the security extensions.
+ */
+static inline bool gic_has_groups(GICState *s)
+{
+ return s->revision == 2 || s->security_extn;
+}
+
/* TODO: Many places that call this routine could be optimized. */
/* Update interrupt status after enabled or pending bits have been changed. */
void gic_update(GICState *s)
@@ -52,16 +60,18 @@ void gic_update(GICState *s)
int best_irq;
int best_prio;
int irq;
- int level;
+ int irq_level, fiq_level;
int cpu;
int cm;
for (cpu = 0; cpu < NUM_CPU(s); cpu++) {
cm = 1 << cpu;
s->current_pending[cpu] = 1023;
- if (!s->enabled || !s->cpu_enabled[cpu]) {
+ if (!(s->ctlr & (GICD_CTLR_EN_GRP0 | GICD_CTLR_EN_GRP1))
+ || !(s->cpu_ctlr[cpu] & (GICC_CTLR_EN_GRP0 | GICC_CTLR_EN_GRP1))) {
qemu_irq_lower(s->parent_irq[cpu]);
- return;
+ qemu_irq_lower(s->parent_fiq[cpu]);
+ continue;
}
best_prio = 0x100;
best_irq = 1023;
@@ -74,15 +84,31 @@ void gic_update(GICState *s)
}
}
}
- level = 0;
+
+ irq_level = fiq_level = 0;
+
if (best_prio < s->priority_mask[cpu]) {
s->current_pending[cpu] = best_irq;
if (best_prio < s->running_priority[cpu]) {
- DPRINTF("Raised pending IRQ %d (cpu %d)\n", best_irq, cpu);
- level = 1;
+ int group = GIC_TEST_GROUP(best_irq, cm);
+
+ if (extract32(s->ctlr, group, 1) &&
+ extract32(s->cpu_ctlr[cpu], group, 1)) {
+ if (group == 0 && s->cpu_ctlr[cpu] & GICC_CTLR_FIQ_EN) {
+ DPRINTF("Raised pending FIQ %d (cpu %d)\n",
+ best_irq, cpu);
+ fiq_level = 1;
+ } else {
+ DPRINTF("Raised pending IRQ %d (cpu %d)\n",
+ best_irq, cpu);
+ irq_level = 1;
+ }
+ }
}
}
- qemu_set_irq(s->parent_irq[cpu], level);
+
+ qemu_set_irq(s->parent_irq[cpu], irq_level);
+ qemu_set_irq(s->parent_fiq[cpu], fiq_level);
}
}
@@ -167,6 +193,32 @@ static void gic_set_irq(void *opaque, int irq, int level)
gic_update(s);
}
+static uint16_t gic_get_current_pending_irq(GICState *s, int cpu,
+ MemTxAttrs attrs)
+{
+ uint16_t pending_irq = s->current_pending[cpu];
+
+ if (pending_irq < GIC_MAXIRQ && gic_has_groups(s)) {
+ int group = GIC_TEST_GROUP(pending_irq, (1 << cpu));
+ /* On a GIC without the security extensions, reading this register
+ * behaves in the same way as a secure access to a GIC with them.
+ */
+ bool secure = !s->security_extn || attrs.secure;
+
+ if (group == 0 && !secure) {
+ /* Group0 interrupts hidden from Non-secure access */
+ return 1023;
+ }
+ if (group == 1 && secure && !(s->cpu_ctlr[cpu] & GICC_CTLR_ACK_CTL)) {
+ /* Group1 interrupts only seen by Secure access if
+ * AckCtl bit set.
+ */
+ return 1022;
+ }
+ }
+ return pending_irq;
+}
+
static void gic_set_running_irq(GICState *s, int cpu, int irq)
{
s->running_irq[cpu] = irq;
@@ -178,14 +230,24 @@ static void gic_set_running_irq(GICState *s, int cpu, int irq)
gic_update(s);
}
-uint32_t gic_acknowledge_irq(GICState *s, int cpu)
+uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs)
{
int ret, irq, src;
int cm = 1 << cpu;
- irq = s->current_pending[cpu];
- if (irq == 1023
- || GIC_GET_PRIORITY(irq, cpu) >= s->running_priority[cpu]) {
- DPRINTF("ACK no pending IRQ\n");
+
+ /* gic_get_current_pending_irq() will return 1022 or 1023 appropriately
+ * for the case where this GIC supports grouping and the pending interrupt
+ * is in the wrong group.
+ */
+ irq = gic_get_current_pending_irq(s, cpu, attrs);;
+
+ if (irq >= GIC_MAXIRQ) {
+ DPRINTF("ACK, no pending interrupt or it is hidden: %d\n", irq);
+ return irq;
+ }
+
+ if (GIC_GET_PRIORITY(irq, cpu) >= s->running_priority[cpu]) {
+ DPRINTF("ACK, pending interrupt (%d) has insufficient priority\n", irq);
return 1023;
}
s->last_active[irq][cpu] = s->running_irq[cpu];
@@ -224,8 +286,16 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu)
return ret;
}
-void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val)
+void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val,
+ MemTxAttrs attrs)
{
+ if (s->security_extn && !attrs.secure) {
+ if (!GIC_TEST_GROUP(irq, (1 << cpu))) {
+ return; /* Ignore Non-secure access of Group0 IRQ */
+ }
+ val = 0x80 | (val >> 1); /* Non-secure view */
+ }
+
if (irq < GIC_INTERNAL) {
s->priority1[irq][cpu] = val;
} else {
@@ -233,7 +303,113 @@ void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val)
}
}
-void gic_complete_irq(GICState *s, int cpu, int irq)
+static uint32_t gic_get_priority(GICState *s, int cpu, int irq,
+ MemTxAttrs attrs)
+{
+ uint32_t prio = GIC_GET_PRIORITY(irq, cpu);
+
+ if (s->security_extn && !attrs.secure) {
+ if (!GIC_TEST_GROUP(irq, (1 << cpu))) {
+ return 0; /* Non-secure access cannot read priority of Group0 IRQ */
+ }
+ prio = (prio << 1) & 0xff; /* Non-secure view */
+ }
+ return prio;
+}
+
+static void gic_set_priority_mask(GICState *s, int cpu, uint8_t pmask,
+ MemTxAttrs attrs)
+{
+ if (s->security_extn && !attrs.secure) {
+ if (s->priority_mask[cpu] & 0x80) {
+ /* Priority Mask in upper half */
+ pmask = 0x80 | (pmask >> 1);
+ } else {
+ /* Non-secure write ignored if priority mask is in lower half */
+ return;
+ }
+ }
+ s->priority_mask[cpu] = pmask;
+}
+
+static uint32_t gic_get_priority_mask(GICState *s, int cpu, MemTxAttrs attrs)
+{
+ uint32_t pmask = s->priority_mask[cpu];
+
+ if (s->security_extn && !attrs.secure) {
+ if (pmask & 0x80) {
+ /* Priority Mask in upper half, return Non-secure view */
+ pmask = (pmask << 1) & 0xff;
+ } else {
+ /* Priority Mask in lower half, RAZ */
+ pmask = 0;
+ }
+ }
+ return pmask;
+}
+
+static uint32_t gic_get_cpu_control(GICState *s, int cpu, MemTxAttrs attrs)
+{
+ uint32_t ret = s->cpu_ctlr[cpu];
+
+ if (s->security_extn && !attrs.secure) {
+ /* Construct the NS banked view of GICC_CTLR from the correct
+ * bits of the S banked view. We don't need to move the bypass
+ * control bits because we don't implement that (IMPDEF) part
+ * of the GIC architecture.
+ */
+ ret = (ret & (GICC_CTLR_EN_GRP1 | GICC_CTLR_EOIMODE_NS)) >> 1;
+ }
+ return ret;
+}
+
+static void gic_set_cpu_control(GICState *s, int cpu, uint32_t value,
+ MemTxAttrs attrs)
+{
+ uint32_t mask;
+
+ if (s->security_extn && !attrs.secure) {
+ /* The NS view can only write certain bits in the register;
+ * the rest are unchanged
+ */
+ mask = GICC_CTLR_EN_GRP1;
+ if (s->revision == 2) {
+ mask |= GICC_CTLR_EOIMODE_NS;
+ }
+ s->cpu_ctlr[cpu] &= ~mask;
+ s->cpu_ctlr[cpu] |= (value << 1) & mask;
+ } else {
+ if (s->revision == 2) {
+ mask = s->security_extn ? GICC_CTLR_V2_S_MASK : GICC_CTLR_V2_MASK;
+ } else {
+ mask = s->security_extn ? GICC_CTLR_V1_S_MASK : GICC_CTLR_V1_MASK;
+ }
+ s->cpu_ctlr[cpu] = value & mask;
+ }
+ DPRINTF("CPU Interface %d: Group0 Interrupts %sabled, "
+ "Group1 Interrupts %sabled\n", cpu,
+ (s->cpu_ctlr[cpu] & GICC_CTLR_EN_GRP0) ? "En" : "Dis",
+ (s->cpu_ctlr[cpu] & GICC_CTLR_EN_GRP1) ? "En" : "Dis");
+}
+
+static uint8_t gic_get_running_priority(GICState *s, int cpu, MemTxAttrs attrs)
+{
+ if (s->security_extn && !attrs.secure) {
+ if (s->running_priority[cpu] & 0x80) {
+ /* Running priority in upper half of range: return the Non-secure
+ * view of the priority.
+ */
+ return s->running_priority[cpu] << 1;
+ } else {
+ /* Running priority in lower half of range: RAZ */
+ return 0;
+ }
+ } else {
+ return s->running_priority[cpu];
+ }
+}
+
+void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
{
int update = 0;
int cm = 1 << cpu;
@@ -263,6 +439,16 @@ void gic_complete_irq(GICState *s, int cpu, int irq)
}
}
+ if (s->security_extn && !attrs.secure && !GIC_TEST_GROUP(irq, cm)) {
+ DPRINTF("Non-secure EOI for Group0 interrupt %d ignored\n", irq);
+ return;
+ }
+
+ /* Secure EOI with GICC_CTLR.AckCtl == 0 when the IRQ is a Group 1
+ * interrupt is UNPREDICTABLE. We choose to handle it as if AckCtl == 1,
+ * i.e. go ahead and complete the irq anyway.
+ */
+
if (irq != s->running_irq[cpu]) {
/* Complete an IRQ that is not currently running. */
int tmp = s->running_irq[cpu];
@@ -282,7 +468,7 @@ void gic_complete_irq(GICState *s, int cpu, int irq)
}
}
-static uint32_t gic_dist_readb(void *opaque, hwaddr offset)
+static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs)
{
GICState *s = (GICState *)opaque;
uint32_t res;
@@ -295,15 +481,42 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset)
cpu = gic_get_current_cpu(s);
cm = 1 << cpu;
if (offset < 0x100) {
- if (offset == 0)
- return s->enabled;
+ if (offset == 0) { /* GICD_CTLR */
+ if (s->security_extn && !attrs.secure) {
+ /* The NS bank of this register is just an alias of the
+ * EnableGrp1 bit in the S bank version.
+ */
+ return extract32(s->ctlr, 1, 1);
+ } else {
+ return s->ctlr;
+ }
+ }
if (offset == 4)
- return ((s->num_irq / 32) - 1) | ((NUM_CPU(s) - 1) << 5);
+ /* Interrupt Controller Type Register */
+ return ((s->num_irq / 32) - 1)
+ | ((NUM_CPU(s) - 1) << 5)
+ | (s->security_extn << 10);
if (offset < 0x08)
return 0;
if (offset >= 0x80) {
- /* Interrupt Security , RAZ/WI */
- return 0;
+ /* Interrupt Group Registers: these RAZ/WI if this is an NS
+ * access to a GIC with the security extensions, or if the GIC
+ * doesn't have groups at all.
+ */
+ res = 0;
+ if (!(s->security_extn && !attrs.secure) && gic_has_groups(s)) {
+ /* Every byte offset holds 8 group status bits */
+ irq = (offset - 0x080) * 8 + GIC_BASE_IRQ;
+ if (irq >= s->num_irq) {
+ goto bad_reg;
+ }
+ for (i = 0; i < 8; i++) {
+ if (GIC_TEST_GROUP(irq + i, cm)) {
+ res |= (1 << i);
+ }
+ }
+ }
+ return res;
}
goto bad_reg;
} else if (offset < 0x200) {
@@ -354,7 +567,7 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset)
irq = (offset - 0x400) + GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
- res = GIC_GET_PRIORITY(irq, cpu);
+ res = gic_get_priority(s, cpu, irq, attrs);
} else if (offset < 0xc00) {
/* Interrupt CPU Target. */
if (s->num_cpu == 1 && s->revision != REV_11MPCORE) {
@@ -415,24 +628,30 @@ bad_reg:
return 0;
}
-static uint32_t gic_dist_readw(void *opaque, hwaddr offset)
-{
- uint32_t val;
- val = gic_dist_readb(opaque, offset);
- val |= gic_dist_readb(opaque, offset + 1) << 8;
- return val;
-}
-
-static uint32_t gic_dist_readl(void *opaque, hwaddr offset)
+static MemTxResult gic_dist_read(void *opaque, hwaddr offset, uint64_t *data,
+ unsigned size, MemTxAttrs attrs)
{
- uint32_t val;
- val = gic_dist_readw(opaque, offset);
- val |= gic_dist_readw(opaque, offset + 2) << 16;
- return val;
+ switch (size) {
+ case 1:
+ *data = gic_dist_readb(opaque, offset, attrs);
+ return MEMTX_OK;
+ case 2:
+ *data = gic_dist_readb(opaque, offset, attrs);
+ *data |= gic_dist_readb(opaque, offset + 1, attrs) << 8;
+ return MEMTX_OK;
+ case 4:
+ *data = gic_dist_readb(opaque, offset, attrs);
+ *data |= gic_dist_readb(opaque, offset + 1, attrs) << 8;
+ *data |= gic_dist_readb(opaque, offset + 2, attrs) << 16;
+ *data |= gic_dist_readb(opaque, offset + 3, attrs) << 24;
+ return MEMTX_OK;
+ default:
+ return MEMTX_ERROR;
+ }
}
static void gic_dist_writeb(void *opaque, hwaddr offset,
- uint32_t value)
+ uint32_t value, MemTxAttrs attrs)
{
GICState *s = (GICState *)opaque;
int irq;
@@ -442,12 +661,41 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
cpu = gic_get_current_cpu(s);
if (offset < 0x100) {
if (offset == 0) {
- s->enabled = (value & 1);
- DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis");
+ if (s->security_extn && !attrs.secure) {
+ /* NS version is just an alias of the S version's bit 1 */
+ s->ctlr = deposit32(s->ctlr, 1, 1, value);
+ } else if (gic_has_groups(s)) {
+ s->ctlr = value & (GICD_CTLR_EN_GRP0 | GICD_CTLR_EN_GRP1);
+ } else {
+ s->ctlr = value & GICD_CTLR_EN_GRP0;
+ }
+ DPRINTF("Distributor: Group0 %sabled; Group 1 %sabled\n",
+ s->ctlr & GICD_CTLR_EN_GRP0 ? "En" : "Dis",
+ s->ctlr & GICD_CTLR_EN_GRP1 ? "En" : "Dis");
} else if (offset < 4) {
/* ignored. */
} else if (offset >= 0x80) {
- /* Interrupt Security Registers, RAZ/WI */
+ /* Interrupt Group Registers: RAZ/WI for NS access to secure
+ * GIC, or for GICs without groups.
+ */
+ if (!(s->security_extn && !attrs.secure) && gic_has_groups(s)) {
+ /* Every byte offset holds 8 group status bits */
+ irq = (offset - 0x80) * 8 + GIC_BASE_IRQ;
+ if (irq >= s->num_irq) {
+ goto bad_reg;
+ }
+ for (i = 0; i < 8; i++) {
+ /* Group bits are banked for private interrupts */
+ int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
+ if (value & (1 << i)) {
+ /* Group1 (Non-secure) */
+ GIC_SET_GROUP(irq + i, cm);
+ } else {
+ /* Group0 (Secure) */
+ GIC_CLEAR_GROUP(irq + i, cm);
+ }
+ }
+ }
} else {
goto bad_reg;
}
@@ -537,7 +785,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
irq = (offset - 0x400) + GIC_BASE_IRQ;
if (irq >= s->num_irq)
goto bad_reg;
- gic_set_priority(s, cpu, irq, value);
+ gic_set_priority(s, cpu, irq, value, attrs);
} else if (offset < 0xc00) {
/* Interrupt CPU Target. RAZ/WI on uniprocessor GICs, with the
* annoying exception of the 11MPCore's GIC.
@@ -609,14 +857,14 @@ bad_reg:
}
static void gic_dist_writew(void *opaque, hwaddr offset,
- uint32_t value)
+ uint32_t value, MemTxAttrs attrs)
{
- gic_dist_writeb(opaque, offset, value & 0xff);
- gic_dist_writeb(opaque, offset + 1, value >> 8);
+ gic_dist_writeb(opaque, offset, value & 0xff, attrs);
+ gic_dist_writeb(opaque, offset + 1, value >> 8, attrs);
}
static void gic_dist_writel(void *opaque, hwaddr offset,
- uint32_t value)
+ uint32_t value, MemTxAttrs attrs)
{
GICState *s = (GICState *)opaque;
if (offset == 0xf00) {
@@ -652,63 +900,110 @@ static void gic_dist_writel(void *opaque, hwaddr offset,
gic_update(s);
return;
}
- gic_dist_writew(opaque, offset, value & 0xffff);
- gic_dist_writew(opaque, offset + 2, value >> 16);
+ gic_dist_writew(opaque, offset, value & 0xffff, attrs);
+ gic_dist_writew(opaque, offset + 2, value >> 16, attrs);
+}
+
+static MemTxResult gic_dist_write(void *opaque, hwaddr offset, uint64_t data,
+ unsigned size, MemTxAttrs attrs)
+{
+ switch (size) {
+ case 1:
+ gic_dist_writeb(opaque, offset, data, attrs);
+ return MEMTX_OK;
+ case 2:
+ gic_dist_writew(opaque, offset, data, attrs);
+ return MEMTX_OK;
+ case 4:
+ gic_dist_writel(opaque, offset, data, attrs);
+ return MEMTX_OK;
+ default:
+ return MEMTX_ERROR;
+ }
}
static const MemoryRegionOps gic_dist_ops = {
- .old_mmio = {
- .read = { gic_dist_readb, gic_dist_readw, gic_dist_readl, },
- .write = { gic_dist_writeb, gic_dist_writew, gic_dist_writel, },
- },
+ .read_with_attrs = gic_dist_read,
+ .write_with_attrs = gic_dist_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static uint32_t gic_cpu_read(GICState *s, int cpu, int offset)
+static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset,
+ uint64_t *data, MemTxAttrs attrs)
{
switch (offset) {
case 0x00: /* Control */
- return s->cpu_enabled[cpu];
+ *data = gic_get_cpu_control(s, cpu, attrs);
+ break;
case 0x04: /* Priority mask */
- return s->priority_mask[cpu];
+ *data = gic_get_priority_mask(s, cpu, attrs);
+ break;
case 0x08: /* Binary Point */
- return s->bpr[cpu];
+ if (s->security_extn && !attrs.secure) {
+ /* BPR is banked. Non-secure copy stored in ABPR. */
+ *data = s->abpr[cpu];
+ } else {
+ *data = s->bpr[cpu];
+ }
+ break;
case 0x0c: /* Acknowledge */
- return gic_acknowledge_irq(s, cpu);
+ *data = gic_acknowledge_irq(s, cpu, attrs);
+ break;
case 0x14: /* Running Priority */
- return s->running_priority[cpu];
+ *data = gic_get_running_priority(s, cpu, attrs);
+ break;
case 0x18: /* Highest Pending Interrupt */
- return s->current_pending[cpu];
+ *data = gic_get_current_pending_irq(s, cpu, attrs);
+ break;
case 0x1c: /* Aliased Binary Point */
- return s->abpr[cpu];
+ /* GIC v2, no security: ABPR
+ * GIC v1, no security: not implemented (RAZ/WI)
+ * With security extensions, secure access: ABPR (alias of NS BPR)
+ * With security extensions, nonsecure access: RAZ/WI
+ */
+ if (!gic_has_groups(s) || (s->security_extn && !attrs.secure)) {
+ *data = 0;
+ } else {
+ *data = s->abpr[cpu];
+ }
+ break;
case 0xd0: case 0xd4: case 0xd8: case 0xdc:
- return s->apr[(offset - 0xd0) / 4][cpu];
+ *data = s->apr[(offset - 0xd0) / 4][cpu];
+ break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"gic_cpu_read: Bad offset %x\n", (int)offset);
- return 0;
+ return MEMTX_ERROR;
}
+ return MEMTX_OK;
}
-static void gic_cpu_write(GICState *s, int cpu, int offset, uint32_t value)
+static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset,
+ uint32_t value, MemTxAttrs attrs)
{
switch (offset) {
case 0x00: /* Control */
- s->cpu_enabled[cpu] = (value & 1);
- DPRINTF("CPU %d %sabled\n", cpu, s->cpu_enabled[cpu] ? "En" : "Dis");
+ gic_set_cpu_control(s, cpu, value, attrs);
break;
case 0x04: /* Priority mask */
- s->priority_mask[cpu] = (value & 0xff);
+ gic_set_priority_mask(s, cpu, value, attrs);
break;
case 0x08: /* Binary Point */
- s->bpr[cpu] = (value & 0x7);
+ if (s->security_extn && !attrs.secure) {
+ s->abpr[cpu] = MAX(value & 0x7, GIC_MIN_ABPR);
+ } else {
+ s->bpr[cpu] = MAX(value & 0x7, GIC_MIN_BPR);
+ }
break;
case 0x10: /* End Of Interrupt */
- gic_complete_irq(s, cpu, value & 0x3ff);
- return;
+ gic_complete_irq(s, cpu, value & 0x3ff, attrs);
+ return MEMTX_OK;
case 0x1c: /* Aliased Binary Point */
- if (s->revision >= 2) {
- s->abpr[cpu] = (value & 0x7);
+ if (!gic_has_groups(s) || (s->security_extn && !attrs.secure)) {
+ /* unimplemented, or NS access: RAZ/WI */
+ return MEMTX_OK;
+ } else {
+ s->abpr[cpu] = MAX(value & 0x7, GIC_MIN_ABPR);
}
break;
case 0xd0: case 0xd4: case 0xd8: case 0xdc:
@@ -717,56 +1012,59 @@ static void gic_cpu_write(GICState *s, int cpu, int offset, uint32_t value)
default:
qemu_log_mask(LOG_GUEST_ERROR,
"gic_cpu_write: Bad offset %x\n", (int)offset);
- return;
+ return MEMTX_ERROR;
}
gic_update(s);
+ return MEMTX_OK;
}
/* Wrappers to read/write the GIC CPU interface for the current CPU */
-static uint64_t gic_thiscpu_read(void *opaque, hwaddr addr,
- unsigned size)
+static MemTxResult gic_thiscpu_read(void *opaque, hwaddr addr, uint64_t *data,
+ unsigned size, MemTxAttrs attrs)
{
GICState *s = (GICState *)opaque;
- return gic_cpu_read(s, gic_get_current_cpu(s), addr);
+ return gic_cpu_read(s, gic_get_current_cpu(s), addr, data, attrs);
}
-static void gic_thiscpu_write(void *opaque, hwaddr addr,
- uint64_t value, unsigned size)
+static MemTxResult gic_thiscpu_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size,
+ MemTxAttrs attrs)
{
GICState *s = (GICState *)opaque;
- gic_cpu_write(s, gic_get_current_cpu(s), addr, value);
+ return gic_cpu_write(s, gic_get_current_cpu(s), addr, value, attrs);
}
/* Wrappers to read/write the GIC CPU interface for a specific CPU.
* These just decode the opaque pointer into GICState* + cpu id.
*/
-static uint64_t gic_do_cpu_read(void *opaque, hwaddr addr,
- unsigned size)
+static MemTxResult gic_do_cpu_read(void *opaque, hwaddr addr, uint64_t *data,
+ unsigned size, MemTxAttrs attrs)
{
GICState **backref = (GICState **)opaque;
GICState *s = *backref;
int id = (backref - s->backref);
- return gic_cpu_read(s, id, addr);
+ return gic_cpu_read(s, id, addr, data, attrs);
}
-static void gic_do_cpu_write(void *opaque, hwaddr addr,
- uint64_t value, unsigned size)
+static MemTxResult gic_do_cpu_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size,
+ MemTxAttrs attrs)
{
GICState **backref = (GICState **)opaque;
GICState *s = *backref;
int id = (backref - s->backref);
- gic_cpu_write(s, id, addr, value);
+ return gic_cpu_write(s, id, addr, value, attrs);
}
static const MemoryRegionOps gic_thiscpu_ops = {
- .read = gic_thiscpu_read,
- .write = gic_thiscpu_write,
+ .read_with_attrs = gic_thiscpu_read,
+ .write_with_attrs = gic_thiscpu_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static const MemoryRegionOps gic_cpu_ops = {
- .read = gic_do_cpu_read,
- .write = gic_do_cpu_write,
+ .read_with_attrs = gic_do_cpu_read,
+ .write_with_attrs = gic_do_cpu_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
@@ -790,6 +1088,9 @@ void gic_init_irqs_and_distributor(GICState *s)
for (i = 0; i < NUM_CPU(s); i++) {
sysbus_init_irq(sbd, &s->parent_irq[i]);
}
+ for (i = 0; i < NUM_CPU(s); i++) {
+ sysbus_init_irq(sbd, &s->parent_fiq[i]);
+ }
memory_region_init_io(&s->iomem, OBJECT(s), &gic_dist_ops, s,
"gic_dist", 0x1000);
}
diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c
index 18b01ba0c..a64d0714e 100644
--- a/hw/intc/arm_gic_common.c
+++ b/hw/intc/arm_gic_common.c
@@ -52,19 +52,20 @@ static const VMStateDescription vmstate_gic_irq_state = {
VMSTATE_UINT8(level, gic_irq_state),
VMSTATE_BOOL(model, gic_irq_state),
VMSTATE_BOOL(edge_trigger, gic_irq_state),
+ VMSTATE_UINT8(group, gic_irq_state),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_gic = {
.name = "arm_gic",
- .version_id = 7,
- .minimum_version_id = 7,
+ .version_id = 10,
+ .minimum_version_id = 10,
.pre_save = gic_pre_save,
.post_load = gic_post_load,
.fields = (VMStateField[]) {
- VMSTATE_BOOL(enabled, GICState),
- VMSTATE_BOOL_ARRAY(cpu_enabled, GICState, GIC_NCPU),
+ VMSTATE_UINT32(ctlr, GICState),
+ VMSTATE_UINT32_ARRAY(cpu_ctlr, GICState, GIC_NCPU),
VMSTATE_STRUCT_ARRAY(irq_state, GICState, GIC_MAXIRQ, 1,
vmstate_gic_irq_state, gic_irq_state),
VMSTATE_UINT8_ARRAY(irq_target, GICState, GIC_MAXIRQ),
@@ -110,12 +111,19 @@ static void arm_gic_common_realize(DeviceState *dev, Error **errp)
num_irq);
return;
}
+
+ if (s->security_extn &&
+ (s->revision == REV_11MPCORE || s->revision == REV_NVIC)) {
+ error_setg(errp, "this GIC revision does not implement "
+ "the security extensions");
+ return;
+ }
}
static void arm_gic_common_reset(DeviceState *dev)
{
GICState *s = ARM_GIC_COMMON(dev);
- int i;
+ int i, j;
memset(s->irq_state, 0, GIC_MAXIRQ * sizeof(gic_irq_state));
for (i = 0 ; i < s->num_cpu; i++) {
if (s->revision == REV_11MPCORE) {
@@ -126,19 +134,34 @@ static void arm_gic_common_reset(DeviceState *dev)
s->current_pending[i] = 1023;
s->running_irq[i] = 1023;
s->running_priority[i] = 0x100;
- s->cpu_enabled[i] = false;
+ s->cpu_ctlr[i] = 0;
+ s->bpr[i] = GIC_MIN_BPR;
+ s->abpr[i] = GIC_MIN_ABPR;
+ for (j = 0; j < GIC_INTERNAL; j++) {
+ s->priority1[j][i] = 0;
+ }
+ for (j = 0; j < GIC_NR_SGIS; j++) {
+ s->sgi_pending[j][i] = 0;
+ }
}
for (i = 0; i < GIC_NR_SGIS; i++) {
GIC_SET_ENABLED(i, ALL_CPU_MASK);
GIC_SET_EDGE_TRIGGER(i);
}
- if (s->num_cpu == 1) {
+
+ for (i = 0; i < ARRAY_SIZE(s->priority2); i++) {
+ s->priority2[i] = 0;
+ }
+
+ for (i = 0; i < GIC_MAXIRQ; i++) {
/* For uniprocessor GICs all interrupts always target the sole CPU */
- for (i = 0; i < GIC_MAXIRQ; i++) {
+ if (s->num_cpu == 1) {
s->irq_target[i] = 1;
+ } else {
+ s->irq_target[i] = 0;
}
}
- s->enabled = false;
+ s->ctlr = 0;
}
static Property arm_gic_common_properties[] = {
@@ -149,6 +172,8 @@ static Property arm_gic_common_properties[] = {
* (Internally, 0xffffffff also indicates "not a GIC but an NVIC".)
*/
DEFINE_PROP_UINT32("revision", GICState, revision, 1),
+ /* True if the GIC should implement the security extensions */
+ DEFINE_PROP_BOOL("has-security-extensions", GICState, security_extn, 0),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c
index e1952ad97..f56bff1af 100644
--- a/hw/intc/arm_gic_kvm.c
+++ b/hw/intc/arm_gic_kvm.c
@@ -176,6 +176,20 @@ static void translate_clear(GICState *s, int irq, int cpu,
}
}
+static void translate_group(GICState *s, int irq, int cpu,
+ uint32_t *field, bool to_kernel)
+{
+ int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
+
+ if (to_kernel) {
+ *field = GIC_TEST_GROUP(irq, cm);
+ } else {
+ if (*field & 1) {
+ GIC_SET_GROUP(irq, cm);
+ }
+ }
+}
+
static void translate_enabled(GICState *s, int irq, int cpu,
uint32_t *field, bool to_kernel)
{
@@ -237,7 +251,7 @@ static void translate_priority(GICState *s, int irq, int cpu,
if (to_kernel) {
*field = GIC_GET_PRIORITY(irq, cpu) & 0xff;
} else {
- gic_set_priority(s, cpu, irq, *field & 0xff);
+ gic_set_priority(s, cpu, irq, *field & 0xff, MEMTXATTRS_UNSPECIFIED);
}
}
@@ -339,8 +353,8 @@ static void kvm_arm_gic_put(GICState *s)
* Distributor State
*/
- /* s->enabled -> GICD_CTLR */
- reg = s->enabled;
+ /* s->ctlr -> GICD_CTLR */
+ reg = s->ctlr;
kvm_gicd_access(s, 0x0, 0, &reg, true);
/* Sanity checking on GICD_TYPER and s->num_irq, s->num_cpu */
@@ -365,6 +379,9 @@ static void kvm_arm_gic_put(GICState *s)
kvm_dist_put(s, 0x180, 1, s->num_irq, translate_clear);
kvm_dist_put(s, 0x100, 1, s->num_irq, translate_enabled);
+ /* irq_state[n].group -> GICD_IGROUPRn */
+ kvm_dist_put(s, 0x80, 1, s->num_irq, translate_group);
+
/* s->irq_target[irq] -> GICD_ITARGETSRn
* (restore targets before pending to ensure the pending state is set on
* the appropriate CPU interfaces in the kernel) */
@@ -397,8 +414,8 @@ static void kvm_arm_gic_put(GICState *s)
*/
for (cpu = 0; cpu < s->num_cpu; cpu++) {
- /* s->cpu_enabled[cpu] -> GICC_CTLR */
- reg = s->cpu_enabled[cpu];
+ /* s->cpu_ctlr[cpu] -> GICC_CTLR */
+ reg = s->cpu_ctlr[cpu];
kvm_gicc_access(s, 0x00, cpu, &reg, true);
/* s->priority_mask[cpu] -> GICC_PMR */
@@ -436,9 +453,9 @@ static void kvm_arm_gic_get(GICState *s)
* Distributor State
*/
- /* GICD_CTLR -> s->enabled */
+ /* GICD_CTLR -> s->ctlr */
kvm_gicd_access(s, 0x0, 0, &reg, false);
- s->enabled = reg & 1;
+ s->ctlr = reg;
/* Sanity checking on GICD_TYPER -> s->num_irq, s->num_cpu */
kvm_gicd_access(s, 0x4, 0, &reg, false);
@@ -454,21 +471,14 @@ static void kvm_arm_gic_get(GICState *s)
/* GICD_IIDR -> ? */
kvm_gicd_access(s, 0x8, 0, &reg, false);
- /* Verify no GROUP 1 interrupts configured in the kernel */
- for_each_irq_reg(i, s->num_irq, 1) {
- kvm_gicd_access(s, 0x80 + (i * 4), 0, &reg, false);
- if (reg != 0) {
- fprintf(stderr, "Unsupported GICD_IGROUPRn value: %08x\n",
- reg);
- abort();
- }
- }
-
/* Clear all the IRQ settings */
for (i = 0; i < s->num_irq; i++) {
memset(&s->irq_state[i], 0, sizeof(s->irq_state[0]));
}
+ /* GICD_IGROUPRn -> irq_state[n].group */
+ kvm_dist_get(s, 0x80, 1, s->num_irq, translate_group);
+
/* GICD_ISENABLERn -> irq_state[n].enabled */
kvm_dist_get(s, 0x100, 1, s->num_irq, translate_enabled);
@@ -496,9 +506,9 @@ static void kvm_arm_gic_get(GICState *s)
*/
for (cpu = 0; cpu < s->num_cpu; cpu++) {
- /* GICC_CTLR -> s->cpu_enabled[cpu] */
+ /* GICC_CTLR -> s->cpu_ctlr[cpu] */
kvm_gicc_access(s, 0x00, cpu, &reg, false);
- s->cpu_enabled[cpu] = (reg & 1);
+ s->cpu_ctlr[cpu] = reg;
/* GICC_PMR -> s->priority_mask[cpu] */
kvm_gicc_access(s, 0x04, cpu, &reg, false);
@@ -544,6 +554,12 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
return;
}
+ if (s->security_extn) {
+ error_setg(errp, "the in-kernel VGIC does not implement the "
+ "security extensions");
+ return;
+ }
+
i = s->num_irq - GIC_INTERNAL;
/* For the GIC, also expose incoming GPIO lines for PPIs for each CPU.
* GPIO array layout is thus:
@@ -554,12 +570,21 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
*/
i += (GIC_INTERNAL * s->num_cpu);
qdev_init_gpio_in(dev, kvm_arm_gic_set_irq, i);
- /* We never use our outbound IRQ lines but provide them so that
+
+ for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) {
+ qemu_irq irq = qdev_get_gpio_in(dev, i);
+ kvm_irqchip_set_qemuirq_gsi(kvm_state, irq, i);
+ }
+
+ /* We never use our outbound IRQ/FIQ lines but provide them so that
* we maintain the same interface as the non-KVM GIC.
*/
for (i = 0; i < s->num_cpu; i++) {
sysbus_init_irq(sbd, &s->parent_irq[i]);
}
+ for (i = 0; i < s->num_cpu; i++) {
+ sysbus_init_irq(sbd, &s->parent_fiq[i]);
+ }
/* Try to create the device via the device control API */
s->dev_fd = -1;
diff --git a/hw/intc/arm_gicv2m.c b/hw/intc/arm_gicv2m.c
new file mode 100644
index 000000000..43d1976c4
--- /dev/null
+++ b/hw/intc/arm_gicv2m.c
@@ -0,0 +1,192 @@
+/*
+ * GICv2m extension for MSI/MSI-x support with a GICv2-based system
+ *
+ * Copyright (C) 2015 Linaro, All rights reserved.
+ *
+ * Author: Christoffer Dall <christoffer.dall@linaro.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements an emulated GICv2m widget as described in the ARM
+ * Server Base System Architecture (SBSA) specification Version 2.2
+ * (ARM-DEN-0029 v2.2) pages 35-39 without any optional implementation defined
+ * identification registers and with a single non-secure MSI register frame.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/pci/msi.h"
+
+#define TYPE_ARM_GICV2M "arm-gicv2m"
+#define ARM_GICV2M(obj) OBJECT_CHECK(ARMGICv2mState, (obj), TYPE_ARM_GICV2M)
+
+#define GICV2M_NUM_SPI_MAX 128
+
+#define V2M_MSI_TYPER 0x008
+#define V2M_MSI_SETSPI_NS 0x040
+#define V2M_MSI_IIDR 0xFCC
+#define V2M_IIDR0 0xFD0
+#define V2M_IIDR11 0xFFC
+
+#define PRODUCT_ID_QEMU 0x51 /* ASCII code Q */
+
+typedef struct ARMGICv2mState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion iomem;
+ qemu_irq spi[GICV2M_NUM_SPI_MAX];
+
+ uint32_t base_spi;
+ uint32_t num_spi;
+} ARMGICv2mState;
+
+static void gicv2m_set_irq(void *opaque, int irq)
+{
+ ARMGICv2mState *s = (ARMGICv2mState *)opaque;
+
+ qemu_irq_pulse(s->spi[irq]);
+}
+
+static uint64_t gicv2m_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ ARMGICv2mState *s = (ARMGICv2mState *)opaque;
+ uint32_t val;
+
+ if (size != 4) {
+ qemu_log_mask(LOG_GUEST_ERROR, "gicv2m_read: bad size %u\n", size);
+ return 0;
+ }
+
+ switch (offset) {
+ case V2M_MSI_TYPER:
+ val = (s->base_spi + 32) << 16;
+ val |= s->num_spi;
+ return val;
+ case V2M_MSI_IIDR:
+ /* We don't have any valid implementor so we leave that field as zero
+ * and we return 0 in the arch revision as per the spec.
+ */
+ return (PRODUCT_ID_QEMU << 20);
+ case V2M_IIDR0 ... V2M_IIDR11:
+ /* We do not implement any optional identification registers and the
+ * mandatory MSI_PIDR2 register reads as 0x0, so we capture all
+ * implementation defined registers here.
+ */
+ return 0;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "gicv2m_read: Bad offset %x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void gicv2m_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ ARMGICv2mState *s = (ARMGICv2mState *)opaque;
+
+ if (size != 2 && size != 4) {
+ qemu_log_mask(LOG_GUEST_ERROR, "gicv2m_write: bad size %u\n", size);
+ return;
+ }
+
+ switch (offset) {
+ case V2M_MSI_SETSPI_NS: {
+ int spi;
+
+ spi = (value & 0x3ff) - (s->base_spi + 32);
+ if (spi >= 0 && spi < s->num_spi) {
+ gicv2m_set_irq(s, spi);
+ }
+ return;
+ }
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "gicv2m_write: Bad offset %x\n", (int)offset);
+ }
+}
+
+static const MemoryRegionOps gicv2m_ops = {
+ .read = gicv2m_read,
+ .write = gicv2m_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void gicv2m_realize(DeviceState *dev, Error **errp)
+{
+ ARMGICv2mState *s = ARM_GICV2M(dev);
+ int i;
+
+ if (s->num_spi > GICV2M_NUM_SPI_MAX) {
+ error_setg(errp,
+ "requested %u SPIs exceeds GICv2m frame maximum %d",
+ s->num_spi, GICV2M_NUM_SPI_MAX);
+ return;
+ }
+
+ if (s->base_spi + 32 > 1020 - s->num_spi) {
+ error_setg(errp,
+ "requested base SPI %u+%u exceeds max. number 1020",
+ s->base_spi + 32, s->num_spi);
+ return;
+ }
+
+ for (i = 0; i < s->num_spi; i++) {
+ sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->spi[i]);
+ }
+
+ msi_supported = true;
+ kvm_gsi_direct_mapping = true;
+ kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled();
+}
+
+static void gicv2m_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ ARMGICv2mState *s = ARM_GICV2M(obj);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &gicv2m_ops, s,
+ "gicv2m", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static Property gicv2m_properties[] = {
+ DEFINE_PROP_UINT32("base-spi", ARMGICv2mState, base_spi, 0),
+ DEFINE_PROP_UINT32("num-spi", ARMGICv2mState, num_spi, 64),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void gicv2m_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->props = gicv2m_properties;
+ dc->realize = gicv2m_realize;
+}
+
+static const TypeInfo gicv2m_info = {
+ .name = TYPE_ARM_GICV2M,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(ARMGICv2mState),
+ .instance_init = gicv2m_init,
+ .class_init = gicv2m_class_init,
+};
+
+static void gicv2m_register_types(void)
+{
+ type_register_static(&gicv2m_info);
+}
+
+type_init(gicv2m_register_types)
diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c
index 6ff6c7f0c..e13b729e1 100644
--- a/hw/intc/armv7m_nvic.c
+++ b/hw/intc/armv7m_nvic.c
@@ -77,6 +77,15 @@ static inline int64_t systick_scale(nvic_state *s)
static void systick_reload(nvic_state *s, int reset)
{
+ /* The Cortex-M3 Devices Generic User Guide says that "When the
+ * ENABLE bit is set to 1, the counter loads the RELOAD value from the
+ * SYST RVR register and then counts down". So, we need to check the
+ * ENABLE bit before reloading the value.
+ */
+ if ((s->systick.control & SYSTICK_ENABLE) == 0) {
+ return;
+ }
+
if (reset)
s->systick.tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
s->systick.tick += (s->systick.reload + 1) * systick_scale(s);
@@ -122,7 +131,7 @@ int armv7m_nvic_acknowledge_irq(void *opaque)
nvic_state *s = (nvic_state *)opaque;
uint32_t irq;
- irq = gic_acknowledge_irq(&s->gic, 0);
+ irq = gic_acknowledge_irq(&s->gic, 0, MEMTXATTRS_UNSPECIFIED);
if (irq == 1023)
hw_error("Interrupt but no vector\n");
if (irq >= 32)
@@ -135,7 +144,7 @@ void armv7m_nvic_complete_irq(void *opaque, int irq)
nvic_state *s = (nvic_state *)opaque;
if (irq >= 16)
irq += 16;
- gic_complete_irq(&s->gic, 0, irq);
+ gic_complete_irq(&s->gic, 0, irq, MEMTXATTRS_UNSPECIFIED);
}
static uint32_t nvic_readl(nvic_state *s, uint32_t offset)
@@ -465,10 +474,10 @@ static void armv7m_nvic_reset(DeviceState *dev)
* as enabled by default, and with a priority mask which allows
* all interrupts through.
*/
- s->gic.cpu_enabled[0] = true;
+ s->gic.cpu_ctlr[0] = GICC_CTLR_EN_GRP0;
s->gic.priority_mask[0] = 0x100;
/* The NVIC as a whole is always enabled. */
- s->gic.enabled = true;
+ s->gic.ctlr = 1;
systick_reset(s);
}
diff --git a/hw/intc/exynos4210_gic.c b/hw/intc/exynos4210_gic.c
index 0590d5dfb..b2a4950bc 100644
--- a/hw/intc/exynos4210_gic.c
+++ b/hw/intc/exynos4210_gic.c
@@ -213,9 +213,6 @@ void exynos4210_init_board_irqs(Exynos4210Irq *s)
uint32_t grp, bit, irq_id, n;
for (n = 0; n < EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ; n++) {
- s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
- s->ext_combiner_irq[n]);
-
irq_id = 0;
if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 4) ||
n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4)) {
@@ -230,8 +227,10 @@ void exynos4210_init_board_irqs(Exynos4210Irq *s)
if (irq_id) {
s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
s->ext_gic_irq[irq_id-32]);
+ } else {
+ s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
+ s->ext_combiner_irq[n]);
}
-
}
for (; n < EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; n++) {
/* these IDs are passed to Internal Combiner and External GIC */
diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h
index e87ef36d0..20c1e8a24 100644
--- a/hw/intc/gic_internal.h
+++ b/hw/intc/gic_internal.h
@@ -50,17 +50,40 @@
s->priority1[irq][cpu] : \
s->priority2[(irq) - GIC_INTERNAL])
#define GIC_TARGET(irq) s->irq_target[irq]
+#define GIC_CLEAR_GROUP(irq, cm) (s->irq_state[irq].group &= ~(cm))
+#define GIC_SET_GROUP(irq, cm) (s->irq_state[irq].group |= (cm))
+#define GIC_TEST_GROUP(irq, cm) ((s->irq_state[irq].group & (cm)) != 0)
+
+#define GICD_CTLR_EN_GRP0 (1U << 0)
+#define GICD_CTLR_EN_GRP1 (1U << 1)
+
+#define GICC_CTLR_EN_GRP0 (1U << 0)
+#define GICC_CTLR_EN_GRP1 (1U << 1)
+#define GICC_CTLR_ACK_CTL (1U << 2)
+#define GICC_CTLR_FIQ_EN (1U << 3)
+#define GICC_CTLR_CBPR (1U << 4) /* GICv1: SBPR */
+#define GICC_CTLR_EOIMODE (1U << 9)
+#define GICC_CTLR_EOIMODE_NS (1U << 10)
+
+/* Valid bits for GICC_CTLR for GICv1, v1 with security extensions,
+ * GICv2 and GICv2 with security extensions:
+ */
+#define GICC_CTLR_V1_MASK 0x1
+#define GICC_CTLR_V1_S_MASK 0x1f
+#define GICC_CTLR_V2_MASK 0x21f
+#define GICC_CTLR_V2_S_MASK 0x61f
/* The special cases for the revision property: */
#define REV_11MPCORE 0
#define REV_NVIC 0xffffffff
void gic_set_pending_private(GICState *s, int cpu, int irq);
-uint32_t gic_acknowledge_irq(GICState *s, int cpu);
-void gic_complete_irq(GICState *s, int cpu, int irq);
+uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs);
+void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs);
void gic_update(GICState *s);
void gic_init_irqs_and_distributor(GICState *s);
-void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val);
+void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val,
+ MemTxAttrs attrs);
static inline bool gic_test_pending(GICState *s, int irq, int cm)
{
diff --git a/hw/intc/omap_intc.c b/hw/intc/omap_intc.c
index ad3931c11..e9b38a3c6 100644
--- a/hw/intc/omap_intc.c
+++ b/hw/intc/omap_intc.c
@@ -60,7 +60,7 @@ struct omap_intr_handler_s {
static void omap_inth_sir_update(struct omap_intr_handler_s *s, int is_fiq)
{
- int i, j, sir_intr, p_intr, p, f;
+ int i, j, sir_intr, p_intr, p;
uint32_t level;
sir_intr = 0;
p_intr = 255;
@@ -72,14 +72,15 @@ static void omap_inth_sir_update(struct omap_intr_handler_s *s, int is_fiq)
for (j = 0; j < s->nbanks; ++j) {
level = s->bank[j].irqs & ~s->bank[j].mask &
(is_fiq ? s->bank[j].fiq : ~s->bank[j].fiq);
- for (f = ffs(level), i = f - 1, level >>= f - 1; f; i += f,
- level >>= f) {
+
+ while (level != 0) {
+ i = ctz32(level);
p = s->bank[j].priority[i];
if (p <= p_intr) {
p_intr = p;
sir_intr = 32 * j + i;
}
- f = ffs(level >> 1);
+ level &= level - 1;
}
}
s->sir_intr[is_fiq] = sir_intr;
diff --git a/hw/intc/openpic.c b/hw/intc/openpic.c
index 87fe2e865..14ab0e31b 100644
--- a/hw/intc/openpic.c
+++ b/hw/intc/openpic.c
@@ -1556,9 +1556,9 @@ static void openpic_realize(DeviceState *dev, Error **errp)
};
if (opp->nb_cpus > MAX_CPU) {
- error_set(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE,
- TYPE_OPENPIC, "nb_cpus", (uint64_t)opp->nb_cpus,
- (uint64_t)0, (uint64_t)MAX_CPU);
+ error_setg(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE,
+ TYPE_OPENPIC, "nb_cpus", (uint64_t)opp->nb_cpus,
+ (uint64_t)0, (uint64_t)MAX_CPU);
return;
}
diff --git a/hw/intc/xics.c b/hw/intc/xics.c
index 0fd2a84c7..924b1ae3c 100644
--- a/hw/intc/xics.c
+++ b/hw/intc/xics.c
@@ -806,7 +806,7 @@ void xics_free(XICSState *icp, int irq, int num)
* Guest interfaces
*/
-static target_ulong h_cppr(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
CPUState *cs = CPU(cpu);
@@ -816,7 +816,7 @@ static target_ulong h_cppr(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return H_SUCCESS;
}
-static target_ulong h_ipi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
target_ulong server = get_cpu_index_by_dt_id(args[0]);
@@ -830,7 +830,7 @@ static target_ulong h_ipi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return H_SUCCESS;
}
-static target_ulong h_xirr(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
CPUState *cs = CPU(cpu);
@@ -840,7 +840,7 @@ static target_ulong h_xirr(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return H_SUCCESS;
}
-static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
CPUState *cs = CPU(cpu);
@@ -852,7 +852,7 @@ static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return H_SUCCESS;
}
-static target_ulong h_eoi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
CPUState *cs = CPU(cpu);
@@ -862,7 +862,7 @@ static target_ulong h_eoi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return H_SUCCESS;
}
-static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
CPUState *cs = CPU(cpu);
@@ -874,7 +874,7 @@ static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return H_SUCCESS;
}
-static void rtas_set_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t token,
uint32_t nargs, target_ulong args,
uint32_t nret, target_ulong rets)
@@ -902,7 +902,7 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr,
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
}
-static void rtas_get_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static void rtas_get_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t token,
uint32_t nargs, target_ulong args,
uint32_t nret, target_ulong rets)
@@ -927,7 +927,7 @@ static void rtas_get_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr,
rtas_st(rets, 2, ics->irqs[nr - ics->offset].priority);
}
-static void rtas_int_off(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t token,
uint32_t nargs, target_ulong args,
uint32_t nret, target_ulong rets)
@@ -953,7 +953,7 @@ static void rtas_int_off(PowerPCCPU *cpu, sPAPREnvironment *spapr,
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
}
-static void rtas_int_on(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t token,
uint32_t nargs, target_ulong args,
uint32_t nret, target_ulong rets)
diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c
index c15453f26..d58729cfa 100644
--- a/hw/intc/xics_kvm.c
+++ b/hw/intc/xics_kvm.c
@@ -331,6 +331,15 @@ static void xics_kvm_cpu_setup(XICSState *icp, PowerPCCPU *cpu)
abort();
}
+ /*
+ * If we are reusing a parked vCPU fd corresponding to the CPU
+ * which was hot-removed earlier we don't have to renable
+ * KVM_CAP_IRQ_XICS capability again.
+ */
+ if (ss->cap_irq_xics_enabled) {
+ return;
+ }
+
if (icpkvm->kernel_xics_fd != -1) {
int ret;
@@ -343,6 +352,7 @@ static void xics_kvm_cpu_setup(XICSState *icp, PowerPCCPU *cpu)
kvm_arch_vcpu_id(cs), strerror(errno));
exit(1);
}
+ ss->cap_irq_xics_enabled = true;
}
}
@@ -368,7 +378,7 @@ static void xics_kvm_set_nr_servers(XICSState *icp, uint32_t nr_servers,
}
}
-static void rtas_dummy(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static void rtas_dummy(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t token,
uint32_t nargs, target_ulong args,
uint32_t nret, target_ulong rets)
diff --git a/hw/isa/i82378.c b/hw/isa/i82378.c
index 9da9dfc4d..fcf97d86a 100644
--- a/hw/isa/i82378.c
+++ b/hw/isa/i82378.c
@@ -65,7 +65,6 @@ static void i82378_realize(PCIDevice *pci, Error **errp)
uint8_t *pci_conf;
ISABus *isabus;
ISADevice *isa;
- qemu_irq *out0_irq;
pci_conf = pci->config;
pci_set_word(pci_conf + PCI_COMMAND,
@@ -88,11 +87,9 @@ static void i82378_realize(PCIDevice *pci, Error **errp)
All devices accept byte access only, except timer
*/
- /* Workaround the fact that i8259 is not qdev'ified... */
- out0_irq = qemu_allocate_irqs(i82378_request_out0_irq, s, 1);
-
/* 2 82C59 (irq) */
- s->i8259 = i8259_init(isabus, *out0_irq);
+ s->i8259 = i8259_init(isabus,
+ qemu_allocate_irq(i82378_request_out0_irq, s, 0));
isa_bus_irqs(isabus, s->i8259);
/* 1 82C54 (pit) */
diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c
index 825aa627d..43e0cd8dd 100644
--- a/hw/isa/isa-bus.c
+++ b/hw/isa/isa-bus.c
@@ -21,6 +21,7 @@
#include "hw/sysbus.h"
#include "sysemu/sysemu.h"
#include "hw/isa/isa.h"
+#include "hw/i386/pc.h"
static ISABus *isabus;
@@ -178,6 +179,9 @@ ISADevice *isa_vga_init(ISABus *bus)
case VGA_VMWARE:
fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __func__);
return NULL;
+ case VGA_VIRTIO:
+ fprintf(stderr, "%s: virtio-vga: no PCI bus\n", __func__);
+ return NULL;
case VGA_NONE:
default:
return NULL;
@@ -267,3 +271,28 @@ MemoryRegion *isa_address_space_io(ISADevice *dev)
}
type_init(isabus_register_types)
+
+static void parallel_init(ISABus *bus, int index, CharDriverState *chr)
+{
+ DeviceState *dev;
+ ISADevice *isadev;
+
+ isadev = isa_create(bus, "isa-parallel");
+ dev = DEVICE(isadev);
+ qdev_prop_set_uint32(dev, "index", index);
+ qdev_prop_set_chr(dev, "chardev", chr);
+ qdev_init_nofail(dev);
+}
+
+void parallel_hds_isa_init(ISABus *bus, int n)
+{
+ int i;
+
+ assert(n <= MAX_PARALLEL_PORTS);
+
+ for (i = 0; i < n; i++) {
+ if (parallel_hds[i]) {
+ parallel_init(bus, i, parallel_hds[i]);
+ }
+ }
+}
diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index dba758595..360699f6f 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -138,6 +138,7 @@ static void ich9_cc_reset(ICH9LPCState *lpc)
pci_set_long(c + ICH9_CC_D27IR, ICH9_CC_DIR_DEFAULT);
pci_set_long(c + ICH9_CC_D26IR, ICH9_CC_DIR_DEFAULT);
pci_set_long(c + ICH9_CC_D25IR, ICH9_CC_DIR_DEFAULT);
+ pci_set_long(c + ICH9_CC_GCS, ICH9_CC_GCS_DEFAULT);
ich9_cc_update(lpc);
}
@@ -313,6 +314,16 @@ PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin)
return route;
}
+void ich9_generate_smi(void)
+{
+ cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI);
+}
+
+void ich9_generate_nmi(void)
+{
+ cpu_interrupt(first_cpu, CPU_INTERRUPT_NMI);
+}
+
static int ich9_lpc_sci_irq(ICH9LPCState *lpc)
{
switch (lpc->d.config[ICH9_LPC_ACPI_CTRL] &
@@ -357,14 +368,13 @@ static void ich9_set_sci(void *opaque, int irq_num, int level)
}
}
-void ich9_lpc_pm_init(PCIDevice *lpc_pci)
+void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool smm_enabled, bool enable_tco)
{
ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci);
- qemu_irq *sci_irq;
-
- sci_irq = qemu_allocate_irqs(ich9_set_sci, lpc, 1);
- ich9_pm_init(lpc_pci, &lpc->pm, sci_irq[0]);
+ qemu_irq sci_irq;
+ sci_irq = qemu_allocate_irq(ich9_set_sci, lpc, 0);
+ ich9_pm_init(lpc_pci, &lpc->pm, smm_enabled, enable_tco, sci_irq);
ich9_lpc_reset(&lpc->d.qdev);
}
@@ -378,6 +388,9 @@ static void ich9_apm_ctrl_changed(uint32_t val, void *arg)
acpi_pm1_cnt_update(&lpc->pm.acpi_regs,
val == ICH9_APM_ACPI_ENABLE,
val == ICH9_APM_ACPI_DISABLE);
+ if (val == ICH9_APM_ACPI_ENABLE || val == ICH9_APM_ACPI_DISABLE) {
+ return;
+ }
/* SMI_EN = PMBASE + 30. SMI control and enable register */
if (lpc->pm.smi_en & ICH9_PMIO_SMI_EN_APMC_EN) {
@@ -410,12 +423,28 @@ static void ich9_lpc_rcba_update(ICH9LPCState *lpc, uint32_t rbca_old)
}
}
+/* config:GEN_PMCON* */
+static void
+ich9_lpc_pmcon_update(ICH9LPCState *lpc)
+{
+ uint16_t gen_pmcon_1 = pci_get_word(lpc->d.config + ICH9_LPC_GEN_PMCON_1);
+ uint16_t wmask;
+
+ if (gen_pmcon_1 & ICH9_LPC_GEN_PMCON_1_SMI_LOCK) {
+ wmask = pci_get_word(lpc->d.wmask + ICH9_LPC_GEN_PMCON_1);
+ wmask &= ~ICH9_LPC_GEN_PMCON_1_SMI_LOCK;
+ pci_set_word(lpc->d.wmask + ICH9_LPC_GEN_PMCON_1, wmask);
+ lpc->pm.smi_en_wmask &= ~1;
+ }
+}
+
static int ich9_lpc_post_load(void *opaque, int version_id)
{
ICH9LPCState *lpc = opaque;
ich9_lpc_pmbase_update(lpc);
ich9_lpc_rcba_update(lpc, 0 /* disabled ICH9_LPC_RBCA_EN */);
+ ich9_lpc_pmcon_update(lpc);
return 0;
}
@@ -438,6 +467,9 @@ static void ich9_lpc_config_write(PCIDevice *d,
if (ranges_overlap(addr, len, ICH9_LPC_PIRQE_ROUT, 4)) {
pci_bus_fire_intx_routing_notifier(lpc->d.bus);
}
+ if (ranges_overlap(addr, len, ICH9_LPC_GEN_PMCON_1, 8)) {
+ ich9_lpc_pmcon_update(lpc);
+ }
}
static void ich9_lpc_reset(DeviceState *qdev)
@@ -494,7 +526,7 @@ static void ich9_lpc_machine_ready(Notifier *n, void *opaque)
/* lpt */
pci_conf[0x82] |= 0x04;
}
- if (memory_region_present(io_as, 0x3f0)) {
+ if (memory_region_present(io_as, 0x3f2)) {
/* floppy */
pci_conf[0x82] |= 0x08;
}
@@ -634,6 +666,7 @@ static const VMStateDescription vmstate_ich9_rst_cnt = {
.name = "ICH9LPC/rst_cnt",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = ich9_rst_cnt_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8(rst_cnt, ICH9LPCState),
VMSTATE_END_OF_LIST()
@@ -653,15 +686,17 @@ static const VMStateDescription vmstate_ich9_lpc = {
VMSTATE_UINT32(sci_level, ICH9LPCState),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_ich9_rst_cnt,
- .needed = ich9_rst_cnt_needed
- },
- { 0 }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_ich9_rst_cnt,
+ NULL
}
};
+static Property ich9_lpc_properties[] = {
+ DEFINE_PROP_BOOL("noreboot", ICH9LPCState, pin_strap.spkr_hi, true),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void ich9_lpc_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -673,6 +708,7 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data)
dc->reset = ich9_lpc_reset;
k->init = ich9_lpc_init;
dc->vmsd = &vmstate_ich9_lpc;
+ dc->props = ich9_lpc_properties;
k->config_write = ich9_lpc_config_write;
dc->desc = "ICH9 LPC bridge";
k->vendor_id = PCI_VENDOR_ID_INTEL;
diff --git a/hw/isa/piix4.c b/hw/isa/piix4.c
index d9522b1f4..2c59e91ff 100644
--- a/hw/isa/piix4.c
+++ b/hw/isa/piix4.c
@@ -34,6 +34,10 @@ typedef struct PIIX4State {
PCIDevice dev;
} PIIX4State;
+#define TYPE_PIIX4_PCI_DEVICE "PIIX4"
+#define PIIX4_PCI_DEVICE(obj) \
+ OBJECT_CHECK(PIIX4State, (obj), TYPE_PIIX4_PCI_DEVICE)
+
static void piix4_reset(void *opaque)
{
PIIX4State *d = opaque;
@@ -84,7 +88,7 @@ static const VMStateDescription vmstate_piix4 = {
static void piix4_realize(PCIDevice *dev, Error **errp)
{
- PIIX4State *d = DO_UPCAST(PIIX4State, dev, dev);
+ PIIX4State *d = PIIX4_PCI_DEVICE(dev);
isa_bus_new(DEVICE(d), pci_address_space(dev),
pci_address_space_io(dev));
@@ -121,7 +125,7 @@ static void piix4_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo piix4_info = {
- .name = "PIIX4",
+ .name = TYPE_PIIX4_PCI_DEVICE,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(PIIX4State),
.class_init = piix4_class_init,
diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c
index b8197b115..252e1d714 100644
--- a/hw/isa/vt82c686.c
+++ b/hw/isa/vt82c686.c
@@ -47,6 +47,10 @@ typedef struct VT82C686BState {
SuperIOConfig superio_conf;
} VT82C686BState;
+#define TYPE_VT82C686B_DEVICE "VT82C686B"
+#define VT82C686B_DEVICE(obj) \
+ OBJECT_CHECK(VT82C686BState, (obj), TYPE_VT82C686B_DEVICE)
+
static void superio_ioport_writeb(void *opaque, hwaddr addr, uint64_t data,
unsigned size)
{
@@ -114,7 +118,7 @@ static void vt82c686b_reset(void * opaque)
{
PCIDevice *d = opaque;
uint8_t *pci_conf = d->config;
- VT82C686BState *vt82c = DO_UPCAST(VT82C686BState, dev, d);
+ VT82C686BState *vt82c = VT82C686B_DEVICE(d);
pci_set_long(pci_conf + PCI_CAPABILITY_LIST, 0x000000c0);
pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
@@ -142,7 +146,7 @@ static void vt82c686b_reset(void * opaque)
static void vt82c686b_write_config(PCIDevice * d, uint32_t address,
uint32_t val, int len)
{
- VT82C686BState *vt686 = DO_UPCAST(VT82C686BState, dev, d);
+ VT82C686BState *vt686 = VT82C686B_DEVICE(d);
DPRINTF("vt82c686b_write_config address 0x%x val 0x%x len 0x%x\n",
address, val, len);
@@ -172,6 +176,18 @@ typedef struct VT686MC97State {
PCIDevice dev;
} VT686MC97State;
+#define TYPE_VT82C686B_PM_DEVICE "VT82C686B_PM"
+#define VT82C686B_PM_DEVICE(obj) \
+ OBJECT_CHECK(VT686PMState, (obj), TYPE_VT82C686B_PM_DEVICE)
+
+#define TYPE_VT82C686B_MC97_DEVICE "VT82C686B_MC97"
+#define VT82C686B_MC97_DEVICE(obj) \
+ OBJECT_CHECK(VT686MC97State, (obj), TYPE_VT82C686B_MC97_DEVICE)
+
+#define TYPE_VT82C686B_AC97_DEVICE "VT82C686B_AC97"
+#define VT82C686B_AC97_DEVICE(obj) \
+ OBJECT_CHECK(VT686AC97State, (obj), TYPE_VT82C686B_AC97_DEVICE)
+
static void pm_update_sci(VT686PMState *s)
{
int sci_level, pmsts;
@@ -247,7 +263,7 @@ static const VMStateDescription vmstate_acpi = {
static void vt82c686b_ac97_realize(PCIDevice *dev, Error **errp)
{
- VT686AC97State *s = DO_UPCAST(VT686AC97State, dev, dev);
+ VT686AC97State *s = VT82C686B_AC97_DEVICE(dev);
uint8_t *pci_conf = s->dev.config;
pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_INVALIDATE |
@@ -261,7 +277,7 @@ void vt82c686b_ac97_init(PCIBus *bus, int devfn)
{
PCIDevice *dev;
- dev = pci_create(bus, devfn, "VT82C686B_AC97");
+ dev = pci_create(bus, devfn, TYPE_VT82C686B_AC97_DEVICE);
qdev_init_nofail(&dev->qdev);
}
@@ -280,7 +296,7 @@ static void via_ac97_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo via_ac97_info = {
- .name = "VT82C686B_AC97",
+ .name = TYPE_VT82C686B_AC97_DEVICE,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(VT686AC97State),
.class_init = via_ac97_class_init,
@@ -288,7 +304,7 @@ static const TypeInfo via_ac97_info = {
static void vt82c686b_mc97_realize(PCIDevice *dev, Error **errp)
{
- VT686MC97State *s = DO_UPCAST(VT686MC97State, dev, dev);
+ VT686MC97State *s = VT82C686B_MC97_DEVICE(dev);
uint8_t *pci_conf = s->dev.config;
pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_INVALIDATE |
@@ -301,7 +317,7 @@ void vt82c686b_mc97_init(PCIBus *bus, int devfn)
{
PCIDevice *dev;
- dev = pci_create(bus, devfn, "VT82C686B_MC97");
+ dev = pci_create(bus, devfn, TYPE_VT82C686B_MC97_DEVICE);
qdev_init_nofail(&dev->qdev);
}
@@ -320,7 +336,7 @@ static void via_mc97_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo via_mc97_info = {
- .name = "VT82C686B_MC97",
+ .name = TYPE_VT82C686B_MC97_DEVICE,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(VT686MC97State),
.class_init = via_mc97_class_init,
@@ -329,7 +345,7 @@ static const TypeInfo via_mc97_info = {
/* vt82c686 pm init */
static void vt82c686b_pm_realize(PCIDevice *dev, Error **errp)
{
- VT686PMState *s = DO_UPCAST(VT686PMState, dev, dev);
+ VT686PMState *s = VT82C686B_PM_DEVICE(dev);
uint8_t *pci_conf;
pci_conf = s->dev.config;
@@ -356,7 +372,7 @@ static void vt82c686b_pm_realize(PCIDevice *dev, Error **errp)
acpi_pm_tmr_init(&s->ar, pm_tmr_timer, &s->io);
acpi_pm1_evt_init(&s->ar, pm_tmr_timer, &s->io);
- acpi_pm1_cnt_init(&s->ar, &s->io, 2);
+ acpi_pm1_cnt_init(&s->ar, &s->io, false, false, 2);
}
I2CBus *vt82c686b_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
@@ -365,10 +381,10 @@ I2CBus *vt82c686b_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
PCIDevice *dev;
VT686PMState *s;
- dev = pci_create(bus, devfn, "VT82C686B_PM");
+ dev = pci_create(bus, devfn, TYPE_VT82C686B_PM_DEVICE);
qdev_prop_set_uint32(&dev->qdev, "smb_io_base", smb_io_base);
- s = DO_UPCAST(VT686PMState, dev, dev);
+ s = VT82C686B_PM_DEVICE(dev);
qdev_init_nofail(&dev->qdev);
@@ -398,7 +414,7 @@ static void via_pm_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo via_pm_info = {
- .name = "VT82C686B_PM",
+ .name = TYPE_VT82C686B_PM_DEVICE,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(VT686PMState),
.class_init = via_pm_class_init,
@@ -417,7 +433,7 @@ static const VMStateDescription vmstate_via = {
/* init the PCI-to-ISA bridge */
static void vt82c686b_realize(PCIDevice *d, Error **errp)
{
- VT82C686BState *vt82c = DO_UPCAST(VT82C686BState, dev, d);
+ VT82C686BState *vt82c = VT82C686B_DEVICE(d);
uint8_t *pci_conf;
ISABus *isa_bus;
uint8_t *wmask;
@@ -451,7 +467,8 @@ ISABus *vt82c686b_init(PCIBus *bus, int devfn)
{
PCIDevice *d;
- d = pci_create_simple_multifunction(bus, devfn, true, "VT82C686B");
+ d = pci_create_simple_multifunction(bus, devfn, true,
+ TYPE_VT82C686B_DEVICE);
return ISA_BUS(qdev_get_child_bus(DEVICE(d), "isa.0"));
}
@@ -477,7 +494,7 @@ static void via_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo via_info = {
- .name = "VT82C686B",
+ .name = TYPE_VT82C686B_DEVICE,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(VT82C686BState),
.class_init = via_class_init,
diff --git a/hw/lm32/lm32_boards.c b/hw/lm32/lm32_boards.c
index 14d0efcdd..70f48d3b1 100644
--- a/hw/lm32/lm32_boards.c
+++ b/hw/lm32/lm32_boards.c
@@ -78,7 +78,7 @@ static void lm32_evr_init(MachineState *machine)
DriveInfo *dinfo;
MemoryRegion *address_space_mem = get_system_memory();
MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
- qemu_irq *cpu_irq, irq[32];
+ qemu_irq irq[32];
ResetInfo *reset_info;
int i;
@@ -123,8 +123,7 @@ static void lm32_evr_init(MachineState *machine)
1, 2, 0x01, 0x7e, 0x43, 0x00, 0x555, 0x2aa, 1);
/* create irq lines */
- cpu_irq = qemu_allocate_irqs(cpu_irq_handler, cpu, 1);
- env->pic_state = lm32_pic_init(*cpu_irq);
+ env->pic_state = lm32_pic_init(qemu_allocate_irq(cpu_irq_handler, cpu, 0));
for (i = 0; i < 32; i++) {
irq[i] = qdev_get_gpio_in(env->pic_state, i);
}
@@ -173,7 +172,7 @@ static void lm32_uclinux_init(MachineState *machine)
DriveInfo *dinfo;
MemoryRegion *address_space_mem = get_system_memory();
MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
- qemu_irq *cpu_irq, irq[32];
+ qemu_irq irq[32];
HWSetup *hw;
ResetInfo *reset_info;
int i;
@@ -225,8 +224,7 @@ static void lm32_uclinux_init(MachineState *machine)
1, 2, 0x01, 0x7e, 0x43, 0x00, 0x555, 0x2aa, 1);
/* create irq lines */
- cpu_irq = qemu_allocate_irqs(cpu_irq_handler, env, 1);
- env->pic_state = lm32_pic_init(*cpu_irq);
+ env->pic_state = lm32_pic_init(qemu_allocate_irq(cpu_irq_handler, env, 0));
for (i = 0; i < 32; i++) {
irq[i] = qdev_get_gpio_in(env->pic_state, i);
}
diff --git a/hw/lm32/milkymist.c b/hw/lm32/milkymist.c
index e0cec7dc4..e755f5b24 100644
--- a/hw/lm32/milkymist.c
+++ b/hw/lm32/milkymist.c
@@ -86,7 +86,7 @@ milkymist_init(MachineState *machine)
DriveInfo *dinfo;
MemoryRegion *address_space_mem = get_system_memory();
MemoryRegion *phys_sdram = g_new(MemoryRegion, 1);
- qemu_irq irq[32], *cpu_irq;
+ qemu_irq irq[32];
int i;
char *bios_filename;
ResetInfo *reset_info;
@@ -130,8 +130,7 @@ milkymist_init(MachineState *machine)
2, 0x00, 0x89, 0x00, 0x1d, 1);
/* create irq lines */
- cpu_irq = qemu_allocate_irqs(cpu_irq_handler, cpu, 1);
- env->pic_state = lm32_pic_init(*cpu_irq);
+ env->pic_state = lm32_pic_init(qemu_allocate_irq(cpu_irq_handler, cpu, 0));
for (i = 0; i < 32; i++) {
irq[i] = qdev_get_gpio_in(env->pic_state, i);
}
diff --git a/hw/m68k/mcf_intc.c b/hw/m68k/mcf_intc.c
index 621423c3e..f13c7f3ae 100644
--- a/hw/m68k/mcf_intc.c
+++ b/hw/m68k/mcf_intc.c
@@ -102,6 +102,20 @@ static void mcf_intc_write(void *opaque, hwaddr addr,
case 0x0c:
s->imr = (s->imr & 0xffffffff00000000ull) | (uint32_t)val;
break;
+ case 0x1c:
+ if (val & 0x40) {
+ s->imr = ~0ull;
+ } else {
+ s->imr |= (0x1ull << (val & 0x3f));
+ }
+ break;
+ case 0x1d:
+ if (val & 0x40) {
+ s->imr = 0ull;
+ } else {
+ s->imr &= ~(0x1ull << (val & 0x3f));
+ }
+ break;
default:
hw_error("mcf_intc_write: Bad write offset %d\n", offset);
break;
diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c
index 39f0c97c0..bb04862de 100644
--- a/hw/mem/pc-dimm.c
+++ b/hw/mem/pc-dimm.c
@@ -23,12 +23,96 @@
#include "qapi/visitor.h"
#include "qemu/range.h"
#include "sysemu/numa.h"
+#include "sysemu/kvm.h"
+#include "trace.h"
typedef struct pc_dimms_capacity {
uint64_t size;
Error **errp;
} pc_dimms_capacity;
+void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms,
+ MemoryRegion *mr, uint64_t align, Error **errp)
+{
+ int slot;
+ MachineState *machine = MACHINE(qdev_get_machine());
+ PCDIMMDevice *dimm = PC_DIMM(dev);
+ Error *local_err = NULL;
+ uint64_t existing_dimms_capacity = 0;
+ uint64_t addr;
+
+ addr = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP, &local_err);
+ if (local_err) {
+ goto out;
+ }
+
+ addr = pc_dimm_get_free_addr(hpms->base,
+ memory_region_size(&hpms->mr),
+ !addr ? NULL : &addr, align,
+ memory_region_size(mr), &local_err);
+ if (local_err) {
+ goto out;
+ }
+
+ existing_dimms_capacity = pc_existing_dimms_capacity(&local_err);
+ if (local_err) {
+ goto out;
+ }
+
+ if (existing_dimms_capacity + memory_region_size(mr) >
+ machine->maxram_size - machine->ram_size) {
+ error_setg(&local_err, "not enough space, currently 0x%" PRIx64
+ " in use of total hot pluggable 0x" RAM_ADDR_FMT,
+ existing_dimms_capacity,
+ machine->maxram_size - machine->ram_size);
+ goto out;
+ }
+
+ object_property_set_int(OBJECT(dev), addr, PC_DIMM_ADDR_PROP, &local_err);
+ if (local_err) {
+ goto out;
+ }
+ trace_mhp_pc_dimm_assigned_address(addr);
+
+ slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, &local_err);
+ if (local_err) {
+ goto out;
+ }
+
+ slot = pc_dimm_get_free_slot(slot == PC_DIMM_UNASSIGNED_SLOT ? NULL : &slot,
+ machine->ram_slots, &local_err);
+ if (local_err) {
+ goto out;
+ }
+ object_property_set_int(OBJECT(dev), slot, PC_DIMM_SLOT_PROP, &local_err);
+ if (local_err) {
+ goto out;
+ }
+ trace_mhp_pc_dimm_assigned_slot(slot);
+
+ if (kvm_enabled() && !kvm_has_free_slot(machine)) {
+ error_setg(&local_err, "hypervisor has no free memory slots left");
+ goto out;
+ }
+
+ memory_region_add_subregion(&hpms->mr, addr - hpms->base, mr);
+ vmstate_register_ram(mr, dev);
+ numa_set_mem_node_id(addr, memory_region_size(mr), dimm->node);
+
+out:
+ error_propagate(errp, local_err);
+}
+
+void pc_dimm_memory_unplug(DeviceState *dev, MemoryHotplugState *hpms,
+ MemoryRegion *mr)
+{
+ PCDIMMDevice *dimm = PC_DIMM(dev);
+
+ numa_unset_mem_node_id(dimm->addr, memory_region_size(mr), dimm->node);
+ memory_region_del_subregion(&hpms->mr, mr);
+ vmstate_unregister_ram(mr, dev);
+}
+
static int pc_existing_dimms_capacity_internal(Object *obj, void *opaque)
{
pc_dimms_capacity *cap = opaque;
@@ -211,7 +295,6 @@ uint64_t pc_dimm_get_free_addr(uint64_t address_space_start,
uint64_t address_space_end = address_space_start + address_space_size;
g_assert(QEMU_ALIGN_UP(address_space_start, align) == address_space_start);
- g_assert(QEMU_ALIGN_UP(address_space_size, align) == address_space_size);
if (!address_space_size) {
error_setg(errp, "memory hotplug is not enabled, "
diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c
index 38c59dbe9..3e8820f36 100644
--- a/hw/microblaze/boot.c
+++ b/hw/microblaze/boot.c
@@ -48,13 +48,14 @@ static struct
static void main_cpu_reset(void *opaque)
{
MicroBlazeCPU *cpu = opaque;
+ CPUState *cs = CPU(cpu);
CPUMBState *env = &cpu->env;
- cpu_reset(CPU(cpu));
+ cpu_reset(cs);
env->regs[5] = boot_info.cmdline;
env->regs[6] = boot_info.initrd_start;
env->regs[7] = boot_info.fdt;
- env->sregs[SR_PC] = boot_info.bootstrap_pc;
+ cpu_set_pc(cs, boot_info.bootstrap_pc);
if (boot_info.machine_cpu_reset) {
boot_info.machine_cpu_reset(cpu);
}
@@ -113,15 +114,15 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base,
const char *kernel_filename;
const char *kernel_cmdline;
const char *dtb_arg;
+ char *filename = NULL;
machine_opts = qemu_get_machine_opts();
kernel_filename = qemu_opt_get(machine_opts, "kernel");
kernel_cmdline = qemu_opt_get(machine_opts, "append");
dtb_arg = qemu_opt_get(machine_opts, "dtb");
- if (dtb_arg) { /* Preference a -dtb argument */
- dtb_filename = dtb_arg;
- } else { /* default to pcbios dtb as passed by machine_init */
- dtb_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_filename);
+ /* default to pcbios dtb as passed by machine_init */
+ if (!dtb_arg) {
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_filename);
}
boot_info.machine_cpu_reset = machine_cpu_reset;
@@ -203,7 +204,8 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base,
boot_info.initrd_start,
boot_info.initrd_end,
kernel_cmdline,
- dtb_filename);
+ /* Preference a -dtb argument */
+ dtb_arg ? dtb_arg : filename);
}
-
+ g_free(filename);
}
diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c
index 48c264b9d..ed84a37e6 100644
--- a/hw/microblaze/petalogix_ml605_mmu.c
+++ b/hw/microblaze/petalogix_ml605_mmu.c
@@ -64,20 +64,6 @@
#define SPI_IRQ 4
#define UART16550_IRQ 5
-static void machine_cpu_reset(MicroBlazeCPU *cpu)
-{
- CPUMBState *env = &cpu->env;
-
- env->pvr.regs[10] = 0x0e000000; /* virtex 6 */
- /* setup pvr to match kernel setting */
- env->pvr.regs[5] |= PVR5_DCACHE_WRITEBACK_MASK;
- env->pvr.regs[0] |= PVR0_USE_FPU_MASK | PVR0_ENDI;
- env->pvr.regs[0] = (env->pvr.regs[0] & ~PVR0_VERSION_MASK) | (0x14 << 8);
- env->pvr.regs[2] ^= PVR2_USE_FPU2_MASK;
- env->pvr.regs[4] = 0xc56b8000;
- env->pvr.regs[5] = 0xc56be000;
-}
-
static void
petalogix_ml605_init(MachineState *machine)
{
@@ -95,6 +81,13 @@ petalogix_ml605_init(MachineState *machine)
/* init CPUs */
cpu = MICROBLAZE_CPU(object_new(TYPE_MICROBLAZE_CPU));
+ /* Use FPU but don't use floating point conversion and square
+ * root instructions
+ */
+ object_property_set_int(OBJECT(cpu), 1, "use-fpu", &error_abort);
+ object_property_set_bool(OBJECT(cpu), true, "dcache-writeback",
+ &error_abort);
+ object_property_set_bool(OBJECT(cpu), true, "endianness", &error_abort);
object_property_set_bool(OBJECT(cpu), true, "realized", &error_abort);
/* Attach emulated BRAM through the LMB. */
@@ -201,10 +194,15 @@ petalogix_ml605_init(MachineState *machine)
}
}
+ /* setup PVR to match kernel settings */
+ cpu->env.pvr.regs[4] = 0xc56b8000;
+ cpu->env.pvr.regs[5] = 0xc56be000;
+ cpu->env.pvr.regs[10] = 0x0e000000; /* virtex 6 */
+
microblaze_load_kernel(cpu, MEMORY_BASEADDR, ram_size,
machine->initrd_filename,
BINARY_DEVICE_TREE_FILE,
- machine_cpu_reset);
+ NULL);
}
diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c
index 84f6e7465..0c2140c3f 100644
--- a/hw/microblaze/petalogix_s3adsp1800_mmu.c
+++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c
@@ -51,18 +51,10 @@
#define ETHLITE_IRQ 1
#define UARTLITE_IRQ 3
-static void machine_cpu_reset(MicroBlazeCPU *cpu)
-{
- CPUMBState *env = &cpu->env;
-
- env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family. */
-}
-
static void
petalogix_s3adsp1800_init(MachineState *machine)
{
ram_addr_t ram_size = machine->ram_size;
- const char *cpu_model = machine->cpu_model;
DeviceState *dev;
MicroBlazeCPU *cpu;
DriveInfo *dinfo;
@@ -73,11 +65,8 @@ petalogix_s3adsp1800_init(MachineState *machine)
qemu_irq irq[32];
MemoryRegion *sysmem = get_system_memory();
- /* init CPUs */
- if (cpu_model == NULL) {
- cpu_model = "microblaze";
- }
- cpu = cpu_mb_init(cpu_model);
+ cpu = MICROBLAZE_CPU(object_new(TYPE_MICROBLAZE_CPU));
+ object_property_set_bool(OBJECT(cpu), true, "realized", &error_abort);
/* Attach emulated BRAM through the LMB. */
memory_region_init_ram(phys_lmb_bram, NULL,
@@ -132,7 +121,7 @@ petalogix_s3adsp1800_init(MachineState *machine)
microblaze_load_kernel(cpu, ddr_base, ram_size,
machine->initrd_filename,
BINARY_DEVICE_TREE_FILE,
- machine_cpu_reset);
+ NULL);
}
static QEMUMachine petalogix_s3adsp1800_machine = {
diff --git a/hw/mips/Makefile.objs b/hw/mips/Makefile.objs
index 0a652f852..9633f3a57 100644
--- a/hw/mips/Makefile.objs
+++ b/hw/mips/Makefile.objs
@@ -1,4 +1,5 @@
-obj-y += mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o
+obj-y += mips_r4k.o mips_malta.o mips_mipssim.o
obj-y += addr.o cputimer.o mips_int.o
+obj-$(CONFIG_JAZZ) += mips_jazz.o
obj-$(CONFIG_FULONG) += mips_fulong2e.o
obj-y += gt64xxx_pci.o
diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c
index 4aae64a9e..dea941ad8 100644
--- a/hw/mips/mips_fulong2e.c
+++ b/hw/mips/mips_fulong2e.c
@@ -168,6 +168,7 @@ static int64_t load_kernel (CPUMIPSState *env)
rom_add_blob_fixed("prom", prom_buf, prom_size,
cpu_mips_kseg0_to_phys(NULL, ENVP_ADDR));
+ g_free(prom_buf);
return kernel_entry;
}
diff --git a/hw/mips/mips_jazz.c b/hw/mips/mips_jazz.c
index 07f3c270d..9d60633ef 100644
--- a/hw/mips/mips_jazz.c
+++ b/hw/mips/mips_jazz.c
@@ -61,7 +61,8 @@ static void main_cpu_reset(void *opaque)
static uint64_t rtc_read(void *opaque, hwaddr addr, unsigned size)
{
uint8_t val;
- address_space_read(&address_space_memory, 0x90000071, &val, 1);
+ address_space_read(&address_space_memory, 0x90000071,
+ MEMTXATTRS_UNSPECIFIED, &val, 1);
return val;
}
@@ -69,7 +70,8 @@ static void rtc_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
uint8_t buf = val & 0xff;
- address_space_write(&address_space_memory, 0x90000071, &buf, 1);
+ address_space_write(&address_space_memory, 0x90000071,
+ MEMTXATTRS_UNSPECIFIED, &buf, 1);
}
static const MemoryRegionOps rtc_ops = {
@@ -133,16 +135,16 @@ static void mips_jazz_init(MachineState *machine,
MIPSCPU *cpu;
CPUClass *cc;
CPUMIPSState *env;
- qemu_irq *rc4030, *i8259;
+ qemu_irq *i8259;
rc4030_dma *dmas;
- void* rc4030_opaque;
+ MemoryRegion *rc4030_dma_mr;
MemoryRegion *isa_mem = g_new(MemoryRegion, 1);
MemoryRegion *isa_io = g_new(MemoryRegion, 1);
MemoryRegion *rtc = g_new(MemoryRegion, 1);
MemoryRegion *i8042 = g_new(MemoryRegion, 1);
MemoryRegion *dma_dummy = g_new(MemoryRegion, 1);
NICInfo *nd;
- DeviceState *dev;
+ DeviceState *dev, *rc4030;
SysBusDevice *sysbus;
ISABus *isa_bus;
ISADevice *pit;
@@ -155,12 +157,7 @@ static void mips_jazz_init(MachineState *machine,
/* init CPUs */
if (cpu_model == NULL) {
-#ifdef TARGET_MIPS64
cpu_model = "R4000";
-#else
- /* FIXME: All wrong, this maybe should be R3000 for the older JAZZs. */
- cpu_model = "24Kf";
-#endif
}
cpu = cpu_mips_init(cpu_model);
if (cpu == NULL) {
@@ -216,8 +213,14 @@ static void mips_jazz_init(MachineState *machine,
cpu_mips_clock_init(env);
/* Chipset */
- rc4030_opaque = rc4030_init(env->irq[6], env->irq[3], &rc4030, &dmas,
- address_space);
+ rc4030 = rc4030_init(&dmas, &rc4030_dma_mr);
+ sysbus = SYS_BUS_DEVICE(rc4030);
+ sysbus_connect_irq(sysbus, 0, env->irq[6]);
+ sysbus_connect_irq(sysbus, 1, env->irq[3]);
+ memory_region_add_subregion(address_space, 0x80000000,
+ sysbus_mmio_get_region(sysbus, 0));
+ memory_region_add_subregion(address_space, 0xf0000000,
+ sysbus_mmio_get_region(sysbus, 1));
memory_region_init_io(dma_dummy, NULL, &dma_dummy_ops, NULL, "dummy_dma", 0x1000);
memory_region_add_subregion(address_space, 0x8000d000, dma_dummy);
@@ -244,7 +247,7 @@ static void mips_jazz_init(MachineState *machine,
sysbus = SYS_BUS_DEVICE(dev);
sysbus_mmio_map(sysbus, 0, 0x60080000);
sysbus_mmio_map(sysbus, 1, 0x40000000);
- sysbus_connect_irq(sysbus, 0, rc4030[3]);
+ sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(rc4030, 3));
{
/* Simple ROM, so user doesn't have to provide one */
MemoryRegion *rom_mr = g_new(MemoryRegion, 1);
@@ -270,8 +273,17 @@ static void mips_jazz_init(MachineState *machine,
if (!nd->model)
nd->model = g_strdup("dp83932");
if (strcmp(nd->model, "dp83932") == 0) {
- dp83932_init(nd, 0x80001000, 2, get_system_memory(), rc4030[4],
- rc4030_opaque, rc4030_dma_memory_rw);
+ qemu_check_nic_model(nd, "dp83932");
+
+ dev = qdev_create(NULL, "dp8393x");
+ qdev_set_nic_properties(dev, nd);
+ qdev_prop_set_uint8(dev, "it_shift", 2);
+ qdev_prop_set_ptr(dev, "dma_mr", rc4030_dma_mr);
+ qdev_init_nofail(dev);
+ sysbus = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(sysbus, 0, 0x80001000);
+ sysbus_mmio_map(sysbus, 1, 0x8000b000);
+ sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(rc4030, 4));
break;
} else if (is_help_option(nd->model)) {
fprintf(stderr, "qemu: Supported NICs: dp83932\n");
@@ -285,7 +297,7 @@ static void mips_jazz_init(MachineState *machine,
/* SCSI adapter */
esp_init(0x80002000, 0,
rc4030_dma_read, rc4030_dma_write, dmas[0],
- rc4030[5], &esp_reset, &dma_enable);
+ qdev_get_gpio_in(rc4030, 5), &esp_reset, &dma_enable);
/* Floppy */
if (drive_get_max_bus(IF_FLOPPY) >= MAX_FD) {
@@ -295,7 +307,7 @@ static void mips_jazz_init(MachineState *machine,
for (n = 0; n < MAX_FD; n++) {
fds[n] = drive_get(IF_FLOPPY, 0, n);
}
- fdctrl_init_sysbus(rc4030[1], 0, 0x80003000, fds);
+ fdctrl_init_sysbus(qdev_get_gpio_in(rc4030, 1), 0, 0x80003000, fds);
/* Real time clock */
rtc_init(isa_bus, 1980, NULL);
@@ -303,23 +315,26 @@ static void mips_jazz_init(MachineState *machine,
memory_region_add_subregion(address_space, 0x80004000, rtc);
/* Keyboard (i8042) */
- i8042_mm_init(rc4030[6], rc4030[7], i8042, 0x1000, 0x1);
+ i8042_mm_init(qdev_get_gpio_in(rc4030, 6), qdev_get_gpio_in(rc4030, 7),
+ i8042, 0x1000, 0x1);
memory_region_add_subregion(address_space, 0x80005000, i8042);
/* Serial ports */
if (serial_hds[0]) {
- serial_mm_init(address_space, 0x80006000, 0, rc4030[8], 8000000/16,
+ serial_mm_init(address_space, 0x80006000, 0,
+ qdev_get_gpio_in(rc4030, 8), 8000000/16,
serial_hds[0], DEVICE_NATIVE_ENDIAN);
}
if (serial_hds[1]) {
- serial_mm_init(address_space, 0x80007000, 0, rc4030[9], 8000000/16,
+ serial_mm_init(address_space, 0x80007000, 0,
+ qdev_get_gpio_in(rc4030, 9), 8000000/16,
serial_hds[1], DEVICE_NATIVE_ENDIAN);
}
/* Parallel port */
if (parallel_hds[0])
- parallel_mm_init(address_space, 0x80008000, 0, rc4030[0],
- parallel_hds[0]);
+ parallel_mm_init(address_space, 0x80008000, 0,
+ qdev_get_gpio_in(rc4030, 0), parallel_hds[0]);
/* FIXME: missing Jazz sound at 0x8000c000, rc4030[2] */
diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c
index b0fa71a51..3082e7534 100644
--- a/hw/mips/mips_malta.c
+++ b/hw/mips/mips_malta.c
@@ -53,6 +53,7 @@
#include "qemu/error-report.h"
#include "hw/empty_slot.h"
#include "sysemu/kvm.h"
+#include "exec/semihost.h"
//#define DEBUG_BOARD_INIT
@@ -97,7 +98,7 @@ typedef struct {
static ISADevice *pit;
static struct _loaderparams {
- int ram_size;
+ int ram_size, ram_low_size;
const char *kernel_filename;
const char *kernel_cmdline;
const char *initrd_filename;
@@ -634,15 +635,21 @@ static void write_bootloader (CPUMIPSState *env, uint8_t *base,
/* Second part of the bootloader */
p = (uint32_t *) (base + 0x580);
- stl_p(p++, 0x24040002); /* addiu a0, zero, 2 */
+
+ if (semihosting_get_argc()) {
+ /* Preserve a0 content as arguments have been passed */
+ stl_p(p++, 0x00000000); /* nop */
+ } else {
+ stl_p(p++, 0x24040002); /* addiu a0, zero, 2 */
+ }
stl_p(p++, 0x3c1d0000 | (((ENVP_ADDR - 64) >> 16) & 0xffff)); /* lui sp, high(ENVP_ADDR) */
stl_p(p++, 0x37bd0000 | ((ENVP_ADDR - 64) & 0xffff)); /* ori sp, sp, low(ENVP_ADDR) */
stl_p(p++, 0x3c050000 | ((ENVP_ADDR >> 16) & 0xffff)); /* lui a1, high(ENVP_ADDR) */
stl_p(p++, 0x34a50000 | (ENVP_ADDR & 0xffff)); /* ori a1, a1, low(ENVP_ADDR) */
stl_p(p++, 0x3c060000 | (((ENVP_ADDR + 8) >> 16) & 0xffff)); /* lui a2, high(ENVP_ADDR + 8) */
stl_p(p++, 0x34c60000 | ((ENVP_ADDR + 8) & 0xffff)); /* ori a2, a2, low(ENVP_ADDR + 8) */
- stl_p(p++, 0x3c070000 | (loaderparams.ram_size >> 16)); /* lui a3, high(ram_size) */
- stl_p(p++, 0x34e70000 | (loaderparams.ram_size & 0xffff)); /* ori a3, a3, low(ram_size) */
+ stl_p(p++, 0x3c070000 | (loaderparams.ram_low_size >> 16)); /* lui a3, high(ram_low_size) */
+ stl_p(p++, 0x34e70000 | (loaderparams.ram_low_size & 0xffff)); /* ori a3, a3, low(ram_low_size) */
/* Load BAR registers as done by YAMON */
stl_p(p++, 0x3c09b400); /* lui t1, 0xb400 */
@@ -851,8 +858,10 @@ static int64_t load_kernel (void)
}
prom_set(prom_buf, prom_index++, "memsize");
- prom_set(prom_buf, prom_index++, "%i",
- MIN(loaderparams.ram_size, 256 << 20));
+ prom_set(prom_buf, prom_index++, "%u", loaderparams.ram_low_size);
+
+ prom_set(prom_buf, prom_index++, "ememsize");
+ prom_set(prom_buf, prom_index++, "%u", loaderparams.ram_size);
prom_set(prom_buf, prom_index++, "modetty0");
prom_set(prom_buf, prom_index++, "38400n8r");
@@ -861,6 +870,7 @@ static int64_t load_kernel (void)
rom_add_blob_fixed("prom", prom_buf, prom_size,
cpu_mips_kseg0_to_phys(NULL, ENVP_ADDR));
+ g_free(prom_buf);
return kernel_entry;
}
@@ -884,7 +894,7 @@ static void main_cpu_reset(void *opaque)
read only location. The kernel location and the arguments table
location does not change. */
if (loaderparams.kernel_filename) {
- env->CP0_Status &= ~((1 << CP0St_BEV) | (1 << CP0St_ERL));
+ env->CP0_Status &= ~(1 << CP0St_ERL);
}
malta_mips_config(cpu);
@@ -1053,7 +1063,8 @@ void mips_malta_init(MachineState *machine)
}
/* Write a small bootloader to the flash location. */
- loaderparams.ram_size = ram_low_size;
+ loaderparams.ram_size = ram_size;
+ loaderparams.ram_low_size = ram_low_size;
loaderparams.kernel_filename = kernel_filename;
loaderparams.kernel_cmdline = kernel_cmdline;
loaderparams.initrd_filename = initrd_filename;
@@ -1160,7 +1171,7 @@ void mips_malta_init(MachineState *machine)
pci_piix4_ide_init(pci_bus, hd, piix4_devfn + 1);
pci_create_simple(pci_bus, piix4_devfn + 2, "piix4-usb-uhci");
smbus = piix4_pm_init(pci_bus, piix4_devfn + 3, 0x1100,
- isa_get_irq(NULL, 9), NULL, 0, NULL, NULL);
+ isa_get_irq(NULL, 9), NULL, 0, NULL);
smbus_eeprom_init(smbus, 8, smbus_eeprom_buf, smbus_eeprom_size);
g_free(smbus_eeprom_buf);
pit = pit_init(isa_bus, 0x40, 0, NULL);
diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c
index 66e2a58e8..f4dcacd86 100644
--- a/hw/mips/mips_r4k.c
+++ b/hw/mips/mips_r4k.c
@@ -139,6 +139,7 @@ static int64_t load_kernel(void)
rom_add_blob_fixed("params", params_buf, params_size,
(16 << 20) - 264);
+ g_free(params_buf);
return entry;
}
diff --git a/hw/misc/arm_integrator_debug.c b/hw/misc/arm_integrator_debug.c
index 99b720fbb..6d9dd74e3 100644
--- a/hw/misc/arm_integrator_debug.c
+++ b/hw/misc/arm_integrator_debug.c
@@ -79,7 +79,7 @@ static void intdbg_control_init(Object *obj)
SysBusDevice *sd = SYS_BUS_DEVICE(obj);
IntegratorDebugState *s = INTEGRATOR_DEBUG(obj);
- memory_region_init_io(&s->iomem, NULL, &intdbg_control_ops,
+ memory_region_init_io(&s->iomem, obj, &intdbg_control_ops,
NULL, "dbg-leds", 0x1000000);
sysbus_init_mmio(sd, &s->iomem);
}
diff --git a/hw/misc/edu.c b/hw/misc/edu.c
index f601069e8..fe50b42af 100644
--- a/hw/misc/edu.c
+++ b/hw/misc/edu.c
@@ -279,7 +279,7 @@ static const MemoryRegionOps edu_mmio_ops = {
};
/*
- * We purposedly use a thread, so that users are forced to wait for the status
+ * We purposely use a thread, so that users are forced to wait for the status
* register.
*/
static void *edu_fact_thread(void *opaque)
diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c
index 5d272c84e..cc76989a3 100644
--- a/hw/misc/ivshmem.c
+++ b/hw/misc/ivshmem.c
@@ -22,7 +22,7 @@
#include "hw/pci/msix.h"
#include "sysemu/kvm.h"
#include "migration/migration.h"
-#include "qapi/qmp/qerror.h"
+#include "qemu/error-report.h"
#include "qemu/event_notifier.h"
#include "qemu/fifo8.h"
#include "sysemu/char.h"
@@ -698,7 +698,6 @@ static void ivshmem_write_config(PCIDevice *pci_dev, uint32_t address,
uint32_t val, int len)
{
pci_default_write_config(pci_dev, address, val, len);
- msix_write_config(pci_dev, address, val, len);
}
static int pci_ivshmem_init(PCIDevice *dev)
diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c
index f3984e3a2..5d7043e99 100644
--- a/hw/misc/macio/cuda.c
+++ b/hw/misc/macio/cuda.c
@@ -713,7 +713,7 @@ static void cuda_initfn(Object *obj)
CUDAState *s = CUDA(obj);
int i;
- memory_region_init_io(&s->mem, NULL, &cuda_ops, s, "cuda", 0x2000);
+ memory_region_init_io(&s->mem, obj, &cuda_ops, s, "cuda", 0x2000);
sysbus_init_mmio(d, &s->mem);
sysbus_init_irq(d, &s->irq);
diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c
index b25e8511b..779683cc0 100644
--- a/hw/misc/macio/mac_dbdma.c
+++ b/hw/misc/macio/mac_dbdma.c
@@ -590,10 +590,11 @@ dbdma_control_write(DBDMA_channel *ch)
if ((ch->regs[DBDMA_STATUS] & RUN) && !(status & RUN)) {
/* RUN is cleared */
status &= ~(ACTIVE|DEAD);
- if ((status & FLUSH) && ch->flush) {
- ch->flush(&ch->io);
- status &= ~FLUSH;
- }
+ }
+
+ if ((status & FLUSH) && ch->flush) {
+ ch->flush(&ch->io);
+ status &= ~FLUSH;
}
DBDMA_DPRINTF(" status 0x%08x\n", status);
@@ -603,9 +604,6 @@ dbdma_control_write(DBDMA_channel *ch)
if (status & ACTIVE) {
DBDMA_kick(dbdma_from_ch(ch));
}
- if ((status & FLUSH) && ch->flush) {
- ch->flush(&ch->io);
- }
}
static void dbdma_write(void *opaque, hwaddr addr,
diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c
index 063ad8041..c661f86c2 100644
--- a/hw/misc/macio/macio.c
+++ b/hw/misc/macio/macio.c
@@ -105,10 +105,10 @@ static void macio_escc_legacy_setup(MacIOState *macio_state)
0xF0, 0xE0,
};
- memory_region_init(escc_legacy, NULL, "escc-legacy", 256);
+ memory_region_init(escc_legacy, OBJECT(macio_state), "escc-legacy", 256);
for (i = 0; i < ARRAY_SIZE(maps); i += 2) {
MemoryRegion *port = g_new(MemoryRegion, 1);
- memory_region_init_alias(port, NULL, "escc-legacy-port",
+ memory_region_init_alias(port, OBJECT(macio_state), "escc-legacy-port",
macio_state->escc_mem, maps[i+1], 0x2);
memory_region_add_subregion(escc_legacy, maps[i], port);
}
@@ -126,17 +126,20 @@ static void macio_bar_setup(MacIOState *macio_state)
}
}
-static int macio_common_initfn(PCIDevice *d)
+static void macio_common_realize(PCIDevice *d, Error **errp)
{
MacIOState *s = MACIO(d);
SysBusDevice *sysbus_dev;
- int ret;
+ Error *err = NULL;
+ MemoryRegion *dbdma_mem;
- d->config[0x3d] = 0x01; // interrupt on pin 1
+ s->dbdma = DBDMA_init(&dbdma_mem);
+ memory_region_add_subregion(&s->bar, 0x08000, dbdma_mem);
- ret = qdev_init(DEVICE(&s->cuda));
- if (ret < 0) {
- return ret;
+ object_property_set_bool(OBJECT(&s->cuda), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
}
sysbus_dev = SYS_BUS_DEVICE(&s->cuda);
memory_region_add_subregion(&s->bar, 0x16000,
@@ -144,12 +147,11 @@ static int macio_common_initfn(PCIDevice *d)
macio_bar_setup(s);
pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar);
-
- return 0;
}
-static int macio_initfn_ide(MacIOState *s, MACIOIDEState *ide, qemu_irq irq0,
- qemu_irq irq1, int dmaid)
+static void macio_realize_ide(MacIOState *s, MACIOIDEState *ide,
+ qemu_irq irq0, qemu_irq irq1, int dmaid,
+ Error **errp)
{
SysBusDevice *sysbus_dev;
@@ -157,27 +159,31 @@ static int macio_initfn_ide(MacIOState *s, MACIOIDEState *ide, qemu_irq irq0,
sysbus_connect_irq(sysbus_dev, 0, irq0);
sysbus_connect_irq(sysbus_dev, 1, irq1);
macio_ide_register_dma(ide, s->dbdma, dmaid);
- return qdev_init(DEVICE(ide));
+ object_property_set_bool(OBJECT(ide), true, "realized", errp);
}
-static int macio_oldworld_initfn(PCIDevice *d)
+static void macio_oldworld_realize(PCIDevice *d, Error **errp)
{
MacIOState *s = MACIO(d);
OldWorldMacIOState *os = OLDWORLD_MACIO(d);
+ Error *err = NULL;
SysBusDevice *sysbus_dev;
int i;
int cur_irq = 0;
- int ret = macio_common_initfn(d);
- if (ret < 0) {
- return ret;
+
+ macio_common_realize(d, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
}
sysbus_dev = SYS_BUS_DEVICE(&s->cuda);
sysbus_connect_irq(sysbus_dev, 0, os->irqs[cur_irq++]);
- ret = qdev_init(DEVICE(&os->nvram));
- if (ret < 0) {
- return ret;
+ object_property_set_bool(OBJECT(&os->nvram), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
}
sysbus_dev = SYS_BUS_DEVICE(&os->nvram);
memory_region_add_subregion(&s->bar, 0x60000,
@@ -194,13 +200,12 @@ static int macio_oldworld_initfn(PCIDevice *d)
qemu_irq irq0 = os->irqs[cur_irq++];
qemu_irq irq1 = os->irqs[cur_irq++];
- ret = macio_initfn_ide(s, &os->ide[i], irq0, irq1, 0x16 + (i * 4));
- if (ret < 0) {
- return ret;
+ macio_realize_ide(s, &os->ide[i], irq0, irq1, 0x16 + (i * 4), &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
}
}
-
- return 0;
}
static void macio_init_ide(MacIOState *s, MACIOIDEState *ide, size_t ide_size,
@@ -268,17 +273,20 @@ static const MemoryRegionOps timer_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
-static int macio_newworld_initfn(PCIDevice *d)
+static void macio_newworld_realize(PCIDevice *d, Error **errp)
{
MacIOState *s = MACIO(d);
NewWorldMacIOState *ns = NEWWORLD_MACIO(d);
+ Error *err = NULL;
SysBusDevice *sysbus_dev;
MemoryRegion *timer_memory = NULL;
int i;
int cur_irq = 0;
- int ret = macio_common_initfn(d);
- if (ret < 0) {
- return ret;
+
+ macio_common_realize(d, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
}
sysbus_dev = SYS_BUS_DEVICE(&s->cuda);
@@ -294,9 +302,10 @@ static int macio_newworld_initfn(PCIDevice *d)
qemu_irq irq0 = ns->irqs[cur_irq++];
qemu_irq irq1 = ns->irqs[cur_irq++];
- ret = macio_initfn_ide(s, &ns->ide[i], irq0, irq1, 0x16 + (i * 4));
- if (ret < 0) {
- return ret;
+ macio_realize_ide(s, &ns->ide[i], irq0, irq1, 0x16 + (i * 4), &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
}
}
@@ -305,8 +314,6 @@ static int macio_newworld_initfn(PCIDevice *d)
memory_region_init_io(timer_memory, OBJECT(s), &timer_ops, NULL, "timer",
0x1000);
memory_region_add_subregion(&s->bar, 0x15000, timer_memory);
-
- return 0;
}
static void macio_newworld_init(Object *obj)
@@ -325,16 +332,12 @@ static void macio_newworld_init(Object *obj)
static void macio_instance_init(Object *obj)
{
MacIOState *s = MACIO(obj);
- MemoryRegion *dbdma_mem;
- memory_region_init(&s->bar, NULL, "macio", 0x80000);
+ memory_region_init(&s->bar, obj, "macio", 0x80000);
object_initialize(&s->cuda, sizeof(s->cuda), TYPE_CUDA);
qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default());
object_property_add_child(obj, "cuda", OBJECT(&s->cuda), NULL);
-
- s->dbdma = DBDMA_init(&dbdma_mem);
- memory_region_add_subregion(&s->bar, 0x08000, dbdma_mem);
}
static const VMStateDescription vmstate_macio_oldworld = {
@@ -352,7 +355,7 @@ static void macio_oldworld_class_init(ObjectClass *oc, void *data)
PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc);
DeviceClass *dc = DEVICE_CLASS(oc);
- pdc->init = macio_oldworld_initfn;
+ pdc->realize = macio_oldworld_realize;
pdc->device_id = PCI_DEVICE_ID_APPLE_343S1201;
dc->vmsd = &vmstate_macio_oldworld;
}
@@ -372,7 +375,7 @@ static void macio_newworld_class_init(ObjectClass *oc, void *data)
PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc);
DeviceClass *dc = DEVICE_CLASS(oc);
- pdc->init = macio_newworld_initfn;
+ pdc->realize = macio_newworld_realize;
pdc->device_id = PCI_DEVICE_ID_APPLE_UNI_N_KEYL;
dc->vmsd = &vmstate_macio_newworld;
}
diff --git a/hw/misc/slavio_misc.c b/hw/misc/slavio_misc.c
index 50985958a..ec50f1075 100644
--- a/hw/misc/slavio_misc.c
+++ b/hw/misc/slavio_misc.c
@@ -68,6 +68,7 @@ typedef struct APCState {
} APCState;
#define MISC_SIZE 1
+#define LED_SIZE 2
#define SYSCTRL_SIZE 4
#define AUX1_TC 0x02
@@ -452,13 +453,13 @@ static int slavio_misc_init1(SysBusDevice *sbd)
/* 16 bit registers */
/* ss600mp diag LEDs */
memory_region_init_io(&s->led_iomem, OBJECT(s), &slavio_led_mem_ops, s,
- "leds", MISC_SIZE);
+ "leds", LED_SIZE);
sysbus_init_mmio(sbd, &s->led_iomem);
/* 32 bit registers */
/* System control */
memory_region_init_io(&s->sysctrl_iomem, OBJECT(s), &slavio_sysctrl_mem_ops, s,
- "system-control", MISC_SIZE);
+ "system-control", SYSCTRL_SIZE);
sysbus_init_mmio(sbd, &s->sysctrl_iomem);
/* AUX 1 (Misc System Functions) */
diff --git a/hw/misc/zynq_slcr.c b/hw/misc/zynq_slcr.c
index 964f2532f..3d7870850 100644
--- a/hw/misc/zynq_slcr.c
+++ b/hw/misc/zynq_slcr.c
@@ -393,12 +393,12 @@ static void zynq_slcr_write(void *opaque, hwaddr offset,
return;
}
- if (!s->regs[LOCKSTA]) {
- s->regs[offset / 4] = val;
- } else {
- DB_PRINT("SCLR registers are locked. Unlock them first\n");
+ if (s->regs[LOCKSTA]) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "SCLR registers are locked. Unlock them first\n");
return;
}
+ s->regs[offset] = val;
switch (offset) {
case PSS_RST_CTRL:
diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
index ea9329312..98801739e 100644
--- a/hw/net/Makefile.objs
+++ b/hw/net/Makefile.objs
@@ -35,3 +35,8 @@ obj-y += vhost_net.o
obj-$(CONFIG_ETSEC) += fsl_etsec/etsec.o fsl_etsec/registers.o \
fsl_etsec/rings.o fsl_etsec/miim.o
+
+common-obj-$(CONFIG_ROCKER) += rocker/rocker.o rocker/rocker_fp.o \
+ rocker/rocker_desc.o rocker/rocker_world.o \
+ rocker/rocker_of_dpa.o
+obj-$(call lnot,$(CONFIG_ROCKER)) += rocker/qmp-norocker.o
diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c
index 55b629387..494a346cf 100644
--- a/hw/net/cadence_gem.c
+++ b/hw/net/cadence_gem.c
@@ -24,8 +24,7 @@
#include <zlib.h> /* For crc32 */
-#include "hw/sysbus.h"
-#include "net/net.h"
+#include "hw/net/cadence_gem.h"
#include "net/checksum.h"
#ifdef CADENCE_GEM_ERR_DEBUG
@@ -141,8 +140,6 @@
#define GEM_DESCONF6 (0x00000294/4)
#define GEM_DESCONF7 (0x00000298/4)
-#define GEM_MAXREG (0x00000640/4) /* Last valid GEM address */
-
/*****************************************/
#define GEM_NWCTRL_TXSTART 0x00000200 /* Transmit Enable */
#define GEM_NWCTRL_TXENA 0x00000008 /* Transmit Enable */
@@ -158,7 +155,7 @@
#define GEM_NWCFG_BCAST_REJ 0x00000020 /* Reject broadcast packets */
#define GEM_NWCFG_PROMISC 0x00000010 /* Accept all packets */
-#define GEM_DMACFG_RBUFSZ_M 0x007F0000 /* DMA RX Buffer Size mask */
+#define GEM_DMACFG_RBUFSZ_M 0x00FF0000 /* DMA RX Buffer Size mask */
#define GEM_DMACFG_RBUFSZ_S 16 /* DMA RX Buffer Size shift */
#define GEM_DMACFG_RBUFSZ_MUL 64 /* DMA RX Buffer Size multiplier */
#define GEM_DMACFG_TXCSUM_OFFL 0x00000800 /* Transmit checksum offload */
@@ -349,44 +346,6 @@ static inline void rx_desc_set_sar(unsigned *desc, int sar_idx)
desc[1] |= R_DESC_1_RX_SAR_MATCH;
}
-#define TYPE_CADENCE_GEM "cadence_gem"
-#define GEM(obj) OBJECT_CHECK(GemState, (obj), TYPE_CADENCE_GEM)
-
-typedef struct GemState {
- SysBusDevice parent_obj;
-
- MemoryRegion iomem;
- NICState *nic;
- NICConf conf;
- qemu_irq irq;
-
- /* GEM registers backing store */
- uint32_t regs[GEM_MAXREG];
- /* Mask of register bits which are write only */
- uint32_t regs_wo[GEM_MAXREG];
- /* Mask of register bits which are read only */
- uint32_t regs_ro[GEM_MAXREG];
- /* Mask of register bits which are clear on read */
- uint32_t regs_rtc[GEM_MAXREG];
- /* Mask of register bits which are write 1 to clear */
- uint32_t regs_w1c[GEM_MAXREG];
-
- /* PHY registers backing store */
- uint16_t phy_regs[32];
-
- uint8_t phy_loop; /* Are we in phy loopback? */
-
- /* The current DMA descriptor pointers */
- uint32_t rx_desc_addr;
- uint32_t tx_desc_addr;
-
- uint8_t can_rx_state; /* Debug only */
-
- unsigned rx_desc[2];
-
- bool sar_active[4];
-} GemState;
-
/* The broadcast MAC address: 0xFFFFFFFFFFFF */
static const uint8_t broadcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
@@ -395,7 +354,7 @@ static const uint8_t broadcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
* One time initialization.
* Set masks to identify which register bits have magical clear properties
*/
-static void gem_init_register_masks(GemState *s)
+static void gem_init_register_masks(CadenceGEMState *s)
{
/* Mask of register bits which are read only */
memset(&s->regs_ro[0], 0, sizeof(s->regs_ro));
@@ -430,7 +389,7 @@ static void gem_init_register_masks(GemState *s)
* phy_update_link:
* Make the emulated PHY link state match the QEMU "interface" state.
*/
-static void phy_update_link(GemState *s)
+static void phy_update_link(CadenceGEMState *s)
{
DB_PRINT("down %d\n", qemu_get_queue(s->nic)->link_down);
@@ -450,7 +409,7 @@ static void phy_update_link(GemState *s)
static int gem_can_receive(NetClientState *nc)
{
- GemState *s;
+ CadenceGEMState *s;
s = qemu_get_nic_opaque(nc);
@@ -483,7 +442,7 @@ static int gem_can_receive(NetClientState *nc)
* gem_update_int_status:
* Raise or lower interrupt based on current status.
*/
-static void gem_update_int_status(GemState *s)
+static void gem_update_int_status(CadenceGEMState *s)
{
if (s->regs[GEM_ISR]) {
DB_PRINT("asserting int. (0x%08x)\n", s->regs[GEM_ISR]);
@@ -495,7 +454,7 @@ static void gem_update_int_status(GemState *s)
* gem_receive_updatestats:
* Increment receive statistics.
*/
-static void gem_receive_updatestats(GemState *s, const uint8_t *packet,
+static void gem_receive_updatestats(CadenceGEMState *s, const uint8_t *packet,
unsigned bytes)
{
uint64_t octets;
@@ -586,7 +545,7 @@ static unsigned calc_mac_hash(const uint8_t *mac)
* GEM_RM_PROMISCUOUS_ACCEPT, GEM_RX_BROADCAST_ACCEPT,
* GEM_RX_MULTICAST_HASH_ACCEPT or GEM_RX_UNICAST_HASH_ACCEPT
*/
-static int gem_mac_address_filter(GemState *s, const uint8_t *packet)
+static int gem_mac_address_filter(CadenceGEMState *s, const uint8_t *packet)
{
uint8_t *gem_spaddr;
int i;
@@ -636,7 +595,7 @@ static int gem_mac_address_filter(GemState *s, const uint8_t *packet)
return GEM_RX_REJECT;
}
-static void gem_get_rx_desc(GemState *s)
+static void gem_get_rx_desc(CadenceGEMState *s)
{
DB_PRINT("read descriptor 0x%x\n", (unsigned)s->rx_desc_addr);
/* read current descriptor */
@@ -660,7 +619,7 @@ static void gem_get_rx_desc(GemState *s)
*/
static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size)
{
- GemState *s;
+ CadenceGEMState *s;
unsigned rxbufsize, bytes_to_copy;
unsigned rxbuf_offset;
uint8_t rxbuf[2048];
@@ -810,7 +769,7 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size)
* gem_transmit_updatestats:
* Increment transmit statistics.
*/
-static void gem_transmit_updatestats(GemState *s, const uint8_t *packet,
+static void gem_transmit_updatestats(CadenceGEMState *s, const uint8_t *packet,
unsigned bytes)
{
uint64_t octets;
@@ -856,7 +815,7 @@ static void gem_transmit_updatestats(GemState *s, const uint8_t *packet,
* gem_transmit:
* Fish packets out of the descriptor ring and feed them to QEMU
*/
-static void gem_transmit(GemState *s)
+static void gem_transmit(CadenceGEMState *s)
{
unsigned desc[2];
hwaddr packet_desc_addr;
@@ -976,7 +935,7 @@ static void gem_transmit(GemState *s)
}
}
-static void gem_phy_reset(GemState *s)
+static void gem_phy_reset(CadenceGEMState *s)
{
memset(&s->phy_regs[0], 0, sizeof(s->phy_regs));
s->phy_regs[PHY_REG_CONTROL] = 0x1140;
@@ -1004,7 +963,7 @@ static void gem_phy_reset(GemState *s)
static void gem_reset(DeviceState *d)
{
int i;
- GemState *s = GEM(d);
+ CadenceGEMState *s = CADENCE_GEM(d);
DB_PRINT("\n");
@@ -1032,13 +991,13 @@ static void gem_reset(DeviceState *d)
gem_update_int_status(s);
}
-static uint16_t gem_phy_read(GemState *s, unsigned reg_num)
+static uint16_t gem_phy_read(CadenceGEMState *s, unsigned reg_num)
{
DB_PRINT("reg: %d value: 0x%04x\n", reg_num, s->phy_regs[reg_num]);
return s->phy_regs[reg_num];
}
-static void gem_phy_write(GemState *s, unsigned reg_num, uint16_t val)
+static void gem_phy_write(CadenceGEMState *s, unsigned reg_num, uint16_t val)
{
DB_PRINT("reg: %d value: 0x%04x\n", reg_num, val);
@@ -1072,10 +1031,10 @@ static void gem_phy_write(GemState *s, unsigned reg_num, uint16_t val)
*/
static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size)
{
- GemState *s;
+ CadenceGEMState *s;
uint32_t retval;
- s = (GemState *)opaque;
+ s = (CadenceGEMState *)opaque;
offset >>= 2;
retval = s->regs[offset];
@@ -1120,7 +1079,7 @@ static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size)
static void gem_write(void *opaque, hwaddr offset, uint64_t val,
unsigned size)
{
- GemState *s = (GemState *)opaque;
+ CadenceGEMState *s = (CadenceGEMState *)opaque;
uint32_t readonly;
DB_PRINT("offset: 0x%04x write: 0x%08x ", (unsigned)offset, (unsigned)val);
@@ -1226,7 +1185,7 @@ static NetClientInfo net_gem_info = {
static int gem_init(SysBusDevice *sbd)
{
DeviceState *dev = DEVICE(sbd);
- GemState *s = GEM(dev);
+ CadenceGEMState *s = CADENCE_GEM(dev);
DB_PRINT("\n");
@@ -1248,18 +1207,18 @@ static const VMStateDescription vmstate_cadence_gem = {
.version_id = 2,
.minimum_version_id = 2,
.fields = (VMStateField[]) {
- VMSTATE_UINT32_ARRAY(regs, GemState, GEM_MAXREG),
- VMSTATE_UINT16_ARRAY(phy_regs, GemState, 32),
- VMSTATE_UINT8(phy_loop, GemState),
- VMSTATE_UINT32(rx_desc_addr, GemState),
- VMSTATE_UINT32(tx_desc_addr, GemState),
- VMSTATE_BOOL_ARRAY(sar_active, GemState, 4),
+ VMSTATE_UINT32_ARRAY(regs, CadenceGEMState, CADENCE_GEM_MAXREG),
+ VMSTATE_UINT16_ARRAY(phy_regs, CadenceGEMState, 32),
+ VMSTATE_UINT8(phy_loop, CadenceGEMState),
+ VMSTATE_UINT32(rx_desc_addr, CadenceGEMState),
+ VMSTATE_UINT32(tx_desc_addr, CadenceGEMState),
+ VMSTATE_BOOL_ARRAY(sar_active, CadenceGEMState, 4),
VMSTATE_END_OF_LIST(),
}
};
static Property gem_properties[] = {
- DEFINE_NIC_PROPERTIES(GemState, conf),
+ DEFINE_NIC_PROPERTIES(CadenceGEMState, conf),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1277,7 +1236,7 @@ static void gem_class_init(ObjectClass *klass, void *data)
static const TypeInfo gem_info = {
.name = TYPE_CADENCE_GEM,
.parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(GemState),
+ .instance_size = sizeof(CadenceGEMState),
.class_init = gem_class_init,
};
diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c
index 7ce13d2b4..ab607e484 100644
--- a/hw/net/dp8393x.c
+++ b/hw/net/dp8393x.c
@@ -17,20 +17,15 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
-#include "hw/hw.h"
-#include "qemu/timer.h"
+#include "hw/sysbus.h"
+#include "hw/devices.h"
#include "net/net.h"
-#include "hw/mips/mips.h"
+#include "qemu/timer.h"
+#include <zlib.h>
//#define DEBUG_SONIC
-/* Calculate CRCs properly on Rx packets */
-#define SONIC_CALCULATE_RXCRC
-
-#if defined(SONIC_CALCULATE_RXCRC)
-/* For crc32 */
-#include <zlib.h>
-#endif
+#define SONIC_PROM_SIZE 0x1000
#ifdef DEBUG_SONIC
#define DPRINTF(fmt, ...) \
@@ -145,9 +140,14 @@ do { printf("sonic ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
#define SONIC_ISR_PINT 0x0800
#define SONIC_ISR_LCD 0x1000
+#define TYPE_DP8393X "dp8393x"
+#define DP8393X(obj) OBJECT_CHECK(dp8393xState, (obj), TYPE_DP8393X)
+
typedef struct dp8393xState {
+ SysBusDevice parent_obj;
+
/* Hardware */
- int it_shift;
+ uint8_t it_shift;
qemu_irq irq;
#ifdef DEBUG_SONIC
int irq_level;
@@ -156,8 +156,8 @@ typedef struct dp8393xState {
int64_t wt_last_update;
NICConf conf;
NICState *nic;
- MemoryRegion *address_space;
MemoryRegion mmio;
+ MemoryRegion prom;
/* Registers */
uint8_t cam[16][6];
@@ -168,8 +168,8 @@ typedef struct dp8393xState {
int loopback_packet;
/* Memory access */
- void (*memory_rw)(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write);
- void* mem_opaque;
+ void *dma_mr;
+ AddressSpace as;
} dp8393xState;
static void dp8393x_update_irq(dp8393xState *s)
@@ -190,7 +190,7 @@ static void dp8393x_update_irq(dp8393xState *s)
qemu_set_irq(s->irq, level);
}
-static void do_load_cam(dp8393xState *s)
+static void dp8393x_do_load_cam(dp8393xState *s)
{
uint16_t data[8];
int width, size;
@@ -201,9 +201,9 @@ static void do_load_cam(dp8393xState *s)
while (s->regs[SONIC_CDC] & 0x1f) {
/* Fill current entry */
- s->memory_rw(s->mem_opaque,
+ address_space_rw(&s->as,
(s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP],
- (uint8_t *)data, size, 0);
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
s->cam[index][0] = data[1 * width] & 0xff;
s->cam[index][1] = data[1 * width] >> 8;
s->cam[index][2] = data[2 * width] & 0xff;
@@ -220,9 +220,9 @@ static void do_load_cam(dp8393xState *s)
}
/* Read CAM enable */
- s->memory_rw(s->mem_opaque,
+ address_space_rw(&s->as,
(s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP],
- (uint8_t *)data, size, 0);
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
s->regs[SONIC_CE] = data[0 * width];
DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]);
@@ -232,7 +232,7 @@ static void do_load_cam(dp8393xState *s)
dp8393x_update_irq(s);
}
-static void do_read_rra(dp8393xState *s)
+static void dp8393x_do_read_rra(dp8393xState *s)
{
uint16_t data[8];
int width, size;
@@ -240,9 +240,9 @@ static void do_read_rra(dp8393xState *s)
/* Read memory */
width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
size = sizeof(uint16_t) * 4 * width;
- s->memory_rw(s->mem_opaque,
+ address_space_rw(&s->as,
(s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP],
- (uint8_t *)data, size, 0);
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
/* Update SONIC registers */
s->regs[SONIC_CRBA0] = data[0 * width];
@@ -272,7 +272,7 @@ static void do_read_rra(dp8393xState *s)
s->regs[SONIC_CR] &= ~SONIC_CR_RRRA;
}
-static void do_software_reset(dp8393xState *s)
+static void dp8393x_do_software_reset(dp8393xState *s)
{
timer_del(s->watchdog);
@@ -280,7 +280,7 @@ static void do_software_reset(dp8393xState *s)
s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS;
}
-static void set_next_tick(dp8393xState *s)
+static void dp8393x_set_next_tick(dp8393xState *s)
{
uint32_t ticks;
int64_t delay;
@@ -296,7 +296,7 @@ static void set_next_tick(dp8393xState *s)
timer_mod(s->watchdog, s->wt_last_update + delay);
}
-static void update_wt_regs(dp8393xState *s)
+static void dp8393x_update_wt_regs(dp8393xState *s)
{
int64_t elapsed;
uint32_t val;
@@ -311,33 +311,38 @@ static void update_wt_regs(dp8393xState *s)
val -= elapsed / 5000000;
s->regs[SONIC_WT1] = (val >> 16) & 0xffff;
s->regs[SONIC_WT0] = (val >> 0) & 0xffff;
- set_next_tick(s);
+ dp8393x_set_next_tick(s);
}
-static void do_start_timer(dp8393xState *s)
+static void dp8393x_do_start_timer(dp8393xState *s)
{
s->regs[SONIC_CR] &= ~SONIC_CR_STP;
- set_next_tick(s);
+ dp8393x_set_next_tick(s);
}
-static void do_stop_timer(dp8393xState *s)
+static void dp8393x_do_stop_timer(dp8393xState *s)
{
s->regs[SONIC_CR] &= ~SONIC_CR_ST;
- update_wt_regs(s);
+ dp8393x_update_wt_regs(s);
}
-static void do_receiver_enable(dp8393xState *s)
+static int dp8393x_can_receive(NetClientState *nc);
+
+static void dp8393x_do_receiver_enable(dp8393xState *s)
{
s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS;
+ if (dp8393x_can_receive(s->nic->ncs)) {
+ qemu_flush_queued_packets(qemu_get_queue(s->nic));
+ }
}
-static void do_receiver_disable(dp8393xState *s)
+static void dp8393x_do_receiver_disable(dp8393xState *s)
{
s->regs[SONIC_CR] &= ~SONIC_CR_RXEN;
}
-static void do_transmit_packets(dp8393xState *s)
+static void dp8393x_do_transmit_packets(dp8393xState *s)
{
NetClientState *nc = qemu_get_queue(s->nic);
uint16_t data[12];
@@ -353,9 +358,9 @@ static void do_transmit_packets(dp8393xState *s)
(s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_CTDA]);
size = sizeof(uint16_t) * 6 * width;
s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA];
- s->memory_rw(s->mem_opaque,
+ address_space_rw(&s->as,
((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * width,
- (uint8_t *)data, size, 0);
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
tx_len = 0;
/* Update registers */
@@ -379,18 +384,18 @@ static void do_transmit_packets(dp8393xState *s)
if (tx_len + len > sizeof(s->tx_buffer)) {
len = sizeof(s->tx_buffer) - tx_len;
}
- s->memory_rw(s->mem_opaque,
+ address_space_rw(&s->as,
(s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0],
- &s->tx_buffer[tx_len], len, 0);
+ MEMTXATTRS_UNSPECIFIED, &s->tx_buffer[tx_len], len, 0);
tx_len += len;
i++;
if (i != s->regs[SONIC_TFC]) {
/* Read next fragment details */
size = sizeof(uint16_t) * 3 * width;
- s->memory_rw(s->mem_opaque,
+ address_space_rw(&s->as,
((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * i) * width,
- (uint8_t *)data, size, 0);
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
s->regs[SONIC_TSA0] = data[0 * width];
s->regs[SONIC_TSA1] = data[1 * width];
s->regs[SONIC_TFS] = data[2 * width];
@@ -422,16 +427,16 @@ static void do_transmit_packets(dp8393xState *s)
/* Write status */
data[0 * width] = s->regs[SONIC_TCR] & 0x0fff; /* status */
size = sizeof(uint16_t) * width;
- s->memory_rw(s->mem_opaque,
+ address_space_rw(&s->as,
(s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA],
- (uint8_t *)data, size, 1);
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1);
if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) {
/* Read footer of packet */
size = sizeof(uint16_t) * width;
- s->memory_rw(s->mem_opaque,
+ address_space_rw(&s->as,
((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * s->regs[SONIC_TFC]) * width,
- (uint8_t *)data, size, 0);
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
s->regs[SONIC_CTDA] = data[0 * width] & ~0x1;
if (data[0 * width] & 0x1) {
/* EOL detected */
@@ -446,12 +451,12 @@ static void do_transmit_packets(dp8393xState *s)
dp8393x_update_irq(s);
}
-static void do_halt_transmission(dp8393xState *s)
+static void dp8393x_do_halt_transmission(dp8393xState *s)
{
/* Nothing to do */
}
-static void do_command(dp8393xState *s, uint16_t command)
+static void dp8393x_do_command(dp8393xState *s, uint16_t command)
{
if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) {
s->regs[SONIC_CR] &= ~SONIC_CR_RST;
@@ -461,34 +466,36 @@ static void do_command(dp8393xState *s, uint16_t command)
s->regs[SONIC_CR] |= (command & SONIC_CR_MASK);
if (command & SONIC_CR_HTX)
- do_halt_transmission(s);
+ dp8393x_do_halt_transmission(s);
if (command & SONIC_CR_TXP)
- do_transmit_packets(s);
+ dp8393x_do_transmit_packets(s);
if (command & SONIC_CR_RXDIS)
- do_receiver_disable(s);
+ dp8393x_do_receiver_disable(s);
if (command & SONIC_CR_RXEN)
- do_receiver_enable(s);
+ dp8393x_do_receiver_enable(s);
if (command & SONIC_CR_STP)
- do_stop_timer(s);
+ dp8393x_do_stop_timer(s);
if (command & SONIC_CR_ST)
- do_start_timer(s);
+ dp8393x_do_start_timer(s);
if (command & SONIC_CR_RST)
- do_software_reset(s);
+ dp8393x_do_software_reset(s);
if (command & SONIC_CR_RRRA)
- do_read_rra(s);
+ dp8393x_do_read_rra(s);
if (command & SONIC_CR_LCAM)
- do_load_cam(s);
+ dp8393x_do_load_cam(s);
}
-static uint16_t read_register(dp8393xState *s, int reg)
+static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size)
{
+ dp8393xState *s = opaque;
+ int reg = addr >> s->it_shift;
uint16_t val = 0;
switch (reg) {
/* Update data before reading it */
case SONIC_WT0:
case SONIC_WT1:
- update_wt_regs(s);
+ dp8393x_update_wt_regs(s);
val = s->regs[reg];
break;
/* Accept read to some registers only when in reset mode */
@@ -510,14 +517,18 @@ static uint16_t read_register(dp8393xState *s, int reg)
return val;
}
-static void write_register(dp8393xState *s, int reg, uint16_t val)
+static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned int size)
{
- DPRINTF("write 0x%04x to reg %s\n", val, reg_names[reg]);
+ dp8393xState *s = opaque;
+ int reg = addr >> s->it_shift;
+
+ DPRINTF("write 0x%04x to reg %s\n", (uint16_t)data, reg_names[reg]);
switch (reg) {
/* Command register */
case SONIC_CR:
- do_command(s, val);
+ dp8393x_do_command(s, data);
break;
/* Prevent write to read-only registers */
case SONIC_CAP2:
@@ -530,63 +541,74 @@ static void write_register(dp8393xState *s, int reg, uint16_t val)
/* Accept write to some registers only when in reset mode */
case SONIC_DCR:
if (s->regs[SONIC_CR] & SONIC_CR_RST) {
- s->regs[reg] = val & 0xbfff;
+ s->regs[reg] = data & 0xbfff;
} else {
DPRINTF("writing to DCR invalid\n");
}
break;
case SONIC_DCR2:
if (s->regs[SONIC_CR] & SONIC_CR_RST) {
- s->regs[reg] = val & 0xf017;
+ s->regs[reg] = data & 0xf017;
} else {
DPRINTF("writing to DCR2 invalid\n");
}
break;
/* 12 lower bytes are Read Only */
case SONIC_TCR:
- s->regs[reg] = val & 0xf000;
+ s->regs[reg] = data & 0xf000;
break;
/* 9 lower bytes are Read Only */
case SONIC_RCR:
- s->regs[reg] = val & 0xffe0;
+ s->regs[reg] = data & 0xffe0;
break;
/* Ignore most significant bit */
case SONIC_IMR:
- s->regs[reg] = val & 0x7fff;
+ s->regs[reg] = data & 0x7fff;
dp8393x_update_irq(s);
break;
/* Clear bits by writing 1 to them */
case SONIC_ISR:
- val &= s->regs[reg];
- s->regs[reg] &= ~val;
- if (val & SONIC_ISR_RBE) {
- do_read_rra(s);
+ data &= s->regs[reg];
+ s->regs[reg] &= ~data;
+ if (data & SONIC_ISR_RBE) {
+ dp8393x_do_read_rra(s);
}
dp8393x_update_irq(s);
+ if (dp8393x_can_receive(s->nic->ncs)) {
+ qemu_flush_queued_packets(qemu_get_queue(s->nic));
+ }
break;
/* Ignore least significant bit */
case SONIC_RSA:
case SONIC_REA:
case SONIC_RRP:
case SONIC_RWP:
- s->regs[reg] = val & 0xfffe;
+ s->regs[reg] = data & 0xfffe;
break;
/* Invert written value for some registers */
case SONIC_CRCT:
case SONIC_FAET:
case SONIC_MPT:
- s->regs[reg] = val ^ 0xffff;
+ s->regs[reg] = data ^ 0xffff;
break;
/* All other registers have no special contrainst */
default:
- s->regs[reg] = val;
+ s->regs[reg] = data;
}
if (reg == SONIC_WT0 || reg == SONIC_WT1) {
- set_next_tick(s);
+ dp8393x_set_next_tick(s);
}
}
+static const MemoryRegionOps dp8393x_ops = {
+ .read = dp8393x_read,
+ .write = dp8393x_write,
+ .impl.min_access_size = 2,
+ .impl.max_access_size = 2,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
static void dp8393x_watchdog(void *opaque)
{
dp8393xState *s = opaque;
@@ -597,84 +619,14 @@ static void dp8393x_watchdog(void *opaque)
s->regs[SONIC_WT1] = 0xffff;
s->regs[SONIC_WT0] = 0xffff;
- set_next_tick(s);
+ dp8393x_set_next_tick(s);
/* Signal underflow */
s->regs[SONIC_ISR] |= SONIC_ISR_TC;
dp8393x_update_irq(s);
}
-static uint32_t dp8393x_readw(void *opaque, hwaddr addr)
-{
- dp8393xState *s = opaque;
- int reg;
-
- if ((addr & ((1 << s->it_shift) - 1)) != 0) {
- return 0;
- }
-
- reg = addr >> s->it_shift;
- return read_register(s, reg);
-}
-
-static uint32_t dp8393x_readb(void *opaque, hwaddr addr)
-{
- uint16_t v = dp8393x_readw(opaque, addr & ~0x1);
- return (v >> (8 * (addr & 0x1))) & 0xff;
-}
-
-static uint32_t dp8393x_readl(void *opaque, hwaddr addr)
-{
- uint32_t v;
- v = dp8393x_readw(opaque, addr);
- v |= dp8393x_readw(opaque, addr + 2) << 16;
- return v;
-}
-
-static void dp8393x_writew(void *opaque, hwaddr addr, uint32_t val)
-{
- dp8393xState *s = opaque;
- int reg;
-
- if ((addr & ((1 << s->it_shift) - 1)) != 0) {
- return;
- }
-
- reg = addr >> s->it_shift;
-
- write_register(s, reg, (uint16_t)val);
-}
-
-static void dp8393x_writeb(void *opaque, hwaddr addr, uint32_t val)
-{
- uint16_t old_val = dp8393x_readw(opaque, addr & ~0x1);
-
- switch (addr & 3) {
- case 0:
- val = val | (old_val & 0xff00);
- break;
- case 1:
- val = (val << 8) | (old_val & 0x00ff);
- break;
- }
- dp8393x_writew(opaque, addr & ~0x1, val);
-}
-
-static void dp8393x_writel(void *opaque, hwaddr addr, uint32_t val)
-{
- dp8393x_writew(opaque, addr, val & 0xffff);
- dp8393x_writew(opaque, addr + 2, (val >> 16) & 0xffff);
-}
-
-static const MemoryRegionOps dp8393x_ops = {
- .old_mmio = {
- .read = { dp8393x_readb, dp8393x_readw, dp8393x_readl, },
- .write = { dp8393x_writeb, dp8393x_writew, dp8393x_writel, },
- },
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int nic_can_receive(NetClientState *nc)
+static int dp8393x_can_receive(NetClientState *nc)
{
dp8393xState *s = qemu_get_nic_opaque(nc);
@@ -685,16 +637,12 @@ static int nic_can_receive(NetClientState *nc)
return 1;
}
-static int receive_filter(dp8393xState *s, const uint8_t * buf, int size)
+static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf,
+ int size)
{
static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
int i;
- /* Check for runt packet (remember that checksum is not there) */
- if (size < 64 - 4) {
- return (s->regs[SONIC_RCR] & SONIC_RCR_RNT) ? 0 : -1;
- }
-
/* Check promiscuous mode */
if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) {
return 0;
@@ -723,7 +671,8 @@ static int receive_filter(dp8393xState *s, const uint8_t * buf, int size)
return -1;
}
-static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
+static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf,
+ size_t size)
{
dp8393xState *s = qemu_get_nic_opaque(nc);
uint16_t data[10];
@@ -737,7 +686,7 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER |
SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC);
- packet_type = receive_filter(s, buf, size);
+ packet_type = dp8393x_receive_filter(s, buf, size);
if (packet_type < 0) {
DPRINTF("packet not for netcard\n");
return -1;
@@ -750,7 +699,8 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
/* Are we still in resource exhaustion? */
size = sizeof(uint16_t) * 1 * width;
address = ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width;
- s->memory_rw(s->mem_opaque, address, (uint8_t*)data, size, 0);
+ address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED,
+ (uint8_t *)data, size, 0);
if (data[0 * width] & 0x1) {
/* Still EOL ; stop reception */
return -1;
@@ -764,18 +714,16 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0];
/* Calculate the ethernet checksum */
-#ifdef SONIC_CALCULATE_RXCRC
checksum = cpu_to_le32(crc32(0, buf, rx_len));
-#else
- checksum = 0;
-#endif
/* Put packet into RBA */
DPRINTF("Receive packet at %08x\n", (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]);
address = (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0];
- s->memory_rw(s->mem_opaque, address, (uint8_t*)buf, rx_len, 1);
+ address_space_rw(&s->as, address,
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)buf, rx_len, 1);
address += rx_len;
- s->memory_rw(s->mem_opaque, address, (uint8_t*)&checksum, 4, 1);
+ address_space_rw(&s->as, address,
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)&checksum, 4, 1);
rx_len += 4;
s->regs[SONIC_CRBA1] = address >> 16;
s->regs[SONIC_CRBA0] = address & 0xffff;
@@ -803,29 +751,30 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
data[3 * width] = s->regs[SONIC_TRBA1]; /* pkt_ptr1 */
data[4 * width] = s->regs[SONIC_RSC]; /* seq_no */
size = sizeof(uint16_t) * 5 * width;
- s->memory_rw(s->mem_opaque, (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA], (uint8_t *)data, size, 1);
+ address_space_rw(&s->as, (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA],
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1);
/* Move to next descriptor */
size = sizeof(uint16_t) * width;
- s->memory_rw(s->mem_opaque,
+ address_space_rw(&s->as,
((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width,
- (uint8_t *)data, size, 0);
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
s->regs[SONIC_LLFA] = data[0 * width];
if (s->regs[SONIC_LLFA] & 0x1) {
/* EOL detected */
s->regs[SONIC_ISR] |= SONIC_ISR_RDE;
} else {
data[0 * width] = 0; /* in_use */
- s->memory_rw(s->mem_opaque,
+ address_space_rw(&s->as,
((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 6 * width,
- (uint8_t *)data, size, 1);
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, sizeof(uint16_t), 1);
s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff);
if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) {
/* Read next RRA */
- do_read_rra(s);
+ dp8393x_do_read_rra(s);
}
}
@@ -835,11 +784,12 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
return size;
}
-static void nic_reset(void *opaque)
+static void dp8393x_reset(DeviceState *dev)
{
- dp8393xState *s = opaque;
+ dp8393xState *s = DP8393X(dev);
timer_del(s->watchdog);
+ memset(s->regs, 0, sizeof(s->regs));
s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS;
s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR);
s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT);
@@ -862,39 +812,99 @@ static void nic_reset(void *opaque)
static NetClientInfo net_dp83932_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = nic_can_receive,
- .receive = nic_receive,
+ .can_receive = dp8393x_can_receive,
+ .receive = dp8393x_receive,
};
-void dp83932_init(NICInfo *nd, hwaddr base, int it_shift,
- MemoryRegion *address_space,
- qemu_irq irq, void* mem_opaque,
- void (*memory_rw)(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write))
+static void dp8393x_instance_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ dp8393xState *s = DP8393X(obj);
+
+ sysbus_init_mmio(sbd, &s->mmio);
+ sysbus_init_mmio(sbd, &s->prom);
+ sysbus_init_irq(sbd, &s->irq);
+}
+
+static void dp8393x_realize(DeviceState *dev, Error **errp)
{
- dp8393xState *s;
+ dp8393xState *s = DP8393X(dev);
+ int i, checksum;
+ uint8_t *prom;
+ Error *local_err = NULL;
- qemu_check_nic_model(nd, "dp83932");
+ address_space_init(&s->as, s->dma_mr, "dp8393x");
+ memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s,
+ "dp8393x-regs", 0x40 << s->it_shift);
- s = g_malloc0(sizeof(dp8393xState));
+ s->nic = qemu_new_nic(&net_dp83932_info, &s->conf,
+ object_get_typename(OBJECT(dev)), dev->id, s);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
- s->address_space = address_space;
- s->mem_opaque = mem_opaque;
- s->memory_rw = memory_rw;
- s->it_shift = it_shift;
- s->irq = irq;
s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s);
s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */
- s->conf.macaddr = nd->macaddr;
- s->conf.peers.ncs[0] = nd->netdev;
+ memory_region_init_ram(&s->prom, OBJECT(dev),
+ "dp8393x-prom", SONIC_PROM_SIZE, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ memory_region_set_readonly(&s->prom, true);
+ prom = memory_region_get_ram_ptr(&s->prom);
+ checksum = 0;
+ for (i = 0; i < 6; i++) {
+ prom[i] = s->conf.macaddr.a[i];
+ checksum += prom[i];
+ if (checksum > 0xff) {
+ checksum = (checksum + 1) & 0xff;
+ }
+ }
+ prom[7] = 0xff - checksum;
+}
+
+static const VMStateDescription vmstate_dp8393x = {
+ .name = "dp8393x",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField []) {
+ VMSTATE_BUFFER_UNSAFE(cam, dp8393xState, 0, 16 * 6),
+ VMSTATE_UINT16_ARRAY(regs, dp8393xState, 0x40),
+ VMSTATE_END_OF_LIST()
+ }
+};
- s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, nd->model, nd->name, s);
+static Property dp8393x_properties[] = {
+ DEFINE_NIC_PROPERTIES(dp8393xState, conf),
+ DEFINE_PROP_PTR("dma_mr", dp8393xState, dma_mr),
+ DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
- qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
- qemu_register_reset(nic_reset, s);
- nic_reset(s);
+static void dp8393x_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
+ dc->realize = dp8393x_realize;
+ dc->reset = dp8393x_reset;
+ dc->vmsd = &vmstate_dp8393x;
+ dc->props = dp8393x_properties;
+ /* Reason: dma_mr property can't be set */
+ dc->cannot_instantiate_with_device_add_yet = true;
+}
+
+static const TypeInfo dp8393x_info = {
+ .name = TYPE_DP8393X,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(dp8393xState),
+ .instance_init = dp8393x_instance_init,
+ .class_init = dp8393x_class_init,
+};
- memory_region_init_io(&s->mmio, NULL, &dp8393x_ops, s,
- "dp8393x", 0x40 << it_shift);
- memory_region_add_subregion(address_space, base, &s->mmio);
+static void dp8393x_register_types(void)
+{
+ type_register_static(&dp8393x_info);
}
+
+type_init(dp8393x_register_types)
diff --git a/hw/net/e1000.c b/hw/net/e1000.c
index 091d61acc..09c9e9d53 100644
--- a/hw/net/e1000.c
+++ b/hw/net/e1000.c
@@ -185,6 +185,9 @@ e1000_link_up(E1000State *s)
{
s->mac_reg[STATUS] |= E1000_STATUS_LU;
s->phy_reg[PHY_STATUS] |= MII_SR_LINK_STATUS;
+
+ /* E1000_STATUS_LU is tested by e1000_can_receive() */
+ qemu_flush_queued_packets(qemu_get_queue(s->nic));
}
static bool
@@ -737,7 +740,8 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp)
memmove(tp->data, tp->header, tp->hdr_len);
tp->size = tp->hdr_len;
}
- } while (split_size -= bytes);
+ split_size -= bytes;
+ } while (bytes && split_size);
} else if (!tp->tse && tp->cptse) {
// context descriptor TSE is not set, while data descriptor TSE is set
DBGOUT(TXERR, "TCP segmentation error\n");
@@ -1370,6 +1374,7 @@ static const VMStateDescription vmstate_e1000_mit_state = {
.name = "e1000/mit_state",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = e1000_mit_state_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT32(mac_reg[RDTR], E1000State),
VMSTATE_UINT32(mac_reg[RADV], E1000State),
@@ -1457,13 +1462,9 @@ static const VMStateDescription vmstate_e1000 = {
VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, VFTA, 128),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_e1000_mit_state,
- .needed = e1000_mit_state_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_e1000_mit_state,
+ NULL
}
};
diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c
index c374c1a25..60333b7fc 100644
--- a/hw/net/eepro100.c
+++ b/hw/net/eepro100.c
@@ -1617,16 +1617,6 @@ static const MemoryRegionOps eepro100_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
-static int nic_can_receive(NetClientState *nc)
-{
- EEPRO100State *s = qemu_get_nic_opaque(nc);
- TRACE(RXTX, logout("%p\n", s));
- return get_ru_state(s) == ru_ready;
-#if 0
- return !eepro100_buffer_full(s);
-#endif
-}
-
static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
{
/* TODO:
@@ -1844,7 +1834,6 @@ static void pci_nic_uninit(PCIDevice *pci_dev)
static NetClientInfo net_eepro100_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = nic_can_receive,
.receive = nic_receive,
};
diff --git a/hw/net/etraxfs_eth.c b/hw/net/etraxfs_eth.c
index 4773dea92..d6002750f 100644
--- a/hw/net/etraxfs_eth.c
+++ b/hw/net/etraxfs_eth.c
@@ -520,11 +520,6 @@ static int eth_match_groupaddr(ETRAXFSEthState *eth, const unsigned char *sa)
return match;
}
-static int eth_can_receive(NetClientState *nc)
-{
- return 1;
-}
-
static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size)
{
unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
@@ -584,7 +579,6 @@ static const MemoryRegionOps eth_ops = {
static NetClientInfo net_etraxfs_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = eth_can_receive,
.receive = eth_receive,
.link_status_changed = eth_set_link,
};
diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c
index c57365fde..0f5cf4477 100644
--- a/hw/net/fsl_etsec/etsec.c
+++ b/hw/net/fsl_etsec/etsec.c
@@ -338,25 +338,26 @@ static void etsec_reset(DeviceState *d)
MII_SR_100X_FD_CAPS | MII_SR_100T4_CAPS;
}
-static int etsec_can_receive(NetClientState *nc)
-{
- eTSEC *etsec = qemu_get_nic_opaque(nc);
-
- return etsec->rx_buffer_len == 0;
-}
-
static ssize_t etsec_receive(NetClientState *nc,
const uint8_t *buf,
size_t size)
{
+ ssize_t ret;
eTSEC *etsec = qemu_get_nic_opaque(nc);
#if defined(HEX_DUMP)
fprintf(stderr, "%s receive size:%d\n", etsec->nic->nc.name, size);
qemu_hexdump(buf, stderr, "", size);
#endif
- etsec_rx_ring_write(etsec, buf, size);
- return size;
+ /* Flush is unnecessary as are already in receiving path */
+ etsec->need_flush = false;
+ ret = etsec_rx_ring_write(etsec, buf, size);
+ if (ret == 0) {
+ /* The packet will be queued, let's flush it when buffer is avilable
+ * again. */
+ etsec->need_flush = true;
+ }
+ return ret;
}
@@ -370,7 +371,6 @@ static void etsec_set_link_status(NetClientState *nc)
static NetClientInfo net_etsec_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = etsec_can_receive,
.receive = etsec_receive,
.link_status_changed = etsec_set_link_status,
};
diff --git a/hw/net/fsl_etsec/etsec.h b/hw/net/fsl_etsec/etsec.h
index 78d2c57ed..e7dc0a4b9 100644
--- a/hw/net/fsl_etsec/etsec.h
+++ b/hw/net/fsl_etsec/etsec.h
@@ -144,6 +144,8 @@ typedef struct eTSEC {
QEMUBH *bh;
struct ptimer_state *ptimer;
+ /* Whether we should flush the rx queue when buffer becomes available. */
+ bool need_flush;
} eTSEC;
#define TYPE_ETSEC_COMMON "eTSEC"
@@ -162,7 +164,7 @@ DeviceState *etsec_create(hwaddr base,
void etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr);
void etsec_walk_rx_ring(eTSEC *etsec, int ring_nbr);
-void etsec_rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size);
+ssize_t etsec_rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size);
void etsec_write_miim(eTSEC *etsec,
eTSEC_Register *reg,
diff --git a/hw/net/fsl_etsec/rings.c b/hw/net/fsl_etsec/rings.c
index d4a494f6a..68e7b6d16 100644
--- a/hw/net/fsl_etsec/rings.c
+++ b/hw/net/fsl_etsec/rings.c
@@ -481,40 +481,42 @@ static void rx_init_frame(eTSEC *etsec, const uint8_t *buf, size_t size)
etsec->rx_buffer_len, etsec->rx_padding);
}
-void etsec_rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size)
+ssize_t etsec_rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size)
{
int ring_nbr = 0; /* Always use ring0 (no filer) */
if (etsec->rx_buffer_len != 0) {
RING_DEBUG("%s: We can't receive now,"
" a buffer is already in the pipe\n", __func__);
- return;
+ return 0;
}
if (etsec->regs[RSTAT].value & 1 << (23 - ring_nbr)) {
RING_DEBUG("%s: The ring is halted\n", __func__);
- return;
+ return -1;
}
if (etsec->regs[DMACTRL].value & DMACTRL_GRS) {
RING_DEBUG("%s: Graceful receive stop\n", __func__);
- return;
+ return -1;
}
if (!(etsec->regs[MACCFG1].value & MACCFG1_RX_EN)) {
RING_DEBUG("%s: MAC Receive not enabled\n", __func__);
- return;
+ return -1;
}
if ((etsec->regs[RCTRL].value & RCTRL_RSF) && (size < 60)) {
/* CRC is not in the packet yet, so short frame is below 60 bytes */
RING_DEBUG("%s: Drop short frame\n", __func__);
- return;
+ return -1;
}
rx_init_frame(etsec, buf, size);
etsec_walk_rx_ring(etsec, ring_nbr);
+
+ return size;
}
void etsec_walk_rx_ring(eTSEC *etsec, int ring_nbr)
@@ -644,6 +646,9 @@ void etsec_walk_rx_ring(eTSEC *etsec, int ring_nbr)
} else {
etsec->rx_buffer_len = 0;
etsec->rx_buffer = NULL;
+ if (etsec->need_flush) {
+ qemu_flush_queued_packets(qemu_get_queue(etsec->nic));
+ }
}
RING_DEBUG("eTSEC End of ring_write: remaining_data:%zu\n", remaining_data);
diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c
index f169c383d..4f0e840f0 100644
--- a/hw/net/lan9118.c
+++ b/hw/net/lan9118.c
@@ -461,11 +461,6 @@ static void lan9118_reset(DeviceState *d)
lan9118_reload_eeprom(s);
}
-static int lan9118_can_receive(NetClientState *nc)
-{
- return 1;
-}
-
static void rx_fifo_push(lan9118_state *s, uint32_t val)
{
int fifo_pos;
@@ -1312,7 +1307,6 @@ static const MemoryRegionOps lan9118_16bit_mem_ops = {
static NetClientInfo net_lan9118_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = lan9118_can_receive,
.receive = lan9118_receive,
.link_status_changed = lan9118_set_link,
};
diff --git a/hw/net/lance.c b/hw/net/lance.c
index 4baa0169e..780b39d65 100644
--- a/hw/net/lance.c
+++ b/hw/net/lance.c
@@ -94,7 +94,6 @@ static const MemoryRegionOps lance_mem_ops = {
static NetClientInfo net_lance_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = pcnet_can_receive,
.receive = pcnet_receive,
.link_status_changed = pcnet_set_link_status,
};
diff --git a/hw/net/mcf_fec.c b/hw/net/mcf_fec.c
index 0255612f1..21928f9f3 100644
--- a/hw/net/mcf_fec.c
+++ b/hw/net/mcf_fec.c
@@ -8,6 +8,7 @@
#include "hw/hw.h"
#include "net/net.h"
#include "hw/m68k/mcf.h"
+#include "hw/net/mii.h"
/* For crc32 */
#include <zlib.h>
#include "exec/address-spaces.h"
@@ -195,12 +196,14 @@ static void mcf_fec_do_tx(mcf_fec_state *s)
static void mcf_fec_enable_rx(mcf_fec_state *s)
{
+ NetClientState *nc = qemu_get_queue(s->nic);
mcf_fec_bd bd;
mcf_fec_read_bd(&bd, s->rx_descriptor);
s->rx_enabled = ((bd.flags & FEC_BD_E) != 0);
- if (!s->rx_enabled)
- DPRINTF("RX buffer full\n");
+ if (s->rx_enabled) {
+ qemu_flush_queued_packets(nc);
+ }
}
static void mcf_fec_reset(mcf_fec_state *s)
@@ -216,6 +219,51 @@ static void mcf_fec_reset(mcf_fec_state *s)
s->rfsr = 0x500;
}
+#define MMFR_WRITE_OP (1 << 28)
+#define MMFR_READ_OP (2 << 28)
+#define MMFR_PHYADDR(v) (((v) >> 23) & 0x1f)
+#define MMFR_REGNUM(v) (((v) >> 18) & 0x1f)
+
+static uint64_t mcf_fec_read_mdio(mcf_fec_state *s)
+{
+ uint64_t v;
+
+ if (s->mmfr & MMFR_WRITE_OP)
+ return s->mmfr;
+ if (MMFR_PHYADDR(s->mmfr) != 1)
+ return s->mmfr |= 0xffff;
+
+ switch (MMFR_REGNUM(s->mmfr)) {
+ case MII_BMCR:
+ v = MII_BMCR_SPEED | MII_BMCR_AUTOEN | MII_BMCR_FD;
+ break;
+ case MII_BMSR:
+ v = MII_BMSR_100TX_FD | MII_BMSR_100TX_HD | MII_BMSR_10T_FD |
+ MII_BMSR_10T_HD | MII_BMSR_MFPS | MII_BMSR_AN_COMP |
+ MII_BMSR_AUTONEG | MII_BMSR_LINK_ST;
+ break;
+ case MII_PHYID1:
+ v = DP83848_PHYID1;
+ break;
+ case MII_PHYID2:
+ v = DP83848_PHYID2;
+ break;
+ case MII_ANAR:
+ v = MII_ANAR_TXFD | MII_ANAR_TX | MII_ANAR_10FD |
+ MII_ANAR_10 | MII_ANAR_CSMACD;
+ break;
+ case MII_ANLPAR:
+ v = MII_ANLPAR_ACK | MII_ANLPAR_TXFD | MII_ANLPAR_TX |
+ MII_ANLPAR_10FD | MII_ANLPAR_10 | MII_ANLPAR_CSMACD;
+ break;
+ default:
+ v = 0xffff;
+ break;
+ }
+ s->mmfr = (s->mmfr & ~0xffff) | v;
+ return s->mmfr;
+}
+
static uint64_t mcf_fec_read(void *opaque, hwaddr addr,
unsigned size)
{
@@ -226,7 +274,7 @@ static uint64_t mcf_fec_read(void *opaque, hwaddr addr,
case 0x010: return s->rx_enabled ? (1 << 24) : 0; /* RDAR */
case 0x014: return 0; /* TDAR */
case 0x024: return s->ecr;
- case 0x040: return s->mmfr;
+ case 0x040: return mcf_fec_read_mdio(s);
case 0x044: return s->mscr;
case 0x064: return 0; /* MIBC */
case 0x084: return s->rcr;
@@ -287,8 +335,8 @@ static void mcf_fec_write(void *opaque, hwaddr addr,
}
break;
case 0x040:
- /* TODO: Implement MII. */
s->mmfr = value;
+ s->eir |= FEC_INT_MII;
break;
case 0x044:
s->mscr = value & 0xfe;
@@ -351,10 +399,30 @@ static void mcf_fec_write(void *opaque, hwaddr addr,
mcf_fec_update(s);
}
-static int mcf_fec_can_receive(NetClientState *nc)
+static int mcf_fec_have_receive_space(mcf_fec_state *s, size_t want)
{
- mcf_fec_state *s = qemu_get_nic_opaque(nc);
- return s->rx_enabled;
+ mcf_fec_bd bd;
+ uint32_t addr;
+
+ /* Walk descriptor list to determine if we have enough buffer */
+ addr = s->rx_descriptor;
+ while (want > 0) {
+ mcf_fec_read_bd(&bd, addr);
+ if ((bd.flags & FEC_BD_E) == 0) {
+ return 0;
+ }
+ if (want < s->emrbr) {
+ return 1;
+ }
+ want -= s->emrbr;
+ /* Advance to the next descriptor. */
+ if ((bd.flags & FEC_BD_W) != 0) {
+ addr = s->erdsr;
+ } else {
+ addr += 8;
+ }
+ }
+ return 0;
}
static ssize_t mcf_fec_receive(NetClientState *nc, const uint8_t *buf, size_t size)
@@ -367,10 +435,11 @@ static ssize_t mcf_fec_receive(NetClientState *nc, const uint8_t *buf, size_t si
uint32_t buf_addr;
uint8_t *crc_ptr;
unsigned int buf_len;
+ size_t retsize;
DPRINTF("do_rx len %d\n", size);
if (!s->rx_enabled) {
- fprintf(stderr, "mcf_fec_receive: Unexpected packet\n");
+ return -1;
}
/* 4 bytes for the CRC. */
size += 4;
@@ -385,17 +454,14 @@ static ssize_t mcf_fec_receive(NetClientState *nc, const uint8_t *buf, size_t si
if (size > (s->rcr >> 16)) {
flags |= FEC_BD_LG;
}
+ /* Check if we have enough space in current descriptors */
+ if (!mcf_fec_have_receive_space(s, size)) {
+ return 0;
+ }
addr = s->rx_descriptor;
+ retsize = size;
while (size > 0) {
mcf_fec_read_bd(&bd, addr);
- if ((bd.flags & FEC_BD_E) == 0) {
- /* No descriptors available. Bail out. */
- /* FIXME: This is wrong. We should probably either save the
- remainder for when more RX buffers are available, or
- flag an error. */
- fprintf(stderr, "mcf_fec: Lost end of frame\n");
- break;
- }
buf_len = (size <= s->emrbr) ? size: s->emrbr;
bd.length = buf_len;
size -= buf_len;
@@ -430,7 +496,7 @@ static ssize_t mcf_fec_receive(NetClientState *nc, const uint8_t *buf, size_t si
s->rx_descriptor = addr;
mcf_fec_enable_rx(s);
mcf_fec_update(s);
- return size;
+ return retsize;
}
static const MemoryRegionOps mcf_fec_ops = {
@@ -442,7 +508,6 @@ static const MemoryRegionOps mcf_fec_ops = {
static NetClientInfo net_mcf_fec_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = mcf_fec_can_receive,
.receive = mcf_fec_receive,
};
diff --git a/hw/net/milkymist-minimac2.c b/hw/net/milkymist-minimac2.c
index f06afaa58..5d1cf0851 100644
--- a/hw/net/milkymist-minimac2.c
+++ b/hw/net/milkymist-minimac2.c
@@ -303,8 +303,7 @@ static ssize_t minimac2_rx(NetClientState *nc, const uint8_t *buf, size_t size)
r_state = R_STATE1;
rx_buf = s->rx1_buf;
} else {
- trace_milkymist_minimac2_drop_rx_frame(buf);
- return size;
+ return 0;
}
/* assemble frame */
@@ -354,6 +353,18 @@ minimac2_read(void *opaque, hwaddr addr, unsigned size)
return r;
}
+static int minimac2_can_rx(MilkymistMinimac2State *s)
+{
+ if (s->regs[R_STATE0] == STATE_LOADED) {
+ return 1;
+ }
+ if (s->regs[R_STATE1] == STATE_LOADED) {
+ return 1;
+ }
+
+ return 0;
+}
+
static void
minimac2_write(void *opaque, hwaddr addr, uint64_t value,
unsigned size)
@@ -387,6 +398,9 @@ minimac2_write(void *opaque, hwaddr addr, uint64_t value,
case R_STATE1:
s->regs[addr] = value;
update_rx_interrupt(s);
+ if (minimac2_can_rx(s)) {
+ qemu_flush_queued_packets(qemu_get_queue(s->nic));
+ }
break;
case R_SETUP:
case R_COUNT0:
@@ -411,20 +425,6 @@ static const MemoryRegionOps minimac2_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static int minimac2_can_rx(NetClientState *nc)
-{
- MilkymistMinimac2State *s = qemu_get_nic_opaque(nc);
-
- if (s->regs[R_STATE0] == STATE_LOADED) {
- return 1;
- }
- if (s->regs[R_STATE1] == STATE_LOADED) {
- return 1;
- }
-
- return 0;
-}
-
static void milkymist_minimac2_reset(DeviceState *d)
{
MilkymistMinimac2State *s = MILKYMIST_MINIMAC2(d);
@@ -445,7 +445,6 @@ static void milkymist_minimac2_reset(DeviceState *d)
static NetClientInfo net_milkymist_minimac2_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = minimac2_can_rx,
.receive = minimac2_rx,
};
diff --git a/hw/net/mipsnet.c b/hw/net/mipsnet.c
index c813e0caa..f261011a2 100644
--- a/hw/net/mipsnet.c
+++ b/hw/net/mipsnet.c
@@ -80,7 +80,7 @@ static ssize_t mipsnet_receive(NetClientState *nc, const uint8_t *buf, size_t si
trace_mipsnet_receive(size);
if (!mipsnet_can_receive(nc))
- return -1;
+ return 0;
s->busy = 1;
@@ -134,6 +134,9 @@ static uint64_t mipsnet_ioport_read(void *opaque, hwaddr addr,
if (s->rx_count) {
s->rx_count--;
ret = s->rx_buffer[s->rx_read++];
+ if (mipsnet_can_receive(s->nic->ncs)) {
+ qemu_flush_queued_packets(qemu_get_queue(s->nic));
+ }
}
break;
/* Reads as zero. */
@@ -170,6 +173,9 @@ static void mipsnet_ioport_write(void *opaque, hwaddr addr,
}
s->busy = !!s->intctl;
mipsnet_update_irq(s);
+ if (mipsnet_can_receive(s->nic->ncs)) {
+ qemu_flush_queued_packets(qemu_get_queue(s->nic));
+ }
break;
case MIPSNET_TX_DATA_BUFFER:
s->tx_buffer[s->tx_written++] = val;
@@ -214,7 +220,6 @@ static const VMStateDescription vmstate_mipsnet = {
static NetClientInfo net_mipsnet_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = mipsnet_can_receive,
.receive = mipsnet_receive,
};
diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c
index 3492db366..2bdb4c927 100644
--- a/hw/net/ne2000.c
+++ b/hw/net/ne2000.c
@@ -230,6 +230,9 @@ ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
}
index = s->curpag << 8;
+ if (index >= NE2000_PMEM_END) {
+ index = s->start;
+ }
/* 4 bytes for header */
total_len = size + 4;
/* address for next packet (4 bytes for CRC) */
@@ -253,7 +256,7 @@ ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
if (index <= s->stop)
avail = s->stop - index;
else
- avail = 0;
+ break;
len = size;
if (len > avail)
len = avail;
@@ -315,13 +318,19 @@ static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val)
offset = addr | (page << 4);
switch(offset) {
case EN0_STARTPG:
- s->start = val << 8;
+ if (val << 8 <= NE2000_PMEM_END) {
+ s->start = val << 8;
+ }
break;
case EN0_STOPPG:
- s->stop = val << 8;
+ if (val << 8 <= NE2000_PMEM_END) {
+ s->stop = val << 8;
+ }
break;
case EN0_BOUNDARY:
- s->boundary = val;
+ if (val << 8 < NE2000_PMEM_END) {
+ s->boundary = val;
+ }
break;
case EN0_IMR:
s->imr = val;
@@ -362,7 +371,9 @@ static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val)
s->phys[offset - EN1_PHYS] = val;
break;
case EN1_CURPAG:
- s->curpag = val;
+ if (val << 8 < NE2000_PMEM_END) {
+ s->curpag = val;
+ }
break;
case EN1_MULT ... EN1_MULT + 7:
s->mult[offset - EN1_MULT] = val;
diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c
index 8305d1bdf..b4d60b812 100644
--- a/hw/net/pcnet-pci.c
+++ b/hw/net/pcnet-pci.c
@@ -273,7 +273,6 @@ static void pci_pcnet_uninit(PCIDevice *dev)
static NetClientInfo net_pci_pcnet_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = pcnet_can_receive,
.receive = pcnet_receive,
.link_status_changed = pcnet_set_link_status,
};
diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c
index bdfd38f4c..34373767d 100644
--- a/hw/net/pcnet.c
+++ b/hw/net/pcnet.c
@@ -995,15 +995,6 @@ static int pcnet_tdte_poll(PCNetState *s)
return !!(CSR_CXST(s) & 0x8000);
}
-int pcnet_can_receive(NetClientState *nc)
-{
- PCNetState *s = qemu_get_nic_opaque(nc);
- if (CSR_STOP(s) || CSR_SPND(s))
- return 0;
-
- return sizeof(s->buffer)-16;
-}
-
#define MIN_BUF_SIZE 60
ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
@@ -1241,6 +1232,14 @@ static void pcnet_transmit(PCNetState *s)
}
bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT);
+
+ /* if multi-tmd packet outsizes s->buffer then skip it silently.
+ Note: this is not what real hw does */
+ if (s->xmit_pos + bcnt > sizeof(s->buffer)) {
+ s->xmit_pos = -1;
+ goto txdone;
+ }
+
s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr),
s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s));
s->xmit_pos += bcnt;
diff --git a/hw/net/pcnet.h b/hw/net/pcnet.h
index 79c4c84f0..dec8de834 100644
--- a/hw/net/pcnet.h
+++ b/hw/net/pcnet.h
@@ -60,7 +60,6 @@ uint32_t pcnet_ioport_readw(void *opaque, uint32_t addr);
void pcnet_ioport_writel(void *opaque, uint32_t addr, uint32_t val);
uint32_t pcnet_ioport_readl(void *opaque, uint32_t addr);
uint32_t pcnet_bcr_readw(PCNetState *s, uint32_t rap);
-int pcnet_can_receive(NetClientState *nc);
ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_);
void pcnet_set_link_status(NetClientState *nc);
void pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info);
diff --git a/hw/net/rocker/qmp-norocker.c b/hw/net/rocker/qmp-norocker.c
new file mode 100644
index 000000000..49b498b64
--- /dev/null
+++ b/hw/net/rocker/qmp-norocker.c
@@ -0,0 +1,50 @@
+/*
+ * QMP Target options - Commands handled based on a target config
+ * versus a host config
+ *
+ * Copyright (c) 2015 David Ahern <dsahern@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "qemu-common.h"
+#include "qmp-commands.h"
+#include "qapi/qmp/qerror.h"
+
+RockerSwitch *qmp_query_rocker(const char *name, Error **errp)
+{
+ error_setg(errp, QERR_FEATURE_DISABLED, "rocker");
+ return NULL;
+};
+
+RockerPortList *qmp_query_rocker_ports(const char *name, Error **errp)
+{
+ error_setg(errp, QERR_FEATURE_DISABLED, "rocker");
+ return NULL;
+};
+
+RockerOfDpaFlowList *qmp_query_rocker_of_dpa_flows(const char *name,
+ bool has_tbl_id,
+ uint32_t tbl_id,
+ Error **errp)
+{
+ error_setg(errp, QERR_FEATURE_DISABLED, "rocker");
+ return NULL;
+};
+
+RockerOfDpaGroupList *qmp_query_rocker_of_dpa_groups(const char *name,
+ bool has_type,
+ uint8_t type,
+ Error **errp)
+{
+ error_setg(errp, QERR_FEATURE_DISABLED, "rocker");
+ return NULL;
+};
diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c
new file mode 100644
index 000000000..47d080fd3
--- /dev/null
+++ b/hw/net/rocker/rocker.c
@@ -0,0 +1,1553 @@
+/*
+ * QEMU rocker switch emulation - PCI device
+ *
+ * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
+ * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/msix.h"
+#include "net/net.h"
+#include "net/eth.h"
+#include "qemu/iov.h"
+#include "qemu/bitops.h"
+#include "qmp-commands.h"
+
+#include "rocker.h"
+#include "rocker_hw.h"
+#include "rocker_fp.h"
+#include "rocker_desc.h"
+#include "rocker_tlv.h"
+#include "rocker_world.h"
+#include "rocker_of_dpa.h"
+
+struct rocker {
+ /* private */
+ PCIDevice parent_obj;
+ /* public */
+
+ MemoryRegion mmio;
+ MemoryRegion msix_bar;
+
+ /* switch configuration */
+ char *name; /* switch name */
+ uint32_t fp_ports; /* front-panel port count */
+ NICPeers *fp_ports_peers;
+ MACAddr fp_start_macaddr; /* front-panel port 0 mac addr */
+ uint64_t switch_id; /* switch id */
+
+ /* front-panel ports */
+ FpPort *fp_port[ROCKER_FP_PORTS_MAX];
+
+ /* register backings */
+ uint32_t test_reg;
+ uint64_t test_reg64;
+ dma_addr_t test_dma_addr;
+ uint32_t test_dma_size;
+ uint64_t lower32; /* lower 32-bit val in 2-part 64-bit access */
+
+ /* desc rings */
+ DescRing **rings;
+
+ /* switch worlds */
+ World *worlds[ROCKER_WORLD_TYPE_MAX];
+ World *world_dflt;
+
+ QLIST_ENTRY(rocker) next;
+};
+
+#define ROCKER "rocker"
+
+#define to_rocker(obj) \
+ OBJECT_CHECK(Rocker, (obj), ROCKER)
+
+static QLIST_HEAD(, rocker) rockers;
+
+Rocker *rocker_find(const char *name)
+{
+ Rocker *r;
+
+ QLIST_FOREACH(r, &rockers, next)
+ if (strcmp(r->name, name) == 0) {
+ return r;
+ }
+
+ return NULL;
+}
+
+World *rocker_get_world(Rocker *r, enum rocker_world_type type)
+{
+ if (type < ROCKER_WORLD_TYPE_MAX) {
+ return r->worlds[type];
+ }
+ return NULL;
+}
+
+RockerSwitch *qmp_query_rocker(const char *name, Error **errp)
+{
+ RockerSwitch *rocker;
+ Rocker *r;
+
+ r = rocker_find(name);
+ if (!r) {
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "rocker %s not found", name);
+ return NULL;
+ }
+
+ rocker = g_new0(RockerSwitch, 1);
+ rocker->name = g_strdup(r->name);
+ rocker->id = r->switch_id;
+ rocker->ports = r->fp_ports;
+
+ return rocker;
+}
+
+RockerPortList *qmp_query_rocker_ports(const char *name, Error **errp)
+{
+ RockerPortList *list = NULL;
+ Rocker *r;
+ int i;
+
+ r = rocker_find(name);
+ if (!r) {
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "rocker %s not found", name);
+ return NULL;
+ }
+
+ for (i = r->fp_ports - 1; i >= 0; i--) {
+ RockerPortList *info = g_malloc0(sizeof(*info));
+ info->value = g_malloc0(sizeof(*info->value));
+ struct fp_port *port = r->fp_port[i];
+
+ fp_port_get_info(port, info);
+ info->next = list;
+ list = info;
+ }
+
+ return list;
+}
+
+uint32_t rocker_fp_ports(Rocker *r)
+{
+ return r->fp_ports;
+}
+
+static uint32_t rocker_get_pport_by_tx_ring(Rocker *r,
+ DescRing *ring)
+{
+ return (desc_ring_index(ring) - 2) / 2 + 1;
+}
+
+static int tx_consume(Rocker *r, DescInfo *info)
+{
+ PCIDevice *dev = PCI_DEVICE(r);
+ char *buf = desc_get_buf(info, true);
+ RockerTlv *tlv_frag;
+ RockerTlv *tlvs[ROCKER_TLV_TX_MAX + 1];
+ struct iovec iov[ROCKER_TX_FRAGS_MAX] = { { 0, }, };
+ uint32_t pport;
+ uint32_t port;
+ uint16_t tx_offload = ROCKER_TX_OFFLOAD_NONE;
+ uint16_t tx_l3_csum_off = 0;
+ uint16_t tx_tso_mss = 0;
+ uint16_t tx_tso_hdr_len = 0;
+ int iovcnt = 0;
+ int err = ROCKER_OK;
+ int rem;
+ int i;
+
+ if (!buf) {
+ return -ROCKER_ENXIO;
+ }
+
+ rocker_tlv_parse(tlvs, ROCKER_TLV_TX_MAX, buf, desc_tlv_size(info));
+
+ if (!tlvs[ROCKER_TLV_TX_FRAGS]) {
+ return -ROCKER_EINVAL;
+ }
+
+ pport = rocker_get_pport_by_tx_ring(r, desc_get_ring(info));
+ if (!fp_port_from_pport(pport, &port)) {
+ return -ROCKER_EINVAL;
+ }
+
+ if (tlvs[ROCKER_TLV_TX_OFFLOAD]) {
+ tx_offload = rocker_tlv_get_u8(tlvs[ROCKER_TLV_TX_OFFLOAD]);
+ }
+
+ switch (tx_offload) {
+ case ROCKER_TX_OFFLOAD_L3_CSUM:
+ if (!tlvs[ROCKER_TLV_TX_L3_CSUM_OFF]) {
+ return -ROCKER_EINVAL;
+ }
+ break;
+ case ROCKER_TX_OFFLOAD_TSO:
+ if (!tlvs[ROCKER_TLV_TX_TSO_MSS] ||
+ !tlvs[ROCKER_TLV_TX_TSO_HDR_LEN]) {
+ return -ROCKER_EINVAL;
+ }
+ break;
+ }
+
+ if (tlvs[ROCKER_TLV_TX_L3_CSUM_OFF]) {
+ tx_l3_csum_off = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_L3_CSUM_OFF]);
+ }
+
+ if (tlvs[ROCKER_TLV_TX_TSO_MSS]) {
+ tx_tso_mss = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_TSO_MSS]);
+ }
+
+ if (tlvs[ROCKER_TLV_TX_TSO_HDR_LEN]) {
+ tx_tso_hdr_len = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_TSO_HDR_LEN]);
+ }
+
+ rocker_tlv_for_each_nested(tlv_frag, tlvs[ROCKER_TLV_TX_FRAGS], rem) {
+ hwaddr frag_addr;
+ uint16_t frag_len;
+
+ if (rocker_tlv_type(tlv_frag) != ROCKER_TLV_TX_FRAG) {
+ err = -ROCKER_EINVAL;
+ goto err_bad_attr;
+ }
+
+ rocker_tlv_parse_nested(tlvs, ROCKER_TLV_TX_FRAG_ATTR_MAX, tlv_frag);
+
+ if (!tlvs[ROCKER_TLV_TX_FRAG_ATTR_ADDR] ||
+ !tlvs[ROCKER_TLV_TX_FRAG_ATTR_LEN]) {
+ err = -ROCKER_EINVAL;
+ goto err_bad_attr;
+ }
+
+ frag_addr = rocker_tlv_get_le64(tlvs[ROCKER_TLV_TX_FRAG_ATTR_ADDR]);
+ frag_len = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_FRAG_ATTR_LEN]);
+
+ iov[iovcnt].iov_len = frag_len;
+ iov[iovcnt].iov_base = g_malloc(frag_len);
+ if (!iov[iovcnt].iov_base) {
+ err = -ROCKER_ENOMEM;
+ goto err_no_mem;
+ }
+
+ if (pci_dma_read(dev, frag_addr, iov[iovcnt].iov_base,
+ iov[iovcnt].iov_len)) {
+ err = -ROCKER_ENXIO;
+ goto err_bad_io;
+ }
+
+ if (++iovcnt > ROCKER_TX_FRAGS_MAX) {
+ goto err_too_many_frags;
+ }
+ }
+
+ if (iovcnt) {
+ /* XXX perform Tx offloads */
+ /* XXX silence compiler for now */
+ tx_l3_csum_off += tx_tso_mss = tx_tso_hdr_len = 0;
+ }
+
+ err = fp_port_eg(r->fp_port[port], iov, iovcnt);
+
+err_too_many_frags:
+err_bad_io:
+err_no_mem:
+err_bad_attr:
+ for (i = 0; i < ROCKER_TX_FRAGS_MAX; i++) {
+ if (iov[i].iov_base) {
+ g_free(iov[i].iov_base);
+ }
+ }
+
+ return err;
+}
+
+static int cmd_get_port_settings(Rocker *r,
+ DescInfo *info, char *buf,
+ RockerTlv *cmd_info_tlv)
+{
+ RockerTlv *tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1];
+ RockerTlv *nest;
+ FpPort *fp_port;
+ uint32_t pport;
+ uint32_t port;
+ uint32_t speed;
+ uint8_t duplex;
+ uint8_t autoneg;
+ uint8_t learning;
+ char *phys_name;
+ MACAddr macaddr;
+ enum rocker_world_type mode;
+ size_t tlv_size;
+ int pos;
+ int err;
+
+ rocker_tlv_parse_nested(tlvs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX,
+ cmd_info_tlv);
+
+ if (!tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_PPORT]) {
+ return -ROCKER_EINVAL;
+ }
+
+ pport = rocker_tlv_get_le32(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_PPORT]);
+ if (!fp_port_from_pport(pport, &port)) {
+ return -ROCKER_EINVAL;
+ }
+ fp_port = r->fp_port[port];
+
+ err = fp_port_get_settings(fp_port, &speed, &duplex, &autoneg);
+ if (err) {
+ return err;
+ }
+
+ fp_port_get_macaddr(fp_port, &macaddr);
+ mode = world_type(fp_port_get_world(fp_port));
+ learning = fp_port_get_learning(fp_port);
+ phys_name = fp_port_get_name(fp_port);
+
+ tlv_size = rocker_tlv_total_size(0) + /* nest */
+ rocker_tlv_total_size(sizeof(uint32_t)) + /* pport */
+ rocker_tlv_total_size(sizeof(uint32_t)) + /* speed */
+ rocker_tlv_total_size(sizeof(uint8_t)) + /* duplex */
+ rocker_tlv_total_size(sizeof(uint8_t)) + /* autoneg */
+ rocker_tlv_total_size(sizeof(macaddr.a)) + /* macaddr */
+ rocker_tlv_total_size(sizeof(uint8_t)) + /* mode */
+ rocker_tlv_total_size(sizeof(uint8_t)) + /* learning */
+ rocker_tlv_total_size(strlen(phys_name));
+
+ if (tlv_size > desc_buf_size(info)) {
+ return -ROCKER_EMSGSIZE;
+ }
+
+ pos = 0;
+ nest = rocker_tlv_nest_start(buf, &pos, ROCKER_TLV_CMD_INFO);
+ rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT, pport);
+ rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_SPEED, speed);
+ rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX, duplex);
+ rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG, autoneg);
+ rocker_tlv_put(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR,
+ sizeof(macaddr.a), macaddr.a);
+ rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_MODE, mode);
+ rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING,
+ learning);
+ rocker_tlv_put(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_PHYS_NAME,
+ strlen(phys_name), phys_name);
+ rocker_tlv_nest_end(buf, &pos, nest);
+
+ return desc_set_buf(info, tlv_size);
+}
+
+static int cmd_set_port_settings(Rocker *r,
+ RockerTlv *cmd_info_tlv)
+{
+ RockerTlv *tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1];
+ FpPort *fp_port;
+ uint32_t pport;
+ uint32_t port;
+ uint32_t speed;
+ uint8_t duplex;
+ uint8_t autoneg;
+ uint8_t learning;
+ MACAddr macaddr;
+ enum rocker_world_type mode;
+ int err;
+
+ rocker_tlv_parse_nested(tlvs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX,
+ cmd_info_tlv);
+
+ if (!tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_PPORT]) {
+ return -ROCKER_EINVAL;
+ }
+
+ pport = rocker_tlv_get_le32(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_PPORT]);
+ if (!fp_port_from_pport(pport, &port)) {
+ return -ROCKER_EINVAL;
+ }
+ fp_port = r->fp_port[port];
+
+ if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_SPEED] &&
+ tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX] &&
+ tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG]) {
+
+ speed = rocker_tlv_get_le32(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_SPEED]);
+ duplex = rocker_tlv_get_u8(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX]);
+ autoneg = rocker_tlv_get_u8(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG]);
+
+ err = fp_port_set_settings(fp_port, speed, duplex, autoneg);
+ if (err) {
+ return err;
+ }
+ }
+
+ if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR]) {
+ if (rocker_tlv_len(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR]) !=
+ sizeof(macaddr.a)) {
+ return -ROCKER_EINVAL;
+ }
+ memcpy(macaddr.a,
+ rocker_tlv_data(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR]),
+ sizeof(macaddr.a));
+ fp_port_set_macaddr(fp_port, &macaddr);
+ }
+
+ if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE]) {
+ mode = rocker_tlv_get_u8(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE]);
+ fp_port_set_world(fp_port, r->worlds[mode]);
+ }
+
+ if (tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING]) {
+ learning =
+ rocker_tlv_get_u8(tlvs[ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING]);
+ fp_port_set_learning(fp_port, learning);
+ }
+
+ return ROCKER_OK;
+}
+
+static int cmd_consume(Rocker *r, DescInfo *info)
+{
+ char *buf = desc_get_buf(info, false);
+ RockerTlv *tlvs[ROCKER_TLV_CMD_MAX + 1];
+ RockerTlv *info_tlv;
+ World *world;
+ uint16_t cmd;
+ int err;
+
+ if (!buf) {
+ return -ROCKER_ENXIO;
+ }
+
+ rocker_tlv_parse(tlvs, ROCKER_TLV_CMD_MAX, buf, desc_tlv_size(info));
+
+ if (!tlvs[ROCKER_TLV_CMD_TYPE] || !tlvs[ROCKER_TLV_CMD_INFO]) {
+ return -ROCKER_EINVAL;
+ }
+
+ cmd = rocker_tlv_get_le16(tlvs[ROCKER_TLV_CMD_TYPE]);
+ info_tlv = tlvs[ROCKER_TLV_CMD_INFO];
+
+ /* This might be reworked to something like this:
+ * Every world will have an array of command handlers from
+ * ROCKER_TLV_CMD_TYPE_UNSPEC to ROCKER_TLV_CMD_TYPE_MAX. There is
+ * up to each world to implement whatever command it want.
+ * It can reference "generic" commands as cmd_set_port_settings or
+ * cmd_get_port_settings
+ */
+
+ switch (cmd) {
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD:
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_MOD:
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL:
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_GET_STATS:
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD:
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_MOD:
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL:
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_GET_STATS:
+ world = r->worlds[ROCKER_WORLD_TYPE_OF_DPA];
+ err = world_do_cmd(world, info, buf, cmd, info_tlv);
+ break;
+ case ROCKER_TLV_CMD_TYPE_GET_PORT_SETTINGS:
+ err = cmd_get_port_settings(r, info, buf, info_tlv);
+ break;
+ case ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS:
+ err = cmd_set_port_settings(r, info_tlv);
+ break;
+ default:
+ err = -ROCKER_EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+static void rocker_msix_irq(Rocker *r, unsigned vector)
+{
+ PCIDevice *dev = PCI_DEVICE(r);
+
+ DPRINTF("MSI-X notify request for vector %d\n", vector);
+ if (vector >= ROCKER_MSIX_VEC_COUNT(r->fp_ports)) {
+ DPRINTF("incorrect vector %d\n", vector);
+ return;
+ }
+ msix_notify(dev, vector);
+}
+
+int rocker_event_link_changed(Rocker *r, uint32_t pport, bool link_up)
+{
+ DescRing *ring = r->rings[ROCKER_RING_EVENT];
+ DescInfo *info = desc_ring_fetch_desc(ring);
+ RockerTlv *nest;
+ char *buf;
+ size_t tlv_size;
+ int pos;
+ int err;
+
+ if (!info) {
+ return -ROCKER_ENOBUFS;
+ }
+
+ tlv_size = rocker_tlv_total_size(sizeof(uint16_t)) + /* event type */
+ rocker_tlv_total_size(0) + /* nest */
+ rocker_tlv_total_size(sizeof(uint32_t)) + /* pport */
+ rocker_tlv_total_size(sizeof(uint8_t)); /* link up */
+
+ if (tlv_size > desc_buf_size(info)) {
+ err = -ROCKER_EMSGSIZE;
+ goto err_too_big;
+ }
+
+ buf = desc_get_buf(info, false);
+ if (!buf) {
+ err = -ROCKER_ENOMEM;
+ goto err_no_mem;
+ }
+
+ pos = 0;
+ rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_EVENT_TYPE,
+ ROCKER_TLV_EVENT_TYPE_LINK_CHANGED);
+ nest = rocker_tlv_nest_start(buf, &pos, ROCKER_TLV_EVENT_INFO);
+ rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_EVENT_LINK_CHANGED_PPORT, pport);
+ rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_EVENT_LINK_CHANGED_LINKUP,
+ link_up ? 1 : 0);
+ rocker_tlv_nest_end(buf, &pos, nest);
+
+ err = desc_set_buf(info, tlv_size);
+
+err_too_big:
+err_no_mem:
+ if (desc_ring_post_desc(ring, err)) {
+ rocker_msix_irq(r, ROCKER_MSIX_VEC_EVENT);
+ }
+
+ return err;
+}
+
+int rocker_event_mac_vlan_seen(Rocker *r, uint32_t pport, uint8_t *addr,
+ uint16_t vlan_id)
+{
+ DescRing *ring = r->rings[ROCKER_RING_EVENT];
+ DescInfo *info;
+ FpPort *fp_port;
+ uint32_t port;
+ RockerTlv *nest;
+ char *buf;
+ size_t tlv_size;
+ int pos;
+ int err;
+
+ if (!fp_port_from_pport(pport, &port)) {
+ return -ROCKER_EINVAL;
+ }
+ fp_port = r->fp_port[port];
+ if (!fp_port_get_learning(fp_port)) {
+ return ROCKER_OK;
+ }
+
+ info = desc_ring_fetch_desc(ring);
+ if (!info) {
+ return -ROCKER_ENOBUFS;
+ }
+
+ tlv_size = rocker_tlv_total_size(sizeof(uint16_t)) + /* event type */
+ rocker_tlv_total_size(0) + /* nest */
+ rocker_tlv_total_size(sizeof(uint32_t)) + /* pport */
+ rocker_tlv_total_size(ETH_ALEN) + /* mac addr */
+ rocker_tlv_total_size(sizeof(uint16_t)); /* vlan_id */
+
+ if (tlv_size > desc_buf_size(info)) {
+ err = -ROCKER_EMSGSIZE;
+ goto err_too_big;
+ }
+
+ buf = desc_get_buf(info, false);
+ if (!buf) {
+ err = -ROCKER_ENOMEM;
+ goto err_no_mem;
+ }
+
+ pos = 0;
+ rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_EVENT_TYPE,
+ ROCKER_TLV_EVENT_TYPE_MAC_VLAN_SEEN);
+ nest = rocker_tlv_nest_start(buf, &pos, ROCKER_TLV_EVENT_INFO);
+ rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_EVENT_MAC_VLAN_PPORT, pport);
+ rocker_tlv_put(buf, &pos, ROCKER_TLV_EVENT_MAC_VLAN_MAC, ETH_ALEN, addr);
+ rocker_tlv_put_u16(buf, &pos, ROCKER_TLV_EVENT_MAC_VLAN_VLAN_ID, vlan_id);
+ rocker_tlv_nest_end(buf, &pos, nest);
+
+ err = desc_set_buf(info, tlv_size);
+
+err_too_big:
+err_no_mem:
+ if (desc_ring_post_desc(ring, err)) {
+ rocker_msix_irq(r, ROCKER_MSIX_VEC_EVENT);
+ }
+
+ return err;
+}
+
+static DescRing *rocker_get_rx_ring_by_pport(Rocker *r,
+ uint32_t pport)
+{
+ return r->rings[(pport - 1) * 2 + 3];
+}
+
+int rx_produce(World *world, uint32_t pport,
+ const struct iovec *iov, int iovcnt, uint8_t copy_to_cpu)
+{
+ Rocker *r = world_rocker(world);
+ PCIDevice *dev = (PCIDevice *)r;
+ DescRing *ring = rocker_get_rx_ring_by_pport(r, pport);
+ DescInfo *info = desc_ring_fetch_desc(ring);
+ char *data;
+ size_t data_size = iov_size(iov, iovcnt);
+ char *buf;
+ uint16_t rx_flags = 0;
+ uint16_t rx_csum = 0;
+ size_t tlv_size;
+ RockerTlv *tlvs[ROCKER_TLV_RX_MAX + 1];
+ hwaddr frag_addr;
+ uint16_t frag_max_len;
+ int pos;
+ int err;
+
+ if (!info) {
+ return -ROCKER_ENOBUFS;
+ }
+
+ buf = desc_get_buf(info, false);
+ if (!buf) {
+ err = -ROCKER_ENXIO;
+ goto out;
+ }
+ rocker_tlv_parse(tlvs, ROCKER_TLV_RX_MAX, buf, desc_tlv_size(info));
+
+ if (!tlvs[ROCKER_TLV_RX_FRAG_ADDR] ||
+ !tlvs[ROCKER_TLV_RX_FRAG_MAX_LEN]) {
+ err = -ROCKER_EINVAL;
+ goto out;
+ }
+
+ frag_addr = rocker_tlv_get_le64(tlvs[ROCKER_TLV_RX_FRAG_ADDR]);
+ frag_max_len = rocker_tlv_get_le16(tlvs[ROCKER_TLV_RX_FRAG_MAX_LEN]);
+
+ if (data_size > frag_max_len) {
+ err = -ROCKER_EMSGSIZE;
+ goto out;
+ }
+
+ if (copy_to_cpu) {
+ rx_flags |= ROCKER_RX_FLAGS_FWD_OFFLOAD;
+ }
+
+ /* XXX calc rx flags/csum */
+
+ tlv_size = rocker_tlv_total_size(sizeof(uint16_t)) + /* flags */
+ rocker_tlv_total_size(sizeof(uint16_t)) + /* scum */
+ rocker_tlv_total_size(sizeof(uint64_t)) + /* frag addr */
+ rocker_tlv_total_size(sizeof(uint16_t)) + /* frag max len */
+ rocker_tlv_total_size(sizeof(uint16_t)); /* frag len */
+
+ if (tlv_size > desc_buf_size(info)) {
+ err = -ROCKER_EMSGSIZE;
+ goto out;
+ }
+
+ /* TODO:
+ * iov dma write can be optimized in similar way e1000 does it in
+ * e1000_receive_iov. But maybe if would make sense to introduce
+ * generic helper iov_dma_write.
+ */
+
+ data = g_malloc(data_size);
+ if (!data) {
+ err = -ROCKER_ENOMEM;
+ goto out;
+ }
+ iov_to_buf(iov, iovcnt, 0, data, data_size);
+ pci_dma_write(dev, frag_addr, data, data_size);
+ g_free(data);
+
+ pos = 0;
+ rocker_tlv_put_le16(buf, &pos, ROCKER_TLV_RX_FLAGS, rx_flags);
+ rocker_tlv_put_le16(buf, &pos, ROCKER_TLV_RX_CSUM, rx_csum);
+ rocker_tlv_put_le64(buf, &pos, ROCKER_TLV_RX_FRAG_ADDR, frag_addr);
+ rocker_tlv_put_le16(buf, &pos, ROCKER_TLV_RX_FRAG_MAX_LEN, frag_max_len);
+ rocker_tlv_put_le16(buf, &pos, ROCKER_TLV_RX_FRAG_LEN, data_size);
+
+ err = desc_set_buf(info, tlv_size);
+
+out:
+ if (desc_ring_post_desc(ring, err)) {
+ rocker_msix_irq(r, ROCKER_MSIX_VEC_RX(pport - 1));
+ }
+
+ return err;
+}
+
+int rocker_port_eg(Rocker *r, uint32_t pport,
+ const struct iovec *iov, int iovcnt)
+{
+ FpPort *fp_port;
+ uint32_t port;
+
+ if (!fp_port_from_pport(pport, &port)) {
+ return -ROCKER_EINVAL;
+ }
+
+ fp_port = r->fp_port[port];
+
+ return fp_port_eg(fp_port, iov, iovcnt);
+}
+
+static void rocker_test_dma_ctrl(Rocker *r, uint32_t val)
+{
+ PCIDevice *dev = PCI_DEVICE(r);
+ char *buf;
+ int i;
+
+ buf = g_malloc(r->test_dma_size);
+
+ if (!buf) {
+ DPRINTF("test dma buffer alloc failed");
+ return;
+ }
+
+ switch (val) {
+ case ROCKER_TEST_DMA_CTRL_CLEAR:
+ memset(buf, 0, r->test_dma_size);
+ break;
+ case ROCKER_TEST_DMA_CTRL_FILL:
+ memset(buf, 0x96, r->test_dma_size);
+ break;
+ case ROCKER_TEST_DMA_CTRL_INVERT:
+ pci_dma_read(dev, r->test_dma_addr, buf, r->test_dma_size);
+ for (i = 0; i < r->test_dma_size; i++) {
+ buf[i] = ~buf[i];
+ }
+ break;
+ default:
+ DPRINTF("not test dma control val=0x%08x\n", val);
+ goto err_out;
+ }
+ pci_dma_write(dev, r->test_dma_addr, buf, r->test_dma_size);
+
+ rocker_msix_irq(r, ROCKER_MSIX_VEC_TEST);
+
+err_out:
+ g_free(buf);
+}
+
+static void rocker_reset(DeviceState *dev);
+
+static void rocker_control(Rocker *r, uint32_t val)
+{
+ if (val & ROCKER_CONTROL_RESET) {
+ rocker_reset(DEVICE(r));
+ }
+}
+
+static int rocker_pci_ring_count(Rocker *r)
+{
+ /* There are:
+ * - command ring
+ * - event ring
+ * - tx and rx ring per each port
+ */
+ return 2 + (2 * r->fp_ports);
+}
+
+static bool rocker_addr_is_desc_reg(Rocker *r, hwaddr addr)
+{
+ hwaddr start = ROCKER_DMA_DESC_BASE;
+ hwaddr end = start + (ROCKER_DMA_DESC_SIZE * rocker_pci_ring_count(r));
+
+ return addr >= start && addr < end;
+}
+
+static void rocker_port_phys_enable_write(Rocker *r, uint64_t new)
+{
+ int i;
+ bool old_enabled;
+ bool new_enabled;
+ FpPort *fp_port;
+
+ for (i = 0; i < r->fp_ports; i++) {
+ fp_port = r->fp_port[i];
+ old_enabled = fp_port_enabled(fp_port);
+ new_enabled = (new >> (i + 1)) & 0x1;
+ if (new_enabled == old_enabled) {
+ continue;
+ }
+ if (new_enabled) {
+ fp_port_enable(r->fp_port[i]);
+ } else {
+ fp_port_disable(r->fp_port[i]);
+ }
+ }
+}
+
+static void rocker_io_writel(void *opaque, hwaddr addr, uint32_t val)
+{
+ Rocker *r = opaque;
+
+ if (rocker_addr_is_desc_reg(r, addr)) {
+ unsigned index = ROCKER_RING_INDEX(addr);
+ unsigned offset = addr & ROCKER_DMA_DESC_MASK;
+
+ switch (offset) {
+ case ROCKER_DMA_DESC_ADDR_OFFSET:
+ r->lower32 = (uint64_t)val;
+ break;
+ case ROCKER_DMA_DESC_ADDR_OFFSET + 4:
+ desc_ring_set_base_addr(r->rings[index],
+ ((uint64_t)val) << 32 | r->lower32);
+ r->lower32 = 0;
+ break;
+ case ROCKER_DMA_DESC_SIZE_OFFSET:
+ desc_ring_set_size(r->rings[index], val);
+ break;
+ case ROCKER_DMA_DESC_HEAD_OFFSET:
+ if (desc_ring_set_head(r->rings[index], val)) {
+ rocker_msix_irq(r, desc_ring_get_msix_vector(r->rings[index]));
+ }
+ break;
+ case ROCKER_DMA_DESC_CTRL_OFFSET:
+ desc_ring_set_ctrl(r->rings[index], val);
+ break;
+ case ROCKER_DMA_DESC_CREDITS_OFFSET:
+ if (desc_ring_ret_credits(r->rings[index], val)) {
+ rocker_msix_irq(r, desc_ring_get_msix_vector(r->rings[index]));
+ }
+ break;
+ default:
+ DPRINTF("not implemented dma reg write(l) addr=0x" TARGET_FMT_plx
+ " val=0x%08x (ring %d, addr=0x%02x)\n",
+ addr, val, index, offset);
+ break;
+ }
+ return;
+ }
+
+ switch (addr) {
+ case ROCKER_TEST_REG:
+ r->test_reg = val;
+ break;
+ case ROCKER_TEST_REG64:
+ case ROCKER_TEST_DMA_ADDR:
+ case ROCKER_PORT_PHYS_ENABLE:
+ r->lower32 = (uint64_t)val;
+ break;
+ case ROCKER_TEST_REG64 + 4:
+ r->test_reg64 = ((uint64_t)val) << 32 | r->lower32;
+ r->lower32 = 0;
+ break;
+ case ROCKER_TEST_IRQ:
+ rocker_msix_irq(r, val);
+ break;
+ case ROCKER_TEST_DMA_SIZE:
+ r->test_dma_size = val;
+ break;
+ case ROCKER_TEST_DMA_ADDR + 4:
+ r->test_dma_addr = ((uint64_t)val) << 32 | r->lower32;
+ r->lower32 = 0;
+ break;
+ case ROCKER_TEST_DMA_CTRL:
+ rocker_test_dma_ctrl(r, val);
+ break;
+ case ROCKER_CONTROL:
+ rocker_control(r, val);
+ break;
+ case ROCKER_PORT_PHYS_ENABLE + 4:
+ rocker_port_phys_enable_write(r, ((uint64_t)val) << 32 | r->lower32);
+ r->lower32 = 0;
+ break;
+ default:
+ DPRINTF("not implemented write(l) addr=0x" TARGET_FMT_plx
+ " val=0x%08x\n", addr, val);
+ break;
+ }
+}
+
+static void rocker_io_writeq(void *opaque, hwaddr addr, uint64_t val)
+{
+ Rocker *r = opaque;
+
+ if (rocker_addr_is_desc_reg(r, addr)) {
+ unsigned index = ROCKER_RING_INDEX(addr);
+ unsigned offset = addr & ROCKER_DMA_DESC_MASK;
+
+ switch (offset) {
+ case ROCKER_DMA_DESC_ADDR_OFFSET:
+ desc_ring_set_base_addr(r->rings[index], val);
+ break;
+ default:
+ DPRINTF("not implemented dma reg write(q) addr=0x" TARGET_FMT_plx
+ " val=0x" TARGET_FMT_plx " (ring %d, offset=0x%02x)\n",
+ addr, val, index, offset);
+ break;
+ }
+ return;
+ }
+
+ switch (addr) {
+ case ROCKER_TEST_REG64:
+ r->test_reg64 = val;
+ break;
+ case ROCKER_TEST_DMA_ADDR:
+ r->test_dma_addr = val;
+ break;
+ case ROCKER_PORT_PHYS_ENABLE:
+ rocker_port_phys_enable_write(r, val);
+ break;
+ default:
+ DPRINTF("not implemented write(q) addr=0x" TARGET_FMT_plx
+ " val=0x" TARGET_FMT_plx "\n", addr, val);
+ break;
+ }
+}
+
+#ifdef DEBUG_ROCKER
+#define regname(reg) case (reg): return #reg
+static const char *rocker_reg_name(void *opaque, hwaddr addr)
+{
+ Rocker *r = opaque;
+
+ if (rocker_addr_is_desc_reg(r, addr)) {
+ unsigned index = ROCKER_RING_INDEX(addr);
+ unsigned offset = addr & ROCKER_DMA_DESC_MASK;
+ static char buf[100];
+ char ring_name[10];
+
+ switch (index) {
+ case 0:
+ sprintf(ring_name, "cmd");
+ break;
+ case 1:
+ sprintf(ring_name, "event");
+ break;
+ default:
+ sprintf(ring_name, "%s-%d", index % 2 ? "rx" : "tx",
+ (index - 2) / 2);
+ }
+
+ switch (offset) {
+ case ROCKER_DMA_DESC_ADDR_OFFSET:
+ sprintf(buf, "Ring[%s] ADDR", ring_name);
+ return buf;
+ case ROCKER_DMA_DESC_ADDR_OFFSET+4:
+ sprintf(buf, "Ring[%s] ADDR+4", ring_name);
+ return buf;
+ case ROCKER_DMA_DESC_SIZE_OFFSET:
+ sprintf(buf, "Ring[%s] SIZE", ring_name);
+ return buf;
+ case ROCKER_DMA_DESC_HEAD_OFFSET:
+ sprintf(buf, "Ring[%s] HEAD", ring_name);
+ return buf;
+ case ROCKER_DMA_DESC_TAIL_OFFSET:
+ sprintf(buf, "Ring[%s] TAIL", ring_name);
+ return buf;
+ case ROCKER_DMA_DESC_CTRL_OFFSET:
+ sprintf(buf, "Ring[%s] CTRL", ring_name);
+ return buf;
+ case ROCKER_DMA_DESC_CREDITS_OFFSET:
+ sprintf(buf, "Ring[%s] CREDITS", ring_name);
+ return buf;
+ default:
+ sprintf(buf, "Ring[%s] ???", ring_name);
+ return buf;
+ }
+ } else {
+ switch (addr) {
+ regname(ROCKER_BOGUS_REG0);
+ regname(ROCKER_BOGUS_REG1);
+ regname(ROCKER_BOGUS_REG2);
+ regname(ROCKER_BOGUS_REG3);
+ regname(ROCKER_TEST_REG);
+ regname(ROCKER_TEST_REG64);
+ regname(ROCKER_TEST_REG64+4);
+ regname(ROCKER_TEST_IRQ);
+ regname(ROCKER_TEST_DMA_ADDR);
+ regname(ROCKER_TEST_DMA_ADDR+4);
+ regname(ROCKER_TEST_DMA_SIZE);
+ regname(ROCKER_TEST_DMA_CTRL);
+ regname(ROCKER_CONTROL);
+ regname(ROCKER_PORT_PHYS_COUNT);
+ regname(ROCKER_PORT_PHYS_LINK_STATUS);
+ regname(ROCKER_PORT_PHYS_LINK_STATUS+4);
+ regname(ROCKER_PORT_PHYS_ENABLE);
+ regname(ROCKER_PORT_PHYS_ENABLE+4);
+ regname(ROCKER_SWITCH_ID);
+ regname(ROCKER_SWITCH_ID+4);
+ }
+ }
+ return "???";
+}
+#else
+static const char *rocker_reg_name(void *opaque, hwaddr addr)
+{
+ return NULL;
+}
+#endif
+
+static void rocker_mmio_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ DPRINTF("Write %s addr " TARGET_FMT_plx
+ ", size %u, val " TARGET_FMT_plx "\n",
+ rocker_reg_name(opaque, addr), addr, size, val);
+
+ switch (size) {
+ case 4:
+ rocker_io_writel(opaque, addr, val);
+ break;
+ case 8:
+ rocker_io_writeq(opaque, addr, val);
+ break;
+ }
+}
+
+static uint64_t rocker_port_phys_link_status(Rocker *r)
+{
+ int i;
+ uint64_t status = 0;
+
+ for (i = 0; i < r->fp_ports; i++) {
+ FpPort *port = r->fp_port[i];
+
+ if (fp_port_get_link_up(port)) {
+ status |= 1 << (i + 1);
+ }
+ }
+ return status;
+}
+
+static uint64_t rocker_port_phys_enable_read(Rocker *r)
+{
+ int i;
+ uint64_t ret = 0;
+
+ for (i = 0; i < r->fp_ports; i++) {
+ FpPort *port = r->fp_port[i];
+
+ if (fp_port_enabled(port)) {
+ ret |= 1 << (i + 1);
+ }
+ }
+ return ret;
+}
+
+static uint32_t rocker_io_readl(void *opaque, hwaddr addr)
+{
+ Rocker *r = opaque;
+ uint32_t ret;
+
+ if (rocker_addr_is_desc_reg(r, addr)) {
+ unsigned index = ROCKER_RING_INDEX(addr);
+ unsigned offset = addr & ROCKER_DMA_DESC_MASK;
+
+ switch (offset) {
+ case ROCKER_DMA_DESC_ADDR_OFFSET:
+ ret = (uint32_t)desc_ring_get_base_addr(r->rings[index]);
+ break;
+ case ROCKER_DMA_DESC_ADDR_OFFSET + 4:
+ ret = (uint32_t)(desc_ring_get_base_addr(r->rings[index]) >> 32);
+ break;
+ case ROCKER_DMA_DESC_SIZE_OFFSET:
+ ret = desc_ring_get_size(r->rings[index]);
+ break;
+ case ROCKER_DMA_DESC_HEAD_OFFSET:
+ ret = desc_ring_get_head(r->rings[index]);
+ break;
+ case ROCKER_DMA_DESC_TAIL_OFFSET:
+ ret = desc_ring_get_tail(r->rings[index]);
+ break;
+ case ROCKER_DMA_DESC_CREDITS_OFFSET:
+ ret = desc_ring_get_credits(r->rings[index]);
+ break;
+ default:
+ DPRINTF("not implemented dma reg read(l) addr=0x" TARGET_FMT_plx
+ " (ring %d, addr=0x%02x)\n", addr, index, offset);
+ ret = 0;
+ break;
+ }
+ return ret;
+ }
+
+ switch (addr) {
+ case ROCKER_BOGUS_REG0:
+ case ROCKER_BOGUS_REG1:
+ case ROCKER_BOGUS_REG2:
+ case ROCKER_BOGUS_REG3:
+ ret = 0xDEADBABE;
+ break;
+ case ROCKER_TEST_REG:
+ ret = r->test_reg * 2;
+ break;
+ case ROCKER_TEST_REG64:
+ ret = (uint32_t)(r->test_reg64 * 2);
+ break;
+ case ROCKER_TEST_REG64 + 4:
+ ret = (uint32_t)((r->test_reg64 * 2) >> 32);
+ break;
+ case ROCKER_TEST_DMA_SIZE:
+ ret = r->test_dma_size;
+ break;
+ case ROCKER_TEST_DMA_ADDR:
+ ret = (uint32_t)r->test_dma_addr;
+ break;
+ case ROCKER_TEST_DMA_ADDR + 4:
+ ret = (uint32_t)(r->test_dma_addr >> 32);
+ break;
+ case ROCKER_PORT_PHYS_COUNT:
+ ret = r->fp_ports;
+ break;
+ case ROCKER_PORT_PHYS_LINK_STATUS:
+ ret = (uint32_t)rocker_port_phys_link_status(r);
+ break;
+ case ROCKER_PORT_PHYS_LINK_STATUS + 4:
+ ret = (uint32_t)(rocker_port_phys_link_status(r) >> 32);
+ break;
+ case ROCKER_PORT_PHYS_ENABLE:
+ ret = (uint32_t)rocker_port_phys_enable_read(r);
+ break;
+ case ROCKER_PORT_PHYS_ENABLE + 4:
+ ret = (uint32_t)(rocker_port_phys_enable_read(r) >> 32);
+ break;
+ case ROCKER_SWITCH_ID:
+ ret = (uint32_t)r->switch_id;
+ break;
+ case ROCKER_SWITCH_ID + 4:
+ ret = (uint32_t)(r->switch_id >> 32);
+ break;
+ default:
+ DPRINTF("not implemented read(l) addr=0x" TARGET_FMT_plx "\n", addr);
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+static uint64_t rocker_io_readq(void *opaque, hwaddr addr)
+{
+ Rocker *r = opaque;
+ uint64_t ret;
+
+ if (rocker_addr_is_desc_reg(r, addr)) {
+ unsigned index = ROCKER_RING_INDEX(addr);
+ unsigned offset = addr & ROCKER_DMA_DESC_MASK;
+
+ switch (addr & ROCKER_DMA_DESC_MASK) {
+ case ROCKER_DMA_DESC_ADDR_OFFSET:
+ ret = desc_ring_get_base_addr(r->rings[index]);
+ break;
+ default:
+ DPRINTF("not implemented dma reg read(q) addr=0x" TARGET_FMT_plx
+ " (ring %d, addr=0x%02x)\n", addr, index, offset);
+ ret = 0;
+ break;
+ }
+ return ret;
+ }
+
+ switch (addr) {
+ case ROCKER_BOGUS_REG0:
+ case ROCKER_BOGUS_REG2:
+ ret = 0xDEADBABEDEADBABEULL;
+ break;
+ case ROCKER_TEST_REG64:
+ ret = r->test_reg64 * 2;
+ break;
+ case ROCKER_TEST_DMA_ADDR:
+ ret = r->test_dma_addr;
+ break;
+ case ROCKER_PORT_PHYS_LINK_STATUS:
+ ret = rocker_port_phys_link_status(r);
+ break;
+ case ROCKER_PORT_PHYS_ENABLE:
+ ret = rocker_port_phys_enable_read(r);
+ break;
+ case ROCKER_SWITCH_ID:
+ ret = r->switch_id;
+ break;
+ default:
+ DPRINTF("not implemented read(q) addr=0x" TARGET_FMT_plx "\n", addr);
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+static uint64_t rocker_mmio_read(void *opaque, hwaddr addr, unsigned size)
+{
+ DPRINTF("Read %s addr " TARGET_FMT_plx ", size %u\n",
+ rocker_reg_name(opaque, addr), addr, size);
+
+ switch (size) {
+ case 4:
+ return rocker_io_readl(opaque, addr);
+ case 8:
+ return rocker_io_readq(opaque, addr);
+ }
+
+ return -1;
+}
+
+static const MemoryRegionOps rocker_mmio_ops = {
+ .read = rocker_mmio_read,
+ .write = rocker_mmio_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 8,
+ },
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 8,
+ },
+};
+
+static void rocker_msix_vectors_unuse(Rocker *r,
+ unsigned int num_vectors)
+{
+ PCIDevice *dev = PCI_DEVICE(r);
+ int i;
+
+ for (i = 0; i < num_vectors; i++) {
+ msix_vector_unuse(dev, i);
+ }
+}
+
+static int rocker_msix_vectors_use(Rocker *r,
+ unsigned int num_vectors)
+{
+ PCIDevice *dev = PCI_DEVICE(r);
+ int err;
+ int i;
+
+ for (i = 0; i < num_vectors; i++) {
+ err = msix_vector_use(dev, i);
+ if (err) {
+ goto rollback;
+ }
+ }
+ return 0;
+
+rollback:
+ rocker_msix_vectors_unuse(r, i);
+ return err;
+}
+
+static int rocker_msix_init(Rocker *r)
+{
+ PCIDevice *dev = PCI_DEVICE(r);
+ int err;
+
+ err = msix_init(dev, ROCKER_MSIX_VEC_COUNT(r->fp_ports),
+ &r->msix_bar,
+ ROCKER_PCI_MSIX_BAR_IDX, ROCKER_PCI_MSIX_TABLE_OFFSET,
+ &r->msix_bar,
+ ROCKER_PCI_MSIX_BAR_IDX, ROCKER_PCI_MSIX_PBA_OFFSET,
+ 0);
+ if (err) {
+ return err;
+ }
+
+ err = rocker_msix_vectors_use(r, ROCKER_MSIX_VEC_COUNT(r->fp_ports));
+ if (err) {
+ goto err_msix_vectors_use;
+ }
+
+ return 0;
+
+err_msix_vectors_use:
+ msix_uninit(dev, &r->msix_bar, &r->msix_bar);
+ return err;
+}
+
+static void rocker_msix_uninit(Rocker *r)
+{
+ PCIDevice *dev = PCI_DEVICE(r);
+
+ msix_uninit(dev, &r->msix_bar, &r->msix_bar);
+ rocker_msix_vectors_unuse(r, ROCKER_MSIX_VEC_COUNT(r->fp_ports));
+}
+
+static int pci_rocker_init(PCIDevice *dev)
+{
+ Rocker *r = to_rocker(dev);
+ const MACAddr zero = { .a = { 0, 0, 0, 0, 0, 0 } };
+ const MACAddr dflt = { .a = { 0x52, 0x54, 0x00, 0x12, 0x35, 0x01 } };
+ static int sw_index;
+ int i, err = 0;
+
+ /* allocate worlds */
+
+ r->worlds[ROCKER_WORLD_TYPE_OF_DPA] = of_dpa_world_alloc(r);
+ r->world_dflt = r->worlds[ROCKER_WORLD_TYPE_OF_DPA];
+
+ for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) {
+ if (!r->worlds[i]) {
+ goto err_world_alloc;
+ }
+ }
+
+ /* set up memory-mapped region at BAR0 */
+
+ memory_region_init_io(&r->mmio, OBJECT(r), &rocker_mmio_ops, r,
+ "rocker-mmio", ROCKER_PCI_BAR0_SIZE);
+ pci_register_bar(dev, ROCKER_PCI_BAR0_IDX,
+ PCI_BASE_ADDRESS_SPACE_MEMORY, &r->mmio);
+
+ /* set up memory-mapped region for MSI-X */
+
+ memory_region_init(&r->msix_bar, OBJECT(r), "rocker-msix-bar",
+ ROCKER_PCI_MSIX_BAR_SIZE);
+ pci_register_bar(dev, ROCKER_PCI_MSIX_BAR_IDX,
+ PCI_BASE_ADDRESS_SPACE_MEMORY, &r->msix_bar);
+
+ /* MSI-X init */
+
+ err = rocker_msix_init(r);
+ if (err) {
+ goto err_msix_init;
+ }
+
+ /* validate switch properties */
+
+ if (!r->name) {
+ r->name = g_strdup(ROCKER);
+ }
+
+ if (rocker_find(r->name)) {
+ err = -EEXIST;
+ goto err_duplicate;
+ }
+
+ /* Rocker name is passed in port name requests to OS with the intention
+ * that the name is used in interface names. Limit the length of the
+ * rocker name to avoid naming problems in the OS. Also, adding the
+ * port number as p# and unganged breakout b#, where # is at most 2
+ * digits, so leave room for it too (-1 for string terminator, -3 for
+ * p# and -3 for b#)
+ */
+#define ROCKER_IFNAMSIZ 16
+#define MAX_ROCKER_NAME_LEN (ROCKER_IFNAMSIZ - 1 - 3 - 3)
+ if (strlen(r->name) > MAX_ROCKER_NAME_LEN) {
+ fprintf(stderr,
+ "rocker: name too long; please shorten to at most %d chars\n",
+ MAX_ROCKER_NAME_LEN);
+ return -EINVAL;
+ }
+
+ if (memcmp(&r->fp_start_macaddr, &zero, sizeof(zero)) == 0) {
+ memcpy(&r->fp_start_macaddr, &dflt, sizeof(dflt));
+ r->fp_start_macaddr.a[4] += (sw_index++);
+ }
+
+ if (!r->switch_id) {
+ memcpy(&r->switch_id, &r->fp_start_macaddr,
+ sizeof(r->fp_start_macaddr));
+ }
+
+ if (r->fp_ports > ROCKER_FP_PORTS_MAX) {
+ r->fp_ports = ROCKER_FP_PORTS_MAX;
+ }
+
+ r->rings = g_malloc(sizeof(DescRing *) * rocker_pci_ring_count(r));
+ if (!r->rings) {
+ goto err_rings_alloc;
+ }
+
+ /* Rings are ordered like this:
+ * - command ring
+ * - event ring
+ * - port0 tx ring
+ * - port0 rx ring
+ * - port1 tx ring
+ * - port1 rx ring
+ * .....
+ */
+
+ err = -ENOMEM;
+ for (i = 0; i < rocker_pci_ring_count(r); i++) {
+ DescRing *ring = desc_ring_alloc(r, i);
+
+ if (!ring) {
+ goto err_ring_alloc;
+ }
+
+ if (i == ROCKER_RING_CMD) {
+ desc_ring_set_consume(ring, cmd_consume, ROCKER_MSIX_VEC_CMD);
+ } else if (i == ROCKER_RING_EVENT) {
+ desc_ring_set_consume(ring, NULL, ROCKER_MSIX_VEC_EVENT);
+ } else if (i % 2 == 0) {
+ desc_ring_set_consume(ring, tx_consume,
+ ROCKER_MSIX_VEC_TX((i - 2) / 2));
+ } else if (i % 2 == 1) {
+ desc_ring_set_consume(ring, NULL, ROCKER_MSIX_VEC_RX((i - 3) / 2));
+ }
+
+ r->rings[i] = ring;
+ }
+
+ for (i = 0; i < r->fp_ports; i++) {
+ FpPort *port =
+ fp_port_alloc(r, r->name, &r->fp_start_macaddr,
+ i, &r->fp_ports_peers[i]);
+
+ if (!port) {
+ goto err_port_alloc;
+ }
+
+ r->fp_port[i] = port;
+ fp_port_set_world(port, r->world_dflt);
+ }
+
+ QLIST_INSERT_HEAD(&rockers, r, next);
+
+ return 0;
+
+err_port_alloc:
+ for (--i; i >= 0; i--) {
+ FpPort *port = r->fp_port[i];
+ fp_port_free(port);
+ }
+ i = rocker_pci_ring_count(r);
+err_ring_alloc:
+ for (--i; i >= 0; i--) {
+ desc_ring_free(r->rings[i]);
+ }
+ g_free(r->rings);
+err_rings_alloc:
+err_duplicate:
+ rocker_msix_uninit(r);
+err_msix_init:
+ object_unparent(OBJECT(&r->msix_bar));
+ object_unparent(OBJECT(&r->mmio));
+err_world_alloc:
+ for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) {
+ if (r->worlds[i]) {
+ world_free(r->worlds[i]);
+ }
+ }
+ return err;
+}
+
+static void pci_rocker_uninit(PCIDevice *dev)
+{
+ Rocker *r = to_rocker(dev);
+ int i;
+
+ QLIST_REMOVE(r, next);
+
+ for (i = 0; i < r->fp_ports; i++) {
+ FpPort *port = r->fp_port[i];
+
+ fp_port_free(port);
+ r->fp_port[i] = NULL;
+ }
+
+ for (i = 0; i < rocker_pci_ring_count(r); i++) {
+ if (r->rings[i]) {
+ desc_ring_free(r->rings[i]);
+ }
+ }
+ g_free(r->rings);
+
+ rocker_msix_uninit(r);
+ object_unparent(OBJECT(&r->msix_bar));
+ object_unparent(OBJECT(&r->mmio));
+
+ for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) {
+ if (r->worlds[i]) {
+ world_free(r->worlds[i]);
+ }
+ }
+ g_free(r->fp_ports_peers);
+}
+
+static void rocker_reset(DeviceState *dev)
+{
+ Rocker *r = to_rocker(dev);
+ int i;
+
+ for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) {
+ if (r->worlds[i]) {
+ world_reset(r->worlds[i]);
+ }
+ }
+ for (i = 0; i < r->fp_ports; i++) {
+ fp_port_reset(r->fp_port[i]);
+ fp_port_set_world(r->fp_port[i], r->world_dflt);
+ }
+
+ r->test_reg = 0;
+ r->test_reg64 = 0;
+ r->test_dma_addr = 0;
+ r->test_dma_size = 0;
+
+ for (i = 0; i < rocker_pci_ring_count(r); i++) {
+ desc_ring_reset(r->rings[i]);
+ }
+
+ DPRINTF("Reset done\n");
+}
+
+static Property rocker_properties[] = {
+ DEFINE_PROP_STRING("name", Rocker, name),
+ DEFINE_PROP_MACADDR("fp_start_macaddr", Rocker,
+ fp_start_macaddr),
+ DEFINE_PROP_UINT64("switch_id", Rocker,
+ switch_id, 0),
+ DEFINE_PROP_ARRAY("ports", Rocker, fp_ports,
+ fp_ports_peers, qdev_prop_netdev, NICPeers),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription rocker_vmsd = {
+ .name = ROCKER,
+ .unmigratable = 1,
+};
+
+static void rocker_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->init = pci_rocker_init;
+ k->exit = pci_rocker_uninit;
+ k->vendor_id = PCI_VENDOR_ID_REDHAT;
+ k->device_id = PCI_DEVICE_ID_REDHAT_ROCKER;
+ k->revision = ROCKER_PCI_REVISION;
+ k->class_id = PCI_CLASS_NETWORK_OTHER;
+ set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
+ dc->desc = "Rocker Switch";
+ dc->reset = rocker_reset;
+ dc->props = rocker_properties;
+ dc->vmsd = &rocker_vmsd;
+}
+
+static const TypeInfo rocker_info = {
+ .name = ROCKER,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(Rocker),
+ .class_init = rocker_class_init,
+};
+
+static void rocker_register_types(void)
+{
+ type_register_static(&rocker_info);
+}
+
+type_init(rocker_register_types)
diff --git a/hw/net/rocker/rocker.h b/hw/net/rocker/rocker.h
new file mode 100644
index 000000000..f9c80f801
--- /dev/null
+++ b/hw/net/rocker/rocker.h
@@ -0,0 +1,84 @@
+/*
+ * QEMU rocker switch emulation
+ *
+ * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
+ * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us>
+ * Copyright (c) 2014 Neil Horman <nhorman@tuxdriver.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ROCKER_H_
+#define _ROCKER_H_
+
+#include "qemu/sockets.h"
+
+#if defined(DEBUG_ROCKER)
+# define DPRINTF(fmt, ...) \
+ do { \
+ struct timeval tv; \
+ char timestr[64]; \
+ time_t now; \
+ gettimeofday(&tv, NULL); \
+ now = tv.tv_sec; \
+ strftime(timestr, sizeof(timestr), "%T", localtime(&now)); \
+ fprintf(stderr, "%s.%06ld ", timestr, tv.tv_usec); \
+ fprintf(stderr, "ROCKER: " fmt, ## __VA_ARGS__); \
+ } while (0)
+#else
+static inline GCC_FMT_ATTR(1, 2) int DPRINTF(const char *fmt, ...)
+{
+ return 0;
+}
+#endif
+
+#define __le16 uint16_t
+#define __le32 uint32_t
+#define __le64 uint64_t
+
+#define __be16 uint16_t
+#define __be32 uint32_t
+#define __be64 uint64_t
+
+static inline bool ipv4_addr_is_multicast(__be32 addr)
+{
+ return (addr & htonl(0xf0000000)) == htonl(0xe0000000);
+}
+
+typedef struct ipv6_addr {
+ union {
+ uint8_t addr8[16];
+ __be16 addr16[8];
+ __be32 addr32[4];
+ };
+} Ipv6Addr;
+
+static inline bool ipv6_addr_is_multicast(const Ipv6Addr *addr)
+{
+ return (addr->addr32[0] & htonl(0xFF000000)) == htonl(0xFF000000);
+}
+
+typedef struct rocker Rocker;
+typedef struct world World;
+typedef struct desc_info DescInfo;
+typedef struct desc_ring DescRing;
+
+Rocker *rocker_find(const char *name);
+uint32_t rocker_fp_ports(Rocker *r);
+int rocker_event_link_changed(Rocker *r, uint32_t pport, bool link_up);
+int rocker_event_mac_vlan_seen(Rocker *r, uint32_t pport, uint8_t *addr,
+ uint16_t vlan_id);
+int rx_produce(World *world, uint32_t pport,
+ const struct iovec *iov, int iovcnt, uint8_t copy_to_cpu);
+int rocker_port_eg(Rocker *r, uint32_t pport,
+ const struct iovec *iov, int iovcnt);
+
+#endif /* _ROCKER_H_ */
diff --git a/hw/net/rocker/rocker_desc.c b/hw/net/rocker/rocker_desc.c
new file mode 100644
index 000000000..9d896fe47
--- /dev/null
+++ b/hw/net/rocker/rocker_desc.c
@@ -0,0 +1,377 @@
+/*
+ * QEMU rocker switch emulation - Descriptor ring support
+ *
+ * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "net/net.h"
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+
+#include "rocker.h"
+#include "rocker_hw.h"
+#include "rocker_desc.h"
+
+struct desc_ring {
+ hwaddr base_addr;
+ uint32_t size;
+ uint32_t head;
+ uint32_t tail;
+ uint32_t ctrl;
+ uint32_t credits;
+ Rocker *r;
+ DescInfo *info;
+ int index;
+ desc_ring_consume *consume;
+ unsigned msix_vector;
+};
+
+struct desc_info {
+ DescRing *ring;
+ RockerDesc desc;
+ char *buf;
+ size_t buf_size;
+};
+
+uint16_t desc_buf_size(DescInfo *info)
+{
+ return le16_to_cpu(info->desc.buf_size);
+}
+
+uint16_t desc_tlv_size(DescInfo *info)
+{
+ return le16_to_cpu(info->desc.tlv_size);
+}
+
+char *desc_get_buf(DescInfo *info, bool read_only)
+{
+ PCIDevice *dev = PCI_DEVICE(info->ring->r);
+ size_t size = read_only ? le16_to_cpu(info->desc.tlv_size) :
+ le16_to_cpu(info->desc.buf_size);
+
+ if (size > info->buf_size) {
+ info->buf = g_realloc(info->buf, size);
+ info->buf_size = size;
+ }
+
+ if (!info->buf) {
+ return NULL;
+ }
+
+ if (pci_dma_read(dev, le64_to_cpu(info->desc.buf_addr), info->buf, size)) {
+ return NULL;
+ }
+
+ return info->buf;
+}
+
+int desc_set_buf(DescInfo *info, size_t tlv_size)
+{
+ PCIDevice *dev = PCI_DEVICE(info->ring->r);
+
+ if (tlv_size > info->buf_size) {
+ DPRINTF("ERROR: trying to write more to desc buf than it "
+ "can hold buf_size %zu tlv_size %zu\n",
+ info->buf_size, tlv_size);
+ return -ROCKER_EMSGSIZE;
+ }
+
+ info->desc.tlv_size = cpu_to_le16(tlv_size);
+ pci_dma_write(dev, le64_to_cpu(info->desc.buf_addr), info->buf, tlv_size);
+
+ return ROCKER_OK;
+}
+
+DescRing *desc_get_ring(DescInfo *info)
+{
+ return info->ring;
+}
+
+int desc_ring_index(DescRing *ring)
+{
+ return ring->index;
+}
+
+static bool desc_ring_empty(DescRing *ring)
+{
+ return ring->head == ring->tail;
+}
+
+bool desc_ring_set_base_addr(DescRing *ring, uint64_t base_addr)
+{
+ if (base_addr & 0x7) {
+ DPRINTF("ERROR: ring[%d] desc base addr (0x" TARGET_FMT_plx
+ ") not 8-byte aligned\n", ring->index, base_addr);
+ return false;
+ }
+
+ ring->base_addr = base_addr;
+
+ return true;
+}
+
+uint64_t desc_ring_get_base_addr(DescRing *ring)
+{
+ return ring->base_addr;
+}
+
+bool desc_ring_set_size(DescRing *ring, uint32_t size)
+{
+ int i;
+
+ if (size < 2 || size > 0x10000 || (size & (size - 1))) {
+ DPRINTF("ERROR: ring[%d] size (%d) not a power of 2 "
+ "or in range [2, 64K]\n", ring->index, size);
+ return false;
+ }
+
+ for (i = 0; i < ring->size; i++) {
+ if (ring->info[i].buf) {
+ g_free(ring->info[i].buf);
+ }
+ }
+
+ ring->size = size;
+ ring->head = ring->tail = 0;
+
+ ring->info = g_realloc(ring->info, size * sizeof(DescInfo));
+ if (!ring->info) {
+ return false;
+ }
+
+ memset(ring->info, 0, size * sizeof(DescInfo));
+
+ for (i = 0; i < size; i++) {
+ ring->info[i].ring = ring;
+ }
+
+ return true;
+}
+
+uint32_t desc_ring_get_size(DescRing *ring)
+{
+ return ring->size;
+}
+
+static DescInfo *desc_read(DescRing *ring, uint32_t index)
+{
+ PCIDevice *dev = PCI_DEVICE(ring->r);
+ DescInfo *info = &ring->info[index];
+ hwaddr addr = ring->base_addr + (sizeof(RockerDesc) * index);
+
+ pci_dma_read(dev, addr, &info->desc, sizeof(info->desc));
+
+ return info;
+}
+
+static void desc_write(DescRing *ring, uint32_t index)
+{
+ PCIDevice *dev = PCI_DEVICE(ring->r);
+ DescInfo *info = &ring->info[index];
+ hwaddr addr = ring->base_addr + (sizeof(RockerDesc) * index);
+
+ pci_dma_write(dev, addr, &info->desc, sizeof(info->desc));
+}
+
+static bool desc_ring_base_addr_check(DescRing *ring)
+{
+ if (!ring->base_addr) {
+ DPRINTF("ERROR: ring[%d] not-initialized desc base address!\n",
+ ring->index);
+ return false;
+ }
+ return true;
+}
+
+static DescInfo *__desc_ring_fetch_desc(DescRing *ring)
+{
+ return desc_read(ring, ring->tail);
+}
+
+DescInfo *desc_ring_fetch_desc(DescRing *ring)
+{
+ if (desc_ring_empty(ring) || !desc_ring_base_addr_check(ring)) {
+ return NULL;
+ }
+
+ return desc_read(ring, ring->tail);
+}
+
+static bool __desc_ring_post_desc(DescRing *ring, int err)
+{
+ uint16_t comp_err = 0x8000 | (uint16_t)-err;
+ DescInfo *info = &ring->info[ring->tail];
+
+ info->desc.comp_err = cpu_to_le16(comp_err);
+ desc_write(ring, ring->tail);
+ ring->tail = (ring->tail + 1) % ring->size;
+
+ /* return true if starting credit count */
+
+ return ring->credits++ == 0;
+}
+
+bool desc_ring_post_desc(DescRing *ring, int err)
+{
+ if (desc_ring_empty(ring)) {
+ DPRINTF("ERROR: ring[%d] trying to post desc to empty ring\n",
+ ring->index);
+ return false;
+ }
+
+ if (!desc_ring_base_addr_check(ring)) {
+ return false;
+ }
+
+ return __desc_ring_post_desc(ring, err);
+}
+
+static bool ring_pump(DescRing *ring)
+{
+ DescInfo *info;
+ bool primed = false;
+ int err;
+
+ /* If the ring has a consumer, call consumer for each
+ * desc starting at tail and stopping when tail reaches
+ * head (the empty ring condition).
+ */
+
+ if (ring->consume) {
+ while (ring->head != ring->tail) {
+ info = __desc_ring_fetch_desc(ring);
+ err = ring->consume(ring->r, info);
+ if (__desc_ring_post_desc(ring, err)) {
+ primed = true;
+ }
+ }
+ }
+
+ return primed;
+}
+
+bool desc_ring_set_head(DescRing *ring, uint32_t new)
+{
+ uint32_t tail = ring->tail;
+ uint32_t head = ring->head;
+
+ if (!desc_ring_base_addr_check(ring)) {
+ return false;
+ }
+
+ if (new >= ring->size) {
+ DPRINTF("ERROR: trying to set head (%d) past ring[%d] size (%d)\n",
+ new, ring->index, ring->size);
+ return false;
+ }
+
+ if (((head < tail) && ((new >= tail) || (new < head))) ||
+ ((head > tail) && ((new >= tail) && (new < head)))) {
+ DPRINTF("ERROR: trying to wrap ring[%d] "
+ "(head %d, tail %d, new head %d)\n",
+ ring->index, head, tail, new);
+ return false;
+ }
+
+ if (new == ring->head) {
+ DPRINTF("WARNING: setting head (%d) to current head position\n", new);
+ }
+
+ ring->head = new;
+
+ return ring_pump(ring);
+}
+
+uint32_t desc_ring_get_head(DescRing *ring)
+{
+ return ring->head;
+}
+
+uint32_t desc_ring_get_tail(DescRing *ring)
+{
+ return ring->tail;
+}
+
+void desc_ring_set_ctrl(DescRing *ring, uint32_t val)
+{
+ if (val & ROCKER_DMA_DESC_CTRL_RESET) {
+ DPRINTF("ring[%d] resetting\n", ring->index);
+ desc_ring_reset(ring);
+ }
+}
+
+bool desc_ring_ret_credits(DescRing *ring, uint32_t credits)
+{
+ if (credits > ring->credits) {
+ DPRINTF("ERROR: trying to return more credits (%d) "
+ "than are outstanding (%d)\n", credits, ring->credits);
+ ring->credits = 0;
+ return false;
+ }
+
+ ring->credits -= credits;
+
+ /* return true if credits are still outstanding */
+
+ return ring->credits > 0;
+}
+
+uint32_t desc_ring_get_credits(DescRing *ring)
+{
+ return ring->credits;
+}
+
+void desc_ring_set_consume(DescRing *ring, desc_ring_consume *consume,
+ unsigned vector)
+{
+ ring->consume = consume;
+ ring->msix_vector = vector;
+}
+
+unsigned desc_ring_get_msix_vector(DescRing *ring)
+{
+ return ring->msix_vector;
+}
+
+DescRing *desc_ring_alloc(Rocker *r, int index)
+{
+ DescRing *ring;
+
+ ring = g_malloc0(sizeof(DescRing));
+ if (!ring) {
+ return NULL;
+ }
+
+ ring->r = r;
+ ring->index = index;
+
+ return ring;
+}
+
+void desc_ring_free(DescRing *ring)
+{
+ if (ring->info) {
+ g_free(ring->info);
+ }
+ g_free(ring);
+}
+
+void desc_ring_reset(DescRing *ring)
+{
+ ring->base_addr = 0;
+ ring->size = 0;
+ ring->head = 0;
+ ring->tail = 0;
+ ring->ctrl = 0;
+ ring->credits = 0;
+}
diff --git a/hw/net/rocker/rocker_desc.h b/hw/net/rocker/rocker_desc.h
new file mode 100644
index 000000000..d4041f5c4
--- /dev/null
+++ b/hw/net/rocker/rocker_desc.h
@@ -0,0 +1,53 @@
+/*
+ * QEMU rocker switch emulation - Descriptor ring support
+ *
+ * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef _ROCKER_DESC_H_
+#define _ROCKER_DESC_H_
+
+#include "rocker_hw.h"
+
+typedef int (desc_ring_consume)(Rocker *r, DescInfo *info);
+
+uint16_t desc_buf_size(DescInfo *info);
+uint16_t desc_tlv_size(DescInfo *info);
+char *desc_get_buf(DescInfo *info, bool read_only);
+int desc_set_buf(DescInfo *info, size_t tlv_size);
+DescRing *desc_get_ring(DescInfo *info);
+
+int desc_ring_index(DescRing *ring);
+bool desc_ring_set_base_addr(DescRing *ring, uint64_t base_addr);
+uint64_t desc_ring_get_base_addr(DescRing *ring);
+bool desc_ring_set_size(DescRing *ring, uint32_t size);
+uint32_t desc_ring_get_size(DescRing *ring);
+bool desc_ring_set_head(DescRing *ring, uint32_t new);
+uint32_t desc_ring_get_head(DescRing *ring);
+uint32_t desc_ring_get_tail(DescRing *ring);
+void desc_ring_set_ctrl(DescRing *ring, uint32_t val);
+bool desc_ring_ret_credits(DescRing *ring, uint32_t credits);
+uint32_t desc_ring_get_credits(DescRing *ring);
+
+DescInfo *desc_ring_fetch_desc(DescRing *ring);
+bool desc_ring_post_desc(DescRing *ring, int status);
+
+void desc_ring_set_consume(DescRing *ring, desc_ring_consume *consume,
+ unsigned vector);
+unsigned desc_ring_get_msix_vector(DescRing *ring);
+DescRing *desc_ring_alloc(Rocker *r, int index);
+void desc_ring_free(DescRing *ring);
+void desc_ring_reset(DescRing *ring);
+
+#endif
diff --git a/hw/net/rocker/rocker_fp.c b/hw/net/rocker/rocker_fp.c
new file mode 100644
index 000000000..c693ae508
--- /dev/null
+++ b/hw/net/rocker/rocker_fp.c
@@ -0,0 +1,263 @@
+/*
+ * QEMU rocker switch emulation - front-panel ports
+ *
+ * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "net/clients.h"
+
+#include "rocker.h"
+#include "rocker_hw.h"
+#include "rocker_fp.h"
+#include "rocker_world.h"
+
+enum duplex {
+ DUPLEX_HALF = 0,
+ DUPLEX_FULL
+};
+
+struct fp_port {
+ Rocker *r;
+ World *world;
+ unsigned int index;
+ char *name;
+ uint32_t pport;
+ bool enabled;
+ uint32_t speed;
+ uint8_t duplex;
+ uint8_t autoneg;
+ uint8_t learning;
+ NICState *nic;
+ NICConf conf;
+};
+
+char *fp_port_get_name(FpPort *port)
+{
+ return port->name;
+}
+
+bool fp_port_get_link_up(FpPort *port)
+{
+ return !qemu_get_queue(port->nic)->link_down;
+}
+
+void fp_port_get_info(FpPort *port, RockerPortList *info)
+{
+ info->value->name = g_strdup(port->name);
+ info->value->enabled = port->enabled;
+ info->value->link_up = fp_port_get_link_up(port);
+ info->value->speed = port->speed;
+ info->value->duplex = port->duplex;
+ info->value->autoneg = port->autoneg;
+}
+
+void fp_port_get_macaddr(FpPort *port, MACAddr *macaddr)
+{
+ memcpy(macaddr->a, port->conf.macaddr.a, sizeof(macaddr->a));
+}
+
+void fp_port_set_macaddr(FpPort *port, MACAddr *macaddr)
+{
+/* XXX TODO implement and test setting mac addr
+ * XXX memcpy(port->conf.macaddr.a, macaddr.a, sizeof(port->conf.macaddr.a));
+ */
+}
+
+uint8_t fp_port_get_learning(FpPort *port)
+{
+ return port->learning;
+}
+
+void fp_port_set_learning(FpPort *port, uint8_t learning)
+{
+ port->learning = learning;
+}
+
+int fp_port_get_settings(FpPort *port, uint32_t *speed,
+ uint8_t *duplex, uint8_t *autoneg)
+{
+ *speed = port->speed;
+ *duplex = port->duplex;
+ *autoneg = port->autoneg;
+
+ return ROCKER_OK;
+}
+
+int fp_port_set_settings(FpPort *port, uint32_t speed,
+ uint8_t duplex, uint8_t autoneg)
+{
+ /* XXX validate inputs */
+
+ port->speed = speed;
+ port->duplex = duplex;
+ port->autoneg = autoneg;
+
+ return ROCKER_OK;
+}
+
+bool fp_port_from_pport(uint32_t pport, uint32_t *port)
+{
+ if (pport < 1 || pport > ROCKER_FP_PORTS_MAX) {
+ return false;
+ }
+ *port = pport - 1;
+ return true;
+}
+
+int fp_port_eg(FpPort *port, const struct iovec *iov, int iovcnt)
+{
+ NetClientState *nc = qemu_get_queue(port->nic);
+
+ if (port->enabled) {
+ qemu_sendv_packet(nc, iov, iovcnt);
+ }
+
+ return ROCKER_OK;
+}
+
+static ssize_t fp_port_receive_iov(NetClientState *nc, const struct iovec *iov,
+ int iovcnt)
+{
+ FpPort *port = qemu_get_nic_opaque(nc);
+
+ /* If the port is disabled, we want to drop this pkt
+ * now rather than queing it for later. We don't want
+ * any stale pkts getting into the device when the port
+ * transitions to enabled.
+ */
+
+ if (!port->enabled) {
+ return -1;
+ }
+
+ return world_ingress(port->world, port->pport, iov, iovcnt);
+}
+
+static ssize_t fp_port_receive(NetClientState *nc, const uint8_t *buf,
+ size_t size)
+{
+ const struct iovec iov = {
+ .iov_base = (uint8_t *)buf,
+ .iov_len = size
+ };
+
+ return fp_port_receive_iov(nc, &iov, 1);
+}
+
+static void fp_port_cleanup(NetClientState *nc)
+{
+}
+
+static void fp_port_set_link_status(NetClientState *nc)
+{
+ FpPort *port = qemu_get_nic_opaque(nc);
+
+ rocker_event_link_changed(port->r, port->pport, !nc->link_down);
+}
+
+static NetClientInfo fp_port_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .size = sizeof(NICState),
+ .receive = fp_port_receive,
+ .receive_iov = fp_port_receive_iov,
+ .cleanup = fp_port_cleanup,
+ .link_status_changed = fp_port_set_link_status,
+};
+
+World *fp_port_get_world(FpPort *port)
+{
+ return port->world;
+}
+
+void fp_port_set_world(FpPort *port, World *world)
+{
+ DPRINTF("port %d setting world \"%s\"\n", port->index, world_name(world));
+ port->world = world;
+}
+
+bool fp_port_enabled(FpPort *port)
+{
+ return port->enabled;
+}
+
+static void fp_port_set_link(FpPort *port, bool up)
+{
+ NetClientState *nc = qemu_get_queue(port->nic);
+
+ if (up == nc->link_down) {
+ nc->link_down = !up;
+ nc->info->link_status_changed(nc);
+ }
+}
+
+void fp_port_enable(FpPort *port)
+{
+ fp_port_set_link(port, true);
+ port->enabled = true;
+ DPRINTF("port %d enabled\n", port->index);
+}
+
+void fp_port_disable(FpPort *port)
+{
+ port->enabled = false;
+ fp_port_set_link(port, false);
+ DPRINTF("port %d disabled\n", port->index);
+}
+
+FpPort *fp_port_alloc(Rocker *r, char *sw_name,
+ MACAddr *start_mac, unsigned int index,
+ NICPeers *peers)
+{
+ FpPort *port = g_malloc0(sizeof(FpPort));
+
+ if (!port) {
+ return NULL;
+ }
+
+ port->r = r;
+ port->index = index;
+ port->pport = index + 1;
+
+ /* front-panel switch port names are 1-based */
+
+ port->name = g_strdup_printf("%sp%d", sw_name, port->pport);
+
+ memcpy(port->conf.macaddr.a, start_mac, sizeof(port->conf.macaddr.a));
+ port->conf.macaddr.a[5] += index;
+ port->conf.bootindex = -1;
+ port->conf.peers = *peers;
+
+ port->nic = qemu_new_nic(&fp_port_info, &port->conf,
+ sw_name, NULL, port);
+ qemu_format_nic_info_str(qemu_get_queue(port->nic),
+ port->conf.macaddr.a);
+
+ fp_port_reset(port);
+
+ return port;
+}
+
+void fp_port_free(FpPort *port)
+{
+ qemu_del_nic(port->nic);
+ g_free(port->name);
+ g_free(port);
+}
+
+void fp_port_reset(FpPort *port)
+{
+ fp_port_disable(port);
+ port->speed = 10000; /* 10Gbps */
+ port->duplex = DUPLEX_FULL;
+ port->autoneg = 0;
+}
diff --git a/hw/net/rocker/rocker_fp.h b/hw/net/rocker/rocker_fp.h
new file mode 100644
index 000000000..ab80fd833
--- /dev/null
+++ b/hw/net/rocker/rocker_fp.h
@@ -0,0 +1,53 @@
+/*
+ * QEMU rocker switch emulation - front-panel ports
+ *
+ * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ROCKER_FP_H_
+#define _ROCKER_FP_H_
+
+#include "net/net.h"
+#include "qemu/iov.h"
+
+#define ROCKER_FP_PORTS_MAX 62
+
+typedef struct fp_port FpPort;
+
+int fp_port_eg(FpPort *port, const struct iovec *iov, int iovcnt);
+
+char *fp_port_get_name(FpPort *port);
+bool fp_port_get_link_up(FpPort *port);
+void fp_port_get_info(FpPort *port, RockerPortList *info);
+void fp_port_get_macaddr(FpPort *port, MACAddr *macaddr);
+void fp_port_set_macaddr(FpPort *port, MACAddr *macaddr);
+uint8_t fp_port_get_learning(FpPort *port);
+void fp_port_set_learning(FpPort *port, uint8_t learning);
+int fp_port_get_settings(FpPort *port, uint32_t *speed,
+ uint8_t *duplex, uint8_t *autoneg);
+int fp_port_set_settings(FpPort *port, uint32_t speed,
+ uint8_t duplex, uint8_t autoneg);
+bool fp_port_from_pport(uint32_t pport, uint32_t *port);
+World *fp_port_get_world(FpPort *port);
+void fp_port_set_world(FpPort *port, World *world);
+bool fp_port_enabled(FpPort *port);
+void fp_port_enable(FpPort *port);
+void fp_port_disable(FpPort *port);
+
+FpPort *fp_port_alloc(Rocker *r, char *sw_name,
+ MACAddr *start_mac, unsigned int index,
+ NICPeers *peers);
+void fp_port_free(FpPort *port);
+void fp_port_reset(FpPort *port);
+
+#endif /* _ROCKER_FP_H_ */
diff --git a/hw/net/rocker/rocker_hw.h b/hw/net/rocker/rocker_hw.h
new file mode 100644
index 000000000..8c5083032
--- /dev/null
+++ b/hw/net/rocker/rocker_hw.h
@@ -0,0 +1,493 @@
+/*
+ * Rocker switch hardware register and descriptor definitions.
+ *
+ * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
+ * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us>
+ *
+ */
+
+#ifndef _ROCKER_HW_
+#define _ROCKER_HW_
+
+#define __le16 uint16_t
+#define __le32 uint32_t
+#define __le64 uint64_t
+
+/*
+ * Return codes
+ */
+
+enum {
+ ROCKER_OK = 0,
+ ROCKER_ENOENT = 2,
+ ROCKER_ENXIO = 6,
+ ROCKER_ENOMEM = 12,
+ ROCKER_EEXIST = 17,
+ ROCKER_EINVAL = 22,
+ ROCKER_EMSGSIZE = 90,
+ ROCKER_ENOTSUP = 95,
+ ROCKER_ENOBUFS = 105,
+};
+
+/*
+ * PCI configuration space
+ */
+
+#define ROCKER_PCI_REVISION 0x1
+#define ROCKER_PCI_BAR0_IDX 0
+#define ROCKER_PCI_BAR0_SIZE 0x2000
+#define ROCKER_PCI_MSIX_BAR_IDX 1
+#define ROCKER_PCI_MSIX_BAR_SIZE 0x2000
+#define ROCKER_PCI_MSIX_TABLE_OFFSET 0x0000
+#define ROCKER_PCI_MSIX_PBA_OFFSET 0x1000
+
+/*
+ * MSI-X vectors
+ */
+
+enum {
+ ROCKER_MSIX_VEC_CMD,
+ ROCKER_MSIX_VEC_EVENT,
+ ROCKER_MSIX_VEC_TEST,
+ ROCKER_MSIX_VEC_RESERVED0,
+ __ROCKER_MSIX_VEC_TX,
+ __ROCKER_MSIX_VEC_RX,
+#define ROCKER_MSIX_VEC_TX(port) \
+ (__ROCKER_MSIX_VEC_TX + ((port) * 2))
+#define ROCKER_MSIX_VEC_RX(port) \
+ (__ROCKER_MSIX_VEC_RX + ((port) * 2))
+#define ROCKER_MSIX_VEC_COUNT(portcnt) \
+ (ROCKER_MSIX_VEC_RX((portcnt) - 1) + 1)
+};
+
+/*
+ * Rocker bogus registers
+ */
+#define ROCKER_BOGUS_REG0 0x0000
+#define ROCKER_BOGUS_REG1 0x0004
+#define ROCKER_BOGUS_REG2 0x0008
+#define ROCKER_BOGUS_REG3 0x000c
+
+/*
+ * Rocker test registers
+ */
+#define ROCKER_TEST_REG 0x0010
+#define ROCKER_TEST_REG64 0x0018 /* 8-byte */
+#define ROCKER_TEST_IRQ 0x0020
+#define ROCKER_TEST_DMA_ADDR 0x0028 /* 8-byte */
+#define ROCKER_TEST_DMA_SIZE 0x0030
+#define ROCKER_TEST_DMA_CTRL 0x0034
+
+/*
+ * Rocker test register ctrl
+ */
+#define ROCKER_TEST_DMA_CTRL_CLEAR (1 << 0)
+#define ROCKER_TEST_DMA_CTRL_FILL (1 << 1)
+#define ROCKER_TEST_DMA_CTRL_INVERT (1 << 2)
+
+/*
+ * Rocker DMA ring register offsets
+ */
+#define ROCKER_DMA_DESC_BASE 0x1000
+#define ROCKER_DMA_DESC_SIZE 32
+#define ROCKER_DMA_DESC_MASK 0x1F
+#define ROCKER_DMA_DESC_TOTAL_SIZE \
+ (ROCKER_DMA_DESC_SIZE * 64) /* 62 ports + event + cmd */
+#define ROCKER_DMA_DESC_ADDR_OFFSET 0x00 /* 8-byte */
+#define ROCKER_DMA_DESC_SIZE_OFFSET 0x08
+#define ROCKER_DMA_DESC_HEAD_OFFSET 0x0c
+#define ROCKER_DMA_DESC_TAIL_OFFSET 0x10
+#define ROCKER_DMA_DESC_CTRL_OFFSET 0x14
+#define ROCKER_DMA_DESC_CREDITS_OFFSET 0x18
+#define ROCKER_DMA_DESC_RSVD_OFFSET 0x1c
+
+/*
+ * Rocker dma ctrl register bits
+ */
+#define ROCKER_DMA_DESC_CTRL_RESET (1 << 0)
+
+/*
+ * Rocker ring indices
+ */
+#define ROCKER_RING_CMD 0
+#define ROCKER_RING_EVENT 1
+
+/*
+ * Helper macro to do convert a dma ring register
+ * to its index. Based on the fact that the register
+ * group stride is 32 bytes.
+ */
+#define ROCKER_RING_INDEX(reg) ((reg >> 5) & 0x7F)
+
+/*
+ * Rocker DMA Descriptor
+ */
+
+typedef struct rocker_desc {
+ __le64 buf_addr;
+ uint64_t cookie;
+ __le16 buf_size;
+ __le16 tlv_size;
+ __le16 rsvd[5]; /* pad to 32 bytes */
+ __le16 comp_err;
+} __attribute__((packed, aligned(8))) RockerDesc;
+
+/*
+ * Rocker TLV type fields
+ */
+
+typedef struct rocker_tlv {
+ __le32 type;
+ __le16 len;
+ __le16 rsvd;
+} __attribute__((packed, aligned(8))) RockerTlv;
+
+/* cmd msg */
+enum {
+ ROCKER_TLV_CMD_UNSPEC,
+ ROCKER_TLV_CMD_TYPE, /* u16 */
+ ROCKER_TLV_CMD_INFO, /* nest */
+
+ __ROCKER_TLV_CMD_MAX,
+ ROCKER_TLV_CMD_MAX = __ROCKER_TLV_CMD_MAX - 1,
+};
+
+enum {
+ ROCKER_TLV_CMD_TYPE_UNSPEC,
+ ROCKER_TLV_CMD_TYPE_GET_PORT_SETTINGS,
+ ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS,
+ ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD,
+ ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_MOD,
+ ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL,
+ ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_GET_STATS,
+ ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD,
+ ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_MOD,
+ ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL,
+ ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_GET_STATS,
+
+ __ROCKER_TLV_CMD_TYPE_MAX,
+ ROCKER_TLV_CMD_TYPE_MAX = __ROCKER_TLV_CMD_TYPE_MAX - 1,
+};
+
+/* cmd info nested for set/get port settings */
+enum {
+ ROCKER_TLV_CMD_PORT_SETTINGS_UNSPEC,
+ ROCKER_TLV_CMD_PORT_SETTINGS_PPORT, /* u32 */
+ ROCKER_TLV_CMD_PORT_SETTINGS_SPEED, /* u32 */
+ ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX, /* u8 */
+ ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG, /* u8 */
+ ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR, /* binary */
+ ROCKER_TLV_CMD_PORT_SETTINGS_MODE, /* u8 */
+ ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING, /* u8 */
+ ROCKER_TLV_CMD_PORT_SETTINGS_PHYS_NAME, /* binary */
+
+ __ROCKER_TLV_CMD_PORT_SETTINGS_MAX,
+ ROCKER_TLV_CMD_PORT_SETTINGS_MAX = __ROCKER_TLV_CMD_PORT_SETTINGS_MAX - 1,
+};
+
+enum {
+ ROCKER_PORT_MODE_OF_DPA,
+};
+
+/* event msg */
+enum {
+ ROCKER_TLV_EVENT_UNSPEC,
+ ROCKER_TLV_EVENT_TYPE, /* u16 */
+ ROCKER_TLV_EVENT_INFO, /* nest */
+
+ __ROCKER_TLV_EVENT_MAX,
+ ROCKER_TLV_EVENT_MAX = __ROCKER_TLV_EVENT_MAX - 1,
+};
+
+enum {
+ ROCKER_TLV_EVENT_TYPE_UNSPEC,
+ ROCKER_TLV_EVENT_TYPE_LINK_CHANGED,
+ ROCKER_TLV_EVENT_TYPE_MAC_VLAN_SEEN,
+
+ __ROCKER_TLV_EVENT_TYPE_MAX,
+ ROCKER_TLV_EVENT_TYPE_MAX = __ROCKER_TLV_EVENT_TYPE_MAX - 1,
+};
+
+/* event info nested for link changed */
+enum {
+ ROCKER_TLV_EVENT_LINK_CHANGED_UNSPEC,
+ ROCKER_TLV_EVENT_LINK_CHANGED_PPORT, /* u32 */
+ ROCKER_TLV_EVENT_LINK_CHANGED_LINKUP, /* u8 */
+
+ __ROCKER_TLV_EVENT_LINK_CHANGED_MAX,
+ ROCKER_TLV_EVENT_LINK_CHANGED_MAX = __ROCKER_TLV_EVENT_LINK_CHANGED_MAX - 1,
+};
+
+/* event info nested for MAC/VLAN */
+enum {
+ ROCKER_TLV_EVENT_MAC_VLAN_UNSPEC,
+ ROCKER_TLV_EVENT_MAC_VLAN_PPORT, /* u32 */
+ ROCKER_TLV_EVENT_MAC_VLAN_MAC, /* binary */
+ ROCKER_TLV_EVENT_MAC_VLAN_VLAN_ID, /* __be16 */
+
+ __ROCKER_TLV_EVENT_MAC_VLAN_MAX,
+ ROCKER_TLV_EVENT_MAC_VLAN_MAX = __ROCKER_TLV_EVENT_MAC_VLAN_MAX - 1,
+};
+
+/* Rx msg */
+enum {
+ ROCKER_TLV_RX_UNSPEC,
+ ROCKER_TLV_RX_FLAGS, /* u16, see RX_FLAGS_ */
+ ROCKER_TLV_RX_CSUM, /* u16 */
+ ROCKER_TLV_RX_FRAG_ADDR, /* u64 */
+ ROCKER_TLV_RX_FRAG_MAX_LEN, /* u16 */
+ ROCKER_TLV_RX_FRAG_LEN, /* u16 */
+
+ __ROCKER_TLV_RX_MAX,
+ ROCKER_TLV_RX_MAX = __ROCKER_TLV_RX_MAX - 1,
+};
+
+#define ROCKER_RX_FLAGS_IPV4 (1 << 0)
+#define ROCKER_RX_FLAGS_IPV6 (1 << 1)
+#define ROCKER_RX_FLAGS_CSUM_CALC (1 << 2)
+#define ROCKER_RX_FLAGS_IPV4_CSUM_GOOD (1 << 3)
+#define ROCKER_RX_FLAGS_IP_FRAG (1 << 4)
+#define ROCKER_RX_FLAGS_TCP (1 << 5)
+#define ROCKER_RX_FLAGS_UDP (1 << 6)
+#define ROCKER_RX_FLAGS_TCP_UDP_CSUM_GOOD (1 << 7)
+#define ROCKER_RX_FLAGS_FWD_OFFLOAD (1 << 8)
+
+/* Tx msg */
+enum {
+ ROCKER_TLV_TX_UNSPEC,
+ ROCKER_TLV_TX_OFFLOAD, /* u8, see TX_OFFLOAD_ */
+ ROCKER_TLV_TX_L3_CSUM_OFF, /* u16 */
+ ROCKER_TLV_TX_TSO_MSS, /* u16 */
+ ROCKER_TLV_TX_TSO_HDR_LEN, /* u16 */
+ ROCKER_TLV_TX_FRAGS, /* array */
+
+ __ROCKER_TLV_TX_MAX,
+ ROCKER_TLV_TX_MAX = __ROCKER_TLV_TX_MAX - 1,
+};
+
+#define ROCKER_TX_OFFLOAD_NONE 0
+#define ROCKER_TX_OFFLOAD_IP_CSUM 1
+#define ROCKER_TX_OFFLOAD_TCP_UDP_CSUM 2
+#define ROCKER_TX_OFFLOAD_L3_CSUM 3
+#define ROCKER_TX_OFFLOAD_TSO 4
+
+#define ROCKER_TX_FRAGS_MAX 16
+
+enum {
+ ROCKER_TLV_TX_FRAG_UNSPEC,
+ ROCKER_TLV_TX_FRAG, /* nest */
+
+ __ROCKER_TLV_TX_FRAG_MAX,
+ ROCKER_TLV_TX_FRAG_MAX = __ROCKER_TLV_TX_FRAG_MAX - 1,
+};
+
+enum {
+ ROCKER_TLV_TX_FRAG_ATTR_UNSPEC,
+ ROCKER_TLV_TX_FRAG_ATTR_ADDR, /* u64 */
+ ROCKER_TLV_TX_FRAG_ATTR_LEN, /* u16 */
+
+ __ROCKER_TLV_TX_FRAG_ATTR_MAX,
+ ROCKER_TLV_TX_FRAG_ATTR_MAX = __ROCKER_TLV_TX_FRAG_ATTR_MAX - 1,
+};
+
+/*
+ * cmd info nested for OF-DPA msgs
+ */
+
+enum {
+ ROCKER_TLV_OF_DPA_UNSPEC,
+ ROCKER_TLV_OF_DPA_TABLE_ID, /* u16 */
+ ROCKER_TLV_OF_DPA_PRIORITY, /* u32 */
+ ROCKER_TLV_OF_DPA_HARDTIME, /* u32 */
+ ROCKER_TLV_OF_DPA_IDLETIME, /* u32 */
+ ROCKER_TLV_OF_DPA_COOKIE, /* u64 */
+ ROCKER_TLV_OF_DPA_IN_PPORT, /* u32 */
+ ROCKER_TLV_OF_DPA_IN_PPORT_MASK, /* u32 */
+ ROCKER_TLV_OF_DPA_OUT_PPORT, /* u32 */
+ ROCKER_TLV_OF_DPA_GOTO_TABLE_ID, /* u16 */
+ ROCKER_TLV_OF_DPA_GROUP_ID, /* u32 */
+ ROCKER_TLV_OF_DPA_GROUP_ID_LOWER, /* u32 */
+ ROCKER_TLV_OF_DPA_GROUP_COUNT, /* u16 */
+ ROCKER_TLV_OF_DPA_GROUP_IDS, /* u32 array */
+ ROCKER_TLV_OF_DPA_VLAN_ID, /* __be16 */
+ ROCKER_TLV_OF_DPA_VLAN_ID_MASK, /* __be16 */
+ ROCKER_TLV_OF_DPA_VLAN_PCP, /* __be16 */
+ ROCKER_TLV_OF_DPA_VLAN_PCP_MASK, /* __be16 */
+ ROCKER_TLV_OF_DPA_VLAN_PCP_ACTION, /* u8 */
+ ROCKER_TLV_OF_DPA_NEW_VLAN_ID, /* __be16 */
+ ROCKER_TLV_OF_DPA_NEW_VLAN_PCP, /* u8 */
+ ROCKER_TLV_OF_DPA_TUNNEL_ID, /* u32 */
+ ROCKER_TLV_OF_DPA_TUNNEL_LPORT, /* u32 */
+ ROCKER_TLV_OF_DPA_ETHERTYPE, /* __be16 */
+ ROCKER_TLV_OF_DPA_DST_MAC, /* binary */
+ ROCKER_TLV_OF_DPA_DST_MAC_MASK, /* binary */
+ ROCKER_TLV_OF_DPA_SRC_MAC, /* binary */
+ ROCKER_TLV_OF_DPA_SRC_MAC_MASK, /* binary */
+ ROCKER_TLV_OF_DPA_IP_PROTO, /* u8 */
+ ROCKER_TLV_OF_DPA_IP_PROTO_MASK, /* u8 */
+ ROCKER_TLV_OF_DPA_IP_DSCP, /* u8 */
+ ROCKER_TLV_OF_DPA_IP_DSCP_MASK, /* u8 */
+ ROCKER_TLV_OF_DPA_IP_DSCP_ACTION, /* u8 */
+ ROCKER_TLV_OF_DPA_NEW_IP_DSCP, /* u8 */
+ ROCKER_TLV_OF_DPA_IP_ECN, /* u8 */
+ ROCKER_TLV_OF_DPA_IP_ECN_MASK, /* u8 */
+ ROCKER_TLV_OF_DPA_DST_IP, /* __be32 */
+ ROCKER_TLV_OF_DPA_DST_IP_MASK, /* __be32 */
+ ROCKER_TLV_OF_DPA_SRC_IP, /* __be32 */
+ ROCKER_TLV_OF_DPA_SRC_IP_MASK, /* __be32 */
+ ROCKER_TLV_OF_DPA_DST_IPV6, /* binary */
+ ROCKER_TLV_OF_DPA_DST_IPV6_MASK, /* binary */
+ ROCKER_TLV_OF_DPA_SRC_IPV6, /* binary */
+ ROCKER_TLV_OF_DPA_SRC_IPV6_MASK, /* binary */
+ ROCKER_TLV_OF_DPA_SRC_ARP_IP, /* __be32 */
+ ROCKER_TLV_OF_DPA_SRC_ARP_IP_MASK, /* __be32 */
+ ROCKER_TLV_OF_DPA_L4_DST_PORT, /* __be16 */
+ ROCKER_TLV_OF_DPA_L4_DST_PORT_MASK, /* __be16 */
+ ROCKER_TLV_OF_DPA_L4_SRC_PORT, /* __be16 */
+ ROCKER_TLV_OF_DPA_L4_SRC_PORT_MASK, /* __be16 */
+ ROCKER_TLV_OF_DPA_ICMP_TYPE, /* u8 */
+ ROCKER_TLV_OF_DPA_ICMP_TYPE_MASK, /* u8 */
+ ROCKER_TLV_OF_DPA_ICMP_CODE, /* u8 */
+ ROCKER_TLV_OF_DPA_ICMP_CODE_MASK, /* u8 */
+ ROCKER_TLV_OF_DPA_IPV6_LABEL, /* __be32 */
+ ROCKER_TLV_OF_DPA_IPV6_LABEL_MASK, /* __be32 */
+ ROCKER_TLV_OF_DPA_QUEUE_ID_ACTION, /* u8 */
+ ROCKER_TLV_OF_DPA_NEW_QUEUE_ID, /* u8 */
+ ROCKER_TLV_OF_DPA_CLEAR_ACTIONS, /* u32 */
+ ROCKER_TLV_OF_DPA_POP_VLAN, /* u8 */
+ ROCKER_TLV_OF_DPA_TTL_CHECK, /* u8 */
+ ROCKER_TLV_OF_DPA_COPY_CPU_ACTION, /* u8 */
+
+ __ROCKER_TLV_OF_DPA_MAX,
+ ROCKER_TLV_OF_DPA_MAX = __ROCKER_TLV_OF_DPA_MAX - 1,
+};
+
+/*
+ * OF-DPA table IDs
+ */
+
+enum rocker_of_dpa_table_id {
+ ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT = 0,
+ ROCKER_OF_DPA_TABLE_ID_VLAN = 10,
+ ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC = 20,
+ ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING = 30,
+ ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING = 40,
+ ROCKER_OF_DPA_TABLE_ID_BRIDGING = 50,
+ ROCKER_OF_DPA_TABLE_ID_ACL_POLICY = 60,
+};
+
+/*
+ * OF-DPA flow stats
+ */
+
+enum {
+ ROCKER_TLV_OF_DPA_FLOW_STAT_UNSPEC,
+ ROCKER_TLV_OF_DPA_FLOW_STAT_DURATION, /* u32 */
+ ROCKER_TLV_OF_DPA_FLOW_STAT_RX_PKTS, /* u64 */
+ ROCKER_TLV_OF_DPA_FLOW_STAT_TX_PKTS, /* u64 */
+
+ __ROCKER_TLV_OF_DPA_FLOW_STAT_MAX,
+ ROCKER_TLV_OF_DPA_FLOW_STAT_MAX = __ROCKER_TLV_OF_DPA_FLOW_STAT_MAX - 1,
+};
+
+/*
+ * OF-DPA group types
+ */
+
+enum rocker_of_dpa_group_type {
+ ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE = 0,
+ ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE,
+ ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST,
+ ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST,
+ ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD,
+ ROCKER_OF_DPA_GROUP_TYPE_L3_INTERFACE,
+ ROCKER_OF_DPA_GROUP_TYPE_L3_MCAST,
+ ROCKER_OF_DPA_GROUP_TYPE_L3_ECMP,
+ ROCKER_OF_DPA_GROUP_TYPE_L2_OVERLAY,
+};
+
+/*
+ * OF-DPA group L2 overlay types
+ */
+
+enum rocker_of_dpa_overlay_type {
+ ROCKER_OF_DPA_OVERLAY_TYPE_FLOOD_UCAST = 0,
+ ROCKER_OF_DPA_OVERLAY_TYPE_FLOOD_MCAST,
+ ROCKER_OF_DPA_OVERLAY_TYPE_MCAST_UCAST,
+ ROCKER_OF_DPA_OVERLAY_TYPE_MCAST_MCAST,
+};
+
+/*
+ * OF-DPA group ID encoding
+ */
+
+#define ROCKER_GROUP_TYPE_SHIFT 28
+#define ROCKER_GROUP_TYPE_MASK 0xf0000000
+#define ROCKER_GROUP_VLAN_ID_SHIFT 16
+#define ROCKER_GROUP_VLAN_ID_MASK 0x0fff0000
+#define ROCKER_GROUP_PORT_SHIFT 0
+#define ROCKER_GROUP_PORT_MASK 0x0000ffff
+#define ROCKER_GROUP_TUNNEL_ID_SHIFT 12
+#define ROCKER_GROUP_TUNNEL_ID_MASK 0x0ffff000
+#define ROCKER_GROUP_SUBTYPE_SHIFT 10
+#define ROCKER_GROUP_SUBTYPE_MASK 0x00000c00
+#define ROCKER_GROUP_INDEX_SHIFT 0
+#define ROCKER_GROUP_INDEX_MASK 0x0000ffff
+#define ROCKER_GROUP_INDEX_LONG_SHIFT 0
+#define ROCKER_GROUP_INDEX_LONG_MASK 0x0fffffff
+
+#define ROCKER_GROUP_TYPE_GET(group_id) \
+ (((group_id) & ROCKER_GROUP_TYPE_MASK) >> ROCKER_GROUP_TYPE_SHIFT)
+#define ROCKER_GROUP_TYPE_SET(type) \
+ (((type) << ROCKER_GROUP_TYPE_SHIFT) & ROCKER_GROUP_TYPE_MASK)
+#define ROCKER_GROUP_VLAN_GET(group_id) \
+ (((group_id) & ROCKER_GROUP_VLAN_ID_MASK) >> ROCKER_GROUP_VLAN_ID_SHIFT)
+#define ROCKER_GROUP_VLAN_SET(vlan_id) \
+ (((vlan_id) << ROCKER_GROUP_VLAN_ID_SHIFT) & ROCKER_GROUP_VLAN_ID_MASK)
+#define ROCKER_GROUP_PORT_GET(group_id) \
+ (((group_id) & ROCKER_GROUP_PORT_MASK) >> ROCKER_GROUP_PORT_SHIFT)
+#define ROCKER_GROUP_PORT_SET(port) \
+ (((port) << ROCKER_GROUP_PORT_SHIFT) & ROCKER_GROUP_PORT_MASK)
+#define ROCKER_GROUP_INDEX_GET(group_id) \
+ (((group_id) & ROCKER_GROUP_INDEX_MASK) >> ROCKER_GROUP_INDEX_SHIFT)
+#define ROCKER_GROUP_INDEX_SET(index) \
+ (((index) << ROCKER_GROUP_INDEX_SHIFT) & ROCKER_GROUP_INDEX_MASK)
+#define ROCKER_GROUP_INDEX_LONG_GET(group_id) \
+ (((group_id) & ROCKER_GROUP_INDEX_LONG_MASK) >> \
+ ROCKER_GROUP_INDEX_LONG_SHIFT)
+#define ROCKER_GROUP_INDEX_LONG_SET(index) \
+ (((index) << ROCKER_GROUP_INDEX_LONG_SHIFT) & \
+ ROCKER_GROUP_INDEX_LONG_MASK)
+
+#define ROCKER_GROUP_NONE 0
+#define ROCKER_GROUP_L2_INTERFACE(vlan_id, port) \
+ (ROCKER_GROUP_TYPE_SET(ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE) |\
+ ROCKER_GROUP_VLAN_SET(ntohs(vlan_id)) | ROCKER_GROUP_PORT_SET(port))
+#define ROCKER_GROUP_L2_REWRITE(index) \
+ (ROCKER_GROUP_TYPE_SET(ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE) |\
+ ROCKER_GROUP_INDEX_LONG_SET(index))
+#define ROCKER_GROUP_L2_MCAST(vlan_id, index) \
+ (ROCKER_GROUP_TYPE_SET(ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST) |\
+ ROCKER_GROUP_VLAN_SET(ntohs(vlan_id)) | ROCKER_GROUP_INDEX_SET(index))
+#define ROCKER_GROUP_L2_FLOOD(vlan_id, index) \
+ (ROCKER_GROUP_TYPE_SET(ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD) |\
+ ROCKER_GROUP_VLAN_SET(ntohs(vlan_id)) | ROCKER_GROUP_INDEX_SET(index))
+#define ROCKER_GROUP_L3_UNICAST(index) \
+ (ROCKER_GROUP_TYPE_SET(ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST) |\
+ ROCKER_GROUP_INDEX_LONG_SET(index))
+
+/*
+ * Rocker general purpose registers
+ */
+#define ROCKER_CONTROL 0x0300
+#define ROCKER_PORT_PHYS_COUNT 0x0304
+#define ROCKER_PORT_PHYS_LINK_STATUS 0x0310 /* 8-byte */
+#define ROCKER_PORT_PHYS_ENABLE 0x0318 /* 8-byte */
+#define ROCKER_SWITCH_ID 0x0320 /* 8-byte */
+
+/*
+ * Rocker control bits
+ */
+#define ROCKER_CONTROL_RESET (1 << 0)
+
+#endif /* _ROCKER_HW_ */
diff --git a/hw/net/rocker/rocker_of_dpa.c b/hw/net/rocker/rocker_of_dpa.c
new file mode 100644
index 000000000..874fb01d6
--- /dev/null
+++ b/hw/net/rocker/rocker_of_dpa.c
@@ -0,0 +1,2630 @@
+/*
+ * QEMU rocker switch emulation - OF-DPA flow processing support
+ *
+ * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "net/eth.h"
+#include "qemu/iov.h"
+#include "qemu/timer.h"
+#include "qmp-commands.h"
+
+#include "rocker.h"
+#include "rocker_hw.h"
+#include "rocker_fp.h"
+#include "rocker_tlv.h"
+#include "rocker_world.h"
+#include "rocker_desc.h"
+#include "rocker_of_dpa.h"
+
+static const MACAddr zero_mac = { .a = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
+static const MACAddr ff_mac = { .a = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } };
+
+typedef struct of_dpa {
+ World *world;
+ GHashTable *flow_tbl;
+ GHashTable *group_tbl;
+ unsigned int flow_tbl_max_size;
+ unsigned int group_tbl_max_size;
+} OfDpa;
+
+/* flow_key stolen mostly from OVS
+ *
+ * Note: fields that compare with network packet header fields
+ * are stored in network order (BE) to avoid per-packet field
+ * byte-swaps.
+ */
+
+typedef struct of_dpa_flow_key {
+ uint32_t in_pport; /* ingress port */
+ uint32_t tunnel_id; /* overlay tunnel id */
+ uint32_t tbl_id; /* table id */
+ struct {
+ __be16 vlan_id; /* 0 if no VLAN */
+ MACAddr src; /* ethernet source address */
+ MACAddr dst; /* ethernet destination address */
+ __be16 type; /* ethernet frame type */
+ } eth;
+ struct {
+ uint8_t proto; /* IP protocol or ARP opcode */
+ uint8_t tos; /* IP ToS */
+ uint8_t ttl; /* IP TTL/hop limit */
+ uint8_t frag; /* one of FRAG_TYPE_* */
+ } ip;
+ union {
+ struct {
+ struct {
+ __be32 src; /* IP source address */
+ __be32 dst; /* IP destination address */
+ } addr;
+ union {
+ struct {
+ __be16 src; /* TCP/UDP/SCTP source port */
+ __be16 dst; /* TCP/UDP/SCTP destination port */
+ __be16 flags; /* TCP flags */
+ } tp;
+ struct {
+ MACAddr sha; /* ARP source hardware address */
+ MACAddr tha; /* ARP target hardware address */
+ } arp;
+ };
+ } ipv4;
+ struct {
+ struct {
+ Ipv6Addr src; /* IPv6 source address */
+ Ipv6Addr dst; /* IPv6 destination address */
+ } addr;
+ __be32 label; /* IPv6 flow label */
+ struct {
+ __be16 src; /* TCP/UDP/SCTP source port */
+ __be16 dst; /* TCP/UDP/SCTP destination port */
+ __be16 flags; /* TCP flags */
+ } tp;
+ struct {
+ Ipv6Addr target; /* ND target address */
+ MACAddr sll; /* ND source link layer address */
+ MACAddr tll; /* ND target link layer address */
+ } nd;
+ } ipv6;
+ };
+ int width; /* how many uint64_t's in key? */
+} OfDpaFlowKey;
+
+/* Width of key which includes field 'f' in u64s, rounded up */
+#define FLOW_KEY_WIDTH(f) \
+ ((offsetof(OfDpaFlowKey, f) + \
+ sizeof(((OfDpaFlowKey *)0)->f) + \
+ sizeof(uint64_t) - 1) / sizeof(uint64_t))
+
+typedef struct of_dpa_flow_action {
+ uint32_t goto_tbl;
+ struct {
+ uint32_t group_id;
+ uint32_t tun_log_lport;
+ __be16 vlan_id;
+ } write;
+ struct {
+ __be16 new_vlan_id;
+ uint32_t out_pport;
+ uint8_t copy_to_cpu;
+ __be16 vlan_id;
+ } apply;
+} OfDpaFlowAction;
+
+typedef struct of_dpa_flow {
+ uint32_t lpm;
+ uint32_t priority;
+ uint32_t hardtime;
+ uint32_t idletime;
+ uint64_t cookie;
+ OfDpaFlowKey key;
+ OfDpaFlowKey mask;
+ OfDpaFlowAction action;
+ struct {
+ uint64_t hits;
+ int64_t install_time;
+ int64_t refresh_time;
+ uint64_t rx_pkts;
+ uint64_t tx_pkts;
+ } stats;
+} OfDpaFlow;
+
+typedef struct of_dpa_flow_pkt_fields {
+ uint32_t tunnel_id;
+ struct eth_header *ethhdr;
+ __be16 *h_proto;
+ struct vlan_header *vlanhdr;
+ struct ip_header *ipv4hdr;
+ struct ip6_header *ipv6hdr;
+ Ipv6Addr *ipv6_src_addr;
+ Ipv6Addr *ipv6_dst_addr;
+} OfDpaFlowPktFields;
+
+typedef struct of_dpa_flow_context {
+ uint32_t in_pport;
+ uint32_t tunnel_id;
+ struct iovec *iov;
+ int iovcnt;
+ struct eth_header ethhdr_rewrite;
+ struct vlan_header vlanhdr_rewrite;
+ struct vlan_header vlanhdr;
+ OfDpa *of_dpa;
+ OfDpaFlowPktFields fields;
+ OfDpaFlowAction action_set;
+} OfDpaFlowContext;
+
+typedef struct of_dpa_flow_match {
+ OfDpaFlowKey value;
+ OfDpaFlow *best;
+} OfDpaFlowMatch;
+
+typedef struct of_dpa_group {
+ uint32_t id;
+ union {
+ struct {
+ uint32_t out_pport;
+ uint8_t pop_vlan;
+ } l2_interface;
+ struct {
+ uint32_t group_id;
+ MACAddr src_mac;
+ MACAddr dst_mac;
+ __be16 vlan_id;
+ } l2_rewrite;
+ struct {
+ uint16_t group_count;
+ uint32_t *group_ids;
+ } l2_flood;
+ struct {
+ uint32_t group_id;
+ MACAddr src_mac;
+ MACAddr dst_mac;
+ __be16 vlan_id;
+ uint8_t ttl_check;
+ } l3_unicast;
+ };
+} OfDpaGroup;
+
+static int of_dpa_mask2prefix(__be32 mask)
+{
+ int i;
+ int count = 32;
+
+ for (i = 0; i < 32; i++) {
+ if (!(ntohl(mask) & ((2 << i) - 1))) {
+ count--;
+ }
+ }
+
+ return count;
+}
+
+#if defined(DEBUG_ROCKER)
+static void of_dpa_flow_key_dump(OfDpaFlowKey *key, OfDpaFlowKey *mask)
+{
+ char buf[512], *b = buf, *mac;
+
+ b += sprintf(b, " tbl %2d", key->tbl_id);
+
+ if (key->in_pport || (mask && mask->in_pport)) {
+ b += sprintf(b, " in_pport %2d", key->in_pport);
+ if (mask && mask->in_pport != 0xffffffff) {
+ b += sprintf(b, "/0x%08x", key->in_pport);
+ }
+ }
+
+ if (key->tunnel_id || (mask && mask->tunnel_id)) {
+ b += sprintf(b, " tun %8d", key->tunnel_id);
+ if (mask && mask->tunnel_id != 0xffffffff) {
+ b += sprintf(b, "/0x%08x", key->tunnel_id);
+ }
+ }
+
+ if (key->eth.vlan_id || (mask && mask->eth.vlan_id)) {
+ b += sprintf(b, " vlan %4d", ntohs(key->eth.vlan_id));
+ if (mask && mask->eth.vlan_id != 0xffff) {
+ b += sprintf(b, "/0x%04x", ntohs(key->eth.vlan_id));
+ }
+ }
+
+ if (memcmp(key->eth.src.a, zero_mac.a, ETH_ALEN) ||
+ (mask && memcmp(mask->eth.src.a, zero_mac.a, ETH_ALEN))) {
+ mac = qemu_mac_strdup_printf(key->eth.src.a);
+ b += sprintf(b, " src %s", mac);
+ g_free(mac);
+ if (mask && memcmp(mask->eth.src.a, ff_mac.a, ETH_ALEN)) {
+ mac = qemu_mac_strdup_printf(mask->eth.src.a);
+ b += sprintf(b, "/%s", mac);
+ g_free(mac);
+ }
+ }
+
+ if (memcmp(key->eth.dst.a, zero_mac.a, ETH_ALEN) ||
+ (mask && memcmp(mask->eth.dst.a, zero_mac.a, ETH_ALEN))) {
+ mac = qemu_mac_strdup_printf(key->eth.dst.a);
+ b += sprintf(b, " dst %s", mac);
+ g_free(mac);
+ if (mask && memcmp(mask->eth.dst.a, ff_mac.a, ETH_ALEN)) {
+ mac = qemu_mac_strdup_printf(mask->eth.dst.a);
+ b += sprintf(b, "/%s", mac);
+ g_free(mac);
+ }
+ }
+
+ if (key->eth.type || (mask && mask->eth.type)) {
+ b += sprintf(b, " type 0x%04x", ntohs(key->eth.type));
+ if (mask && mask->eth.type != 0xffff) {
+ b += sprintf(b, "/0x%04x", ntohs(mask->eth.type));
+ }
+ switch (ntohs(key->eth.type)) {
+ case 0x0800:
+ case 0x86dd:
+ if (key->ip.proto || (mask && mask->ip.proto)) {
+ b += sprintf(b, " ip proto %2d", key->ip.proto);
+ if (mask && mask->ip.proto != 0xff) {
+ b += sprintf(b, "/0x%02x", mask->ip.proto);
+ }
+ }
+ if (key->ip.tos || (mask && mask->ip.tos)) {
+ b += sprintf(b, " ip tos %2d", key->ip.tos);
+ if (mask && mask->ip.tos != 0xff) {
+ b += sprintf(b, "/0x%02x", mask->ip.tos);
+ }
+ }
+ break;
+ }
+ switch (ntohs(key->eth.type)) {
+ case 0x0800:
+ if (key->ipv4.addr.dst || (mask && mask->ipv4.addr.dst)) {
+ b += sprintf(b, " dst %s",
+ inet_ntoa(*(struct in_addr *)&key->ipv4.addr.dst));
+ if (mask) {
+ b += sprintf(b, "/%d",
+ of_dpa_mask2prefix(mask->ipv4.addr.dst));
+ }
+ }
+ break;
+ }
+ }
+
+ DPRINTF("%s\n", buf);
+}
+#else
+#define of_dpa_flow_key_dump(k, m)
+#endif
+
+static void _of_dpa_flow_match(void *key, void *value, void *user_data)
+{
+ OfDpaFlow *flow = value;
+ OfDpaFlowMatch *match = user_data;
+ uint64_t *k = (uint64_t *)&flow->key;
+ uint64_t *m = (uint64_t *)&flow->mask;
+ uint64_t *v = (uint64_t *)&match->value;
+ int i;
+
+ if (flow->key.tbl_id == match->value.tbl_id) {
+ of_dpa_flow_key_dump(&flow->key, &flow->mask);
+ }
+
+ if (flow->key.width > match->value.width) {
+ return;
+ }
+
+ for (i = 0; i < flow->key.width; i++, k++, m++, v++) {
+ if ((~*k & *m & *v) | (*k & *m & ~*v)) {
+ return;
+ }
+ }
+
+ DPRINTF("match\n");
+
+ if (!match->best ||
+ flow->priority > match->best->priority ||
+ flow->lpm > match->best->lpm) {
+ match->best = flow;
+ }
+}
+
+static OfDpaFlow *of_dpa_flow_match(OfDpa *of_dpa, OfDpaFlowMatch *match)
+{
+ DPRINTF("\nnew search\n");
+ of_dpa_flow_key_dump(&match->value, NULL);
+
+ g_hash_table_foreach(of_dpa->flow_tbl, _of_dpa_flow_match, match);
+
+ return match->best;
+}
+
+static OfDpaFlow *of_dpa_flow_find(OfDpa *of_dpa, uint64_t cookie)
+{
+ return g_hash_table_lookup(of_dpa->flow_tbl, &cookie);
+}
+
+static int of_dpa_flow_add(OfDpa *of_dpa, OfDpaFlow *flow)
+{
+ g_hash_table_insert(of_dpa->flow_tbl, &flow->cookie, flow);
+
+ return ROCKER_OK;
+}
+
+static void of_dpa_flow_del(OfDpa *of_dpa, OfDpaFlow *flow)
+{
+ g_hash_table_remove(of_dpa->flow_tbl, &flow->cookie);
+}
+
+static OfDpaFlow *of_dpa_flow_alloc(uint64_t cookie)
+{
+ OfDpaFlow *flow;
+ int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) / 1000;
+
+ flow = g_malloc0(sizeof(OfDpaFlow));
+ if (!flow) {
+ return NULL;
+ }
+
+ flow->cookie = cookie;
+ flow->mask.tbl_id = 0xffffffff;
+
+ flow->stats.install_time = flow->stats.refresh_time = now;
+
+ return flow;
+}
+
+static void of_dpa_flow_pkt_hdr_reset(OfDpaFlowContext *fc)
+{
+ OfDpaFlowPktFields *fields = &fc->fields;
+
+ fc->iov[0].iov_base = fields->ethhdr;
+ fc->iov[0].iov_len = sizeof(struct eth_header);
+ fc->iov[1].iov_base = fields->vlanhdr;
+ fc->iov[1].iov_len = fields->vlanhdr ? sizeof(struct vlan_header) : 0;
+}
+
+static void of_dpa_flow_pkt_parse(OfDpaFlowContext *fc,
+ const struct iovec *iov, int iovcnt)
+{
+ OfDpaFlowPktFields *fields = &fc->fields;
+ size_t sofar = 0;
+ int i;
+
+ sofar += sizeof(struct eth_header);
+ if (iov->iov_len < sofar) {
+ DPRINTF("flow_pkt_parse underrun on eth_header\n");
+ return;
+ }
+
+ fields->ethhdr = iov->iov_base;
+ fields->h_proto = &fields->ethhdr->h_proto;
+
+ if (ntohs(*fields->h_proto) == ETH_P_VLAN) {
+ sofar += sizeof(struct vlan_header);
+ if (iov->iov_len < sofar) {
+ DPRINTF("flow_pkt_parse underrun on vlan_header\n");
+ return;
+ }
+ fields->vlanhdr = (struct vlan_header *)(fields->ethhdr + 1);
+ fields->h_proto = &fields->vlanhdr->h_proto;
+ }
+
+ switch (ntohs(*fields->h_proto)) {
+ case ETH_P_IP:
+ sofar += sizeof(struct ip_header);
+ if (iov->iov_len < sofar) {
+ DPRINTF("flow_pkt_parse underrun on ip_header\n");
+ return;
+ }
+ fields->ipv4hdr = (struct ip_header *)(fields->h_proto + 1);
+ break;
+ case ETH_P_IPV6:
+ sofar += sizeof(struct ip6_header);
+ if (iov->iov_len < sofar) {
+ DPRINTF("flow_pkt_parse underrun on ip6_header\n");
+ return;
+ }
+ fields->ipv6hdr = (struct ip6_header *)(fields->h_proto + 1);
+ break;
+ }
+
+ /* To facilitate (potential) VLAN tag insertion, Make a
+ * copy of the iov and insert two new vectors at the
+ * beginning for eth hdr and vlan hdr. No data is copied,
+ * just the vectors.
+ */
+
+ of_dpa_flow_pkt_hdr_reset(fc);
+
+ fc->iov[2].iov_base = fields->h_proto + 1;
+ fc->iov[2].iov_len = iov->iov_len - fc->iov[0].iov_len - fc->iov[1].iov_len;
+
+ for (i = 1; i < iovcnt; i++) {
+ fc->iov[i+2] = iov[i];
+ }
+
+ fc->iovcnt = iovcnt + 2;
+}
+
+static void of_dpa_flow_pkt_insert_vlan(OfDpaFlowContext *fc, __be16 vlan_id)
+{
+ OfDpaFlowPktFields *fields = &fc->fields;
+ uint16_t h_proto = fields->ethhdr->h_proto;
+
+ if (fields->vlanhdr) {
+ DPRINTF("flow_pkt_insert_vlan packet already has vlan\n");
+ return;
+ }
+
+ fields->ethhdr->h_proto = htons(ETH_P_VLAN);
+ fields->vlanhdr = &fc->vlanhdr;
+ fields->vlanhdr->h_tci = vlan_id;
+ fields->vlanhdr->h_proto = h_proto;
+ fields->h_proto = &fields->vlanhdr->h_proto;
+
+ fc->iov[1].iov_base = fields->vlanhdr;
+ fc->iov[1].iov_len = sizeof(struct vlan_header);
+}
+
+static void of_dpa_flow_pkt_strip_vlan(OfDpaFlowContext *fc)
+{
+ OfDpaFlowPktFields *fields = &fc->fields;
+
+ if (!fields->vlanhdr) {
+ return;
+ }
+
+ fc->iov[0].iov_len -= sizeof(fields->ethhdr->h_proto);
+ fc->iov[1].iov_base = fields->h_proto;
+ fc->iov[1].iov_len = sizeof(fields->ethhdr->h_proto);
+}
+
+static void of_dpa_flow_pkt_hdr_rewrite(OfDpaFlowContext *fc,
+ uint8_t *src_mac, uint8_t *dst_mac,
+ __be16 vlan_id)
+{
+ OfDpaFlowPktFields *fields = &fc->fields;
+
+ if (src_mac || dst_mac) {
+ memcpy(&fc->ethhdr_rewrite, fields->ethhdr, sizeof(struct eth_header));
+ if (src_mac && memcmp(src_mac, zero_mac.a, ETH_ALEN)) {
+ memcpy(fc->ethhdr_rewrite.h_source, src_mac, ETH_ALEN);
+ }
+ if (dst_mac && memcmp(dst_mac, zero_mac.a, ETH_ALEN)) {
+ memcpy(fc->ethhdr_rewrite.h_dest, dst_mac, ETH_ALEN);
+ }
+ fc->iov[0].iov_base = &fc->ethhdr_rewrite;
+ }
+
+ if (vlan_id && fields->vlanhdr) {
+ fc->vlanhdr_rewrite = fc->vlanhdr;
+ fc->vlanhdr_rewrite.h_tci = vlan_id;
+ fc->iov[1].iov_base = &fc->vlanhdr_rewrite;
+ }
+}
+
+static void of_dpa_flow_ig_tbl(OfDpaFlowContext *fc, uint32_t tbl_id);
+
+static void of_dpa_ig_port_build_match(OfDpaFlowContext *fc,
+ OfDpaFlowMatch *match)
+{
+ match->value.tbl_id = ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT;
+ match->value.in_pport = fc->in_pport;
+ match->value.width = FLOW_KEY_WIDTH(tbl_id);
+}
+
+static void of_dpa_ig_port_miss(OfDpaFlowContext *fc)
+{
+ uint32_t port;
+
+ /* The default on miss is for packets from physical ports
+ * to go to the VLAN Flow Table. There is no default rule
+ * for packets from logical ports, which are dropped on miss.
+ */
+
+ if (fp_port_from_pport(fc->in_pport, &port)) {
+ of_dpa_flow_ig_tbl(fc, ROCKER_OF_DPA_TABLE_ID_VLAN);
+ }
+}
+
+static void of_dpa_vlan_build_match(OfDpaFlowContext *fc,
+ OfDpaFlowMatch *match)
+{
+ match->value.tbl_id = ROCKER_OF_DPA_TABLE_ID_VLAN;
+ match->value.in_pport = fc->in_pport;
+ if (fc->fields.vlanhdr) {
+ match->value.eth.vlan_id = fc->fields.vlanhdr->h_tci;
+ }
+ match->value.width = FLOW_KEY_WIDTH(eth.vlan_id);
+}
+
+static void of_dpa_vlan_insert(OfDpaFlowContext *fc,
+ OfDpaFlow *flow)
+{
+ if (flow->action.apply.new_vlan_id) {
+ of_dpa_flow_pkt_insert_vlan(fc, flow->action.apply.new_vlan_id);
+ }
+}
+
+static void of_dpa_term_mac_build_match(OfDpaFlowContext *fc,
+ OfDpaFlowMatch *match)
+{
+ match->value.tbl_id = ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC;
+ match->value.in_pport = fc->in_pport;
+ match->value.eth.type = *fc->fields.h_proto;
+ match->value.eth.vlan_id = fc->fields.vlanhdr->h_tci;
+ memcpy(match->value.eth.dst.a, fc->fields.ethhdr->h_dest,
+ sizeof(match->value.eth.dst.a));
+ match->value.width = FLOW_KEY_WIDTH(eth.type);
+}
+
+static void of_dpa_term_mac_miss(OfDpaFlowContext *fc)
+{
+ of_dpa_flow_ig_tbl(fc, ROCKER_OF_DPA_TABLE_ID_BRIDGING);
+}
+
+static void of_dpa_apply_actions(OfDpaFlowContext *fc,
+ OfDpaFlow *flow)
+{
+ fc->action_set.apply.copy_to_cpu = flow->action.apply.copy_to_cpu;
+ fc->action_set.apply.vlan_id = flow->key.eth.vlan_id;
+}
+
+static void of_dpa_bridging_build_match(OfDpaFlowContext *fc,
+ OfDpaFlowMatch *match)
+{
+ match->value.tbl_id = ROCKER_OF_DPA_TABLE_ID_BRIDGING;
+ if (fc->fields.vlanhdr) {
+ match->value.eth.vlan_id = fc->fields.vlanhdr->h_tci;
+ } else if (fc->tunnel_id) {
+ match->value.tunnel_id = fc->tunnel_id;
+ }
+ memcpy(match->value.eth.dst.a, fc->fields.ethhdr->h_dest,
+ sizeof(match->value.eth.dst.a));
+ match->value.width = FLOW_KEY_WIDTH(eth.dst);
+}
+
+static void of_dpa_bridging_learn(OfDpaFlowContext *fc,
+ OfDpaFlow *dst_flow)
+{
+ OfDpaFlowMatch match = { { 0, }, };
+ OfDpaFlow *flow;
+ uint8_t *addr;
+ uint16_t vlan_id;
+ int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) / 1000;
+ int64_t refresh_delay = 1;
+
+ /* Do a lookup in bridge table by src_mac/vlan */
+
+ addr = fc->fields.ethhdr->h_source;
+ vlan_id = fc->fields.vlanhdr->h_tci;
+
+ match.value.tbl_id = ROCKER_OF_DPA_TABLE_ID_BRIDGING;
+ match.value.eth.vlan_id = vlan_id;
+ memcpy(match.value.eth.dst.a, addr, sizeof(match.value.eth.dst.a));
+ match.value.width = FLOW_KEY_WIDTH(eth.dst);
+
+ flow = of_dpa_flow_match(fc->of_dpa, &match);
+ if (flow) {
+ if (!memcmp(flow->mask.eth.dst.a, ff_mac.a,
+ sizeof(flow->mask.eth.dst.a))) {
+ /* src_mac/vlan already learned; if in_port and out_port
+ * don't match, the end station has moved and the port
+ * needs updating */
+ /* XXX implement the in_port/out_port check */
+ if (now - flow->stats.refresh_time < refresh_delay) {
+ return;
+ }
+ flow->stats.refresh_time = now;
+ }
+ }
+
+ /* Let driver know about mac/vlan. This may be a new mac/vlan
+ * or a refresh of existing mac/vlan that's been hit after the
+ * refresh_delay.
+ */
+
+ rocker_event_mac_vlan_seen(world_rocker(fc->of_dpa->world),
+ fc->in_pport, addr, vlan_id);
+}
+
+static void of_dpa_bridging_miss(OfDpaFlowContext *fc)
+{
+ of_dpa_bridging_learn(fc, NULL);
+ of_dpa_flow_ig_tbl(fc, ROCKER_OF_DPA_TABLE_ID_ACL_POLICY);
+}
+
+static void of_dpa_bridging_action_write(OfDpaFlowContext *fc,
+ OfDpaFlow *flow)
+{
+ if (flow->action.write.group_id != ROCKER_GROUP_NONE) {
+ fc->action_set.write.group_id = flow->action.write.group_id;
+ }
+ fc->action_set.write.tun_log_lport = flow->action.write.tun_log_lport;
+}
+
+static void of_dpa_unicast_routing_build_match(OfDpaFlowContext *fc,
+ OfDpaFlowMatch *match)
+{
+ match->value.tbl_id = ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING;
+ match->value.eth.type = *fc->fields.h_proto;
+ if (fc->fields.ipv4hdr) {
+ match->value.ipv4.addr.dst = fc->fields.ipv4hdr->ip_dst;
+ }
+ if (fc->fields.ipv6_dst_addr) {
+ memcpy(&match->value.ipv6.addr.dst, fc->fields.ipv6_dst_addr,
+ sizeof(match->value.ipv6.addr.dst));
+ }
+ match->value.width = FLOW_KEY_WIDTH(ipv6.addr.dst);
+}
+
+static void of_dpa_unicast_routing_miss(OfDpaFlowContext *fc)
+{
+ of_dpa_flow_ig_tbl(fc, ROCKER_OF_DPA_TABLE_ID_ACL_POLICY);
+}
+
+static void of_dpa_unicast_routing_action_write(OfDpaFlowContext *fc,
+ OfDpaFlow *flow)
+{
+ if (flow->action.write.group_id != ROCKER_GROUP_NONE) {
+ fc->action_set.write.group_id = flow->action.write.group_id;
+ }
+}
+
+static void
+of_dpa_multicast_routing_build_match(OfDpaFlowContext *fc,
+ OfDpaFlowMatch *match)
+{
+ match->value.tbl_id = ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING;
+ match->value.eth.type = *fc->fields.h_proto;
+ match->value.eth.vlan_id = fc->fields.vlanhdr->h_tci;
+ if (fc->fields.ipv4hdr) {
+ match->value.ipv4.addr.src = fc->fields.ipv4hdr->ip_src;
+ match->value.ipv4.addr.dst = fc->fields.ipv4hdr->ip_dst;
+ }
+ if (fc->fields.ipv6_src_addr) {
+ memcpy(&match->value.ipv6.addr.src, fc->fields.ipv6_src_addr,
+ sizeof(match->value.ipv6.addr.src));
+ }
+ if (fc->fields.ipv6_dst_addr) {
+ memcpy(&match->value.ipv6.addr.dst, fc->fields.ipv6_dst_addr,
+ sizeof(match->value.ipv6.addr.dst));
+ }
+ match->value.width = FLOW_KEY_WIDTH(ipv6.addr.dst);
+}
+
+static void of_dpa_multicast_routing_miss(OfDpaFlowContext *fc)
+{
+ of_dpa_flow_ig_tbl(fc, ROCKER_OF_DPA_TABLE_ID_ACL_POLICY);
+}
+
+static void
+of_dpa_multicast_routing_action_write(OfDpaFlowContext *fc,
+ OfDpaFlow *flow)
+{
+ if (flow->action.write.group_id != ROCKER_GROUP_NONE) {
+ fc->action_set.write.group_id = flow->action.write.group_id;
+ }
+ fc->action_set.write.vlan_id = flow->action.write.vlan_id;
+}
+
+static void of_dpa_acl_build_match(OfDpaFlowContext *fc,
+ OfDpaFlowMatch *match)
+{
+ match->value.tbl_id = ROCKER_OF_DPA_TABLE_ID_ACL_POLICY;
+ match->value.in_pport = fc->in_pport;
+ memcpy(match->value.eth.src.a, fc->fields.ethhdr->h_source,
+ sizeof(match->value.eth.src.a));
+ memcpy(match->value.eth.dst.a, fc->fields.ethhdr->h_dest,
+ sizeof(match->value.eth.dst.a));
+ match->value.eth.type = *fc->fields.h_proto;
+ match->value.eth.vlan_id = fc->fields.vlanhdr->h_tci;
+ match->value.width = FLOW_KEY_WIDTH(eth.type);
+ if (fc->fields.ipv4hdr) {
+ match->value.ip.proto = fc->fields.ipv4hdr->ip_p;
+ match->value.ip.tos = fc->fields.ipv4hdr->ip_tos;
+ match->value.width = FLOW_KEY_WIDTH(ip.tos);
+ } else if (fc->fields.ipv6hdr) {
+ match->value.ip.proto =
+ fc->fields.ipv6hdr->ip6_ctlun.ip6_un1.ip6_un1_nxt;
+ match->value.ip.tos = 0; /* XXX what goes here? */
+ match->value.width = FLOW_KEY_WIDTH(ip.tos);
+ }
+}
+
+static void of_dpa_eg(OfDpaFlowContext *fc);
+static void of_dpa_acl_hit(OfDpaFlowContext *fc,
+ OfDpaFlow *dst_flow)
+{
+ of_dpa_eg(fc);
+}
+
+static void of_dpa_acl_action_write(OfDpaFlowContext *fc,
+ OfDpaFlow *flow)
+{
+ if (flow->action.write.group_id != ROCKER_GROUP_NONE) {
+ fc->action_set.write.group_id = flow->action.write.group_id;
+ }
+}
+
+static void of_dpa_drop(OfDpaFlowContext *fc)
+{
+ /* drop packet */
+}
+
+static OfDpaGroup *of_dpa_group_find(OfDpa *of_dpa,
+ uint32_t group_id)
+{
+ return g_hash_table_lookup(of_dpa->group_tbl, &group_id);
+}
+
+static int of_dpa_group_add(OfDpa *of_dpa, OfDpaGroup *group)
+{
+ g_hash_table_insert(of_dpa->group_tbl, &group->id, group);
+
+ return 0;
+}
+
+#if 0
+static int of_dpa_group_mod(OfDpa *of_dpa, OfDpaGroup *group)
+{
+ OfDpaGroup *old_group = of_dpa_group_find(of_dpa, group->id);
+
+ if (!old_group) {
+ return -ENOENT;
+ }
+
+ /* XXX */
+
+ return 0;
+}
+#endif
+
+static int of_dpa_group_del(OfDpa *of_dpa, OfDpaGroup *group)
+{
+ g_hash_table_remove(of_dpa->group_tbl, &group->id);
+
+ return 0;
+}
+
+#if 0
+static int of_dpa_group_get_stats(OfDpa *of_dpa, uint32_t id)
+{
+ OfDpaGroup *group = of_dpa_group_find(of_dpa, id);
+
+ if (!group) {
+ return -ENOENT;
+ }
+
+ /* XXX get/return stats */
+
+ return 0;
+}
+#endif
+
+static OfDpaGroup *of_dpa_group_alloc(uint32_t id)
+{
+ OfDpaGroup *group = g_malloc0(sizeof(OfDpaGroup));
+
+ if (!group) {
+ return NULL;
+ }
+
+ group->id = id;
+
+ return group;
+}
+
+static void of_dpa_output_l2_interface(OfDpaFlowContext *fc,
+ OfDpaGroup *group)
+{
+ uint8_t copy_to_cpu = fc->action_set.apply.copy_to_cpu;
+
+ if (group->l2_interface.pop_vlan) {
+ of_dpa_flow_pkt_strip_vlan(fc);
+ }
+
+ /* Note: By default, and as per the OpenFlow 1.3.1
+ * specification, a packet cannot be forwarded back
+ * to the IN_PORT from which it came in. An action
+ * bucket that specifies the particular packet's
+ * egress port is not evaluated.
+ */
+
+ if (group->l2_interface.out_pport == 0) {
+ rx_produce(fc->of_dpa->world, fc->in_pport, fc->iov, fc->iovcnt,
+ copy_to_cpu);
+ } else if (group->l2_interface.out_pport != fc->in_pport) {
+ rocker_port_eg(world_rocker(fc->of_dpa->world),
+ group->l2_interface.out_pport,
+ fc->iov, fc->iovcnt);
+ }
+}
+
+static void of_dpa_output_l2_rewrite(OfDpaFlowContext *fc,
+ OfDpaGroup *group)
+{
+ OfDpaGroup *l2_group =
+ of_dpa_group_find(fc->of_dpa, group->l2_rewrite.group_id);
+
+ if (!l2_group) {
+ return;
+ }
+
+ of_dpa_flow_pkt_hdr_rewrite(fc, group->l2_rewrite.src_mac.a,
+ group->l2_rewrite.dst_mac.a,
+ group->l2_rewrite.vlan_id);
+ of_dpa_output_l2_interface(fc, l2_group);
+}
+
+static void of_dpa_output_l2_flood(OfDpaFlowContext *fc,
+ OfDpaGroup *group)
+{
+ OfDpaGroup *l2_group;
+ int i;
+
+ for (i = 0; i < group->l2_flood.group_count; i++) {
+ of_dpa_flow_pkt_hdr_reset(fc);
+ l2_group = of_dpa_group_find(fc->of_dpa, group->l2_flood.group_ids[i]);
+ if (!l2_group) {
+ continue;
+ }
+ switch (ROCKER_GROUP_TYPE_GET(l2_group->id)) {
+ case ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE:
+ of_dpa_output_l2_interface(fc, l2_group);
+ break;
+ case ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE:
+ of_dpa_output_l2_rewrite(fc, l2_group);
+ break;
+ }
+ }
+}
+
+static void of_dpa_output_l3_unicast(OfDpaFlowContext *fc, OfDpaGroup *group)
+{
+ OfDpaGroup *l2_group =
+ of_dpa_group_find(fc->of_dpa, group->l3_unicast.group_id);
+
+ if (!l2_group) {
+ return;
+ }
+
+ of_dpa_flow_pkt_hdr_rewrite(fc, group->l3_unicast.src_mac.a,
+ group->l3_unicast.dst_mac.a,
+ group->l3_unicast.vlan_id);
+ /* XXX need ttl_check */
+ of_dpa_output_l2_interface(fc, l2_group);
+}
+
+static void of_dpa_eg(OfDpaFlowContext *fc)
+{
+ OfDpaFlowAction *set = &fc->action_set;
+ OfDpaGroup *group;
+ uint32_t group_id;
+
+ /* send a copy of pkt to CPU (controller)? */
+
+ if (set->apply.copy_to_cpu) {
+ group_id = ROCKER_GROUP_L2_INTERFACE(set->apply.vlan_id, 0);
+ group = of_dpa_group_find(fc->of_dpa, group_id);
+ if (group) {
+ of_dpa_output_l2_interface(fc, group);
+ of_dpa_flow_pkt_hdr_reset(fc);
+ }
+ }
+
+ /* process group write actions */
+
+ if (!set->write.group_id) {
+ return;
+ }
+
+ group = of_dpa_group_find(fc->of_dpa, set->write.group_id);
+ if (!group) {
+ return;
+ }
+
+ switch (ROCKER_GROUP_TYPE_GET(group->id)) {
+ case ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE:
+ of_dpa_output_l2_interface(fc, group);
+ break;
+ case ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE:
+ of_dpa_output_l2_rewrite(fc, group);
+ break;
+ case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD:
+ case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST:
+ of_dpa_output_l2_flood(fc, group);
+ break;
+ case ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST:
+ of_dpa_output_l3_unicast(fc, group);
+ break;
+ }
+}
+
+typedef struct of_dpa_flow_tbl_ops {
+ void (*build_match)(OfDpaFlowContext *fc, OfDpaFlowMatch *match);
+ void (*hit)(OfDpaFlowContext *fc, OfDpaFlow *flow);
+ void (*miss)(OfDpaFlowContext *fc);
+ void (*hit_no_goto)(OfDpaFlowContext *fc);
+ void (*action_apply)(OfDpaFlowContext *fc, OfDpaFlow *flow);
+ void (*action_write)(OfDpaFlowContext *fc, OfDpaFlow *flow);
+} OfDpaFlowTblOps;
+
+static OfDpaFlowTblOps of_dpa_tbl_ops[] = {
+ [ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT] = {
+ .build_match = of_dpa_ig_port_build_match,
+ .miss = of_dpa_ig_port_miss,
+ .hit_no_goto = of_dpa_drop,
+ },
+ [ROCKER_OF_DPA_TABLE_ID_VLAN] = {
+ .build_match = of_dpa_vlan_build_match,
+ .hit_no_goto = of_dpa_drop,
+ .action_apply = of_dpa_vlan_insert,
+ },
+ [ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC] = {
+ .build_match = of_dpa_term_mac_build_match,
+ .miss = of_dpa_term_mac_miss,
+ .hit_no_goto = of_dpa_drop,
+ .action_apply = of_dpa_apply_actions,
+ },
+ [ROCKER_OF_DPA_TABLE_ID_BRIDGING] = {
+ .build_match = of_dpa_bridging_build_match,
+ .hit = of_dpa_bridging_learn,
+ .miss = of_dpa_bridging_miss,
+ .hit_no_goto = of_dpa_drop,
+ .action_apply = of_dpa_apply_actions,
+ .action_write = of_dpa_bridging_action_write,
+ },
+ [ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING] = {
+ .build_match = of_dpa_unicast_routing_build_match,
+ .miss = of_dpa_unicast_routing_miss,
+ .hit_no_goto = of_dpa_drop,
+ .action_write = of_dpa_unicast_routing_action_write,
+ },
+ [ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING] = {
+ .build_match = of_dpa_multicast_routing_build_match,
+ .miss = of_dpa_multicast_routing_miss,
+ .hit_no_goto = of_dpa_drop,
+ .action_write = of_dpa_multicast_routing_action_write,
+ },
+ [ROCKER_OF_DPA_TABLE_ID_ACL_POLICY] = {
+ .build_match = of_dpa_acl_build_match,
+ .hit = of_dpa_acl_hit,
+ .miss = of_dpa_eg,
+ .action_apply = of_dpa_apply_actions,
+ .action_write = of_dpa_acl_action_write,
+ },
+};
+
+static void of_dpa_flow_ig_tbl(OfDpaFlowContext *fc, uint32_t tbl_id)
+{
+ OfDpaFlowTblOps *ops = &of_dpa_tbl_ops[tbl_id];
+ OfDpaFlowMatch match = { { 0, }, };
+ OfDpaFlow *flow;
+
+ if (ops->build_match) {
+ ops->build_match(fc, &match);
+ } else {
+ return;
+ }
+
+ flow = of_dpa_flow_match(fc->of_dpa, &match);
+ if (!flow) {
+ if (ops->miss) {
+ ops->miss(fc);
+ }
+ return;
+ }
+
+ flow->stats.hits++;
+
+ if (ops->action_apply) {
+ ops->action_apply(fc, flow);
+ }
+
+ if (ops->action_write) {
+ ops->action_write(fc, flow);
+ }
+
+ if (ops->hit) {
+ ops->hit(fc, flow);
+ }
+
+ if (flow->action.goto_tbl) {
+ of_dpa_flow_ig_tbl(fc, flow->action.goto_tbl);
+ } else if (ops->hit_no_goto) {
+ ops->hit_no_goto(fc);
+ }
+
+ /* drop packet */
+}
+
+static ssize_t of_dpa_ig(World *world, uint32_t pport,
+ const struct iovec *iov, int iovcnt)
+{
+ struct iovec iov_copy[iovcnt + 2];
+ OfDpaFlowContext fc = {
+ .of_dpa = world_private(world),
+ .in_pport = pport,
+ .iov = iov_copy,
+ .iovcnt = iovcnt + 2,
+ };
+
+ of_dpa_flow_pkt_parse(&fc, iov, iovcnt);
+ of_dpa_flow_ig_tbl(&fc, ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT);
+
+ return iov_size(iov, iovcnt);
+}
+
+#define ROCKER_TUNNEL_LPORT 0x00010000
+
+static int of_dpa_cmd_add_ig_port(OfDpaFlow *flow, RockerTlv **flow_tlvs)
+{
+ OfDpaFlowKey *key = &flow->key;
+ OfDpaFlowKey *mask = &flow->mask;
+ OfDpaFlowAction *action = &flow->action;
+ bool overlay_tunnel;
+
+ if (!flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT] ||
+ !flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]) {
+ return -ROCKER_EINVAL;
+ }
+
+ key->tbl_id = ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT;
+ key->width = FLOW_KEY_WIDTH(tbl_id);
+
+ key->in_pport = rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT]);
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT_MASK]) {
+ mask->in_pport =
+ rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT_MASK]);
+ }
+
+ overlay_tunnel = !!(key->in_pport & ROCKER_TUNNEL_LPORT);
+
+ action->goto_tbl =
+ rocker_tlv_get_le16(flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]);
+
+ if (!overlay_tunnel && action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_VLAN) {
+ return -ROCKER_EINVAL;
+ }
+
+ if (overlay_tunnel && action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_BRIDGING) {
+ return -ROCKER_EINVAL;
+ }
+
+ return ROCKER_OK;
+}
+
+static int of_dpa_cmd_add_vlan(OfDpaFlow *flow, RockerTlv **flow_tlvs)
+{
+ OfDpaFlowKey *key = &flow->key;
+ OfDpaFlowKey *mask = &flow->mask;
+ OfDpaFlowAction *action = &flow->action;
+ uint32_t port;
+ bool untagged;
+
+ if (!flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT] ||
+ !flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]) {
+ DPRINTF("Must give in_pport and vlan_id to install VLAN tbl entry\n");
+ return -ROCKER_EINVAL;
+ }
+
+ key->tbl_id = ROCKER_OF_DPA_TABLE_ID_VLAN;
+ key->width = FLOW_KEY_WIDTH(eth.vlan_id);
+
+ key->in_pport = rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT]);
+ if (!fp_port_from_pport(key->in_pport, &port)) {
+ DPRINTF("in_pport (%d) not a front-panel port\n", key->in_pport);
+ return -ROCKER_EINVAL;
+ }
+ mask->in_pport = 0xffffffff;
+
+ key->eth.vlan_id = rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]);
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID_MASK]) {
+ mask->eth.vlan_id =
+ rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID_MASK]);
+ }
+
+ if (key->eth.vlan_id) {
+ untagged = false; /* filtering */
+ } else {
+ untagged = true;
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]) {
+ action->goto_tbl =
+ rocker_tlv_get_le16(flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]);
+ if (action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC) {
+ DPRINTF("Goto tbl (%d) must be TERM_MAC\n", action->goto_tbl);
+ return -ROCKER_EINVAL;
+ }
+ }
+
+ if (untagged) {
+ if (!flow_tlvs[ROCKER_TLV_OF_DPA_NEW_VLAN_ID]) {
+ DPRINTF("Must specify new vlan_id if untagged\n");
+ return -ROCKER_EINVAL;
+ }
+ action->apply.new_vlan_id =
+ rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_NEW_VLAN_ID]);
+ if (1 > ntohs(action->apply.new_vlan_id) ||
+ ntohs(action->apply.new_vlan_id) > 4095) {
+ DPRINTF("New vlan_id (%d) must be between 1 and 4095\n",
+ ntohs(action->apply.new_vlan_id));
+ return -ROCKER_EINVAL;
+ }
+ }
+
+ return ROCKER_OK;
+}
+
+static int of_dpa_cmd_add_term_mac(OfDpaFlow *flow, RockerTlv **flow_tlvs)
+{
+ OfDpaFlowKey *key = &flow->key;
+ OfDpaFlowKey *mask = &flow->mask;
+ OfDpaFlowAction *action = &flow->action;
+ const MACAddr ipv4_mcast = { .a = { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00 } };
+ const MACAddr ipv4_mask = { .a = { 0xff, 0xff, 0xff, 0x80, 0x00, 0x00 } };
+ const MACAddr ipv6_mcast = { .a = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x00 } };
+ const MACAddr ipv6_mask = { .a = { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } };
+ uint32_t port;
+ bool unicast = false;
+ bool multicast = false;
+
+ if (!flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT] ||
+ !flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT_MASK] ||
+ !flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE] ||
+ !flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC] ||
+ !flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC_MASK] ||
+ !flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID] ||
+ !flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID_MASK]) {
+ return -ROCKER_EINVAL;
+ }
+
+ key->tbl_id = ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC;
+ key->width = FLOW_KEY_WIDTH(eth.type);
+
+ key->in_pport = rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT]);
+ if (!fp_port_from_pport(key->in_pport, &port)) {
+ return -ROCKER_EINVAL;
+ }
+ mask->in_pport =
+ rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT_MASK]);
+
+ key->eth.type = rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE]);
+ if (key->eth.type != htons(0x0800) && key->eth.type != htons(0x86dd)) {
+ return -ROCKER_EINVAL;
+ }
+ mask->eth.type = htons(0xffff);
+
+ memcpy(key->eth.dst.a,
+ rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]),
+ sizeof(key->eth.dst.a));
+ memcpy(mask->eth.dst.a,
+ rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC_MASK]),
+ sizeof(mask->eth.dst.a));
+
+ if ((key->eth.dst.a[0] & 0x01) == 0x00) {
+ unicast = true;
+ }
+
+ /* only two wildcard rules are acceptable for IPv4 and IPv6 multicast */
+ if (memcmp(key->eth.dst.a, ipv4_mcast.a, sizeof(key->eth.dst.a)) == 0 &&
+ memcmp(mask->eth.dst.a, ipv4_mask.a, sizeof(mask->eth.dst.a)) == 0) {
+ multicast = true;
+ }
+ if (memcmp(key->eth.dst.a, ipv6_mcast.a, sizeof(key->eth.dst.a)) == 0 &&
+ memcmp(mask->eth.dst.a, ipv6_mask.a, sizeof(mask->eth.dst.a)) == 0) {
+ multicast = true;
+ }
+
+ if (!unicast && !multicast) {
+ return -ROCKER_EINVAL;
+ }
+
+ key->eth.vlan_id = rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]);
+ mask->eth.vlan_id =
+ rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID_MASK]);
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]) {
+ action->goto_tbl =
+ rocker_tlv_get_le16(flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]);
+
+ if (action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING &&
+ action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING) {
+ return -ROCKER_EINVAL;
+ }
+
+ if (unicast &&
+ action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING) {
+ return -ROCKER_EINVAL;
+ }
+
+ if (multicast &&
+ action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING) {
+ return -ROCKER_EINVAL;
+ }
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_COPY_CPU_ACTION]) {
+ action->apply.copy_to_cpu =
+ rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_COPY_CPU_ACTION]);
+ }
+
+ return ROCKER_OK;
+}
+
+static int of_dpa_cmd_add_bridging(OfDpaFlow *flow, RockerTlv **flow_tlvs)
+{
+ OfDpaFlowKey *key = &flow->key;
+ OfDpaFlowKey *mask = &flow->mask;
+ OfDpaFlowAction *action = &flow->action;
+ bool unicast = false;
+ bool dst_mac = false;
+ bool dst_mac_mask = false;
+ enum {
+ BRIDGING_MODE_UNKNOWN,
+ BRIDGING_MODE_VLAN_UCAST,
+ BRIDGING_MODE_VLAN_MCAST,
+ BRIDGING_MODE_VLAN_DFLT,
+ BRIDGING_MODE_TUNNEL_UCAST,
+ BRIDGING_MODE_TUNNEL_MCAST,
+ BRIDGING_MODE_TUNNEL_DFLT,
+ } mode = BRIDGING_MODE_UNKNOWN;
+
+ key->tbl_id = ROCKER_OF_DPA_TABLE_ID_BRIDGING;
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]) {
+ key->eth.vlan_id =
+ rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]);
+ mask->eth.vlan_id = 0xffff;
+ key->width = FLOW_KEY_WIDTH(eth.vlan_id);
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_TUNNEL_ID]) {
+ key->tunnel_id =
+ rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_TUNNEL_ID]);
+ mask->tunnel_id = 0xffffffff;
+ key->width = FLOW_KEY_WIDTH(tunnel_id);
+ }
+
+ /* can't do VLAN bridging and tunnel bridging at same time */
+ if (key->eth.vlan_id && key->tunnel_id) {
+ DPRINTF("can't do VLAN bridging and tunnel bridging at same time\n");
+ return -ROCKER_EINVAL;
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]) {
+ memcpy(key->eth.dst.a,
+ rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]),
+ sizeof(key->eth.dst.a));
+ key->width = FLOW_KEY_WIDTH(eth.dst);
+ dst_mac = true;
+ unicast = (key->eth.dst.a[0] & 0x01) == 0x00;
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC_MASK]) {
+ memcpy(mask->eth.dst.a,
+ rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC_MASK]),
+ sizeof(mask->eth.dst.a));
+ key->width = FLOW_KEY_WIDTH(eth.dst);
+ dst_mac_mask = true;
+ } else if (flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]) {
+ memcpy(mask->eth.dst.a, ff_mac.a, sizeof(mask->eth.dst.a));
+ }
+
+ if (key->eth.vlan_id) {
+ if (dst_mac && !dst_mac_mask) {
+ mode = unicast ? BRIDGING_MODE_VLAN_UCAST :
+ BRIDGING_MODE_VLAN_MCAST;
+ } else if ((dst_mac && dst_mac_mask) || !dst_mac) {
+ mode = BRIDGING_MODE_VLAN_DFLT;
+ }
+ } else if (key->tunnel_id) {
+ if (dst_mac && !dst_mac_mask) {
+ mode = unicast ? BRIDGING_MODE_TUNNEL_UCAST :
+ BRIDGING_MODE_TUNNEL_MCAST;
+ } else if ((dst_mac && dst_mac_mask) || !dst_mac) {
+ mode = BRIDGING_MODE_TUNNEL_DFLT;
+ }
+ }
+
+ if (mode == BRIDGING_MODE_UNKNOWN) {
+ DPRINTF("Unknown bridging mode\n");
+ return -ROCKER_EINVAL;
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]) {
+ action->goto_tbl =
+ rocker_tlv_get_le16(flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]);
+ if (action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_ACL_POLICY) {
+ DPRINTF("Briding goto tbl must be ACL policy\n");
+ return -ROCKER_EINVAL;
+ }
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]) {
+ action->write.group_id =
+ rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]);
+ switch (mode) {
+ case BRIDGING_MODE_VLAN_UCAST:
+ if (ROCKER_GROUP_TYPE_GET(action->write.group_id) !=
+ ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE) {
+ DPRINTF("Bridging mode vlan ucast needs L2 "
+ "interface group (0x%08x)\n",
+ action->write.group_id);
+ return -ROCKER_EINVAL;
+ }
+ break;
+ case BRIDGING_MODE_VLAN_MCAST:
+ if (ROCKER_GROUP_TYPE_GET(action->write.group_id) !=
+ ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST) {
+ DPRINTF("Bridging mode vlan mcast needs L2 "
+ "mcast group (0x%08x)\n",
+ action->write.group_id);
+ return -ROCKER_EINVAL;
+ }
+ break;
+ case BRIDGING_MODE_VLAN_DFLT:
+ if (ROCKER_GROUP_TYPE_GET(action->write.group_id) !=
+ ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD) {
+ DPRINTF("Bridging mode vlan dflt needs L2 "
+ "flood group (0x%08x)\n",
+ action->write.group_id);
+ return -ROCKER_EINVAL;
+ }
+ break;
+ case BRIDGING_MODE_TUNNEL_MCAST:
+ if (ROCKER_GROUP_TYPE_GET(action->write.group_id) !=
+ ROCKER_OF_DPA_GROUP_TYPE_L2_OVERLAY) {
+ DPRINTF("Bridging mode tunnel mcast needs L2 "
+ "overlay group (0x%08x)\n",
+ action->write.group_id);
+ return -ROCKER_EINVAL;
+ }
+ break;
+ case BRIDGING_MODE_TUNNEL_DFLT:
+ if (ROCKER_GROUP_TYPE_GET(action->write.group_id) !=
+ ROCKER_OF_DPA_GROUP_TYPE_L2_OVERLAY) {
+ DPRINTF("Bridging mode tunnel dflt needs L2 "
+ "overlay group (0x%08x)\n",
+ action->write.group_id);
+ return -ROCKER_EINVAL;
+ }
+ break;
+ default:
+ return -ROCKER_EINVAL;
+ }
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_TUNNEL_LPORT]) {
+ action->write.tun_log_lport =
+ rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_TUNNEL_LPORT]);
+ if (mode != BRIDGING_MODE_TUNNEL_UCAST) {
+ DPRINTF("Have tunnel logical port but not "
+ "in bridging tunnel mode\n");
+ return -ROCKER_EINVAL;
+ }
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_COPY_CPU_ACTION]) {
+ action->apply.copy_to_cpu =
+ rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_COPY_CPU_ACTION]);
+ }
+
+ return ROCKER_OK;
+}
+
+static int of_dpa_cmd_add_unicast_routing(OfDpaFlow *flow,
+ RockerTlv **flow_tlvs)
+{
+ OfDpaFlowKey *key = &flow->key;
+ OfDpaFlowKey *mask = &flow->mask;
+ OfDpaFlowAction *action = &flow->action;
+ enum {
+ UNICAST_ROUTING_MODE_UNKNOWN,
+ UNICAST_ROUTING_MODE_IPV4,
+ UNICAST_ROUTING_MODE_IPV6,
+ } mode = UNICAST_ROUTING_MODE_UNKNOWN;
+ uint8_t type;
+
+ if (!flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE]) {
+ return -ROCKER_EINVAL;
+ }
+
+ key->tbl_id = ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING;
+ key->width = FLOW_KEY_WIDTH(ipv6.addr.dst);
+
+ key->eth.type = rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE]);
+ switch (ntohs(key->eth.type)) {
+ case 0x0800:
+ mode = UNICAST_ROUTING_MODE_IPV4;
+ break;
+ case 0x86dd:
+ mode = UNICAST_ROUTING_MODE_IPV6;
+ break;
+ default:
+ return -ROCKER_EINVAL;
+ }
+ mask->eth.type = htons(0xffff);
+
+ switch (mode) {
+ case UNICAST_ROUTING_MODE_IPV4:
+ if (!flow_tlvs[ROCKER_TLV_OF_DPA_DST_IP]) {
+ return -ROCKER_EINVAL;
+ }
+ key->ipv4.addr.dst =
+ rocker_tlv_get_u32(flow_tlvs[ROCKER_TLV_OF_DPA_DST_IP]);
+ if (ipv4_addr_is_multicast(key->ipv4.addr.dst)) {
+ return -ROCKER_EINVAL;
+ }
+ flow->lpm = of_dpa_mask2prefix(htonl(0xffffffff));
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_DST_IP_MASK]) {
+ mask->ipv4.addr.dst =
+ rocker_tlv_get_u32(flow_tlvs[ROCKER_TLV_OF_DPA_DST_IP_MASK]);
+ flow->lpm = of_dpa_mask2prefix(mask->ipv4.addr.dst);
+ }
+ break;
+ case UNICAST_ROUTING_MODE_IPV6:
+ if (!flow_tlvs[ROCKER_TLV_OF_DPA_DST_IPV6]) {
+ return -ROCKER_EINVAL;
+ }
+ memcpy(&key->ipv6.addr.dst,
+ rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_IPV6]),
+ sizeof(key->ipv6.addr.dst));
+ if (ipv6_addr_is_multicast(&key->ipv6.addr.dst)) {
+ return -ROCKER_EINVAL;
+ }
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_DST_IPV6_MASK]) {
+ memcpy(&mask->ipv6.addr.dst,
+ rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_IPV6_MASK]),
+ sizeof(mask->ipv6.addr.dst));
+ }
+ break;
+ default:
+ return -ROCKER_EINVAL;
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]) {
+ action->goto_tbl =
+ rocker_tlv_get_le16(flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]);
+ if (action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_ACL_POLICY) {
+ return -ROCKER_EINVAL;
+ }
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]) {
+ action->write.group_id =
+ rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]);
+ type = ROCKER_GROUP_TYPE_GET(action->write.group_id);
+ if (type != ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE &&
+ type != ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST &&
+ type != ROCKER_OF_DPA_GROUP_TYPE_L3_ECMP) {
+ return -ROCKER_EINVAL;
+ }
+ }
+
+ return ROCKER_OK;
+}
+
+static int of_dpa_cmd_add_multicast_routing(OfDpaFlow *flow,
+ RockerTlv **flow_tlvs)
+{
+ OfDpaFlowKey *key = &flow->key;
+ OfDpaFlowKey *mask = &flow->mask;
+ OfDpaFlowAction *action = &flow->action;
+ enum {
+ MULTICAST_ROUTING_MODE_UNKNOWN,
+ MULTICAST_ROUTING_MODE_IPV4,
+ MULTICAST_ROUTING_MODE_IPV6,
+ } mode = MULTICAST_ROUTING_MODE_UNKNOWN;
+
+ if (!flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE] ||
+ !flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]) {
+ return -ROCKER_EINVAL;
+ }
+
+ key->tbl_id = ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING;
+ key->width = FLOW_KEY_WIDTH(ipv6.addr.dst);
+
+ key->eth.type = rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE]);
+ switch (ntohs(key->eth.type)) {
+ case 0x0800:
+ mode = MULTICAST_ROUTING_MODE_IPV4;
+ break;
+ case 0x86dd:
+ mode = MULTICAST_ROUTING_MODE_IPV6;
+ break;
+ default:
+ return -ROCKER_EINVAL;
+ }
+
+ key->eth.vlan_id = rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]);
+
+ switch (mode) {
+ case MULTICAST_ROUTING_MODE_IPV4:
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IP]) {
+ key->ipv4.addr.src =
+ rocker_tlv_get_u32(flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IP]);
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IP_MASK]) {
+ mask->ipv4.addr.src =
+ rocker_tlv_get_u32(flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IP_MASK]);
+ }
+
+ if (!flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IP]) {
+ if (mask->ipv4.addr.src != 0) {
+ return -ROCKER_EINVAL;
+ }
+ }
+
+ if (!flow_tlvs[ROCKER_TLV_OF_DPA_DST_IP]) {
+ return -ROCKER_EINVAL;
+ }
+
+ key->ipv4.addr.dst =
+ rocker_tlv_get_u32(flow_tlvs[ROCKER_TLV_OF_DPA_DST_IP]);
+ if (!ipv4_addr_is_multicast(key->ipv4.addr.dst)) {
+ return -ROCKER_EINVAL;
+ }
+
+ break;
+
+ case MULTICAST_ROUTING_MODE_IPV6:
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IPV6]) {
+ memcpy(&key->ipv6.addr.src,
+ rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IPV6]),
+ sizeof(key->ipv6.addr.src));
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IPV6_MASK]) {
+ memcpy(&mask->ipv6.addr.src,
+ rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IPV6_MASK]),
+ sizeof(mask->ipv6.addr.src));
+ }
+
+ if (!flow_tlvs[ROCKER_TLV_OF_DPA_SRC_IPV6]) {
+ if (mask->ipv6.addr.src.addr32[0] != 0 &&
+ mask->ipv6.addr.src.addr32[1] != 0 &&
+ mask->ipv6.addr.src.addr32[2] != 0 &&
+ mask->ipv6.addr.src.addr32[3] != 0) {
+ return -ROCKER_EINVAL;
+ }
+ }
+
+ if (!flow_tlvs[ROCKER_TLV_OF_DPA_DST_IPV6]) {
+ return -ROCKER_EINVAL;
+ }
+
+ memcpy(&key->ipv6.addr.dst,
+ rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_IPV6]),
+ sizeof(key->ipv6.addr.dst));
+ if (!ipv6_addr_is_multicast(&key->ipv6.addr.dst)) {
+ return -ROCKER_EINVAL;
+ }
+
+ break;
+
+ default:
+ return -ROCKER_EINVAL;
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]) {
+ action->goto_tbl =
+ rocker_tlv_get_le16(flow_tlvs[ROCKER_TLV_OF_DPA_GOTO_TABLE_ID]);
+ if (action->goto_tbl != ROCKER_OF_DPA_TABLE_ID_ACL_POLICY) {
+ return -ROCKER_EINVAL;
+ }
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]) {
+ action->write.group_id =
+ rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]);
+ if (ROCKER_GROUP_TYPE_GET(action->write.group_id) !=
+ ROCKER_OF_DPA_GROUP_TYPE_L3_MCAST) {
+ return -ROCKER_EINVAL;
+ }
+ action->write.vlan_id = key->eth.vlan_id;
+ }
+
+ return ROCKER_OK;
+}
+
+static int of_dpa_cmd_add_acl_ip(OfDpaFlowKey *key, OfDpaFlowKey *mask,
+ RockerTlv **flow_tlvs)
+{
+ key->width = FLOW_KEY_WIDTH(ip.tos);
+
+ key->ip.proto = 0;
+ key->ip.tos = 0;
+ mask->ip.proto = 0;
+ mask->ip.tos = 0;
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_IP_PROTO]) {
+ key->ip.proto =
+ rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_IP_PROTO]);
+ }
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_IP_PROTO_MASK]) {
+ mask->ip.proto =
+ rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_IP_PROTO_MASK]);
+ }
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_IP_DSCP]) {
+ key->ip.tos =
+ rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_IP_DSCP]);
+ }
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_IP_DSCP_MASK]) {
+ mask->ip.tos =
+ rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_IP_DSCP_MASK]);
+ }
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_IP_ECN]) {
+ key->ip.tos |=
+ rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_IP_ECN]) << 6;
+ }
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_IP_ECN_MASK]) {
+ mask->ip.tos |=
+ rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_IP_ECN_MASK]) << 6;
+ }
+
+ return ROCKER_OK;
+}
+
+static int of_dpa_cmd_add_acl(OfDpaFlow *flow, RockerTlv **flow_tlvs)
+{
+ OfDpaFlowKey *key = &flow->key;
+ OfDpaFlowKey *mask = &flow->mask;
+ OfDpaFlowAction *action = &flow->action;
+ enum {
+ ACL_MODE_UNKNOWN,
+ ACL_MODE_IPV4_VLAN,
+ ACL_MODE_IPV6_VLAN,
+ ACL_MODE_IPV4_TENANT,
+ ACL_MODE_IPV6_TENANT,
+ ACL_MODE_NON_IP_VLAN,
+ ACL_MODE_NON_IP_TENANT,
+ ACL_MODE_ANY_VLAN,
+ ACL_MODE_ANY_TENANT,
+ } mode = ACL_MODE_UNKNOWN;
+ int err = ROCKER_OK;
+
+ if (!flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT] ||
+ !flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE]) {
+ return -ROCKER_EINVAL;
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID] &&
+ flow_tlvs[ROCKER_TLV_OF_DPA_TUNNEL_ID]) {
+ return -ROCKER_EINVAL;
+ }
+
+ key->tbl_id = ROCKER_OF_DPA_TABLE_ID_ACL_POLICY;
+ key->width = FLOW_KEY_WIDTH(eth.type);
+
+ key->in_pport = rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT]);
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT_MASK]) {
+ mask->in_pport =
+ rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT_MASK]);
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC]) {
+ memcpy(key->eth.src.a,
+ rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC]),
+ sizeof(key->eth.src.a));
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC_MASK]) {
+ memcpy(mask->eth.src.a,
+ rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC_MASK]),
+ sizeof(mask->eth.src.a));
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]) {
+ memcpy(key->eth.dst.a,
+ rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]),
+ sizeof(key->eth.dst.a));
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC_MASK]) {
+ memcpy(mask->eth.dst.a,
+ rocker_tlv_data(flow_tlvs[ROCKER_TLV_OF_DPA_DST_MAC_MASK]),
+ sizeof(mask->eth.dst.a));
+ }
+
+ key->eth.type = rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE]);
+ if (key->eth.type) {
+ mask->eth.type = 0xffff;
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]) {
+ key->eth.vlan_id =
+ rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]);
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID_MASK]) {
+ mask->eth.vlan_id =
+ rocker_tlv_get_u16(flow_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID_MASK]);
+ }
+
+ switch (ntohs(key->eth.type)) {
+ case 0x0000:
+ mode = (key->eth.vlan_id) ? ACL_MODE_ANY_VLAN : ACL_MODE_ANY_TENANT;
+ break;
+ case 0x0800:
+ mode = (key->eth.vlan_id) ? ACL_MODE_IPV4_VLAN : ACL_MODE_IPV4_TENANT;
+ break;
+ case 0x86dd:
+ mode = (key->eth.vlan_id) ? ACL_MODE_IPV6_VLAN : ACL_MODE_IPV6_TENANT;
+ break;
+ default:
+ mode = (key->eth.vlan_id) ? ACL_MODE_NON_IP_VLAN :
+ ACL_MODE_NON_IP_TENANT;
+ break;
+ }
+
+ /* XXX only supporting VLAN modes for now */
+ if (mode != ACL_MODE_IPV4_VLAN &&
+ mode != ACL_MODE_IPV6_VLAN &&
+ mode != ACL_MODE_NON_IP_VLAN &&
+ mode != ACL_MODE_ANY_VLAN) {
+ return -ROCKER_EINVAL;
+ }
+
+ switch (ntohs(key->eth.type)) {
+ case 0x0800:
+ case 0x86dd:
+ err = of_dpa_cmd_add_acl_ip(key, mask, flow_tlvs);
+ break;
+ }
+
+ if (err) {
+ return err;
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]) {
+ action->write.group_id =
+ rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]);
+ }
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_COPY_CPU_ACTION]) {
+ action->apply.copy_to_cpu =
+ rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_COPY_CPU_ACTION]);
+ }
+
+ return ROCKER_OK;
+}
+
+static int of_dpa_cmd_flow_add_mod(OfDpa *of_dpa, OfDpaFlow *flow,
+ RockerTlv **flow_tlvs)
+{
+ enum rocker_of_dpa_table_id tbl;
+ int err = ROCKER_OK;
+
+ if (!flow_tlvs[ROCKER_TLV_OF_DPA_TABLE_ID] ||
+ !flow_tlvs[ROCKER_TLV_OF_DPA_PRIORITY] ||
+ !flow_tlvs[ROCKER_TLV_OF_DPA_HARDTIME]) {
+ return -ROCKER_EINVAL;
+ }
+
+ tbl = rocker_tlv_get_le16(flow_tlvs[ROCKER_TLV_OF_DPA_TABLE_ID]);
+ flow->priority = rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_PRIORITY]);
+ flow->hardtime = rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_HARDTIME]);
+
+ if (flow_tlvs[ROCKER_TLV_OF_DPA_IDLETIME]) {
+ if (tbl == ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT ||
+ tbl == ROCKER_OF_DPA_TABLE_ID_VLAN ||
+ tbl == ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC) {
+ return -ROCKER_EINVAL;
+ }
+ flow->idletime =
+ rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_IDLETIME]);
+ }
+
+ switch (tbl) {
+ case ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT:
+ err = of_dpa_cmd_add_ig_port(flow, flow_tlvs);
+ break;
+ case ROCKER_OF_DPA_TABLE_ID_VLAN:
+ err = of_dpa_cmd_add_vlan(flow, flow_tlvs);
+ break;
+ case ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC:
+ err = of_dpa_cmd_add_term_mac(flow, flow_tlvs);
+ break;
+ case ROCKER_OF_DPA_TABLE_ID_BRIDGING:
+ err = of_dpa_cmd_add_bridging(flow, flow_tlvs);
+ break;
+ case ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING:
+ err = of_dpa_cmd_add_unicast_routing(flow, flow_tlvs);
+ break;
+ case ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING:
+ err = of_dpa_cmd_add_multicast_routing(flow, flow_tlvs);
+ break;
+ case ROCKER_OF_DPA_TABLE_ID_ACL_POLICY:
+ err = of_dpa_cmd_add_acl(flow, flow_tlvs);
+ break;
+ }
+
+ return err;
+}
+
+static int of_dpa_cmd_flow_add(OfDpa *of_dpa, uint64_t cookie,
+ RockerTlv **flow_tlvs)
+{
+ OfDpaFlow *flow = of_dpa_flow_find(of_dpa, cookie);
+ int err = ROCKER_OK;
+
+ if (flow) {
+ return -ROCKER_EEXIST;
+ }
+
+ flow = of_dpa_flow_alloc(cookie);
+ if (!flow) {
+ return -ROCKER_ENOMEM;
+ }
+
+ err = of_dpa_cmd_flow_add_mod(of_dpa, flow, flow_tlvs);
+ if (err) {
+ g_free(flow);
+ return err;
+ }
+
+ return of_dpa_flow_add(of_dpa, flow);
+}
+
+static int of_dpa_cmd_flow_mod(OfDpa *of_dpa, uint64_t cookie,
+ RockerTlv **flow_tlvs)
+{
+ OfDpaFlow *flow = of_dpa_flow_find(of_dpa, cookie);
+
+ if (!flow) {
+ return -ROCKER_ENOENT;
+ }
+
+ return of_dpa_cmd_flow_add_mod(of_dpa, flow, flow_tlvs);
+}
+
+static int of_dpa_cmd_flow_del(OfDpa *of_dpa, uint64_t cookie)
+{
+ OfDpaFlow *flow = of_dpa_flow_find(of_dpa, cookie);
+
+ if (!flow) {
+ return -ROCKER_ENOENT;
+ }
+
+ of_dpa_flow_del(of_dpa, flow);
+
+ return ROCKER_OK;
+}
+
+static int of_dpa_cmd_flow_get_stats(OfDpa *of_dpa, uint64_t cookie,
+ struct desc_info *info, char *buf)
+{
+ OfDpaFlow *flow = of_dpa_flow_find(of_dpa, cookie);
+ size_t tlv_size;
+ int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) / 1000;
+ int pos;
+
+ if (!flow) {
+ return -ROCKER_ENOENT;
+ }
+
+ tlv_size = rocker_tlv_total_size(sizeof(uint32_t)) + /* duration */
+ rocker_tlv_total_size(sizeof(uint64_t)) + /* rx_pkts */
+ rocker_tlv_total_size(sizeof(uint64_t)); /* tx_ptks */
+
+ if (tlv_size > desc_buf_size(info)) {
+ return -ROCKER_EMSGSIZE;
+ }
+
+ pos = 0;
+ rocker_tlv_put_le32(buf, &pos, ROCKER_TLV_OF_DPA_FLOW_STAT_DURATION,
+ (int32_t)(now - flow->stats.install_time));
+ rocker_tlv_put_le64(buf, &pos, ROCKER_TLV_OF_DPA_FLOW_STAT_RX_PKTS,
+ flow->stats.rx_pkts);
+ rocker_tlv_put_le64(buf, &pos, ROCKER_TLV_OF_DPA_FLOW_STAT_TX_PKTS,
+ flow->stats.tx_pkts);
+
+ return desc_set_buf(info, tlv_size);
+}
+
+static int of_dpa_flow_cmd(OfDpa *of_dpa, struct desc_info *info,
+ char *buf, uint16_t cmd,
+ RockerTlv **flow_tlvs)
+{
+ uint64_t cookie;
+
+ if (!flow_tlvs[ROCKER_TLV_OF_DPA_COOKIE]) {
+ return -ROCKER_EINVAL;
+ }
+
+ cookie = rocker_tlv_get_le64(flow_tlvs[ROCKER_TLV_OF_DPA_COOKIE]);
+
+ switch (cmd) {
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD:
+ return of_dpa_cmd_flow_add(of_dpa, cookie, flow_tlvs);
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_MOD:
+ return of_dpa_cmd_flow_mod(of_dpa, cookie, flow_tlvs);
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL:
+ return of_dpa_cmd_flow_del(of_dpa, cookie);
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_GET_STATS:
+ return of_dpa_cmd_flow_get_stats(of_dpa, cookie, info, buf);
+ }
+
+ return -ROCKER_ENOTSUP;
+}
+
+static int of_dpa_cmd_add_l2_interface(OfDpaGroup *group,
+ RockerTlv **group_tlvs)
+{
+ if (!group_tlvs[ROCKER_TLV_OF_DPA_OUT_PPORT] ||
+ !group_tlvs[ROCKER_TLV_OF_DPA_POP_VLAN]) {
+ return -ROCKER_EINVAL;
+ }
+
+ group->l2_interface.out_pport =
+ rocker_tlv_get_le32(group_tlvs[ROCKER_TLV_OF_DPA_OUT_PPORT]);
+ group->l2_interface.pop_vlan =
+ rocker_tlv_get_u8(group_tlvs[ROCKER_TLV_OF_DPA_POP_VLAN]);
+
+ return ROCKER_OK;
+}
+
+static int of_dpa_cmd_add_l2_rewrite(OfDpa *of_dpa, OfDpaGroup *group,
+ RockerTlv **group_tlvs)
+{
+ OfDpaGroup *l2_interface_group;
+
+ if (!group_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID_LOWER]) {
+ return -ROCKER_EINVAL;
+ }
+
+ group->l2_rewrite.group_id =
+ rocker_tlv_get_le32(group_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID_LOWER]);
+
+ l2_interface_group = of_dpa_group_find(of_dpa, group->l2_rewrite.group_id);
+ if (!l2_interface_group ||
+ ROCKER_GROUP_TYPE_GET(l2_interface_group->id) !=
+ ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE) {
+ DPRINTF("l2 rewrite group needs a valid l2 interface group\n");
+ return -ROCKER_EINVAL;
+ }
+
+ if (group_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC]) {
+ memcpy(group->l2_rewrite.src_mac.a,
+ rocker_tlv_data(group_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC]),
+ sizeof(group->l2_rewrite.src_mac.a));
+ }
+
+ if (group_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]) {
+ memcpy(group->l2_rewrite.dst_mac.a,
+ rocker_tlv_data(group_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]),
+ sizeof(group->l2_rewrite.dst_mac.a));
+ }
+
+ if (group_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]) {
+ group->l2_rewrite.vlan_id =
+ rocker_tlv_get_u16(group_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]);
+ if (ROCKER_GROUP_VLAN_GET(l2_interface_group->id) !=
+ (ntohs(group->l2_rewrite.vlan_id) & VLAN_VID_MASK)) {
+ DPRINTF("Set VLAN ID must be same as L2 interface group\n");
+ return -ROCKER_EINVAL;
+ }
+ }
+
+ return ROCKER_OK;
+}
+
+static int of_dpa_cmd_add_l2_flood(OfDpa *of_dpa, OfDpaGroup *group,
+ RockerTlv **group_tlvs)
+{
+ OfDpaGroup *l2_group;
+ RockerTlv **tlvs;
+ int err;
+ int i;
+
+ if (!group_tlvs[ROCKER_TLV_OF_DPA_GROUP_COUNT] ||
+ !group_tlvs[ROCKER_TLV_OF_DPA_GROUP_IDS]) {
+ return -ROCKER_EINVAL;
+ }
+
+ group->l2_flood.group_count =
+ rocker_tlv_get_le16(group_tlvs[ROCKER_TLV_OF_DPA_GROUP_COUNT]);
+
+ tlvs = g_malloc0((group->l2_flood.group_count + 1) *
+ sizeof(RockerTlv *));
+ if (!tlvs) {
+ return -ROCKER_ENOMEM;
+ }
+
+ g_free(group->l2_flood.group_ids);
+ group->l2_flood.group_ids =
+ g_malloc0(group->l2_flood.group_count * sizeof(uint32_t));
+ if (!group->l2_flood.group_ids) {
+ err = -ROCKER_ENOMEM;
+ goto err_out;
+ }
+
+ rocker_tlv_parse_nested(tlvs, group->l2_flood.group_count,
+ group_tlvs[ROCKER_TLV_OF_DPA_GROUP_IDS]);
+
+ for (i = 0; i < group->l2_flood.group_count; i++) {
+ group->l2_flood.group_ids[i] = rocker_tlv_get_le32(tlvs[i + 1]);
+ }
+
+ /* All of the L2 interface groups referenced by the L2 flood
+ * must have same VLAN
+ */
+
+ for (i = 0; i < group->l2_flood.group_count; i++) {
+ l2_group = of_dpa_group_find(of_dpa, group->l2_flood.group_ids[i]);
+ if (!l2_group) {
+ continue;
+ }
+ if ((ROCKER_GROUP_TYPE_GET(l2_group->id) ==
+ ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE) &&
+ (ROCKER_GROUP_VLAN_GET(l2_group->id) !=
+ ROCKER_GROUP_VLAN_GET(group->id))) {
+ DPRINTF("l2 interface group 0x%08x VLAN doesn't match l2 "
+ "flood group 0x%08x\n",
+ group->l2_flood.group_ids[i], group->id);
+ err = -ROCKER_EINVAL;
+ goto err_out;
+ }
+ }
+
+ g_free(tlvs);
+ return ROCKER_OK;
+
+err_out:
+ group->l2_flood.group_count = 0;
+ g_free(group->l2_flood.group_ids);
+ g_free(tlvs);
+
+ return err;
+}
+
+static int of_dpa_cmd_add_l3_unicast(OfDpaGroup *group, RockerTlv **group_tlvs)
+{
+ if (!group_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID_LOWER]) {
+ return -ROCKER_EINVAL;
+ }
+
+ group->l3_unicast.group_id =
+ rocker_tlv_get_le32(group_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID_LOWER]);
+
+ if (group_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC]) {
+ memcpy(group->l3_unicast.src_mac.a,
+ rocker_tlv_data(group_tlvs[ROCKER_TLV_OF_DPA_SRC_MAC]),
+ sizeof(group->l3_unicast.src_mac.a));
+ }
+
+ if (group_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]) {
+ memcpy(group->l3_unicast.dst_mac.a,
+ rocker_tlv_data(group_tlvs[ROCKER_TLV_OF_DPA_DST_MAC]),
+ sizeof(group->l3_unicast.dst_mac.a));
+ }
+
+ if (group_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]) {
+ group->l3_unicast.vlan_id =
+ rocker_tlv_get_u16(group_tlvs[ROCKER_TLV_OF_DPA_VLAN_ID]);
+ }
+
+ if (group_tlvs[ROCKER_TLV_OF_DPA_TTL_CHECK]) {
+ group->l3_unicast.ttl_check =
+ rocker_tlv_get_u8(group_tlvs[ROCKER_TLV_OF_DPA_TTL_CHECK]);
+ }
+
+ return ROCKER_OK;
+}
+
+static int of_dpa_cmd_group_do(OfDpa *of_dpa, uint32_t group_id,
+ OfDpaGroup *group, RockerTlv **group_tlvs)
+{
+ uint8_t type = ROCKER_GROUP_TYPE_GET(group_id);
+
+ switch (type) {
+ case ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE:
+ return of_dpa_cmd_add_l2_interface(group, group_tlvs);
+ case ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE:
+ return of_dpa_cmd_add_l2_rewrite(of_dpa, group, group_tlvs);
+ case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD:
+ /* Treat L2 multicast group same as a L2 flood group */
+ case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST:
+ return of_dpa_cmd_add_l2_flood(of_dpa, group, group_tlvs);
+ case ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST:
+ return of_dpa_cmd_add_l3_unicast(group, group_tlvs);
+ }
+
+ return -ROCKER_ENOTSUP;
+}
+
+static int of_dpa_cmd_group_add(OfDpa *of_dpa, uint32_t group_id,
+ RockerTlv **group_tlvs)
+{
+ OfDpaGroup *group = of_dpa_group_find(of_dpa, group_id);
+ int err;
+
+ if (group) {
+ return -ROCKER_EEXIST;
+ }
+
+ group = of_dpa_group_alloc(group_id);
+ if (!group) {
+ return -ROCKER_ENOMEM;
+ }
+
+ err = of_dpa_cmd_group_do(of_dpa, group_id, group, group_tlvs);
+ if (err) {
+ goto err_cmd_add;
+ }
+
+ err = of_dpa_group_add(of_dpa, group);
+ if (err) {
+ goto err_cmd_add;
+ }
+
+ return ROCKER_OK;
+
+err_cmd_add:
+ g_free(group);
+ return err;
+}
+
+static int of_dpa_cmd_group_mod(OfDpa *of_dpa, uint32_t group_id,
+ RockerTlv **group_tlvs)
+{
+ OfDpaGroup *group = of_dpa_group_find(of_dpa, group_id);
+
+ if (!group) {
+ return -ROCKER_ENOENT;
+ }
+
+ return of_dpa_cmd_group_do(of_dpa, group_id, group, group_tlvs);
+}
+
+static int of_dpa_cmd_group_del(OfDpa *of_dpa, uint32_t group_id)
+{
+ OfDpaGroup *group = of_dpa_group_find(of_dpa, group_id);
+
+ if (!group) {
+ return -ROCKER_ENOENT;
+ }
+
+ return of_dpa_group_del(of_dpa, group);
+}
+
+static int of_dpa_cmd_group_get_stats(OfDpa *of_dpa, uint32_t group_id,
+ struct desc_info *info, char *buf)
+{
+ return -ROCKER_ENOTSUP;
+}
+
+static int of_dpa_group_cmd(OfDpa *of_dpa, struct desc_info *info,
+ char *buf, uint16_t cmd, RockerTlv **group_tlvs)
+{
+ uint32_t group_id;
+
+ if (!group_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]) {
+ return -ROCKER_EINVAL;
+ }
+
+ group_id = rocker_tlv_get_le32(group_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]);
+
+ switch (cmd) {
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD:
+ return of_dpa_cmd_group_add(of_dpa, group_id, group_tlvs);
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_MOD:
+ return of_dpa_cmd_group_mod(of_dpa, group_id, group_tlvs);
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL:
+ return of_dpa_cmd_group_del(of_dpa, group_id);
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_GET_STATS:
+ return of_dpa_cmd_group_get_stats(of_dpa, group_id, info, buf);
+ }
+
+ return -ROCKER_ENOTSUP;
+}
+
+static int of_dpa_cmd(World *world, struct desc_info *info,
+ char *buf, uint16_t cmd, RockerTlv *cmd_info_tlv)
+{
+ OfDpa *of_dpa = world_private(world);
+ RockerTlv *tlvs[ROCKER_TLV_OF_DPA_MAX + 1];
+
+ rocker_tlv_parse_nested(tlvs, ROCKER_TLV_OF_DPA_MAX, cmd_info_tlv);
+
+ switch (cmd) {
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD:
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_MOD:
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL:
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_GET_STATS:
+ return of_dpa_flow_cmd(of_dpa, info, buf, cmd, tlvs);
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD:
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_MOD:
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL:
+ case ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_GET_STATS:
+ return of_dpa_group_cmd(of_dpa, info, buf, cmd, tlvs);
+ }
+
+ return -ROCKER_ENOTSUP;
+}
+
+static gboolean rocker_int64_equal(gconstpointer v1, gconstpointer v2)
+{
+ return *((const uint64_t *)v1) == *((const uint64_t *)v2);
+}
+
+static guint rocker_int64_hash(gconstpointer v)
+{
+ return (guint)*(const uint64_t *)v;
+}
+
+static int of_dpa_init(World *world)
+{
+ OfDpa *of_dpa = world_private(world);
+
+ of_dpa->world = world;
+
+ of_dpa->flow_tbl = g_hash_table_new_full(rocker_int64_hash,
+ rocker_int64_equal,
+ NULL, g_free);
+ if (!of_dpa->flow_tbl) {
+ return -ENOMEM;
+ }
+
+ of_dpa->group_tbl = g_hash_table_new_full(g_int_hash, g_int_equal,
+ NULL, g_free);
+ if (!of_dpa->group_tbl) {
+ goto err_group_tbl;
+ }
+
+ /* XXX hardcode some artificial table max values */
+ of_dpa->flow_tbl_max_size = 100;
+ of_dpa->group_tbl_max_size = 100;
+
+ return 0;
+
+err_group_tbl:
+ g_hash_table_destroy(of_dpa->flow_tbl);
+ return -ENOMEM;
+}
+
+static void of_dpa_uninit(World *world)
+{
+ OfDpa *of_dpa = world_private(world);
+
+ g_hash_table_destroy(of_dpa->group_tbl);
+ g_hash_table_destroy(of_dpa->flow_tbl);
+}
+
+struct of_dpa_flow_fill_context {
+ RockerOfDpaFlowList *list;
+ uint32_t tbl_id;
+};
+
+static void of_dpa_flow_fill(void *cookie, void *value, void *user_data)
+{
+ struct of_dpa_flow *flow = value;
+ struct of_dpa_flow_key *key = &flow->key;
+ struct of_dpa_flow_key *mask = &flow->mask;
+ struct of_dpa_flow_fill_context *flow_context = user_data;
+ RockerOfDpaFlowList *new;
+ RockerOfDpaFlow *nflow;
+ RockerOfDpaFlowKey *nkey;
+ RockerOfDpaFlowMask *nmask;
+ RockerOfDpaFlowAction *naction;
+
+ if (flow_context->tbl_id != -1 &&
+ flow_context->tbl_id != key->tbl_id) {
+ return;
+ }
+
+ new = g_malloc0(sizeof(*new));
+ nflow = new->value = g_malloc0(sizeof(*nflow));
+ nkey = nflow->key = g_malloc0(sizeof(*nkey));
+ nmask = nflow->mask = g_malloc0(sizeof(*nmask));
+ naction = nflow->action = g_malloc0(sizeof(*naction));
+
+ nflow->cookie = flow->cookie;
+ nflow->hits = flow->stats.hits;
+ nkey->priority = flow->priority;
+ nkey->tbl_id = key->tbl_id;
+
+ if (key->in_pport || mask->in_pport) {
+ nkey->has_in_pport = true;
+ nkey->in_pport = key->in_pport;
+ }
+
+ if (nkey->has_in_pport && mask->in_pport != 0xffffffff) {
+ nmask->has_in_pport = true;
+ nmask->in_pport = mask->in_pport;
+ }
+
+ if (key->eth.vlan_id || mask->eth.vlan_id) {
+ nkey->has_vlan_id = true;
+ nkey->vlan_id = ntohs(key->eth.vlan_id);
+ }
+
+ if (nkey->has_vlan_id && mask->eth.vlan_id != 0xffff) {
+ nmask->has_vlan_id = true;
+ nmask->vlan_id = ntohs(mask->eth.vlan_id);
+ }
+
+ if (key->tunnel_id || mask->tunnel_id) {
+ nkey->has_tunnel_id = true;
+ nkey->tunnel_id = key->tunnel_id;
+ }
+
+ if (nkey->has_tunnel_id && mask->tunnel_id != 0xffffffff) {
+ nmask->has_tunnel_id = true;
+ nmask->tunnel_id = mask->tunnel_id;
+ }
+
+ if (memcmp(key->eth.src.a, zero_mac.a, ETH_ALEN) ||
+ memcmp(mask->eth.src.a, zero_mac.a, ETH_ALEN)) {
+ nkey->has_eth_src = true;
+ nkey->eth_src = qemu_mac_strdup_printf(key->eth.src.a);
+ }
+
+ if (nkey->has_eth_src && memcmp(mask->eth.src.a, ff_mac.a, ETH_ALEN)) {
+ nmask->has_eth_src = true;
+ nmask->eth_src = qemu_mac_strdup_printf(mask->eth.src.a);
+ }
+
+ if (memcmp(key->eth.dst.a, zero_mac.a, ETH_ALEN) ||
+ memcmp(mask->eth.dst.a, zero_mac.a, ETH_ALEN)) {
+ nkey->has_eth_dst = true;
+ nkey->eth_dst = qemu_mac_strdup_printf(key->eth.dst.a);
+ }
+
+ if (nkey->has_eth_dst && memcmp(mask->eth.dst.a, ff_mac.a, ETH_ALEN)) {
+ nmask->has_eth_dst = true;
+ nmask->eth_dst = qemu_mac_strdup_printf(mask->eth.dst.a);
+ }
+
+ if (key->eth.type) {
+
+ nkey->has_eth_type = true;
+ nkey->eth_type = ntohs(key->eth.type);
+
+ switch (ntohs(key->eth.type)) {
+ case 0x0800:
+ case 0x86dd:
+ if (key->ip.proto || mask->ip.proto) {
+ nkey->has_ip_proto = true;
+ nkey->ip_proto = key->ip.proto;
+ }
+ if (nkey->has_ip_proto && mask->ip.proto != 0xff) {
+ nmask->has_ip_proto = true;
+ nmask->ip_proto = mask->ip.proto;
+ }
+ if (key->ip.tos || mask->ip.tos) {
+ nkey->has_ip_tos = true;
+ nkey->ip_tos = key->ip.tos;
+ }
+ if (nkey->has_ip_tos && mask->ip.tos != 0xff) {
+ nmask->has_ip_tos = true;
+ nmask->ip_tos = mask->ip.tos;
+ }
+ break;
+ }
+
+ switch (ntohs(key->eth.type)) {
+ case 0x0800:
+ if (key->ipv4.addr.dst || mask->ipv4.addr.dst) {
+ char *dst = inet_ntoa(*(struct in_addr *)&key->ipv4.addr.dst);
+ int dst_len = of_dpa_mask2prefix(mask->ipv4.addr.dst);
+ nkey->has_ip_dst = true;
+ nkey->ip_dst = g_strdup_printf("%s/%d", dst, dst_len);
+ }
+ break;
+ }
+ }
+
+ if (flow->action.goto_tbl) {
+ naction->has_goto_tbl = true;
+ naction->goto_tbl = flow->action.goto_tbl;
+ }
+
+ if (flow->action.write.group_id) {
+ naction->has_group_id = true;
+ naction->group_id = flow->action.write.group_id;
+ }
+
+ if (flow->action.apply.new_vlan_id) {
+ naction->has_new_vlan_id = true;
+ naction->new_vlan_id = flow->action.apply.new_vlan_id;
+ }
+
+ new->next = flow_context->list;
+ flow_context->list = new;
+}
+
+RockerOfDpaFlowList *qmp_query_rocker_of_dpa_flows(const char *name,
+ bool has_tbl_id,
+ uint32_t tbl_id,
+ Error **errp)
+{
+ struct rocker *r;
+ struct world *w;
+ struct of_dpa *of_dpa;
+ struct of_dpa_flow_fill_context fill_context = {
+ .list = NULL,
+ .tbl_id = tbl_id,
+ };
+
+ r = rocker_find(name);
+ if (!r) {
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "rocker %s not found", name);
+ return NULL;
+ }
+
+ w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA);
+ if (!w) {
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "rocker %s doesn't have OF-DPA world", name);
+ return NULL;
+ }
+
+ of_dpa = world_private(w);
+
+ g_hash_table_foreach(of_dpa->flow_tbl, of_dpa_flow_fill, &fill_context);
+
+ return fill_context.list;
+}
+
+struct of_dpa_group_fill_context {
+ RockerOfDpaGroupList *list;
+ uint8_t type;
+};
+
+static void of_dpa_group_fill(void *key, void *value, void *user_data)
+{
+ struct of_dpa_group *group = value;
+ struct of_dpa_group_fill_context *flow_context = user_data;
+ RockerOfDpaGroupList *new;
+ RockerOfDpaGroup *ngroup;
+ struct uint32List *id;
+ int i;
+
+ if (flow_context->type != 9 &&
+ flow_context->type != ROCKER_GROUP_TYPE_GET(group->id)) {
+ return;
+ }
+
+ new = g_malloc0(sizeof(*new));
+ ngroup = new->value = g_malloc0(sizeof(*ngroup));
+
+ ngroup->id = group->id;
+
+ ngroup->type = ROCKER_GROUP_TYPE_GET(group->id);
+
+ switch (ngroup->type) {
+ case ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE:
+ ngroup->has_vlan_id = true;
+ ngroup->vlan_id = ROCKER_GROUP_VLAN_GET(group->id);
+ ngroup->has_pport = true;
+ ngroup->pport = ROCKER_GROUP_PORT_GET(group->id);
+ ngroup->has_out_pport = true;
+ ngroup->out_pport = group->l2_interface.out_pport;
+ ngroup->has_pop_vlan = true;
+ ngroup->pop_vlan = group->l2_interface.pop_vlan;
+ break;
+ case ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE:
+ ngroup->has_index = true;
+ ngroup->index = ROCKER_GROUP_INDEX_LONG_GET(group->id);
+ ngroup->has_group_id = true;
+ ngroup->group_id = group->l2_rewrite.group_id;
+ if (group->l2_rewrite.vlan_id) {
+ ngroup->has_set_vlan_id = true;
+ ngroup->set_vlan_id = ntohs(group->l2_rewrite.vlan_id);
+ }
+ if (memcmp(group->l2_rewrite.src_mac.a, zero_mac.a, ETH_ALEN)) {
+ ngroup->has_set_eth_src = true;
+ ngroup->set_eth_src =
+ qemu_mac_strdup_printf(group->l2_rewrite.src_mac.a);
+ }
+ if (memcmp(group->l2_rewrite.dst_mac.a, zero_mac.a, ETH_ALEN)) {
+ ngroup->has_set_eth_dst = true;
+ ngroup->set_eth_dst =
+ qemu_mac_strdup_printf(group->l2_rewrite.dst_mac.a);
+ }
+ break;
+ case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD:
+ case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST:
+ ngroup->has_vlan_id = true;
+ ngroup->vlan_id = ROCKER_GROUP_VLAN_GET(group->id);
+ ngroup->has_index = true;
+ ngroup->index = ROCKER_GROUP_INDEX_GET(group->id);
+ for (i = 0; i < group->l2_flood.group_count; i++) {
+ ngroup->has_group_ids = true;
+ id = g_malloc0(sizeof(*id));
+ id->value = group->l2_flood.group_ids[i];
+ id->next = ngroup->group_ids;
+ ngroup->group_ids = id;
+ }
+ break;
+ case ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST:
+ ngroup->has_index = true;
+ ngroup->index = ROCKER_GROUP_INDEX_LONG_GET(group->id);
+ ngroup->has_group_id = true;
+ ngroup->group_id = group->l3_unicast.group_id;
+ if (group->l3_unicast.vlan_id) {
+ ngroup->has_set_vlan_id = true;
+ ngroup->set_vlan_id = ntohs(group->l3_unicast.vlan_id);
+ }
+ if (memcmp(group->l3_unicast.src_mac.a, zero_mac.a, ETH_ALEN)) {
+ ngroup->has_set_eth_src = true;
+ ngroup->set_eth_src =
+ qemu_mac_strdup_printf(group->l3_unicast.src_mac.a);
+ }
+ if (memcmp(group->l3_unicast.dst_mac.a, zero_mac.a, ETH_ALEN)) {
+ ngroup->has_set_eth_dst = true;
+ ngroup->set_eth_dst =
+ qemu_mac_strdup_printf(group->l3_unicast.dst_mac.a);
+ }
+ if (group->l3_unicast.ttl_check) {
+ ngroup->has_ttl_check = true;
+ ngroup->ttl_check = group->l3_unicast.ttl_check;
+ }
+ break;
+ }
+
+ new->next = flow_context->list;
+ flow_context->list = new;
+}
+
+RockerOfDpaGroupList *qmp_query_rocker_of_dpa_groups(const char *name,
+ bool has_type,
+ uint8_t type,
+ Error **errp)
+{
+ struct rocker *r;
+ struct world *w;
+ struct of_dpa *of_dpa;
+ struct of_dpa_group_fill_context fill_context = {
+ .list = NULL,
+ .type = type,
+ };
+
+ r = rocker_find(name);
+ if (!r) {
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "rocker %s not found", name);
+ return NULL;
+ }
+
+ w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA);
+ if (!w) {
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "rocker %s doesn't have OF-DPA world", name);
+ return NULL;
+ }
+
+ of_dpa = world_private(w);
+
+ g_hash_table_foreach(of_dpa->group_tbl, of_dpa_group_fill, &fill_context);
+
+ return fill_context.list;
+}
+
+static WorldOps of_dpa_ops = {
+ .init = of_dpa_init,
+ .uninit = of_dpa_uninit,
+ .ig = of_dpa_ig,
+ .cmd = of_dpa_cmd,
+};
+
+World *of_dpa_world_alloc(Rocker *r)
+{
+ return world_alloc(r, sizeof(OfDpa), ROCKER_WORLD_TYPE_OF_DPA, &of_dpa_ops);
+}
diff --git a/hw/net/rocker/rocker_of_dpa.h b/hw/net/rocker/rocker_of_dpa.h
new file mode 100644
index 000000000..f3f6d7780
--- /dev/null
+++ b/hw/net/rocker/rocker_of_dpa.h
@@ -0,0 +1,22 @@
+/*
+ * QEMU rocker switch emulation - OF-DPA flow processing support
+ *
+ * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ROCKER_OF_DPA_H_
+#define _ROCKER_OF_DPA_H_
+
+World *of_dpa_world_alloc(Rocker *r);
+
+#endif /* _ROCKER_OF_DPA_H_ */
diff --git a/hw/net/rocker/rocker_tlv.h b/hw/net/rocker/rocker_tlv.h
new file mode 100644
index 000000000..e3c4ab679
--- /dev/null
+++ b/hw/net/rocker/rocker_tlv.h
@@ -0,0 +1,244 @@
+/*
+ * QEMU rocker switch emulation - TLV parsing and composing
+ *
+ * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ROCKER_TLV_H_
+#define _ROCKER_TLV_H_
+
+#define ROCKER_TLV_ALIGNTO 8U
+#define ROCKER_TLV_ALIGN(len) \
+ (((len) + ROCKER_TLV_ALIGNTO - 1) & ~(ROCKER_TLV_ALIGNTO - 1))
+#define ROCKER_TLV_HDRLEN ROCKER_TLV_ALIGN(sizeof(RockerTlv))
+
+/*
+ * <------- ROCKER_TLV_HDRLEN -------> <--- ROCKER_TLV_ALIGN(payload) --->
+ * +-----------------------------+- - -+- - - - - - - - - - - - - - -+- - -+
+ * | Header | Pad | Payload | Pad |
+ * | (RockerTlv) | ing | | ing |
+ * +-----------------------------+- - -+- - - - - - - - - - - - - - -+- - -+
+ * <--------------------------- tlv->len -------------------------->
+ */
+
+static inline RockerTlv *rocker_tlv_next(const RockerTlv *tlv, int *remaining)
+{
+ int totlen = ROCKER_TLV_ALIGN(le16_to_cpu(tlv->len));
+
+ *remaining -= totlen;
+ return (RockerTlv *) ((char *) tlv + totlen);
+}
+
+static inline int rocker_tlv_ok(const RockerTlv *tlv, int remaining)
+{
+ return remaining >= (int) ROCKER_TLV_HDRLEN &&
+ le16_to_cpu(tlv->len) >= ROCKER_TLV_HDRLEN &&
+ le16_to_cpu(tlv->len) <= remaining;
+}
+
+#define rocker_tlv_for_each(pos, head, len, rem) \
+ for (pos = head, rem = len; \
+ rocker_tlv_ok(pos, rem); \
+ pos = rocker_tlv_next(pos, &(rem)))
+
+#define rocker_tlv_for_each_nested(pos, tlv, rem) \
+ rocker_tlv_for_each(pos, rocker_tlv_data(tlv), rocker_tlv_len(tlv), rem)
+
+static inline int rocker_tlv_size(int payload)
+{
+ return ROCKER_TLV_HDRLEN + payload;
+}
+
+static inline int rocker_tlv_total_size(int payload)
+{
+ return ROCKER_TLV_ALIGN(rocker_tlv_size(payload));
+}
+
+static inline int rocker_tlv_padlen(int payload)
+{
+ return rocker_tlv_total_size(payload) - rocker_tlv_size(payload);
+}
+
+static inline int rocker_tlv_type(const RockerTlv *tlv)
+{
+ return le32_to_cpu(tlv->type);
+}
+
+static inline void *rocker_tlv_data(const RockerTlv *tlv)
+{
+ return (char *) tlv + ROCKER_TLV_HDRLEN;
+}
+
+static inline int rocker_tlv_len(const RockerTlv *tlv)
+{
+ return le16_to_cpu(tlv->len) - ROCKER_TLV_HDRLEN;
+}
+
+static inline uint8_t rocker_tlv_get_u8(const RockerTlv *tlv)
+{
+ return *(uint8_t *) rocker_tlv_data(tlv);
+}
+
+static inline uint16_t rocker_tlv_get_u16(const RockerTlv *tlv)
+{
+ return *(uint16_t *) rocker_tlv_data(tlv);
+}
+
+static inline uint32_t rocker_tlv_get_u32(const RockerTlv *tlv)
+{
+ return *(uint32_t *) rocker_tlv_data(tlv);
+}
+
+static inline uint64_t rocker_tlv_get_u64(const RockerTlv *tlv)
+{
+ return *(uint64_t *) rocker_tlv_data(tlv);
+}
+
+static inline uint16_t rocker_tlv_get_le16(const RockerTlv *tlv)
+{
+ return le16_to_cpup((uint16_t *) rocker_tlv_data(tlv));
+}
+
+static inline uint32_t rocker_tlv_get_le32(const RockerTlv *tlv)
+{
+ return le32_to_cpup((uint32_t *) rocker_tlv_data(tlv));
+}
+
+static inline uint64_t rocker_tlv_get_le64(const RockerTlv *tlv)
+{
+ return le64_to_cpup((uint64_t *) rocker_tlv_data(tlv));
+}
+
+static inline void rocker_tlv_parse(RockerTlv **tb, int maxtype,
+ const char *buf, int buf_len)
+{
+ const RockerTlv *tlv;
+ const RockerTlv *head = (const RockerTlv *) buf;
+ int rem;
+
+ memset(tb, 0, sizeof(RockerTlv *) * (maxtype + 1));
+
+ rocker_tlv_for_each(tlv, head, buf_len, rem) {
+ uint32_t type = rocker_tlv_type(tlv);
+
+ if (type > 0 && type <= maxtype) {
+ tb[type] = (RockerTlv *) tlv;
+ }
+ }
+}
+
+static inline void rocker_tlv_parse_nested(RockerTlv **tb, int maxtype,
+ const RockerTlv *tlv)
+{
+ rocker_tlv_parse(tb, maxtype, rocker_tlv_data(tlv), rocker_tlv_len(tlv));
+}
+
+static inline RockerTlv *rocker_tlv_start(char *buf, int buf_pos)
+{
+ return (RockerTlv *) (buf + buf_pos);
+}
+
+static inline void rocker_tlv_put_iov(char *buf, int *buf_pos,
+ int type, const struct iovec *iov,
+ const unsigned int iovcnt)
+{
+ size_t len = iov_size(iov, iovcnt);
+ int total_size = rocker_tlv_total_size(len);
+ RockerTlv *tlv;
+
+ tlv = rocker_tlv_start(buf, *buf_pos);
+ *buf_pos += total_size;
+ tlv->type = cpu_to_le32(type);
+ tlv->len = cpu_to_le16(rocker_tlv_size(len));
+ iov_to_buf(iov, iovcnt, 0, rocker_tlv_data(tlv), len);
+ memset((char *) tlv + le16_to_cpu(tlv->len), 0, rocker_tlv_padlen(len));
+}
+
+static inline void rocker_tlv_put(char *buf, int *buf_pos,
+ int type, int len, void *data)
+{
+ struct iovec iov = {
+ .iov_base = data,
+ .iov_len = len,
+ };
+
+ rocker_tlv_put_iov(buf, buf_pos, type, &iov, 1);
+}
+
+static inline void rocker_tlv_put_u8(char *buf, int *buf_pos,
+ int type, uint8_t value)
+{
+ rocker_tlv_put(buf, buf_pos, type, sizeof(uint8_t), &value);
+}
+
+static inline void rocker_tlv_put_u16(char *buf, int *buf_pos,
+ int type, uint16_t value)
+{
+ rocker_tlv_put(buf, buf_pos, type, sizeof(uint16_t), &value);
+}
+
+static inline void rocker_tlv_put_u32(char *buf, int *buf_pos,
+ int type, uint32_t value)
+{
+ rocker_tlv_put(buf, buf_pos, type, sizeof(uint32_t), &value);
+}
+
+static inline void rocker_tlv_put_u64(char *buf, int *buf_pos,
+ int type, uint64_t value)
+{
+ rocker_tlv_put(buf, buf_pos, type, sizeof(uint64_t), &value);
+}
+
+static inline void rocker_tlv_put_le16(char *buf, int *buf_pos,
+ int type, uint16_t value)
+{
+ value = cpu_to_le16(value);
+ rocker_tlv_put(buf, buf_pos, type, sizeof(uint16_t), &value);
+}
+
+static inline void rocker_tlv_put_le32(char *buf, int *buf_pos,
+ int type, uint32_t value)
+{
+ value = cpu_to_le32(value);
+ rocker_tlv_put(buf, buf_pos, type, sizeof(uint32_t), &value);
+}
+
+static inline void rocker_tlv_put_le64(char *buf, int *buf_pos,
+ int type, uint64_t value)
+{
+ value = cpu_to_le64(value);
+ rocker_tlv_put(buf, buf_pos, type, sizeof(uint64_t), &value);
+}
+
+static inline RockerTlv *rocker_tlv_nest_start(char *buf, int *buf_pos,
+ int type)
+{
+ RockerTlv *start = rocker_tlv_start(buf, *buf_pos);
+
+ rocker_tlv_put(buf, buf_pos, type, 0, NULL);
+ return start;
+}
+
+static inline void rocker_tlv_nest_end(char *buf, int *buf_pos,
+ RockerTlv *start)
+{
+ start->len = (char *) rocker_tlv_start(buf, *buf_pos) - (char *) start;
+}
+
+static inline void rocker_tlv_nest_cancel(char *buf, int *buf_pos,
+ RockerTlv *start)
+{
+ *buf_pos = (char *) start - buf;
+}
+
+#endif
diff --git a/hw/net/rocker/rocker_world.c b/hw/net/rocker/rocker_world.c
new file mode 100644
index 000000000..a6b18f175
--- /dev/null
+++ b/hw/net/rocker/rocker_world.c
@@ -0,0 +1,106 @@
+/*
+ * QEMU rocker switch emulation - switch worlds
+ *
+ * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "qemu/iov.h"
+
+#include "rocker.h"
+#include "rocker_world.h"
+
+struct world {
+ Rocker *r;
+ enum rocker_world_type type;
+ WorldOps *ops;
+};
+
+ssize_t world_ingress(World *world, uint32_t pport,
+ const struct iovec *iov, int iovcnt)
+{
+ if (world->ops->ig) {
+ return world->ops->ig(world, pport, iov, iovcnt);
+ }
+
+ return -1;
+}
+
+int world_do_cmd(World *world, DescInfo *info,
+ char *buf, uint16_t cmd, RockerTlv *cmd_info_tlv)
+{
+ if (world->ops->cmd) {
+ return world->ops->cmd(world, info, buf, cmd, cmd_info_tlv);
+ }
+
+ return -ROCKER_ENOTSUP;
+}
+
+World *world_alloc(Rocker *r, size_t sizeof_private,
+ enum rocker_world_type type, WorldOps *ops)
+{
+ World *w = g_malloc0(sizeof(World) + sizeof_private);
+
+ if (w) {
+ w->r = r;
+ w->type = type;
+ w->ops = ops;
+ if (w->ops->init) {
+ w->ops->init(w);
+ }
+ }
+
+ return w;
+}
+
+void world_free(World *world)
+{
+ if (world->ops->uninit) {
+ world->ops->uninit(world);
+ }
+ g_free(world);
+}
+
+void world_reset(World *world)
+{
+ if (world->ops->uninit) {
+ world->ops->uninit(world);
+ }
+ if (world->ops->init) {
+ world->ops->init(world);
+ }
+}
+
+void *world_private(World *world)
+{
+ return world + 1;
+}
+
+Rocker *world_rocker(World *world)
+{
+ return world->r;
+}
+
+enum rocker_world_type world_type(World *world)
+{
+ return world->type;
+}
+
+const char *world_name(World *world)
+{
+ switch (world->type) {
+ case ROCKER_WORLD_TYPE_OF_DPA:
+ return "OF_DPA";
+ default:
+ return "unknown";
+ }
+}
diff --git a/hw/net/rocker/rocker_world.h b/hw/net/rocker/rocker_world.h
new file mode 100644
index 000000000..18d277b92
--- /dev/null
+++ b/hw/net/rocker/rocker_world.h
@@ -0,0 +1,60 @@
+/*
+ * QEMU rocker switch emulation - switch worlds
+ *
+ * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ROCKER_WORLD_H_
+#define _ROCKER_WORLD_H_
+
+#include "rocker_hw.h"
+
+enum rocker_world_type {
+ ROCKER_WORLD_TYPE_OF_DPA = ROCKER_PORT_MODE_OF_DPA,
+ ROCKER_WORLD_TYPE_MAX,
+};
+
+typedef int (world_init)(World *world);
+typedef void (world_uninit)(World *world);
+typedef ssize_t (world_ig)(World *world, uint32_t pport,
+ const struct iovec *iov, int iovcnt);
+typedef int (world_cmd)(World *world, DescInfo *info,
+ char *buf, uint16_t cmd,
+ RockerTlv *cmd_info_tlv);
+
+typedef struct world_ops {
+ world_init *init;
+ world_uninit *uninit;
+ world_ig *ig;
+ world_cmd *cmd;
+} WorldOps;
+
+ssize_t world_ingress(World *world, uint32_t pport,
+ const struct iovec *iov, int iovcnt);
+int world_do_cmd(World *world, DescInfo *info,
+ char *buf, uint16_t cmd, RockerTlv *cmd_info_tlv);
+
+World *world_alloc(Rocker *r, size_t sizeof_private,
+ enum rocker_world_type type, WorldOps *ops);
+void world_free(World *world);
+void world_reset(World *world);
+
+void *world_private(World *world);
+Rocker *world_rocker(World *world);
+
+enum rocker_world_type world_type(World *world);
+const char *world_name(World *world);
+
+World *rocker_get_world(Rocker *r, enum rocker_world_type type);
+
+#endif /* _ROCKER_WORLD_H_ */
diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c
index d25e8c93d..cb51613eb 100644
--- a/hw/net/rtl8139.c
+++ b/hw/net/rtl8139.c
@@ -1148,7 +1148,9 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t
/* if receiver buffer is empty then avail == 0 */
- if (avail != 0 && size + 8 >= avail)
+#define RX_ALIGN(x) (((x) + 3) & ~0x3)
+
+ if (avail != 0 && RX_ALIGN(size + 8) >= avail)
{
DPRINTF("rx overflow: rx buffer length %d head 0x%04x "
"read 0x%04x === available 0x%04x need 0x%04x\n",
@@ -1157,7 +1159,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t
s->IntrStatus |= RxOverflow;
++s->RxMissed;
rtl8139_update_irq(s);
- return size_;
+ return 0;
}
packet_header |= RxStatusOK;
@@ -1176,7 +1178,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t
rtl8139_write_buffer(s, (uint8_t *)&val, 4);
/* correct buffer write pointer */
- s->RxBufAddr = MOD2((s->RxBufAddr + 3) & ~0x3, s->RxBufferSize);
+ s->RxBufAddr = MOD2(RX_ALIGN(s->RxBufAddr), s->RxBufferSize);
/* now we can signal we have received something */
@@ -3257,6 +3259,7 @@ static const VMStateDescription vmstate_rtl8139_hotplug_ready ={
.name = "rtl8139/hotplug_ready",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = rtl8139_hotplug_ready_needed,
.fields = (VMStateField[]) {
VMSTATE_END_OF_LIST()
}
@@ -3352,13 +3355,9 @@ static const VMStateDescription vmstate_rtl8139 = {
VMSTATE_UINT32_V(cplus_enabled, RTL8139State, 4),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection []) {
- {
- .vmsd = &vmstate_rtl8139_hotplug_ready,
- .needed = rtl8139_hotplug_ready_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_rtl8139_hotplug_ready,
+ NULL
}
};
diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c
index 2dd5ec111..1ca5e9ce6 100644
--- a/hw/net/spapr_llan.c
+++ b/hw/net/spapr_llan.c
@@ -284,7 +284,7 @@ static int check_bd(VIOsPAPRVLANDevice *dev, vlan_bd_t bd,
}
static target_ulong h_register_logical_lan(PowerPCCPU *cpu,
- sPAPREnvironment *spapr,
+ sPAPRMachineState *spapr,
target_ulong opcode,
target_ulong *args)
{
@@ -349,7 +349,8 @@ static target_ulong h_register_logical_lan(PowerPCCPU *cpu,
}
-static target_ulong h_free_logical_lan(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_free_logical_lan(PowerPCCPU *cpu,
+ sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
target_ulong reg = args[0];
@@ -371,7 +372,7 @@ static target_ulong h_free_logical_lan(PowerPCCPU *cpu, sPAPREnvironment *spapr,
}
static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu,
- sPAPREnvironment *spapr,
+ sPAPRMachineState *spapr,
target_ulong opcode,
target_ulong *args)
{
@@ -421,7 +422,8 @@ static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu,
return H_SUCCESS;
}
-static target_ulong h_send_logical_lan(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_send_logical_lan(PowerPCCPU *cpu,
+ sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
target_ulong reg = args[0];
@@ -490,7 +492,7 @@ static target_ulong h_send_logical_lan(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return H_SUCCESS;
}
-static target_ulong h_multicast_ctrl(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_multicast_ctrl(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
target_ulong reg = args[0];
diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c
index 278a6545c..21a47735d 100644
--- a/hw/net/stellaris_enet.c
+++ b/hw/net/stellaris_enet.c
@@ -228,8 +228,7 @@ static ssize_t stellaris_enet_receive(NetClientState *nc, const uint8_t *buf, si
if ((s->rctl & SE_RCTL_RXEN) == 0)
return -1;
if (s->np >= 31) {
- DPRINTF("Packet dropped\n");
- return -1;
+ return 0;
}
DPRINTF("Received packet len=%zu\n", size);
@@ -260,13 +259,8 @@ static ssize_t stellaris_enet_receive(NetClientState *nc, const uint8_t *buf, si
return size;
}
-static int stellaris_enet_can_receive(NetClientState *nc)
+static int stellaris_enet_can_receive(stellaris_enet_state *s)
{
- stellaris_enet_state *s = qemu_get_nic_opaque(nc);
-
- if ((s->rctl & SE_RCTL_RXEN) == 0)
- return 1;
-
return (s->np < 31);
}
@@ -307,6 +301,9 @@ static uint64_t stellaris_enet_read(void *opaque, hwaddr offset,
s->next_packet = 0;
s->np--;
DPRINTF("RX done np=%d\n", s->np);
+ if (!s->np && stellaris_enet_can_receive(s)) {
+ qemu_flush_queued_packets(qemu_get_queue(s->nic));
+ }
}
return val;
}
@@ -454,7 +451,6 @@ static void stellaris_enet_reset(stellaris_enet_state *s)
static NetClientInfo net_stellaris_enet_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = stellaris_enet_can_receive,
.receive = stellaris_enet_receive,
};
diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
index cf23335ba..1d76b94c8 100644
--- a/hw/net/vhost_net.c
+++ b/hw/net/vhost_net.c
@@ -38,6 +38,7 @@
#include "standard-headers/linux/virtio_ring.h"
#include "hw/virtio/vhost.h"
#include "hw/virtio/virtio-bus.h"
+#include "hw/virtio/virtio-access.h"
struct vhost_net {
struct vhost_dev dev;
@@ -52,6 +53,7 @@ static const int kernel_feature_bits[] = {
VIRTIO_RING_F_INDIRECT_DESC,
VIRTIO_RING_F_EVENT_IDX,
VIRTIO_NET_F_MRG_RXBUF,
+ VIRTIO_F_VERSION_1,
VHOST_INVALID_FEATURE_BIT
};
@@ -62,6 +64,7 @@ static const int user_feature_bits[] = {
VIRTIO_RING_F_EVENT_IDX,
VIRTIO_F_ANY_LAYOUT,
+ VIRTIO_F_VERSION_1,
VIRTIO_NET_F_CSUM,
VIRTIO_NET_F_GUEST_CSUM,
VIRTIO_NET_F_GSO,
@@ -107,13 +110,13 @@ static const int *vhost_net_get_feature_bits(struct vhost_net *net)
return feature_bits;
}
-unsigned vhost_net_get_features(struct vhost_net *net, unsigned features)
+uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features)
{
return vhost_get_features(&net->dev, vhost_net_get_feature_bits(net),
features);
}
-void vhost_net_ack_features(struct vhost_net *net, unsigned features)
+void vhost_net_ack_features(struct vhost_net *net, uint64_t features)
{
net->dev.acked_features = net->dev.backend_features;
vhost_ack_features(&net->dev, vhost_net_get_feature_bits(net), features);
@@ -147,7 +150,7 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options)
goto fail;
}
net->dev.backend_features = qemu_has_vnet_hdr(options->net_backend)
- ? 0 : (1 << VHOST_NET_F_VIRTIO_NET_HDR);
+ ? 0 : (1ULL << VHOST_NET_F_VIRTIO_NET_HDR);
net->backend = r;
} else {
net->dev.backend_features = 0;
@@ -159,14 +162,14 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options)
net->dev.vqs = net->vqs;
r = vhost_dev_init(&net->dev, options->opaque,
- options->backend_type, options->force);
+ options->backend_type);
if (r < 0) {
goto fail;
}
if (backend_kernel) {
if (!qemu_has_vnet_hdr_len(options->net_backend,
sizeof(struct virtio_net_hdr_mrg_rxbuf))) {
- net->dev.features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF);
+ net->dev.features &= ~(1ULL << VIRTIO_NET_F_MRG_RXBUF);
}
if (~net->dev.features & net->dev.backend_features) {
fprintf(stderr, "vhost lacks feature mask %" PRIu64
@@ -184,14 +187,30 @@ fail:
return NULL;
}
-bool vhost_net_query(VHostNetState *net, VirtIODevice *dev)
+static void vhost_net_set_vq_index(struct vhost_net *net, int vq_index)
{
- return vhost_dev_query(&net->dev, dev);
+ net->dev.vq_index = vq_index;
}
-static void vhost_net_set_vq_index(struct vhost_net *net, int vq_index)
+static int vhost_net_set_vnet_endian(VirtIODevice *dev, NetClientState *peer,
+ bool set)
{
- net->dev.vq_index = vq_index;
+ int r = 0;
+
+ if (virtio_vdev_has_feature(dev, VIRTIO_F_VERSION_1) ||
+ (virtio_legacy_is_cross_endian(dev) && !virtio_is_big_endian(dev))) {
+ r = qemu_set_vnet_le(peer, set);
+ if (r) {
+ error_report("backend does not support LE vnet headers");
+ }
+ } else if (virtio_legacy_is_cross_endian(dev)) {
+ r = qemu_set_vnet_be(peer, set);
+ if (r) {
+ error_report("backend does not support BE vnet headers");
+ }
+ }
+
+ return r;
}
static int vhost_net_start_one(struct vhost_net *net,
@@ -263,6 +282,13 @@ static void vhost_net_stop_one(struct vhost_net *net,
&file);
assert(r >= 0);
}
+ } else if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) {
+ for (file.index = 0; file.index < net->dev.nvqs; ++file.index) {
+ const VhostOps *vhost_ops = net->dev.vhost_ops;
+ int r = vhost_ops->vhost_call(&net->dev, VHOST_RESET_OWNER,
+ NULL);
+ assert(r >= 0);
+ }
}
if (net->nc->info->poll) {
net->nc->info->poll(net->nc, true);
@@ -271,19 +297,6 @@ static void vhost_net_stop_one(struct vhost_net *net,
vhost_dev_disable_notifiers(&net->dev, dev);
}
-static bool vhost_net_device_endian_ok(VirtIODevice *vdev)
-{
-#ifdef TARGET_IS_BIENDIAN
-#ifdef HOST_WORDS_BIGENDIAN
- return virtio_is_big_endian(vdev);
-#else
- return !virtio_is_big_endian(vdev);
-#endif
-#else
- return true;
-#endif
-}
-
int vhost_net_start(VirtIODevice *dev, NetClientState *ncs,
int total_queues)
{
@@ -292,15 +305,14 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs,
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus);
int r, e, i;
- if (!vhost_net_device_endian_ok(dev)) {
- error_report("vhost-net does not support cross-endian");
+ if (!k->set_guest_notifiers) {
+ error_report("binding does not support guest notifiers");
r = -ENOSYS;
goto err;
}
- if (!k->set_guest_notifiers) {
- error_report("binding does not support guest notifiers");
- r = -ENOSYS;
+ r = vhost_net_set_vnet_endian(dev, ncs[0].peer, true);
+ if (r < 0) {
goto err;
}
@@ -311,7 +323,7 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs,
r = k->set_guest_notifiers(qbus->parent, total_queues * 2, true);
if (r < 0) {
error_report("Error binding guest notifier: %d", -r);
- goto err;
+ goto err_endian;
}
for (i = 0; i < total_queues; i++) {
@@ -333,6 +345,8 @@ err_start:
fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", e);
fflush(stderr);
}
+err_endian:
+ vhost_net_set_vnet_endian(dev, ncs[0].peer, false);
err:
return r;
}
@@ -355,6 +369,8 @@ void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs,
fflush(stderr);
}
assert(r >= 0);
+
+ assert(vhost_net_set_vnet_endian(dev, ncs[0].peer, false) >= 0);
}
void vhost_net_cleanup(struct vhost_net *net)
@@ -402,11 +418,6 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options)
return NULL;
}
-bool vhost_net_query(VHostNetState *net, VirtIODevice *dev)
-{
- return false;
-}
-
int vhost_net_start(VirtIODevice *dev,
NetClientState *ncs,
int total_queues)
@@ -423,11 +434,11 @@ void vhost_net_cleanup(struct vhost_net *net)
{
}
-unsigned vhost_net_get_features(struct vhost_net *net, unsigned features)
+uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features)
{
return features;
}
-void vhost_net_ack_features(struct vhost_net *net, unsigned features)
+void vhost_net_ack_features(struct vhost_net *net, uint64_t features)
{
}
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 2d570e4a8..4aa93fb93 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -86,7 +86,8 @@ static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config)
memcpy(&netcfg, config, n->config_size);
- if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR) &&
+ if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR) &&
+ !virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1) &&
memcmp(netcfg.mac, n->mac, ETH_ALEN)) {
memcpy(n->mac, netcfg.mac, ETH_ALEN);
qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
@@ -127,10 +128,6 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status)
if (!n->vhost_started) {
int r, i;
- if (!vhost_net_query(get_vhost_net(nc->peer), vdev)) {
- return;
- }
-
/* Any packets outstanding? Purge them to avoid touching rings
* when vhost is running.
*/
@@ -165,6 +162,8 @@ static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
virtio_net_vhost_status(n, status);
for (i = 0; i < n->max_queues; i++) {
+ NetClientState *ncs = qemu_get_subqueue(n->nic, i);
+ bool queue_started;
q = &n->vqs[i];
if ((!n->multiqueue && i != 0) || i >= n->curr_queues) {
@@ -172,12 +171,18 @@ static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
} else {
queue_status = status;
}
+ queue_started =
+ virtio_net_started(n, queue_status) && !n->vhost_started;
+
+ if (queue_started) {
+ qemu_flush_queued_packets(ncs);
+ }
if (!q->tx_waiting) {
continue;
}
- if (virtio_net_started(n, queue_status) && !n->vhost_started) {
+ if (queue_started) {
if (q->tx_timer) {
timer_mod(q->tx_timer,
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout);
@@ -226,12 +231,6 @@ static void rxfilter_notify(NetClientState *nc)
}
}
-static char *mac_strdup_printf(const uint8_t *mac)
-{
- return g_strdup_printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", mac[0],
- mac[1], mac[2], mac[3], mac[4], mac[5]);
-}
-
static intList *get_vlan_table(VirtIONet *n)
{
intList *list, *entry;
@@ -284,12 +283,12 @@ static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc)
info->multicast_overflow = n->mac_table.multi_overflow;
info->unicast_overflow = n->mac_table.uni_overflow;
- info->main_mac = mac_strdup_printf(n->mac);
+ info->main_mac = qemu_mac_strdup_printf(n->mac);
str_list = NULL;
for (i = 0; i < n->mac_table.first_multi; i++) {
entry = g_malloc0(sizeof(*entry));
- entry->value = mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
+ entry->value = qemu_mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
entry->next = str_list;
str_list = entry;
}
@@ -298,14 +297,14 @@ static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc)
str_list = NULL;
for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) {
entry = g_malloc0(sizeof(*entry));
- entry->value = mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
+ entry->value = qemu_mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
entry->next = str_list;
str_list = entry;
}
info->multicast_table = str_list;
info->vlan_table = get_vlan_table(n);
- if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_VLAN)) {
+ if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VLAN)) {
info->vlan = RX_STATE_ALL;
} else if (!info->vlan_table) {
info->vlan = RX_STATE_NONE;
@@ -372,15 +371,21 @@ static int peer_has_ufo(VirtIONet *n)
return n->has_ufo;
}
-static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs)
+static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs,
+ int version_1)
{
int i;
NetClientState *nc;
n->mergeable_rx_bufs = mergeable_rx_bufs;
- n->guest_hdr_len = n->mergeable_rx_bufs ?
- sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr);
+ if (version_1) {
+ n->guest_hdr_len = sizeof(struct virtio_net_hdr_mrg_rxbuf);
+ } else {
+ n->guest_hdr_len = n->mergeable_rx_bufs ?
+ sizeof(struct virtio_net_hdr_mrg_rxbuf) :
+ sizeof(struct virtio_net_hdr);
+ }
for (i = 0; i < n->max_queues; i++) {
nc = qemu_get_subqueue(n->nic, i);
@@ -441,11 +446,15 @@ static void virtio_net_set_queues(VirtIONet *n)
static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue);
-static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features)
+static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features,
+ Error **errp)
{
VirtIONet *n = VIRTIO_NET(vdev);
NetClientState *nc = qemu_get_queue(n->nic);
+ /* Firstly sync all virtio-net possible supported features */
+ features |= n->host_features;
+
virtio_add_feature(&features, VIRTIO_NET_F_MAC);
if (!peer_has_vnet_hdr(n)) {
@@ -471,9 +480,9 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features)
return vhost_net_get_features(get_vhost_net(nc->peer), features);
}
-static uint32_t virtio_net_bad_features(VirtIODevice *vdev)
+static uint64_t virtio_net_bad_features(VirtIODevice *vdev)
{
- uint32_t features = 0;
+ uint64_t features = 0;
/* Linux kernel 2.6.25. It understood MAC (as everyone must),
* but also these: */
@@ -514,17 +523,19 @@ static inline uint64_t virtio_net_supported_guest_offloads(VirtIONet *n)
return virtio_net_guest_offloads_by_features(vdev->guest_features);
}
-static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features)
+static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
{
VirtIONet *n = VIRTIO_NET(vdev);
int i;
virtio_net_set_multiqueue(n,
- __virtio_has_feature(features, VIRTIO_NET_F_MQ));
+ virtio_has_feature(features, VIRTIO_NET_F_MQ));
virtio_net_set_mrg_rx_bufs(n,
- __virtio_has_feature(features,
- VIRTIO_NET_F_MRG_RXBUF));
+ virtio_has_feature(features,
+ VIRTIO_NET_F_MRG_RXBUF),
+ virtio_has_feature(features,
+ VIRTIO_F_VERSION_1));
if (n->has_vnet_hdr) {
n->curr_guest_offloads =
@@ -541,7 +552,7 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features)
vhost_net_ack_features(get_vhost_net(nc->peer), features);
}
- if (__virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) {
+ if (virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) {
memset(n->vlans, 0, MAX_VLAN >> 3);
} else {
memset(n->vlans, 0xff, MAX_VLAN >> 3);
@@ -588,7 +599,7 @@ static int virtio_net_handle_offloads(VirtIONet *n, uint8_t cmd,
uint64_t offloads;
size_t s;
- if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
+ if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
return VIRTIO_NET_ERR;
}
@@ -1035,10 +1046,12 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t
if (i == 0)
return -1;
error_report("virtio-net unexpected empty queue: "
- "i %zd mergeable %d offset %zd, size %zd, "
- "guest hdr len %zd, host hdr len %zd guest features 0x%x",
- i, n->mergeable_rx_bufs, offset, size,
- n->guest_hdr_len, n->host_hdr_len, vdev->guest_features);
+ "i %zd mergeable %d offset %zd, size %zd, "
+ "guest hdr len %zd, host hdr len %zd "
+ "guest features 0x%" PRIx64,
+ i, n->mergeable_rx_bufs, offset, size,
+ n->guest_hdr_len, n->host_hdr_len,
+ vdev->guest_features);
exit(1);
}
@@ -1073,13 +1086,7 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t
* must have consumed the complete packet.
* Otherwise, drop it. */
if (!n->mergeable_rx_bufs && offset < size) {
-#if 0
- error_report("virtio-net truncated non-mergeable packet: "
- "i %zd mergeable %d offset %zd, size %zd, "
- "guest hdr len %zd, host hdr len %zd",
- i, n->mergeable_rx_bufs,
- offset, size, n->guest_hdr_len, n->host_hdr_len);
-#endif
+ virtqueue_discard(q->rx_vq, &elem, total);
return size;
}
@@ -1315,40 +1322,86 @@ static void virtio_net_tx_bh(void *opaque)
}
}
-static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue)
+static void virtio_net_add_queue(VirtIONet *n, int index)
{
VirtIODevice *vdev = VIRTIO_DEVICE(n);
- int i, max = multiqueue ? n->max_queues : 1;
- n->multiqueue = multiqueue;
+ n->vqs[index].rx_vq = virtio_add_queue(vdev, 256, virtio_net_handle_rx);
+ if (n->net_conf.tx && !strcmp(n->net_conf.tx, "timer")) {
+ n->vqs[index].tx_vq =
+ virtio_add_queue(vdev, 256, virtio_net_handle_tx_timer);
+ n->vqs[index].tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ virtio_net_tx_timer,
+ &n->vqs[index]);
+ } else {
+ n->vqs[index].tx_vq =
+ virtio_add_queue(vdev, 256, virtio_net_handle_tx_bh);
+ n->vqs[index].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[index]);
+ }
- for (i = 2; i < n->max_queues * 2 + 1; i++) {
- virtio_del_queue(vdev, i);
+ n->vqs[index].tx_waiting = 0;
+ n->vqs[index].n = n;
+}
+
+static void virtio_net_del_queue(VirtIONet *n, int index)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(n);
+ VirtIONetQueue *q = &n->vqs[index];
+ NetClientState *nc = qemu_get_subqueue(n->nic, index);
+
+ qemu_purge_queued_packets(nc);
+
+ virtio_del_queue(vdev, index * 2);
+ if (q->tx_timer) {
+ timer_del(q->tx_timer);
+ timer_free(q->tx_timer);
+ } else {
+ qemu_bh_delete(q->tx_bh);
}
+ virtio_del_queue(vdev, index * 2 + 1);
+}
- for (i = 1; i < max; i++) {
- n->vqs[i].rx_vq = virtio_add_queue(vdev, 256, virtio_net_handle_rx);
- if (n->vqs[i].tx_timer) {
- n->vqs[i].tx_vq =
- virtio_add_queue(vdev, 256, virtio_net_handle_tx_timer);
- n->vqs[i].tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
- virtio_net_tx_timer,
- &n->vqs[i]);
- } else {
- n->vqs[i].tx_vq =
- virtio_add_queue(vdev, 256, virtio_net_handle_tx_bh);
- n->vqs[i].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[i]);
- }
+static void virtio_net_change_num_queues(VirtIONet *n, int new_max_queues)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(n);
+ int old_num_queues = virtio_get_num_queues(vdev);
+ int new_num_queues = new_max_queues * 2 + 1;
+ int i;
+
+ assert(old_num_queues >= 3);
+ assert(old_num_queues % 2 == 1);
- n->vqs[i].tx_waiting = 0;
- n->vqs[i].n = n;
+ if (old_num_queues == new_num_queues) {
+ return;
}
- /* Note: Minux Guests (version 3.2.1) use ctrl vq but don't ack
- * VIRTIO_NET_F_CTRL_VQ. Create ctrl vq unconditionally to avoid
- * breaking them.
+ /*
+ * We always need to remove and add ctrl vq if
+ * old_num_queues != new_num_queues. Remove ctrl_vq first,
+ * and then we only enter one of the following too loops.
*/
+ virtio_del_queue(vdev, old_num_queues - 1);
+
+ for (i = new_num_queues - 1; i < old_num_queues - 1; i += 2) {
+ /* new_num_queues < old_num_queues */
+ virtio_net_del_queue(n, i / 2);
+ }
+
+ for (i = old_num_queues - 1; i < new_num_queues - 1; i += 2) {
+ /* new_num_queues > old_num_queues */
+ virtio_net_add_queue(n, i / 2);
+ }
+
+ /* add ctrl_vq last */
n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl);
+}
+
+static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue)
+{
+ int max = multiqueue ? n->max_queues : 1;
+
+ n->multiqueue = multiqueue;
+ virtio_net_change_num_queues(n, max);
virtio_net_set_queues(n);
}
@@ -1394,7 +1447,7 @@ static void virtio_net_save_device(VirtIODevice *vdev, QEMUFile *f)
}
}
- if (virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
+ if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
qemu_put_be64(f, n->curr_guest_offloads);
}
}
@@ -1403,11 +1456,33 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
{
VirtIONet *n = opaque;
VirtIODevice *vdev = VIRTIO_DEVICE(n);
+ int ret;
if (version_id < 2 || version_id > VIRTIO_NET_VM_VERSION)
return -EINVAL;
- return virtio_load(vdev, f, version_id);
+ ret = virtio_load(vdev, f, version_id);
+ if (ret) {
+ return ret;
+ }
+
+ if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
+ n->curr_guest_offloads = qemu_get_be64(f);
+ } else {
+ n->curr_guest_offloads = virtio_net_supported_guest_offloads(n);
+ }
+
+ if (peer_has_vnet_hdr(n)) {
+ virtio_net_apply_guest_offloads(n);
+ }
+
+ if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) &&
+ virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) {
+ n->announce_counter = SELF_ANNOUNCE_ROUNDS;
+ timer_mod(n->announce_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL));
+ }
+
+ return 0;
}
static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f,
@@ -1419,7 +1494,9 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f,
qemu_get_buffer(f, n->mac, ETH_ALEN);
n->vqs[0].tx_waiting = qemu_get_be32(f);
- virtio_net_set_mrg_rx_bufs(n, qemu_get_be32(f));
+ virtio_net_set_mrg_rx_bufs(n, qemu_get_be32(f),
+ virtio_vdev_has_feature(vdev,
+ VIRTIO_F_VERSION_1));
if (version_id >= 3)
n->status = qemu_get_be16(f);
@@ -1502,16 +1579,6 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f,
}
}
- if (virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) {
- n->curr_guest_offloads = qemu_get_be64(f);
- } else {
- n->curr_guest_offloads = virtio_net_supported_guest_offloads(n);
- }
-
- if (peer_has_vnet_hdr(n)) {
- virtio_net_apply_guest_offloads(n);
- }
-
virtio_net_set_queues(n);
/* Find the first multicast entry in the saved MAC filter */
@@ -1529,12 +1596,6 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f,
qemu_get_subqueue(n->nic, i)->link_down = link_down;
}
- if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) &&
- virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) {
- n->announce_counter = SELF_ANNOUNCE_ROUNDS;
- timer_mod(n->announce_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL));
- }
-
return 0;
}
@@ -1565,7 +1626,7 @@ static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx,
vdev, idx, mask);
}
-void virtio_net_set_config_size(VirtIONet *n, uint32_t host_features)
+static void virtio_net_set_config_size(VirtIONet *n, uint64_t host_features)
{
int i, config_size = 0;
virtio_add_feature(&host_features, VIRTIO_NET_F_MAC);
@@ -1598,20 +1659,19 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
NetClientState *nc;
int i;
+ virtio_net_set_config_size(n, n->host_features);
virtio_init(vdev, "virtio-net", VIRTIO_ID_NET, n->config_size);
n->max_queues = MAX(n->nic_conf.peers.queues, 1);
- if (n->max_queues * 2 + 1 > VIRTIO_PCI_QUEUE_MAX) {
+ if (n->max_queues * 2 + 1 > VIRTIO_QUEUE_MAX) {
error_setg(errp, "Invalid number of queues (= %" PRIu32 "), "
- "must be a postive integer less than %d.",
- n->max_queues, (VIRTIO_PCI_QUEUE_MAX - 1) / 2);
+ "must be a positive integer less than %d.",
+ n->max_queues, (VIRTIO_QUEUE_MAX - 1) / 2);
virtio_cleanup(vdev);
return;
}
n->vqs = g_malloc0(sizeof(VirtIONetQueue) * n->max_queues);
- n->vqs[0].rx_vq = virtio_add_queue(vdev, 256, virtio_net_handle_rx);
n->curr_queues = 1;
- n->vqs[0].n = n;
n->tx_timeout = n->net_conf.txtimer;
if (n->net_conf.tx && strcmp(n->net_conf.tx, "timer")
@@ -1622,16 +1682,10 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
error_report("Defaulting to \"bh\"");
}
- if (n->net_conf.tx && !strcmp(n->net_conf.tx, "timer")) {
- n->vqs[0].tx_vq = virtio_add_queue(vdev, 256,
- virtio_net_handle_tx_timer);
- n->vqs[0].tx_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, virtio_net_tx_timer,
- &n->vqs[0]);
- } else {
- n->vqs[0].tx_vq = virtio_add_queue(vdev, 256,
- virtio_net_handle_tx_bh);
- n->vqs[0].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[0]);
+ for (i = 0; i < n->max_queues; i++) {
+ virtio_net_add_queue(n, i);
}
+
n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl);
qemu_macaddr_default_if_unset(&n->nic_conf.macaddr);
memcpy(&n->mac[0], &n->nic_conf.macaddr, sizeof(n->mac));
@@ -1664,7 +1718,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
n->vqs[0].tx_waiting = 0;
n->tx_burst = n->net_conf.txburst;
- virtio_net_set_mrg_rx_bufs(n, 0);
+ virtio_net_set_mrg_rx_bufs(n, 0, 0);
n->promisc = 1; /* for compatibility */
n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN);
@@ -1683,7 +1737,7 @@ static void virtio_net_device_unrealize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VirtIONet *n = VIRTIO_NET(dev);
- int i;
+ int i, max_queues;
/* This will stop vhost backend if appropriate. */
virtio_net_set_status(vdev, 0);
@@ -1698,18 +1752,9 @@ static void virtio_net_device_unrealize(DeviceState *dev, Error **errp)
g_free(n->mac_table.macs);
g_free(n->vlans);
- for (i = 0; i < n->max_queues; i++) {
- VirtIONetQueue *q = &n->vqs[i];
- NetClientState *nc = qemu_get_subqueue(n->nic, i);
-
- qemu_purge_queued_packets(nc);
-
- if (q->tx_timer) {
- timer_del(q->tx_timer);
- timer_free(q->tx_timer);
- } else if (q->tx_bh) {
- qemu_bh_delete(q->tx_bh);
- }
+ max_queues = n->multiqueue ? n->max_queues : 1;
+ for (i = 0; i < max_queues; i++) {
+ virtio_net_del_queue(n, i);
}
timer_del(n->announce_timer);
@@ -1734,9 +1779,48 @@ static void virtio_net_instance_init(Object *obj)
}
static Property virtio_net_properties[] = {
+ DEFINE_PROP_BIT("csum", VirtIONet, host_features, VIRTIO_NET_F_CSUM, true),
+ DEFINE_PROP_BIT("guest_csum", VirtIONet, host_features,
+ VIRTIO_NET_F_GUEST_CSUM, true),
+ DEFINE_PROP_BIT("gso", VirtIONet, host_features, VIRTIO_NET_F_GSO, true),
+ DEFINE_PROP_BIT("guest_tso4", VirtIONet, host_features,
+ VIRTIO_NET_F_GUEST_TSO4, true),
+ DEFINE_PROP_BIT("guest_tso6", VirtIONet, host_features,
+ VIRTIO_NET_F_GUEST_TSO6, true),
+ DEFINE_PROP_BIT("guest_ecn", VirtIONet, host_features,
+ VIRTIO_NET_F_GUEST_ECN, true),
+ DEFINE_PROP_BIT("guest_ufo", VirtIONet, host_features,
+ VIRTIO_NET_F_GUEST_UFO, true),
+ DEFINE_PROP_BIT("guest_announce", VirtIONet, host_features,
+ VIRTIO_NET_F_GUEST_ANNOUNCE, true),
+ DEFINE_PROP_BIT("host_tso4", VirtIONet, host_features,
+ VIRTIO_NET_F_HOST_TSO4, true),
+ DEFINE_PROP_BIT("host_tso6", VirtIONet, host_features,
+ VIRTIO_NET_F_HOST_TSO6, true),
+ DEFINE_PROP_BIT("host_ecn", VirtIONet, host_features,
+ VIRTIO_NET_F_HOST_ECN, true),
+ DEFINE_PROP_BIT("host_ufo", VirtIONet, host_features,
+ VIRTIO_NET_F_HOST_UFO, true),
+ DEFINE_PROP_BIT("mrg_rxbuf", VirtIONet, host_features,
+ VIRTIO_NET_F_MRG_RXBUF, true),
+ DEFINE_PROP_BIT("status", VirtIONet, host_features,
+ VIRTIO_NET_F_STATUS, true),
+ DEFINE_PROP_BIT("ctrl_vq", VirtIONet, host_features,
+ VIRTIO_NET_F_CTRL_VQ, true),
+ DEFINE_PROP_BIT("ctrl_rx", VirtIONet, host_features,
+ VIRTIO_NET_F_CTRL_RX, true),
+ DEFINE_PROP_BIT("ctrl_vlan", VirtIONet, host_features,
+ VIRTIO_NET_F_CTRL_VLAN, true),
+ DEFINE_PROP_BIT("ctrl_rx_extra", VirtIONet, host_features,
+ VIRTIO_NET_F_CTRL_RX_EXTRA, true),
+ DEFINE_PROP_BIT("ctrl_mac_addr", VirtIONet, host_features,
+ VIRTIO_NET_F_CTRL_MAC_ADDR, true),
+ DEFINE_PROP_BIT("ctrl_guest_offloads", VirtIONet, host_features,
+ VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, true),
+ DEFINE_PROP_BIT("mq", VirtIONet, host_features, VIRTIO_NET_F_MQ, false),
DEFINE_NIC_PROPERTIES(VirtIONet, nic_conf),
DEFINE_PROP_UINT32("x-txtimer", VirtIONet, net_conf.txtimer,
- TX_TIMER_INTERVAL),
+ TX_TIMER_INTERVAL),
DEFINE_PROP_INT32("x-txburst", VirtIONet, net_conf.txburst, TX_BURST),
DEFINE_PROP_STRING("tx", VirtIONet, net_conf.tx),
DEFINE_PROP_END_OF_LIST(),
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
index dfb328deb..2504425cf 100644
--- a/hw/net/vmxnet3.c
+++ b/hw/net/vmxnet3.c
@@ -885,6 +885,63 @@ vmxnet3_get_next_rx_descr(VMXNET3State *s, bool is_head,
}
}
+/* In case packet was csum offloaded (either NEEDS_CSUM or DATA_VALID),
+ * the implementation always passes an RxCompDesc with a "Checksum
+ * calculated and found correct" to the OS (cnc=0 and tuc=1, see
+ * vmxnet3_rx_update_descr). This emulates the observed ESXi behavior.
+ *
+ * Therefore, if packet has the NEEDS_CSUM set, we must calculate
+ * and place a fully computed checksum into the tcp/udp header.
+ * Otherwise, the OS driver will receive a checksum-correct indication
+ * (CHECKSUM_UNNECESSARY), but with the actual tcp/udp checksum field
+ * having just the pseudo header csum value.
+ *
+ * While this is not a problem if packet is destined for local delivery,
+ * in the case the host OS performs forwarding, it will forward an
+ * incorrectly checksummed packet.
+ */
+static void vmxnet3_rx_need_csum_calculate(struct VmxnetRxPkt *pkt,
+ const void *pkt_data,
+ size_t pkt_len)
+{
+ struct virtio_net_hdr *vhdr;
+ bool isip4, isip6, istcp, isudp;
+ uint8_t *data;
+ int len;
+
+ if (!vmxnet_rx_pkt_has_virt_hdr(pkt)) {
+ return;
+ }
+
+ vhdr = vmxnet_rx_pkt_get_vhdr(pkt);
+ if (!VMXNET_FLAG_IS_SET(vhdr->flags, VIRTIO_NET_HDR_F_NEEDS_CSUM)) {
+ return;
+ }
+
+ vmxnet_rx_pkt_get_protocols(pkt, &isip4, &isip6, &isudp, &istcp);
+ if (!(isip4 || isip6) || !(istcp || isudp)) {
+ return;
+ }
+
+ vmxnet3_dump_virt_hdr(vhdr);
+
+ /* Validate packet len: csum_start + scum_offset + length of csum field */
+ if (pkt_len < (vhdr->csum_start + vhdr->csum_offset + 2)) {
+ VMW_PKPRN("packet len:%d < csum_start(%d) + csum_offset(%d) + 2, "
+ "cannot calculate checksum",
+ len, vhdr->csum_start, vhdr->csum_offset);
+ return;
+ }
+
+ data = (uint8_t *)pkt_data + vhdr->csum_start;
+ len = pkt_len - vhdr->csum_start;
+ /* Put the checksum obtained into the packet */
+ stw_be_p(data + vhdr->csum_offset, net_raw_checksum(data, len));
+
+ vhdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM;
+ vhdr->flags |= VIRTIO_NET_HDR_F_DATA_VALID;
+}
+
static void vmxnet3_rx_update_descr(struct VmxnetRxPkt *pkt,
struct Vmxnet3_RxCompDesc *rxcd)
{
@@ -1879,6 +1936,12 @@ vmxnet3_receive(NetClientState *nc, const uint8_t *buf, size_t size)
return -1;
}
+ if (s->peer_has_vhdr) {
+ vmxnet_rx_pkt_set_vhdr(s->rx_pkt, (struct virtio_net_hdr *)buf);
+ buf += sizeof(struct virtio_net_hdr);
+ size -= sizeof(struct virtio_net_hdr);
+ }
+
/* Pad to minimum Ethernet frame length */
if (size < sizeof(min_buf)) {
memcpy(min_buf, buf, size);
@@ -1887,16 +1950,12 @@ vmxnet3_receive(NetClientState *nc, const uint8_t *buf, size_t size)
size = sizeof(min_buf);
}
- if (s->peer_has_vhdr) {
- vmxnet_rx_pkt_set_vhdr(s->rx_pkt, (struct virtio_net_hdr *)buf);
- buf += sizeof(struct virtio_net_hdr);
- size -= sizeof(struct virtio_net_hdr);
- }
-
vmxnet_rx_pkt_set_packet_type(s->rx_pkt,
get_eth_packet_type(PKT_GET_ETH_HDR(buf)));
if (vmxnet3_rx_filter_may_indicate(s, buf, size)) {
+ vmxnet_rx_pkt_set_protocols(s->rx_pkt, buf, size);
+ vmxnet3_rx_need_csum_calculate(s->rx_pkt, buf, size);
vmxnet_rx_pkt_attach_data(s->rx_pkt, buf, size, s->rx_vlan_stripping);
bytes_indicated = vmxnet3_indicate_packet(s) ? size : -1;
if (bytes_indicated < size) {
@@ -1929,7 +1988,6 @@ static void vmxnet3_set_link_status(NetClientState *nc)
static NetClientInfo net_vmxnet3_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = vmxnet3_can_receive,
.receive = vmxnet3_receive,
.link_status_changed = vmxnet3_set_link_status,
};
@@ -2226,6 +2284,7 @@ static const VMStateDescription vmxstate_vmxnet3_mcast_list = {
.version_id = 1,
.minimum_version_id = 1,
.pre_load = vmxnet3_mcast_list_pre_load,
+ .needed = vmxnet3_mc_list_needed,
.fields = (VMStateField[]) {
VMSTATE_VBUFFER_UINT32(mcast_list, VMXNET3State, 0, NULL, 0,
mcast_list_buff_size),
@@ -2470,25 +2529,12 @@ static const VMStateDescription vmstate_vmxnet3 = {
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmxstate_vmxnet3_mcast_list,
- .needed = vmxnet3_mc_list_needed
- },
- {
- /* empty element. */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmxstate_vmxnet3_mcast_list,
+ NULL
}
};
-static void
-vmxnet3_write_config(PCIDevice *pci_dev, uint32_t addr, uint32_t val, int len)
-{
- pci_default_write_config(pci_dev, addr, val, len);
- msix_write_config(pci_dev, addr, val, len);
- msi_write_config(pci_dev, addr, val, len);
-}
-
static Property vmxnet3_properties[] = {
DEFINE_NIC_PROPERTIES(VMXNET3State, conf),
DEFINE_PROP_END_OF_LIST(),
@@ -2507,7 +2553,6 @@ static void vmxnet3_class_init(ObjectClass *class, void *data)
c->class_id = PCI_CLASS_NETWORK_ETHERNET;
c->subsystem_vendor_id = PCI_VENDOR_ID_VMWARE;
c->subsystem_id = PCI_DEVICE_ID_VMWARE_VMXNET3;
- c->config_write = vmxnet3_write_config,
dc->desc = "VMWare Paravirtualized Ethernet v3";
dc->reset = vmxnet3_qdev_reset;
dc->vmsd = &vmstate_vmxnet3;
diff --git a/hw/net/vmxnet_rx_pkt.c b/hw/net/vmxnet_rx_pkt.c
index a40e34629..aa5462931 100644
--- a/hw/net/vmxnet_rx_pkt.c
+++ b/hw/net/vmxnet_rx_pkt.c
@@ -92,9 +92,6 @@ void vmxnet_rx_pkt_attach_data(struct VmxnetRxPkt *pkt, const void *data,
}
pkt->tci = tci;
-
- eth_get_protocols(data, len, &pkt->isip4, &pkt->isip6,
- &pkt->isudp, &pkt->istcp);
}
void vmxnet_rx_pkt_dump(struct VmxnetRxPkt *pkt)
@@ -131,6 +128,15 @@ size_t vmxnet_rx_pkt_get_total_len(struct VmxnetRxPkt *pkt)
return pkt->tot_len;
}
+void vmxnet_rx_pkt_set_protocols(struct VmxnetRxPkt *pkt, const void *data,
+ size_t len)
+{
+ assert(pkt);
+
+ eth_get_protocols(data, len, &pkt->isip4, &pkt->isip6,
+ &pkt->isudp, &pkt->istcp);
+}
+
void vmxnet_rx_pkt_get_protocols(struct VmxnetRxPkt *pkt,
bool *isip4, bool *isip6,
bool *isudp, bool *istcp)
@@ -172,13 +178,6 @@ bool vmxnet_rx_pkt_has_virt_hdr(struct VmxnetRxPkt *pkt)
return pkt->has_virt_hdr;
}
-uint16_t vmxnet_rx_pkt_get_num_frags(struct VmxnetRxPkt *pkt)
-{
- assert(pkt);
-
- return pkt->vec_len;
-}
-
uint16_t vmxnet_rx_pkt_get_vlan_tag(struct VmxnetRxPkt *pkt)
{
assert(pkt);
diff --git a/hw/net/vmxnet_rx_pkt.h b/hw/net/vmxnet_rx_pkt.h
index 6b2c60ef1..a425846b5 100644
--- a/hw/net/vmxnet_rx_pkt.h
+++ b/hw/net/vmxnet_rx_pkt.h
@@ -55,6 +55,17 @@ void vmxnet_rx_pkt_init(struct VmxnetRxPkt **pkt, bool has_virt_hdr);
size_t vmxnet_rx_pkt_get_total_len(struct VmxnetRxPkt *pkt);
/**
+ * parse and set packet analysis results
+ *
+ * @pkt: packet
+ * @data: pointer to the data buffer to be parsed
+ * @len: data length
+ *
+ */
+void vmxnet_rx_pkt_set_protocols(struct VmxnetRxPkt *pkt, const void *data,
+ size_t len);
+
+/**
* fetches packet analysis results
*
* @pkt: packet
@@ -114,15 +125,6 @@ bool vmxnet_rx_pkt_is_vlan_stripped(struct VmxnetRxPkt *pkt);
bool vmxnet_rx_pkt_has_virt_hdr(struct VmxnetRxPkt *pkt);
/**
- * returns number of frags attached to the packet
- *
- * @pkt: packet
- * @ret: number of frags
- *
- */
-uint16_t vmxnet_rx_pkt_get_num_frags(struct VmxnetRxPkt *pkt);
-
-/**
* attach data to rx packet
*
* @pkt: packet
diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c
index 19ecfc4cc..d7cbfc103 100644
--- a/hw/net/xen_nic.c
+++ b/hw/net/xen_nic.c
@@ -234,27 +234,6 @@ static void net_rx_response(struct XenNetDev *netdev,
#define NET_IP_ALIGN 2
-static int net_rx_ok(NetClientState *nc)
-{
- struct XenNetDev *netdev = qemu_get_nic_opaque(nc);
- RING_IDX rc, rp;
-
- if (netdev->xendev.be_state != XenbusStateConnected) {
- return 0;
- }
-
- rc = netdev->rx_ring.req_cons;
- rp = netdev->rx_ring.sring->req_prod;
- xen_rmb();
-
- if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) {
- xen_be_printf(&netdev->xendev, 2, "%s: no rx buffers (%d/%d)\n",
- __FUNCTION__, rc, rp);
- return 0;
- }
- return 1;
-}
-
static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size)
{
struct XenNetDev *netdev = qemu_get_nic_opaque(nc);
@@ -271,8 +250,7 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size
xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) {
- xen_be_printf(&netdev->xendev, 2, "no buffer, drop packet\n");
- return -1;
+ return 0;
}
if (size > XC_PAGE_SIZE - NET_IP_ALIGN) {
xen_be_printf(&netdev->xendev, 0, "packet too big (%lu > %ld)",
@@ -304,7 +282,6 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size
static NetClientInfo net_xen_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = net_rx_ok,
.receive = net_rx_packet,
};
diff --git a/hw/net/xgmac.c b/hw/net/xgmac.c
index b068f3a0d..15fb68194 100644
--- a/hw/net/xgmac.c
+++ b/hw/net/xgmac.c
@@ -312,10 +312,8 @@ static const MemoryRegionOps enet_mem_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
-static int eth_can_rx(NetClientState *nc)
+static int eth_can_rx(XgmacState *s)
{
- XgmacState *s = qemu_get_nic_opaque(nc);
-
/* RX enabled? */
return s->regs[DMA_CONTROL] & DMA_CONTROL_SR;
}
@@ -329,6 +327,9 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
struct desc bd;
ssize_t ret;
+ if (!eth_can_rx(s)) {
+ return -1;
+ }
unicast = ~buf[0] & 0x1;
broadcast = memcmp(buf, sa_bcast, 6) == 0;
multicast = !unicast && !broadcast;
@@ -371,7 +372,6 @@ out:
static NetClientInfo net_xgmac_enet_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = eth_can_rx,
.receive = eth_rx,
};
diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c
index 21efedfc3..d63c42324 100644
--- a/hw/net/xilinx_axienet.c
+++ b/hw/net/xilinx_axienet.c
@@ -26,7 +26,6 @@
#include "qemu/log.h"
#include "net/net.h"
#include "net/checksum.h"
-#include "qapi/qmp/qerror.h"
#include "hw/stream.h"
@@ -402,6 +401,9 @@ struct XilinxAXIEnet {
uint8_t rxapp[CONTROL_PAYLOAD_SIZE];
uint32_t rxappsize;
+
+ /* Whether axienet_eth_rx_notify should flush incoming queue. */
+ bool need_flush;
};
static void axienet_rx_reset(XilinxAXIEnet *s)
@@ -659,10 +661,8 @@ static const MemoryRegionOps enet_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
-static int eth_can_rx(NetClientState *nc)
+static int eth_can_rx(XilinxAXIEnet *s)
{
- XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
-
/* RX enabled? */
return !s->rxsize && !axienet_rx_resetting(s) && axienet_rx_enabled(s);
}
@@ -702,6 +702,10 @@ static void axienet_eth_rx_notify(void *opaque)
s->rxpos += ret;
if (!s->rxsize) {
s->regs[R_IS] |= IS_RX_COMPLETE;
+ if (s->need_flush) {
+ s->need_flush = false;
+ qemu_flush_queued_packets(qemu_get_queue(s->nic));
+ }
}
}
enet_update_irq(s);
@@ -722,6 +726,11 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
DENET(qemu_log("%s: %zd bytes\n", __func__, size));
+ if (!eth_can_rx(s)) {
+ s->need_flush = true;
+ return 0;
+ }
+
unicast = ~buf[0] & 0x1;
broadcast = memcmp(buf, sa_bcast, 6) == 0;
multicast = !unicast && !broadcast;
@@ -926,7 +935,6 @@ xilinx_axienet_data_stream_push(StreamSlave *obj, uint8_t *buf, size_t size)
static NetClientInfo net_xilinx_enet_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = eth_can_rx,
.receive = eth_rx,
};
diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c
index 68eff7798..88481b78c 100644
--- a/hw/nvram/fw_cfg.c
+++ b/hw/nvram/fw_cfg.c
@@ -46,7 +46,6 @@ typedef struct FWCfgEntry {
uint32_t len;
uint8_t *data;
void *callback_opaque;
- FWCfgCallback callback;
FWCfgReadCallback read_callback;
} FWCfgEntry;
@@ -232,19 +231,7 @@ static void fw_cfg_reboot(FWCfgState *s)
static void fw_cfg_write(FWCfgState *s, uint8_t value)
{
- int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
- FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
-
- trace_fw_cfg_write(s, value);
-
- if (s->cur_entry & FW_CFG_WRITE_CHANNEL && e->callback &&
- s->cur_offset < e->len) {
- e->data[s->cur_offset++] = value;
- if (s->cur_offset == e->len) {
- e->callback(e->callback_opaque, e->data);
- s->cur_offset = 0;
- }
- }
+ /* nothing, write support removed in QEMU v2.4+ */
}
static int fw_cfg_select(FWCfgState *s, uint16_t key)
@@ -436,6 +423,7 @@ static void fw_cfg_add_bytes_read_callback(FWCfgState *s, uint16_t key,
key &= FW_CFG_ENTRY_MASK;
assert(key < FW_CFG_MAX_ENTRY && len < UINT32_MAX);
+ assert(s->entries[arch][key].data == NULL); /* avoid key conflict */
s->entries[arch][key].data = data;
s->entries[arch][key].len = (uint32_t)len;
@@ -458,7 +446,6 @@ static void *fw_cfg_modify_bytes_read(FWCfgState *s, uint16_t key,
s->entries[arch][key].data = data;
s->entries[arch][key].len = len;
s->entries[arch][key].callback_opaque = NULL;
- s->entries[arch][key].callback = NULL;
return ptr;
}
@@ -484,6 +471,16 @@ void fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value)
fw_cfg_add_bytes(s, key, copy, sizeof(value));
}
+void fw_cfg_modify_i16(FWCfgState *s, uint16_t key, uint16_t value)
+{
+ uint16_t *copy, *old;
+
+ copy = g_malloc(sizeof(value));
+ *copy = cpu_to_le16(value);
+ old = fw_cfg_modify_bytes_read(s, key, copy, sizeof(value));
+ g_free(old);
+}
+
void fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value)
{
uint32_t *copy;
@@ -502,23 +499,6 @@ void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value)
fw_cfg_add_bytes(s, key, copy, sizeof(value));
}
-void fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback,
- void *callback_opaque, void *data, size_t len)
-{
- int arch = !!(key & FW_CFG_ARCH_LOCAL);
-
- assert(key & FW_CFG_WRITE_CHANNEL);
-
- key &= FW_CFG_ENTRY_MASK;
-
- assert(key < FW_CFG_MAX_ENTRY && len <= UINT32_MAX);
-
- s->entries[arch][key].data = data;
- s->entries[arch][key].len = (uint32_t)len;
- s->entries[arch][key].callback_opaque = callback_opaque;
- s->entries[arch][key].callback = callback;
-}
-
void fw_cfg_add_file_callback(FWCfgState *s, const char *filename,
FWCfgReadCallback callback, void *callback_opaque,
void *data, size_t len)
@@ -535,18 +515,19 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename,
index = be32_to_cpu(s->files->count);
assert(index < FW_CFG_FILE_SLOTS);
- fw_cfg_add_bytes_read_callback(s, FW_CFG_FILE_FIRST + index,
- callback, callback_opaque, data, len);
-
pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name),
filename);
for (i = 0; i < index; i++) {
if (strcmp(s->files->f[index].name, s->files->f[i].name) == 0) {
- trace_fw_cfg_add_file_dupe(s, s->files->f[index].name);
- return;
+ error_report("duplicate fw_cfg file name: %s",
+ s->files->f[index].name);
+ exit(1);
}
}
+ fw_cfg_add_bytes_read_callback(s, FW_CFG_FILE_FIRST + index,
+ callback, callback_opaque, data, len);
+
s->files->f[index].size = cpu_to_be32(len);
s->files->f[index].select = cpu_to_be16(FW_CFG_FILE_FIRST + index);
trace_fw_cfg_add_file(s, index, s->files->f[index].name, len);
diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c
index 11332d14e..fcaa77dd9 100644
--- a/hw/nvram/spapr_nvram.c
+++ b/hw/nvram/spapr_nvram.c
@@ -45,7 +45,7 @@ typedef struct sPAPRNVRAM {
#define DEFAULT_NVRAM_SIZE 65536
#define MAX_NVRAM_SIZE 1048576
-static void rtas_nvram_fetch(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static void rtas_nvram_fetch(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
@@ -86,7 +86,7 @@ static void rtas_nvram_fetch(PowerPCCPU *cpu, sPAPREnvironment *spapr,
rtas_st(rets, 1, len);
}
-static void rtas_nvram_store(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static void rtas_nvram_store(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
diff --git a/hw/pci-bridge/Makefile.objs b/hw/pci-bridge/Makefile.objs
index 96c596eb3..f2adfe348 100644
--- a/hw/pci-bridge/Makefile.objs
+++ b/hw/pci-bridge/Makefile.objs
@@ -1,4 +1,5 @@
common-obj-y += pci_bridge_dev.o
+common-obj-y += pci_expander_bridge.o
common-obj-$(CONFIG_XIO3130) += xio3130_upstream.o xio3130_downstream.o
common-obj-$(CONFIG_IOH3420) += ioh3420.o
common-obj-$(CONFIG_I82801B11) += i82801b11.o
diff --git a/hw/pci-bridge/i82801b11.c b/hw/pci-bridge/i82801b11.c
index 14cd7fd40..7e79bc01e 100644
--- a/hw/pci-bridge/i82801b11.c
+++ b/hw/pci-bridge/i82801b11.c
@@ -101,27 +101,6 @@ static const TypeInfo i82801b11_bridge_info = {
.class_init = i82801b11_bridge_class_init,
};
-PCIBus *ich9_d2pbr_init(PCIBus *bus, int devfn, int sec_bus)
-{
- PCIDevice *d;
- PCIBridge *br;
- char buf[16];
- DeviceState *qdev;
-
- d = pci_create_multifunction(bus, devfn, true, "i82801b11-bridge");
- if (!d) {
- return NULL;
- }
- br = PCI_BRIDGE(d);
- qdev = DEVICE(d);
-
- snprintf(buf, sizeof(buf), "pci.%d", sec_bus);
- pci_bridge_map_irq(br, buf, pci_swizzle_map_irq_fn);
- qdev_init_nofail(qdev);
-
- return pci_bridge_get_sec_bus(br);
-}
-
static void d2pbr_register(void)
{
type_register_static(&i82801b11_bridge_info);
diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c
index 36f73e1f8..26aded9f0 100644
--- a/hw/pci-bridge/pci_bridge_dev.c
+++ b/hw/pci-bridge/pci_bridge_dev.c
@@ -28,7 +28,8 @@
#include "hw/pci/pci_bus.h"
#include "hw/hotplug.h"
-#define TYPE_PCI_BRIDGE_DEV "pci-bridge"
+#define TYPE_PCI_BRIDGE_DEV "pci-bridge"
+#define TYPE_PCI_BRIDGE_SEAT_DEV "pci-bridge-seat"
#define PCI_BRIDGE_DEV(obj) \
OBJECT_CHECK(PCIBridgeDev, (obj), TYPE_PCI_BRIDGE_DEV)
@@ -40,6 +41,7 @@ struct PCIBridgeDev {
MemoryRegion bar;
uint8_t chassis_nr;
#define PCI_BRIDGE_DEV_F_MSI_REQ 0
+#define PCI_BRIDGE_DEV_F_SHPC_REQ 1
uint32_t flags;
};
typedef struct PCIBridgeDev PCIBridgeDev;
@@ -54,11 +56,17 @@ static int pci_bridge_dev_initfn(PCIDevice *dev)
if (err) {
goto bridge_error;
}
- dev->config[PCI_INTERRUPT_PIN] = 0x1;
- memory_region_init(&bridge_dev->bar, OBJECT(dev), "shpc-bar", shpc_bar_size(dev));
- err = shpc_init(dev, &br->sec_bus, &bridge_dev->bar, 0);
- if (err) {
- goto shpc_error;
+ if (bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_SHPC_REQ)) {
+ dev->config[PCI_INTERRUPT_PIN] = 0x1;
+ memory_region_init(&bridge_dev->bar, OBJECT(dev), "shpc-bar",
+ shpc_bar_size(dev));
+ err = shpc_init(dev, &br->sec_bus, &bridge_dev->bar, 0);
+ if (err) {
+ goto shpc_error;
+ }
+ } else {
+ /* MSI is not applicable without SHPC */
+ bridge_dev->flags &= ~(1 << PCI_BRIDGE_DEV_F_MSI_REQ);
}
err = slotid_cap_init(dev, 0, bridge_dev->chassis_nr, 0);
if (err) {
@@ -71,15 +79,19 @@ static int pci_bridge_dev_initfn(PCIDevice *dev)
goto msi_error;
}
}
- /* TODO: spec recommends using 64 bit prefetcheable BAR.
- * Check whether that works well. */
- pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY |
- PCI_BASE_ADDRESS_MEM_TYPE_64, &bridge_dev->bar);
+ if (shpc_present(dev)) {
+ /* TODO: spec recommends using 64 bit prefetcheable BAR.
+ * Check whether that works well. */
+ pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY |
+ PCI_BASE_ADDRESS_MEM_TYPE_64, &bridge_dev->bar);
+ }
return 0;
msi_error:
slotid_cap_cleanup(dev);
slotid_error:
- shpc_cleanup(dev, &bridge_dev->bar);
+ if (shpc_present(dev)) {
+ shpc_cleanup(dev, &bridge_dev->bar);
+ }
shpc_error:
pci_bridge_exitfn(dev);
bridge_error:
@@ -93,12 +105,15 @@ static void pci_bridge_dev_exitfn(PCIDevice *dev)
msi_uninit(dev);
}
slotid_cap_cleanup(dev);
- shpc_cleanup(dev, &bridge_dev->bar);
+ if (shpc_present(dev)) {
+ shpc_cleanup(dev, &bridge_dev->bar);
+ }
pci_bridge_exitfn(dev);
}
static void pci_bridge_dev_instance_finalize(Object *obj)
{
+ /* this function is idempotent and handles (PCIDevice.shpc == NULL) */
shpc_free(PCI_DEVICE(obj));
}
@@ -109,7 +124,9 @@ static void pci_bridge_dev_write_config(PCIDevice *d,
if (msi_present(d)) {
msi_write_config(d, address, val, len);
}
- shpc_cap_write_config(d, address, val, len);
+ if (shpc_present(d)) {
+ shpc_cap_write_config(d, address, val, len);
+ }
}
static void qdev_pci_bridge_dev_reset(DeviceState *qdev)
@@ -117,25 +134,65 @@ static void qdev_pci_bridge_dev_reset(DeviceState *qdev)
PCIDevice *dev = PCI_DEVICE(qdev);
pci_bridge_reset(qdev);
- shpc_reset(dev);
+ if (shpc_present(dev)) {
+ shpc_reset(dev);
+ }
}
static Property pci_bridge_dev_properties[] = {
/* Note: 0 is not a legal chassis number. */
- DEFINE_PROP_UINT8("chassis_nr", PCIBridgeDev, chassis_nr, 0),
- DEFINE_PROP_BIT("msi", PCIBridgeDev, flags, PCI_BRIDGE_DEV_F_MSI_REQ, true),
+ DEFINE_PROP_UINT8(PCI_BRIDGE_DEV_PROP_CHASSIS_NR, PCIBridgeDev, chassis_nr,
+ 0),
+ DEFINE_PROP_BIT(PCI_BRIDGE_DEV_PROP_MSI, PCIBridgeDev, flags,
+ PCI_BRIDGE_DEV_F_MSI_REQ, true),
+ DEFINE_PROP_BIT(PCI_BRIDGE_DEV_PROP_SHPC, PCIBridgeDev, flags,
+ PCI_BRIDGE_DEV_F_SHPC_REQ, true),
DEFINE_PROP_END_OF_LIST(),
};
+static bool pci_device_shpc_present(void *opaque, int version_id)
+{
+ PCIDevice *dev = opaque;
+
+ return shpc_present(dev);
+}
+
static const VMStateDescription pci_bridge_dev_vmstate = {
.name = "pci_bridge",
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(parent_obj, PCIBridge),
- SHPC_VMSTATE(shpc, PCIDevice),
+ SHPC_VMSTATE(shpc, PCIDevice, pci_device_shpc_present),
VMSTATE_END_OF_LIST()
}
};
+static void pci_bridge_dev_hotplug_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev);
+
+ if (!shpc_present(pci_hotplug_dev)) {
+ error_setg(errp, "standard hotplug controller has been disabled for "
+ "this %s", TYPE_PCI_BRIDGE_DEV);
+ return;
+ }
+ shpc_device_hotplug_cb(hotplug_dev, dev, errp);
+}
+
+static void pci_bridge_dev_hot_unplug_request_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev,
+ Error **errp)
+{
+ PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev);
+
+ if (!shpc_present(pci_hotplug_dev)) {
+ error_setg(errp, "standard hotplug controller has been disabled for "
+ "this %s", TYPE_PCI_BRIDGE_DEV);
+ return;
+ }
+ shpc_device_hot_unplug_request_cb(hotplug_dev, dev, errp);
+}
+
static void pci_bridge_dev_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -154,8 +211,8 @@ static void pci_bridge_dev_class_init(ObjectClass *klass, void *data)
dc->props = pci_bridge_dev_properties;
dc->vmsd = &pci_bridge_dev_vmstate;
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
- hc->plug = shpc_device_hotplug_cb;
- hc->unplug_request = shpc_device_hot_unplug_request_cb;
+ hc->plug = pci_bridge_dev_hotplug_cb;
+ hc->unplug_request = pci_bridge_dev_hot_unplug_request_cb;
}
static const TypeInfo pci_bridge_dev_info = {
@@ -170,9 +227,31 @@ static const TypeInfo pci_bridge_dev_info = {
}
};
+/*
+ * Multiseat bridge. Same as the standard pci bridge, only with a
+ * different pci id, so we can match it easily in the guest for
+ * automagic multiseat configuration. See docs/multiseat.txt for more.
+ */
+static void pci_bridge_dev_seat_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE_SEAT;
+ dc->desc = "Standard PCI Bridge (multiseat)";
+}
+
+static const TypeInfo pci_bridge_dev_seat_info = {
+ .name = TYPE_PCI_BRIDGE_SEAT_DEV,
+ .parent = TYPE_PCI_BRIDGE_DEV,
+ .instance_size = sizeof(PCIBridgeDev),
+ .class_init = pci_bridge_dev_seat_class_init,
+};
+
static void pci_bridge_dev_register(void)
{
type_register_static(&pci_bridge_dev_info);
+ type_register_static(&pci_bridge_dev_seat_info);
}
type_init(pci_bridge_dev_register);
diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c
new file mode 100644
index 000000000..57f8a3762
--- /dev/null
+++ b/hw/pci-bridge/pci_expander_bridge.c
@@ -0,0 +1,286 @@
+/*
+ * PCI Expander Bridge Device Emulation
+ *
+ * Copyright (C) 2015 Red Hat Inc
+ *
+ * Authors:
+ * Marcel Apfelbaum <marcel@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_bus.h"
+#include "hw/pci/pci_host.h"
+#include "hw/pci/pci_bus.h"
+#include "hw/pci/pci_bridge.h"
+#include "hw/i386/pc.h"
+#include "qemu/range.h"
+#include "qemu/error-report.h"
+#include "sysemu/numa.h"
+
+#define TYPE_PXB_BUS "pxb-bus"
+#define PXB_BUS(obj) OBJECT_CHECK(PXBBus, (obj), TYPE_PXB_BUS)
+
+typedef struct PXBBus {
+ /*< private >*/
+ PCIBus parent_obj;
+ /*< public >*/
+
+ char bus_path[8];
+} PXBBus;
+
+#define TYPE_PXB_DEVICE "pxb"
+#define PXB_DEV(obj) OBJECT_CHECK(PXBDev, (obj), TYPE_PXB_DEVICE)
+
+typedef struct PXBDev {
+ /*< private >*/
+ PCIDevice parent_obj;
+ /*< public >*/
+
+ uint8_t bus_nr;
+ uint16_t numa_node;
+} PXBDev;
+
+static GList *pxb_dev_list;
+
+#define TYPE_PXB_HOST "pxb-host"
+
+static int pxb_bus_num(PCIBus *bus)
+{
+ PXBDev *pxb = PXB_DEV(bus->parent_dev);
+
+ return pxb->bus_nr;
+}
+
+static bool pxb_is_root(PCIBus *bus)
+{
+ return true; /* by definition */
+}
+
+static uint16_t pxb_bus_numa_node(PCIBus *bus)
+{
+ PXBDev *pxb = PXB_DEV(bus->parent_dev);
+
+ return pxb->numa_node;
+}
+
+static void pxb_bus_class_init(ObjectClass *class, void *data)
+{
+ PCIBusClass *pbc = PCI_BUS_CLASS(class);
+
+ pbc->bus_num = pxb_bus_num;
+ pbc->is_root = pxb_is_root;
+ pbc->numa_node = pxb_bus_numa_node;
+}
+
+static const TypeInfo pxb_bus_info = {
+ .name = TYPE_PXB_BUS,
+ .parent = TYPE_PCI_BUS,
+ .instance_size = sizeof(PXBBus),
+ .class_init = pxb_bus_class_init,
+};
+
+static const char *pxb_host_root_bus_path(PCIHostState *host_bridge,
+ PCIBus *rootbus)
+{
+ PXBBus *bus = PXB_BUS(rootbus);
+
+ snprintf(bus->bus_path, 8, "0000:%02x", pxb_bus_num(rootbus));
+ return bus->bus_path;
+}
+
+static char *pxb_host_ofw_unit_address(const SysBusDevice *dev)
+{
+ const PCIHostState *pxb_host;
+ const PCIBus *pxb_bus;
+ const PXBDev *pxb_dev;
+ int position;
+ const DeviceState *pxb_dev_base;
+ const PCIHostState *main_host;
+ const SysBusDevice *main_host_sbd;
+
+ pxb_host = PCI_HOST_BRIDGE(dev);
+ pxb_bus = pxb_host->bus;
+ pxb_dev = PXB_DEV(pxb_bus->parent_dev);
+ position = g_list_index(pxb_dev_list, pxb_dev);
+ assert(position >= 0);
+
+ pxb_dev_base = DEVICE(pxb_dev);
+ main_host = PCI_HOST_BRIDGE(pxb_dev_base->parent_bus->parent);
+ main_host_sbd = SYS_BUS_DEVICE(main_host);
+
+ if (main_host_sbd->num_mmio > 0) {
+ return g_strdup_printf(TARGET_FMT_plx ",%x",
+ main_host_sbd->mmio[0].addr, position + 1);
+ }
+ if (main_host_sbd->num_pio > 0) {
+ return g_strdup_printf("i%04x,%x",
+ main_host_sbd->pio[0], position + 1);
+ }
+ return NULL;
+}
+
+static void pxb_host_class_init(ObjectClass *class, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(class);
+ SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(class);
+ PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(class);
+
+ dc->fw_name = "pci";
+ sbc->explicit_ofw_unit_address = pxb_host_ofw_unit_address;
+ hc->root_bus_path = pxb_host_root_bus_path;
+}
+
+static const TypeInfo pxb_host_info = {
+ .name = TYPE_PXB_HOST,
+ .parent = TYPE_PCI_HOST_BRIDGE,
+ .class_init = pxb_host_class_init,
+};
+
+/*
+ * Registers the PXB bus as a child of the i440fx root bus.
+ *
+ * Returns 0 on successs, -1 if i440fx host was not
+ * found or the bus number is already in use.
+ */
+static int pxb_register_bus(PCIDevice *dev, PCIBus *pxb_bus)
+{
+ PCIBus *bus = dev->bus;
+ int pxb_bus_num = pci_bus_num(pxb_bus);
+
+ if (bus->parent_dev) {
+ error_report("PXB devices can be attached only to root bus.");
+ return -1;
+ }
+
+ QLIST_FOREACH(bus, &bus->child, sibling) {
+ if (pci_bus_num(bus) == pxb_bus_num) {
+ error_report("Bus %d is already in use.", pxb_bus_num);
+ return -1;
+ }
+ }
+ QLIST_INSERT_HEAD(&dev->bus->child, pxb_bus, sibling);
+
+ return 0;
+}
+
+static int pxb_map_irq_fn(PCIDevice *pci_dev, int pin)
+{
+ PCIDevice *pxb = pci_dev->bus->parent_dev;
+
+ /*
+ * The bios does not index the pxb slot number when
+ * it computes the IRQ because it resides on bus 0
+ * and not on the current bus.
+ * However QEMU routes the irq through bus 0 and adds
+ * the pxb slot to the IRQ computation of the PXB
+ * device.
+ *
+ * Synchronize between bios and QEMU by canceling
+ * pxb's effect.
+ */
+ return pin - PCI_SLOT(pxb->devfn);
+}
+
+static gint pxb_compare(gconstpointer a, gconstpointer b)
+{
+ const PXBDev *pxb_a = a, *pxb_b = b;
+
+ return pxb_a->bus_nr < pxb_b->bus_nr ? -1 :
+ pxb_a->bus_nr > pxb_b->bus_nr ? 1 :
+ 0;
+}
+
+static int pxb_dev_initfn(PCIDevice *dev)
+{
+ PXBDev *pxb = PXB_DEV(dev);
+ DeviceState *ds, *bds;
+ PCIBus *bus;
+ const char *dev_name = NULL;
+
+ if (pxb->numa_node != NUMA_NODE_UNASSIGNED &&
+ pxb->numa_node >= nb_numa_nodes) {
+ error_report("Illegal numa node %d.", pxb->numa_node);
+ return -EINVAL;
+ }
+
+ if (dev->qdev.id && *dev->qdev.id) {
+ dev_name = dev->qdev.id;
+ }
+
+ ds = qdev_create(NULL, TYPE_PXB_HOST);
+ bus = pci_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS);
+
+ bus->parent_dev = dev;
+ bus->address_space_mem = dev->bus->address_space_mem;
+ bus->address_space_io = dev->bus->address_space_io;
+ bus->map_irq = pxb_map_irq_fn;
+
+ bds = qdev_create(BUS(bus), "pci-bridge");
+ bds->id = dev_name;
+ qdev_prop_set_uint8(bds, PCI_BRIDGE_DEV_PROP_CHASSIS_NR, pxb->bus_nr);
+ qdev_prop_set_bit(bds, PCI_BRIDGE_DEV_PROP_SHPC, false);
+
+ PCI_HOST_BRIDGE(ds)->bus = bus;
+
+ if (pxb_register_bus(dev, bus)) {
+ return -EINVAL;
+ }
+
+ qdev_init_nofail(ds);
+ qdev_init_nofail(bds);
+
+ pci_word_test_and_set_mask(dev->config + PCI_STATUS,
+ PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
+ pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_HOST);
+
+ pxb_dev_list = g_list_insert_sorted(pxb_dev_list, pxb, pxb_compare);
+ return 0;
+}
+
+static void pxb_dev_exitfn(PCIDevice *pci_dev)
+{
+ PXBDev *pxb = PXB_DEV(pci_dev);
+
+ pxb_dev_list = g_list_remove(pxb_dev_list, pxb);
+}
+
+static Property pxb_dev_properties[] = {
+ /* Note: 0 is not a legal a PXB bus number. */
+ DEFINE_PROP_UINT8("bus_nr", PXBDev, bus_nr, 0),
+ DEFINE_PROP_UINT16("numa_node", PXBDev, numa_node, NUMA_NODE_UNASSIGNED),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pxb_dev_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->init = pxb_dev_initfn;
+ k->exit = pxb_dev_exitfn;
+ k->vendor_id = PCI_VENDOR_ID_REDHAT;
+ k->device_id = PCI_DEVICE_ID_REDHAT_PXB;
+ k->class_id = PCI_CLASS_BRIDGE_HOST;
+
+ dc->desc = "PCI Expander Bridge";
+ dc->props = pxb_dev_properties;
+}
+
+static const TypeInfo pxb_dev_info = {
+ .name = TYPE_PXB_DEVICE,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PXBDev),
+ .class_init = pxb_dev_class_init,
+};
+
+static void pxb_register_types(void)
+{
+ type_register_static(&pxb_bus_info);
+ type_register_static(&pxb_host_info);
+ type_register_static(&pxb_dev_info);
+}
+
+type_init(pxb_register_types)
diff --git a/hw/pci-host/apb.c b/hw/pci-host/apb.c
index 312fa703c..599768e2d 100644
--- a/hw/pci-host/apb.c
+++ b/hw/pci-host/apb.c
@@ -289,7 +289,8 @@ static IOMMUTLBEntry pbm_translate_iommu(MemoryRegion *iommu, hwaddr addr,
}
}
- tte = ldq_be_phys(&address_space_memory, baseaddr + offset);
+ tte = address_space_ldq_be(&address_space_memory, baseaddr + offset,
+ MEMTXATTRS_UNSPECIFIED, NULL);
if (!(tte & IOMMU_TTE_DATA_V)) {
/* Invalid mapping */
diff --git a/hw/pci-host/bonito.c b/hw/pci-host/bonito.c
index 8134d0bcd..3a731fe18 100644
--- a/hw/pci-host/bonito.c
+++ b/hw/pci-host/bonito.c
@@ -427,7 +427,7 @@ static uint32_t bonito_sbridge_pciaddr(void *opaque, hwaddr addr)
cfgaddr |= (s->regs[BONITO_PCIMAP_CFG] & 0xffff) << 16;
idsel = (cfgaddr & BONITO_PCICONF_IDSEL_MASK) >> BONITO_PCICONF_IDSEL_OFFSET;
- devno = ffs(idsel) - 1;
+ devno = ctz32(idsel);
funno = (cfgaddr & BONITO_PCICONF_FUN_MASK) >> BONITO_PCICONF_FUN_OFFSET;
regno = (cfgaddr & BONITO_PCICONF_REG_MASK) >> BONITO_PCICONF_REG_OFFSET;
diff --git a/hw/pci-host/pam.c b/hw/pci-host/pam.c
index 8272de3f2..17d826cba 100644
--- a/hw/pci-host/pam.c
+++ b/hw/pci-host/pam.c
@@ -31,26 +31,6 @@
#include "sysemu/sysemu.h"
#include "hw/pci-host/pam.h"
-void smram_update(MemoryRegion *smram_region, uint8_t smram,
- uint8_t smm_enabled)
-{
- bool smram_enabled;
-
- smram_enabled = ((smm_enabled && (smram & SMRAM_G_SMRAME)) ||
- (smram & SMRAM_D_OPEN));
- memory_region_set_enabled(smram_region, !smram_enabled);
-}
-
-void smram_set_smm(uint8_t *host_smm_enabled, int smm, uint8_t smram,
- MemoryRegion *smram_region)
-{
- uint8_t smm_enabled = (smm != 0);
- if (*host_smm_enabled != smm_enabled) {
- *host_smm_enabled = smm_enabled;
- smram_update(smram_region, smram, *host_smm_enabled);
- }
-}
-
void init_pam(DeviceState *dev, MemoryRegion *ram_memory,
MemoryRegion *system_memory, MemoryRegion *pci_address_space,
PAMMemoryRegion *mem, uint32_t start, uint32_t size)
diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c
index 723836fb0..ad55f9966 100644
--- a/hw/pci-host/piix.c
+++ b/hw/pci-host/piix.c
@@ -91,6 +91,10 @@ typedef struct PIIX3State {
MemoryRegion rcr_mem;
} PIIX3State;
+#define TYPE_PIIX3_PCI_DEVICE "pci-piix3"
+#define PIIX3_PCI_DEVICE(obj) \
+ OBJECT_CHECK(PIIX3State, (obj), TYPE_PIIX3_PCI_DEVICE)
+
#define TYPE_I440FX_PCI_DEVICE "i440FX"
#define I440FX_PCI_DEVICE(obj) \
OBJECT_CHECK(PCII440FXState, (obj), TYPE_I440FX_PCI_DEVICE)
@@ -105,7 +109,7 @@ struct PCII440FXState {
MemoryRegion *ram_memory;
PAMMemoryRegion pam_regions[13];
MemoryRegion smram_region;
- uint8_t smm_enabled;
+ MemoryRegion smram, low_smram;
};
@@ -138,18 +142,10 @@ static void i440fx_update_memory_mappings(PCII440FXState *d)
pam_update(&d->pam_regions[i], i,
pd->config[I440FX_PAM + ((i + 1) / 2)]);
}
- smram_update(&d->smram_region, pd->config[I440FX_SMRAM], d->smm_enabled);
- memory_region_transaction_commit();
-}
-
-static void i440fx_set_smm(int val, void *arg)
-{
- PCII440FXState *d = arg;
- PCIDevice *pd = PCI_DEVICE(d);
-
- memory_region_transaction_begin();
- smram_set_smm(&d->smm_enabled, val, pd->config[I440FX_SMRAM],
- &d->smram_region);
+ memory_region_set_enabled(&d->smram_region,
+ !(pd->config[I440FX_SMRAM] & SMRAM_D_OPEN));
+ memory_region_set_enabled(&d->smram,
+ pd->config[I440FX_SMRAM] & SMRAM_G_SMRAME);
memory_region_transaction_commit();
}
@@ -172,12 +168,13 @@ static int i440fx_load_old(QEMUFile* f, void *opaque, int version_id)
PCII440FXState *d = opaque;
PCIDevice *pd = PCI_DEVICE(d);
int ret, i;
+ uint8_t smm_enabled;
ret = pci_device_load(pd, f);
if (ret < 0)
return ret;
i440fx_update_memory_mappings(d);
- qemu_get_8s(f, &d->smm_enabled);
+ qemu_get_8s(f, &smm_enabled);
if (version_id == 2) {
for (i = 0; i < PIIX_NUM_PIRQS; i++) {
@@ -205,7 +202,10 @@ static const VMStateDescription vmstate_i440fx = {
.post_load = i440fx_post_load,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(parent_obj, PCII440FXState),
- VMSTATE_UINT8(smm_enabled, PCII440FXState),
+ /* Used to be smm_enabled, which was basically always zero because
+ * SeaBIOS hardly uses SMM. SMRAM is now handled by CPU code.
+ */
+ VMSTATE_UNUSED(1),
VMSTATE_END_OF_LIST()
}
};
@@ -297,11 +297,7 @@ static void i440fx_pcihost_realize(DeviceState *dev, Error **errp)
static void i440fx_realize(PCIDevice *dev, Error **errp)
{
- PCII440FXState *d = I440FX_PCI_DEVICE(dev);
-
dev->config[I440FX_SMRAM] = 0x02;
-
- cpu_smm_register(&i440fx_set_smm, d);
}
PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
@@ -346,11 +342,23 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
pc_pci_as_mapping_init(OBJECT(f), f->system_memory,
f->pci_address_space);
+ /* if *disabled* show SMRAM to all CPUs */
memory_region_init_alias(&f->smram_region, OBJECT(d), "smram-region",
f->pci_address_space, 0xa0000, 0x20000);
memory_region_add_subregion_overlap(f->system_memory, 0xa0000,
&f->smram_region, 1);
- memory_region_set_enabled(&f->smram_region, false);
+ memory_region_set_enabled(&f->smram_region, true);
+
+ /* smram, as seen by SMM CPUs */
+ memory_region_init(&f->smram, OBJECT(d), "smram", 1ull << 32);
+ memory_region_set_enabled(&f->smram, true);
+ memory_region_init_alias(&f->low_smram, OBJECT(d), "smram-low",
+ f->ram_memory, 0xa0000, 0x20000);
+ memory_region_set_enabled(&f->low_smram, true);
+ memory_region_add_subregion(&f->smram, 0xa0000, &f->low_smram);
+ object_property_add_const_link(qdev_get_machine(), "smram",
+ OBJECT(&f->smram), &error_abort);
+
init_pam(dev, f->ram_memory, f->system_memory, f->pci_address_space,
&f->pam_regions[0], PAM_BIOS_BASE, PAM_BIOS_SIZE);
for (i = 0; i < 12; ++i) {
@@ -364,13 +372,15 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
* connected to the IOAPIC directly.
* These additional routes can be discovered through ACPI. */
if (xen_enabled()) {
- piix3 = DO_UPCAST(PIIX3State, dev,
- pci_create_simple_multifunction(b, -1, true, "PIIX3-xen"));
+ PCIDevice *pci_dev = pci_create_simple_multifunction(b,
+ -1, true, "PIIX3-xen");
+ piix3 = PIIX3_PCI_DEVICE(pci_dev);
pci_bus_irqs(b, xen_piix3_set_irq, xen_pci_slot_get_pirq,
piix3, XEN_PIIX_NUM_PIRQS);
} else {
- piix3 = DO_UPCAST(PIIX3State, dev,
- pci_create_simple_multifunction(b, -1, true, "PIIX3"));
+ PCIDevice *pci_dev = pci_create_simple_multifunction(b,
+ -1, true, "PIIX3");
+ piix3 = PIIX3_PCI_DEVICE(pci_dev);
pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, piix3,
PIIX_NUM_PIRQS);
pci_bus_set_route_irq_fn(b, piix3_route_intx_pin_to_irq);
@@ -476,7 +486,7 @@ static void piix3_write_config(PCIDevice *dev,
{
pci_default_write_config(dev, address, val, len);
if (ranges_overlap(address, len, PIIX_PIRQC, 4)) {
- PIIX3State *piix3 = DO_UPCAST(PIIX3State, dev, dev);
+ PIIX3State *piix3 = PIIX3_PCI_DEVICE(dev);
int pic_irq;
pci_bus_fire_intx_routing_notifier(piix3->dev.bus);
@@ -578,6 +588,7 @@ static const VMStateDescription vmstate_piix3_rcr = {
.name = "PIIX3/rcr",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = piix3_rcr_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8(rcr, PIIX3State),
VMSTATE_END_OF_LIST()
@@ -596,12 +607,9 @@ static const VMStateDescription vmstate_piix3 = {
PIIX_NUM_PIRQS, 3),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_piix3_rcr,
- .needed = piix3_rcr_needed,
- },
- { 0 }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_piix3_rcr,
+ NULL
}
};
@@ -632,7 +640,7 @@ static const MemoryRegionOps rcr_ops = {
static void piix3_realize(PCIDevice *dev, Error **errp)
{
- PIIX3State *d = DO_UPCAST(PIIX3State, dev, dev);
+ PIIX3State *d = PIIX3_PCI_DEVICE(dev);
isa_bus_new(DEVICE(d), get_system_memory(),
pci_address_space_io(dev));
@@ -645,7 +653,7 @@ static void piix3_realize(PCIDevice *dev, Error **errp)
qemu_register_reset(piix3_reset, d);
}
-static void piix3_class_init(ObjectClass *klass, void *data)
+static void pci_piix3_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
@@ -654,7 +662,6 @@ static void piix3_class_init(ObjectClass *klass, void *data)
dc->vmsd = &vmstate_piix3;
dc->hotpluggable = false;
k->realize = piix3_realize;
- k->config_write = piix3_write_config;
k->vendor_id = PCI_VENDOR_ID_INTEL;
/* 82371SB PIIX3 PCI-to-ISA bridge (Step A1) */
k->device_id = PCI_DEVICE_ID_INTEL_82371SB_0;
@@ -666,38 +673,37 @@ static void piix3_class_init(ObjectClass *klass, void *data)
dc->cannot_instantiate_with_device_add_yet = true;
}
+static const TypeInfo piix3_pci_type_info = {
+ .name = TYPE_PIIX3_PCI_DEVICE,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PIIX3State),
+ .abstract = true,
+ .class_init = pci_piix3_class_init,
+};
+
+static void piix3_class_init(ObjectClass *klass, void *data)
+{
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->config_write = piix3_write_config;
+}
+
static const TypeInfo piix3_info = {
.name = "PIIX3",
- .parent = TYPE_PCI_DEVICE,
- .instance_size = sizeof(PIIX3State),
+ .parent = TYPE_PIIX3_PCI_DEVICE,
.class_init = piix3_class_init,
};
static void piix3_xen_class_init(ObjectClass *klass, void *data)
{
- DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- dc->desc = "ISA bridge";
- dc->vmsd = &vmstate_piix3;
- dc->hotpluggable = false;
- k->realize = piix3_realize;
k->config_write = piix3_write_config_xen;
- k->vendor_id = PCI_VENDOR_ID_INTEL;
- /* 82371SB PIIX3 PCI-to-ISA bridge (Step A1) */
- k->device_id = PCI_DEVICE_ID_INTEL_82371SB_0;
- k->class_id = PCI_CLASS_BRIDGE_ISA;
- /*
- * Reason: part of PIIX3 southbridge, needs to be wired up by
- * pc_piix.c's pc_init1()
- */
- dc->cannot_instantiate_with_device_add_yet = true;
};
static const TypeInfo piix3_xen_info = {
.name = "PIIX3-xen",
- .parent = TYPE_PCI_DEVICE,
- .instance_size = sizeof(PIIX3State),
+ .parent = TYPE_PIIX3_PCI_DEVICE,
.class_init = piix3_xen_class_init,
};
@@ -770,6 +776,7 @@ static const TypeInfo i440fx_pcihost_info = {
static void i440fx_register_types(void)
{
type_register_static(&i440fx_info);
+ type_register_static(&piix3_pci_type_info);
type_register_static(&piix3_info);
type_register_static(&piix3_xen_info);
type_register_static(&i440fx_pcihost_info);
diff --git a/hw/pci-host/prep.c b/hw/pci-host/prep.c
index 6cea6ffeb..c63f45d21 100644
--- a/hw/pci-host/prep.c
+++ b/hw/pci-host/prep.c
@@ -140,7 +140,8 @@ static uint64_t raven_io_read(void *opaque, hwaddr addr,
uint8_t buf[4];
addr = raven_io_address(s, addr);
- address_space_read(&s->pci_io_as, addr + 0x80000000, buf, size);
+ address_space_read(&s->pci_io_as, addr + 0x80000000,
+ MEMTXATTRS_UNSPECIFIED, buf, size);
if (size == 1) {
return buf[0];
@@ -171,7 +172,8 @@ static void raven_io_write(void *opaque, hwaddr addr,
g_assert_not_reached();
}
- address_space_write(&s->pci_io_as, addr + 0x80000000, buf, size);
+ address_space_write(&s->pci_io_as, addr + 0x80000000,
+ MEMTXATTRS_UNSPECIFIED, buf, size);
}
static const MemoryRegionOps raven_io_ops = {
diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c
index c8827cc00..bd7409456 100644
--- a/hw/pci-host/q35.c
+++ b/hw/pci-host/q35.c
@@ -198,6 +198,28 @@ static const TypeInfo q35_host_info = {
* MCH D0:F0
*/
+static uint64_t tseg_blackhole_read(void *ptr, hwaddr reg, unsigned size)
+{
+ return 0xffffffff;
+}
+
+static void tseg_blackhole_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned width)
+{
+ /* nothing */
+}
+
+static const MemoryRegionOps tseg_blackhole_ops = {
+ .read = tseg_blackhole_read,
+ .write = tseg_blackhole_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .impl.min_access_size = 4,
+ .impl.max_access_size = 4,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
/* PCIe MMCFG */
static void mch_update_pciexbar(MCHPCIState *mch)
{
@@ -266,21 +288,70 @@ static void mch_update_pam(MCHPCIState *mch)
static void mch_update_smram(MCHPCIState *mch)
{
PCIDevice *pd = PCI_DEVICE(mch);
+ bool h_smrame = (pd->config[MCH_HOST_BRIDGE_ESMRAMC] & MCH_HOST_BRIDGE_ESMRAMC_H_SMRAME);
+ uint32_t tseg_size;
+
+ /* implement SMRAM.D_LCK */
+ if (pd->config[MCH_HOST_BRIDGE_SMRAM] & MCH_HOST_BRIDGE_SMRAM_D_LCK) {
+ pd->config[MCH_HOST_BRIDGE_SMRAM] &= ~MCH_HOST_BRIDGE_SMRAM_D_OPEN;
+ pd->wmask[MCH_HOST_BRIDGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_WMASK_LCK;
+ pd->wmask[MCH_HOST_BRIDGE_ESMRAMC] = MCH_HOST_BRIDGE_ESMRAMC_WMASK_LCK;
+ }
memory_region_transaction_begin();
- smram_update(&mch->smram_region, pd->config[MCH_HOST_BRIDGE_SMRAM],
- mch->smm_enabled);
- memory_region_transaction_commit();
-}
-static void mch_set_smm(int smm, void *arg)
-{
- MCHPCIState *mch = arg;
- PCIDevice *pd = PCI_DEVICE(mch);
+ if (pd->config[MCH_HOST_BRIDGE_SMRAM] & SMRAM_D_OPEN) {
+ /* Hide (!) low SMRAM if H_SMRAME = 1 */
+ memory_region_set_enabled(&mch->smram_region, h_smrame);
+ /* Show high SMRAM if H_SMRAME = 1 */
+ memory_region_set_enabled(&mch->open_high_smram, h_smrame);
+ } else {
+ /* Hide high SMRAM and low SMRAM */
+ memory_region_set_enabled(&mch->smram_region, true);
+ memory_region_set_enabled(&mch->open_high_smram, false);
+ }
+
+ if (pd->config[MCH_HOST_BRIDGE_SMRAM] & SMRAM_G_SMRAME) {
+ memory_region_set_enabled(&mch->low_smram, !h_smrame);
+ memory_region_set_enabled(&mch->high_smram, h_smrame);
+ } else {
+ memory_region_set_enabled(&mch->low_smram, false);
+ memory_region_set_enabled(&mch->high_smram, false);
+ }
+
+ if (pd->config[MCH_HOST_BRIDGE_ESMRAMC] & MCH_HOST_BRIDGE_ESMRAMC_T_EN) {
+ switch (pd->config[MCH_HOST_BRIDGE_ESMRAMC] &
+ MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK) {
+ case MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_1MB:
+ tseg_size = 1024 * 1024;
+ break;
+ case MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_2MB:
+ tseg_size = 1024 * 1024 * 2;
+ break;
+ case MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_8MB:
+ tseg_size = 1024 * 1024 * 8;
+ break;
+ default:
+ tseg_size = 0;
+ break;
+ }
+ } else {
+ tseg_size = 0;
+ }
+ memory_region_del_subregion(mch->system_memory, &mch->tseg_blackhole);
+ memory_region_set_enabled(&mch->tseg_blackhole, tseg_size);
+ memory_region_set_size(&mch->tseg_blackhole, tseg_size);
+ memory_region_add_subregion_overlap(mch->system_memory,
+ mch->below_4g_mem_size - tseg_size,
+ &mch->tseg_blackhole, 1);
+
+ memory_region_set_enabled(&mch->tseg_window, tseg_size);
+ memory_region_set_size(&mch->tseg_window, tseg_size);
+ memory_region_set_address(&mch->tseg_window,
+ mch->below_4g_mem_size - tseg_size);
+ memory_region_set_alias_offset(&mch->tseg_window,
+ mch->below_4g_mem_size - tseg_size);
- memory_region_transaction_begin();
- smram_set_smm(&mch->smm_enabled, smm, pd->config[MCH_HOST_BRIDGE_SMRAM],
- &mch->smram_region);
memory_region_transaction_commit();
}
@@ -289,7 +360,6 @@ static void mch_write_config(PCIDevice *d,
{
MCHPCIState *mch = MCH_PCI_DEVICE(d);
- /* XXX: implement SMRAM.D_LOCK */
pci_default_write_config(d, address, val, len);
if (ranges_overlap(address, len, MCH_HOST_BRIDGE_PAM0,
@@ -329,7 +399,10 @@ static const VMStateDescription vmstate_mch = {
.post_load = mch_post_load,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(parent_obj, MCHPCIState),
- VMSTATE_UINT8(smm_enabled, MCHPCIState),
+ /* Used to be smm_enabled, which was basically always zero because
+ * SeaBIOS hardly uses SMM. SMRAM is now handled by CPU code.
+ */
+ VMSTATE_UNUSED(1),
VMSTATE_END_OF_LIST()
}
};
@@ -343,6 +416,9 @@ static void mch_reset(DeviceState *qdev)
MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT);
d->config[MCH_HOST_BRIDGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_DEFAULT;
+ d->config[MCH_HOST_BRIDGE_ESMRAMC] = MCH_HOST_BRIDGE_ESMRAMC_DEFAULT;
+ d->wmask[MCH_HOST_BRIDGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_WMASK;
+ d->wmask[MCH_HOST_BRIDGE_ESMRAMC] = MCH_HOST_BRIDGE_ESMRAMC_WMASK;
mch_update(mch);
}
@@ -399,13 +475,47 @@ static void mch_realize(PCIDevice *d, Error **errp)
pc_pci_as_mapping_init(OBJECT(mch), mch->system_memory,
mch->pci_address_space);
- /* smram */
- cpu_smm_register(&mch_set_smm, mch);
+ /* if *disabled* show SMRAM to all CPUs */
memory_region_init_alias(&mch->smram_region, OBJECT(mch), "smram-region",
mch->pci_address_space, 0xa0000, 0x20000);
memory_region_add_subregion_overlap(mch->system_memory, 0xa0000,
&mch->smram_region, 1);
- memory_region_set_enabled(&mch->smram_region, false);
+ memory_region_set_enabled(&mch->smram_region, true);
+
+ memory_region_init_alias(&mch->open_high_smram, OBJECT(mch), "smram-open-high",
+ mch->ram_memory, 0xa0000, 0x20000);
+ memory_region_add_subregion_overlap(mch->system_memory, 0xfeda0000,
+ &mch->open_high_smram, 1);
+ memory_region_set_enabled(&mch->open_high_smram, false);
+
+ /* smram, as seen by SMM CPUs */
+ memory_region_init(&mch->smram, OBJECT(mch), "smram", 1ull << 32);
+ memory_region_set_enabled(&mch->smram, true);
+ memory_region_init_alias(&mch->low_smram, OBJECT(mch), "smram-low",
+ mch->ram_memory, 0xa0000, 0x20000);
+ memory_region_set_enabled(&mch->low_smram, true);
+ memory_region_add_subregion(&mch->smram, 0xa0000, &mch->low_smram);
+ memory_region_init_alias(&mch->high_smram, OBJECT(mch), "smram-high",
+ mch->ram_memory, 0xa0000, 0x20000);
+ memory_region_set_enabled(&mch->high_smram, true);
+ memory_region_add_subregion(&mch->smram, 0xfeda0000, &mch->high_smram);
+
+ memory_region_init_io(&mch->tseg_blackhole, OBJECT(mch),
+ &tseg_blackhole_ops, NULL,
+ "tseg-blackhole", 0);
+ memory_region_set_enabled(&mch->tseg_blackhole, false);
+ memory_region_add_subregion_overlap(mch->system_memory,
+ mch->below_4g_mem_size,
+ &mch->tseg_blackhole, 1);
+
+ memory_region_init_alias(&mch->tseg_window, OBJECT(mch), "tseg-window",
+ mch->ram_memory, mch->below_4g_mem_size, 0);
+ memory_region_set_enabled(&mch->tseg_window, false);
+ memory_region_add_subregion(&mch->smram, mch->below_4g_mem_size,
+ &mch->tseg_window);
+ object_property_add_const_link(qdev_get_machine(), "smram",
+ OBJECT(&mch->smram), &error_abort);
+
init_pam(DEVICE(mch), mch->ram_memory, mch->system_memory,
mch->pci_address_space, &mch->pam_regions[0],
PAM_BIOS_BASE, PAM_BIOS_SIZE);
diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c
index 53f2b59ae..f0144eb7b 100644
--- a/hw/pci-host/uninorth.c
+++ b/hw/pci-host/uninorth.c
@@ -92,7 +92,10 @@ static uint32_t unin_get_config_reg(uint32_t reg, uint32_t addr)
uint32_t slot, func;
/* Grab CFA0 style values */
- slot = ffs(reg & 0xfffff800) - 1;
+ slot = ctz32(reg & 0xfffff800);
+ if (slot == 32) {
+ slot = -1; /* XXX: should this be 0? */
+ }
func = (reg >> 8) & 7;
/* ... and then convert them to x86 format */
diff --git a/hw/pci-host/versatile.c b/hw/pci-host/versatile.c
index 6d2355309..7172b9095 100644
--- a/hw/pci-host/versatile.c
+++ b/hw/pci-host/versatile.c
@@ -500,6 +500,8 @@ static void pci_vpb_class_init(ObjectClass *klass, void *data)
dc->reset = pci_vpb_reset;
dc->vmsd = &pci_vpb_vmstate;
dc->props = pci_vpb_properties;
+ /* Reason: object_unref() hangs */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo pci_vpb_info = {
@@ -521,10 +523,19 @@ static void pci_realview_init(Object *obj)
s->mem_win_size[2] = 0x08000000;
}
+static void pci_realview_class_init(ObjectClass *class, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(class);
+
+ /* Reason: object_unref() hangs */
+ dc->cannot_destroy_with_object_finalize_yet = true;
+}
+
static const TypeInfo pci_realview_info = {
.name = "realview_pci",
.parent = TYPE_VERSATILE_PCI,
.instance_init = pci_realview_init,
+ .class_init = pci_realview_class_init,
};
static void versatile_pci_register_types(void)
diff --git a/hw/pci/msi.c b/hw/pci/msi.c
index 52d23130d..f9c048442 100644
--- a/hw/pci/msi.c
+++ b/hw/pci/msi.c
@@ -21,10 +21,6 @@
#include "hw/pci/msi.h"
#include "qemu/range.h"
-/* Eventually those constants should go to Linux pci_regs.h */
-#define PCI_MSI_PENDING_32 0x10
-#define PCI_MSI_PENDING_64 0x14
-
/* PCI_MSI_ADDRESS_LO */
#define PCI_MSI_ADDRESS_LO_MASK (~0x3)
@@ -72,7 +68,7 @@ static inline uint8_t msi_cap_sizeof(uint16_t flags)
static inline unsigned int msi_nr_vectors(uint16_t flags)
{
return 1U <<
- ((flags & PCI_MSI_FLAGS_QSIZE) >> (ffs(PCI_MSI_FLAGS_QSIZE) - 1));
+ ((flags & PCI_MSI_FLAGS_QSIZE) >> ctz32(PCI_MSI_FLAGS_QSIZE));
}
static inline uint8_t msi_flags_off(const PCIDevice* dev)
@@ -175,9 +171,9 @@ int msi_init(struct PCIDevice *dev, uint8_t offset,
assert(nr_vectors > 0);
assert(nr_vectors <= PCI_MSI_VECTORS_MAX);
/* the nr of MSI vectors is up to 32 */
- vectors_order = ffs(nr_vectors) - 1;
+ vectors_order = ctz32(nr_vectors);
- flags = vectors_order << (ffs(PCI_MSI_FLAGS_QMASK) - 1);
+ flags = vectors_order << ctz32(PCI_MSI_FLAGS_QMASK);
if (msi64bit) {
flags |= PCI_MSI_FLAGS_64BIT;
}
@@ -291,7 +287,16 @@ void msi_notify(PCIDevice *dev, unsigned int vector)
"notify vector 0x%x"
" address: 0x%"PRIx64" data: 0x%"PRIx32"\n",
vector, msg.address, msg.data);
- stl_le_phys(&dev->bus_master_as, msg.address, msg.data);
+ msi_send_message(dev, msg);
+}
+
+void msi_send_message(PCIDevice *dev, MSIMessage msg)
+{
+ MemTxAttrs attrs = {};
+
+ attrs.stream_id = (pci_bus_num(dev->bus) << 8) | dev->devfn;
+ address_space_stl_le(&dev->bus_master_as, msg.address, msg.data,
+ attrs, NULL);
}
/* Normally called by pci_default_write_config(). */
@@ -354,12 +359,12 @@ void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len)
* just don't crash the host
*/
log_num_vecs =
- (flags & PCI_MSI_FLAGS_QSIZE) >> (ffs(PCI_MSI_FLAGS_QSIZE) - 1);
+ (flags & PCI_MSI_FLAGS_QSIZE) >> ctz32(PCI_MSI_FLAGS_QSIZE);
log_max_vecs =
- (flags & PCI_MSI_FLAGS_QMASK) >> (ffs(PCI_MSI_FLAGS_QMASK) - 1);
+ (flags & PCI_MSI_FLAGS_QMASK) >> ctz32(PCI_MSI_FLAGS_QMASK);
if (log_num_vecs > log_max_vecs) {
flags &= ~PCI_MSI_FLAGS_QSIZE;
- flags |= log_max_vecs << (ffs(PCI_MSI_FLAGS_QSIZE) - 1);
+ flags |= log_max_vecs << ctz32(PCI_MSI_FLAGS_QSIZE);
pci_set_word(dev->config + msi_flags_off(dev), flags);
}
diff --git a/hw/pci/msix.c b/hw/pci/msix.c
index 24de2605f..7716bf364 100644
--- a/hw/pci/msix.c
+++ b/hw/pci/msix.c
@@ -295,29 +295,37 @@ int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries,
{
int ret;
char *name;
+ uint32_t bar_size = 4096;
+ uint32_t bar_pba_offset = bar_size / 2;
+ uint32_t bar_pba_size = (nentries / 8 + 1) * 8;
/*
* Migration compatibility dictates that this remains a 4k
* BAR with the vector table in the lower half and PBA in
- * the upper half. Do not use these elsewhere!
+ * the upper half for nentries which is lower or equal to 128.
+ * No need to care about using more than 65 entries for legacy
+ * machine types who has at most 64 queues.
*/
-#define MSIX_EXCLUSIVE_BAR_SIZE 4096
-#define MSIX_EXCLUSIVE_BAR_TABLE_OFFSET 0
-#define MSIX_EXCLUSIVE_BAR_PBA_OFFSET (MSIX_EXCLUSIVE_BAR_SIZE / 2)
-#define MSIX_EXCLUSIVE_CAP_OFFSET 0
+ if (nentries * PCI_MSIX_ENTRY_SIZE > bar_pba_offset) {
+ bar_pba_offset = nentries * PCI_MSIX_ENTRY_SIZE;
+ }
- if (nentries * PCI_MSIX_ENTRY_SIZE > MSIX_EXCLUSIVE_BAR_PBA_OFFSET) {
- return -EINVAL;
+ if (bar_pba_offset + bar_pba_size > 4096) {
+ bar_size = bar_pba_offset + bar_pba_size;
+ }
+
+ if (bar_size & (bar_size - 1)) {
+ bar_size = 1 << qemu_fls(bar_size);
}
name = g_strdup_printf("%s-msix", dev->name);
- memory_region_init(&dev->msix_exclusive_bar, OBJECT(dev), name, MSIX_EXCLUSIVE_BAR_SIZE);
+ memory_region_init(&dev->msix_exclusive_bar, OBJECT(dev), name, bar_size);
g_free(name);
ret = msix_init(dev, nentries, &dev->msix_exclusive_bar, bar_nr,
- MSIX_EXCLUSIVE_BAR_TABLE_OFFSET, &dev->msix_exclusive_bar,
- bar_nr, MSIX_EXCLUSIVE_BAR_PBA_OFFSET,
- MSIX_EXCLUSIVE_CAP_OFFSET);
+ 0, &dev->msix_exclusive_bar,
+ bar_nr, bar_pba_offset,
+ 0);
if (ret) {
return ret;
}
@@ -435,7 +443,7 @@ void msix_notify(PCIDevice *dev, unsigned vector)
msg = msix_get_message(dev, vector);
- stl_le_phys(&dev->bus_master_as, msg.address, msg.data);
+ msi_send_message(dev, msg);
}
void msix_reset(PCIDevice *dev)
diff --git a/hw/pci/pci-stub.c b/hw/pci/pci-stub.c
index 5e564c3a8..063a7c242 100644
--- a/hw/pci/pci-stub.c
+++ b/hw/pci/pci-stub.c
@@ -20,28 +20,17 @@
#include "sysemu/sysemu.h"
#include "monitor/monitor.h"
+#include "qapi/qmp/qerror.h"
#include "hw/pci/pci.h"
#include "qmp-commands.h"
PciInfoList *qmp_query_pci(Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
return NULL;
}
-static void pci_error_message(Monitor *mon)
+void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict)
{
monitor_printf(mon, "PCI devices not supported\n");
}
-
-int hmp_pcie_aer_inject_error(Monitor *mon,
- const QDict *qdict, QObject **ret_data)
-{
- pci_error_message(mon);
- return -ENOSYS;
-}
-
-void pcie_aer_inject_error_print(Monitor *mon, const QObject *data)
-{
- pci_error_message(mon);
-}
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index b3d510011..a017614d4 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -30,6 +30,7 @@
#include "net/net.h"
#include "sysemu/sysemu.h"
#include "hw/loader.h"
+#include "qemu/error-report.h"
#include "qemu/range.h"
#include "qmp-commands.h"
#include "trace.h"
@@ -88,9 +89,28 @@ static void pci_bus_unrealize(BusState *qbus, Error **errp)
vmstate_unregister(NULL, &vmstate_pcibus, bus);
}
+static bool pcibus_is_root(PCIBus *bus)
+{
+ return !bus->parent_dev;
+}
+
+static int pcibus_num(PCIBus *bus)
+{
+ if (pcibus_is_root(bus)) {
+ return 0; /* pci host bridge */
+ }
+ return bus->parent_dev->config[PCI_SECONDARY_BUS];
+}
+
+static uint16_t pcibus_numa_node(PCIBus *bus)
+{
+ return NUMA_NODE_UNASSIGNED;
+}
+
static void pci_bus_class_init(ObjectClass *klass, void *data)
{
BusClass *k = BUS_CLASS(klass);
+ PCIBusClass *pbc = PCI_BUS_CLASS(klass);
k->print_dev = pcibus_dev_print;
k->get_dev_path = pcibus_get_dev_path;
@@ -98,12 +118,17 @@ static void pci_bus_class_init(ObjectClass *klass, void *data)
k->realize = pci_bus_realize;
k->unrealize = pci_bus_unrealize;
k->reset = pcibus_reset;
+
+ pbc->is_root = pcibus_is_root;
+ pbc->bus_num = pcibus_num;
+ pbc->numa_node = pcibus_numa_node;
}
static const TypeInfo pci_bus_info = {
.name = TYPE_PCI_BUS,
.parent = TYPE_BUS,
.instance_size = sizeof(PCIBus),
+ .class_size = sizeof(PCIBusClass),
.class_init = pci_bus_class_init,
};
@@ -123,7 +148,7 @@ static uint16_t pci_default_sub_device_id = PCI_SUBDEVICE_ID_QEMU;
static QLIST_HEAD(, PCIHostState) pci_host_bridges;
-static int pci_bar(PCIDevice *d, int reg)
+int pci_bar(PCIDevice *d, int reg)
{
uint8_t type;
@@ -278,7 +303,10 @@ PCIBus *pci_device_root_bus(const PCIDevice *d)
{
PCIBus *bus = d->bus;
- while ((d = bus->parent_dev) != NULL) {
+ while (!pci_bus_is_root(bus)) {
+ d = bus->parent_dev;
+ assert(d != NULL);
+
bus = d->bus;
}
@@ -291,7 +319,6 @@ const char *pci_root_bus_path(PCIDevice *dev)
PCIHostState *host_bridge = PCI_HOST_BRIDGE(rootbus->qbus.parent);
PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_GET_CLASS(host_bridge);
- assert(!rootbus->parent_dev);
assert(host_bridge->bus == rootbus);
if (hc->root_bus_path) {
@@ -325,7 +352,7 @@ bool pci_bus_is_express(PCIBus *bus)
bool pci_bus_is_root(PCIBus *bus)
{
- return !bus->parent_dev;
+ return PCI_BUS_GET_CLASS(bus)->is_root(bus);
}
void pci_bus_new_inplace(PCIBus *bus, size_t bus_size, DeviceState *parent,
@@ -379,9 +406,12 @@ PCIBus *pci_register_bus(DeviceState *parent, const char *name,
int pci_bus_num(PCIBus *s)
{
- if (pci_bus_is_root(s))
- return 0; /* pci host bridge */
- return s->parent_dev->config[PCI_SECONDARY_BUS];
+ return PCI_BUS_GET_CLASS(s)->bus_num(s);
+}
+
+int pci_bus_numa_node(PCIBus *bus)
+{
+ return PCI_BUS_GET_CLASS(bus)->numa_node(bus);
}
static int get_pci_config_device(QEMUFile *f, void *pv, size_t size)
@@ -398,6 +428,10 @@ static int get_pci_config_device(QEMUFile *f, void *pv, size_t size)
for (i = 0; i < size; ++i) {
if ((config[i] ^ s->config[i]) &
s->cmask[i] & ~s->wmask[i] & ~s->w1cmask[i]) {
+ error_report("%s: Bad config data: i=0x%x read: %x device: %x "
+ "cmask: %x wmask: %x w1cmask:%x", __func__,
+ i, config[i], s->config[i],
+ s->cmask[i], s->wmask[i], s->w1cmask[i]);
g_free(config);
return -EINVAL;
}
@@ -1456,24 +1490,26 @@ static PciBridgeInfo *qmp_query_pci_bridge(PCIDevice *dev, PCIBus *bus,
int bus_num)
{
PciBridgeInfo *info;
+ PciMemoryRange *range;
- info = g_malloc0(sizeof(*info));
+ info = g_new0(PciBridgeInfo, 1);
- info->bus.number = dev->config[PCI_PRIMARY_BUS];
- info->bus.secondary = dev->config[PCI_SECONDARY_BUS];
- info->bus.subordinate = dev->config[PCI_SUBORDINATE_BUS];
+ info->bus = g_new0(PciBusInfo, 1);
+ info->bus->number = dev->config[PCI_PRIMARY_BUS];
+ info->bus->secondary = dev->config[PCI_SECONDARY_BUS];
+ info->bus->subordinate = dev->config[PCI_SUBORDINATE_BUS];
- info->bus.io_range = g_malloc0(sizeof(*info->bus.io_range));
- info->bus.io_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO);
- info->bus.io_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO);
+ range = info->bus->io_range = g_new0(PciMemoryRange, 1);
+ range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO);
+ range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO);
- info->bus.memory_range = g_malloc0(sizeof(*info->bus.memory_range));
- info->bus.memory_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
- info->bus.memory_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
+ range = info->bus->memory_range = g_new0(PciMemoryRange, 1);
+ range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
+ range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
- info->bus.prefetchable_range = g_malloc0(sizeof(*info->bus.prefetchable_range));
- info->bus.prefetchable_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
- info->bus.prefetchable_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
+ range = info->bus->prefetchable_range = g_new0(PciMemoryRange, 1);
+ range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
+ range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
if (dev->config[PCI_SECONDARY_BUS] != 0) {
PCIBus *child_bus = pci_find_bus_nr(bus, dev->config[PCI_SECONDARY_BUS]);
@@ -1494,21 +1530,23 @@ static PciDeviceInfo *qmp_query_pci_device(PCIDevice *dev, PCIBus *bus,
uint8_t type;
int class;
- info = g_malloc0(sizeof(*info));
+ info = g_new0(PciDeviceInfo, 1);
info->bus = bus_num;
info->slot = PCI_SLOT(dev->devfn);
info->function = PCI_FUNC(dev->devfn);
+ info->class_info = g_new0(PciDeviceClass, 1);
class = pci_get_word(dev->config + PCI_CLASS_DEVICE);
- info->class_info.q_class = class;
+ info->class_info->q_class = class;
desc = get_class_desc(class);
if (desc->desc) {
- info->class_info.has_desc = true;
- info->class_info.desc = g_strdup(desc->desc);
+ info->class_info->has_desc = true;
+ info->class_info->desc = g_strdup(desc->desc);
}
- info->id.vendor = pci_get_word(dev->config + PCI_VENDOR_ID);
- info->id.device = pci_get_word(dev->config + PCI_DEVICE_ID);
+ info->id = g_new0(PciDeviceId, 1);
+ info->id->vendor = pci_get_word(dev->config + PCI_VENDOR_ID);
+ info->id->device = pci_get_word(dev->config + PCI_DEVICE_ID);
info->regions = qmp_query_pci_regions(dev);
info->qdev_id = g_strdup(dev->qdev.id ? dev->qdev.id : "");
@@ -1572,7 +1610,8 @@ PciInfoList *qmp_query_pci(Error **errp)
QLIST_FOREACH(host_bridge, &pci_host_bridges, next) {
info = g_malloc0(sizeof(*info));
- info->value = qmp_query_pci_bus(host_bridge->bus, 0);
+ info->value = qmp_query_pci_bus(host_bridge->bus,
+ pci_bus_num(host_bridge->bus));
/* XXX: waiting for the qapi to support GSList */
if (!cur_item) {
@@ -1611,28 +1650,32 @@ static const char * const pci_nic_names[] = {
};
/* Initialize a PCI NIC. */
-static PCIDevice *pci_nic_init(NICInfo *nd, PCIBus *rootbus,
+PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus,
const char *default_model,
- const char *default_devaddr,
- Error **errp)
+ const char *default_devaddr)
{
const char *devaddr = nd->devaddr ? nd->devaddr : default_devaddr;
Error *err = NULL;
PCIBus *bus;
- int devfn;
PCIDevice *pci_dev;
DeviceState *dev;
+ int devfn;
int i;
+ if (qemu_show_nic_models(nd->model, pci_nic_models)) {
+ exit(0);
+ }
+
i = qemu_find_nic_model(nd, pci_nic_models, default_model);
- if (i < 0)
- return NULL;
+ if (i < 0) {
+ exit(1);
+ }
bus = pci_get_bus_devfn(&devfn, rootbus, devaddr);
if (!bus) {
error_report("Invalid PCI device address %s for device %s",
devaddr, pci_nic_names[i]);
- return NULL;
+ exit(1);
}
pci_dev = pci_create(bus, devfn, pci_nic_names[i]);
@@ -1641,31 +1684,12 @@ static PCIDevice *pci_nic_init(NICInfo *nd, PCIBus *rootbus,
object_property_set_bool(OBJECT(dev), true, "realized", &err);
if (err) {
- error_propagate(errp, err);
+ error_report_err(err);
object_unparent(OBJECT(dev));
- return NULL;
- }
- return pci_dev;
-}
-
-PCIDevice *pci_nic_init_nofail(NICInfo *nd, PCIBus *rootbus,
- const char *default_model,
- const char *default_devaddr)
-{
- Error *err = NULL;
- PCIDevice *res;
-
- if (qemu_show_nic_models(nd->model, pci_nic_models))
- exit(0);
-
- res = pci_nic_init(nd, rootbus, default_model, default_devaddr, &err);
- if (!res) {
- if (err) {
- error_report_err(err);
- }
exit(1);
}
- return res;
+
+ return pci_dev;
}
PCIDevice *pci_vga_init(PCIBus *bus)
@@ -1679,6 +1703,8 @@ PCIDevice *pci_vga_init(PCIBus *bus)
return pci_create_simple(bus, -1, "VGA");
case VGA_VMWARE:
return pci_create_simple(bus, -1, "vmware-svga");
+ case VGA_VIRTIO:
+ return pci_create_simple(bus, -1, "virtio-vga");
case VGA_NONE:
default: /* Other non-PCI types. Checking for unsupported types is already
done in vl.c. */
@@ -1692,10 +1718,28 @@ static bool pci_secondary_bus_in_range(PCIDevice *dev, int bus_num)
{
return !(pci_get_word(dev->config + PCI_BRIDGE_CONTROL) &
PCI_BRIDGE_CTL_BUS_RESET) /* Don't walk the bus if it's reset. */ &&
- dev->config[PCI_SECONDARY_BUS] < bus_num &&
+ dev->config[PCI_SECONDARY_BUS] <= bus_num &&
bus_num <= dev->config[PCI_SUBORDINATE_BUS];
}
+/* Whether a given bus number is in a range of a root bus */
+static bool pci_root_bus_in_range(PCIBus *bus, int bus_num)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) {
+ PCIDevice *dev = bus->devices[i];
+
+ if (dev && PCI_DEVICE_GET_CLASS(dev)->is_bridge) {
+ if (pci_secondary_bus_in_range(dev, bus_num)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num)
{
PCIBus *sec;
@@ -1717,12 +1761,18 @@ static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num)
/* try child bus */
for (; bus; bus = sec) {
QLIST_FOREACH(sec, &bus->child, sibling) {
- assert(!pci_bus_is_root(sec));
- if (sec->parent_dev->config[PCI_SECONDARY_BUS] == bus_num) {
+ if (pci_bus_num(sec) == bus_num) {
return sec;
}
- if (pci_secondary_bus_in_range(sec->parent_dev, bus_num)) {
- break;
+ /* PXB buses assumed to be children of bus 0 */
+ if (pci_bus_is_root(sec)) {
+ if (pci_root_bus_in_range(sec, bus_num)) {
+ break;
+ }
+ } else {
+ if (pci_secondary_bus_in_range(sec->parent_dev, bus_num)) {
+ break;
+ }
}
}
}
@@ -2051,12 +2101,10 @@ static void pci_del_option_rom(PCIDevice *pdev)
}
/*
- * if !offset
- * Reserve space and add capability to the linked list in pci config space
- *
* if offset = 0,
* Find and reserve space and add capability to the linked list
- * in pci config space */
+ * in pci config space
+ */
int pci_add_capability(PCIDevice *pdev, uint8_t cap_id,
uint8_t offset, uint8_t size)
{
diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c
index 1463e65b5..6e28985bd 100644
--- a/hw/pci/pcie.c
+++ b/hw/pci/pcie.c
@@ -26,7 +26,6 @@
#include "hw/pci/pci_bus.h"
#include "hw/pci/pcie_regs.h"
#include "qemu/range.h"
-#include "qapi/qmp/qerror.h"
//#define DEBUG_PCIE
#ifdef DEBUG_PCIE
@@ -79,7 +78,7 @@ int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port)
PCI_EXP_LNK_LS_25);
pci_set_word(exp_cap + PCI_EXP_LNKSTA,
- PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25);
+ PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25 |PCI_EXP_LNKSTA_DLLLA);
pci_set_long(exp_cap + PCI_EXP_DEVCAP2,
PCI_EXP_DEVCAP2_EFF | PCI_EXP_DEVCAP2_EETLPP);
diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c
index eaa3e6ea9..f1847ac21 100644
--- a/hw/pci/pcie_aer.c
+++ b/hw/pci/pcie_aer.c
@@ -410,7 +410,7 @@ static void pcie_aer_msg(PCIDevice *dev, const PCIEAERMsg *msg)
static void pcie_aer_update_log(PCIDevice *dev, const PCIEAERErr *err)
{
uint8_t *aer_cap = dev->config + dev->exp.aer_cap;
- uint8_t first_bit = ffs(err->status) - 1;
+ uint8_t first_bit = ctz32(err->status);
uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP);
int i;
@@ -815,21 +815,6 @@ const VMStateDescription vmstate_pcie_aer_log = {
}
};
-void pcie_aer_inject_error_print(Monitor *mon, const QObject *data)
-{
- QDict *qdict;
- int devfn;
- assert(qobject_type(data) == QTYPE_QDICT);
- qdict = qobject_to_qdict(data);
-
- devfn = (int)qdict_get_int(qdict, "devfn");
- monitor_printf(mon, "OK id: %s root bus: %s, bus: %x devfn: %x.%x\n",
- qdict_get_str(qdict, "id"),
- qdict_get_str(qdict, "root_bus"),
- (int) qdict_get_int(qdict, "bus"),
- PCI_SLOT(devfn), PCI_FUNC(devfn));
-}
-
typedef struct PCIEAERErrorName {
const char *name;
uint32_t val;
@@ -962,8 +947,8 @@ static int pcie_aer_parse_error_string(const char *error_name,
return -EINVAL;
}
-int hmp_pcie_aer_inject_error(Monitor *mon,
- const QDict *qdict, QObject **ret_data)
+static int do_pcie_aer_inject_error(Monitor *mon,
+ const QDict *qdict, QObject **ret_data)
{
const char *id = qdict_get_str(qdict, "id");
const char *error_name;
@@ -990,7 +975,7 @@ int hmp_pcie_aer_inject_error(Monitor *mon,
if (pcie_aer_parse_error_string(error_name, &error_status, &correctable)) {
char *e = NULL;
error_status = strtoul(error_name, &e, 0);
- correctable = qdict_get_try_bool(qdict, "correctable", 0);
+ correctable = qdict_get_try_bool(qdict, "correctable", false);
if (!e || *e != '\0') {
monitor_printf(mon, "invalid error status value. \"%s\"",
error_name);
@@ -1004,7 +989,7 @@ int hmp_pcie_aer_inject_error(Monitor *mon,
if (correctable) {
err.flags |= PCIE_AER_ERR_IS_CORRECTABLE;
}
- if (qdict_get_try_bool(qdict, "advisory_non_fatal", 0)) {
+ if (qdict_get_try_bool(qdict, "advisory_non_fatal", false)) {
err.flags |= PCIE_AER_ERR_MAYBE_ADVISORY;
}
if (qdict_haskey(qdict, "header0")) {
@@ -1035,3 +1020,23 @@ int hmp_pcie_aer_inject_error(Monitor *mon,
return 0;
}
+
+void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict)
+{
+ QObject *data;
+ int devfn;
+
+ if (do_pcie_aer_inject_error(mon, qdict, &data) < 0) {
+ return;
+ }
+
+ assert(qobject_type(data) == QTYPE_QDICT);
+ qdict = qobject_to_qdict(data);
+
+ devfn = (int)qdict_get_int(qdict, "devfn");
+ monitor_printf(mon, "OK id: %s root bus: %s, bus: %x devfn: %x.%x\n",
+ qdict_get_str(qdict, "id"),
+ qdict_get_str(qdict, "root_bus"),
+ (int) qdict_get_int(qdict, "bus"),
+ PCI_SLOT(devfn), PCI_FUNC(devfn));
+}
diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c
index 759910f79..bfb4d31b6 100644
--- a/hw/pci/shpc.c
+++ b/hw/pci/shpc.c
@@ -7,7 +7,6 @@
#include "hw/pci/pci.h"
#include "hw/pci/pci_bus.h"
#include "hw/pci/msi.h"
-#include "qapi/qmp/qerror.h"
/* TODO: model power only and disabled slot states. */
/* TODO: handle SERR and wakeups */
@@ -61,7 +60,7 @@
/* Same slot state masks are used for command and status registers */
#define SHPC_SLOT_STATE_MASK 0x03
#define SHPC_SLOT_STATE_SHIFT \
- (ffs(SHPC_SLOT_STATE_MASK) - 1)
+ ctz32(SHPC_SLOT_STATE_MASK)
#define SHPC_STATE_NO 0x0
#define SHPC_STATE_PWRONLY 0x1
@@ -70,10 +69,10 @@
#define SHPC_SLOT_PWR_LED_MASK 0xC
#define SHPC_SLOT_PWR_LED_SHIFT \
- (ffs(SHPC_SLOT_PWR_LED_MASK) - 1)
+ ctz32(SHPC_SLOT_PWR_LED_MASK)
#define SHPC_SLOT_ATTN_LED_MASK 0x30
#define SHPC_SLOT_ATTN_LED_SHIFT \
- (ffs(SHPC_SLOT_ATTN_LED_MASK) - 1)
+ ctz32(SHPC_SLOT_ATTN_LED_MASK)
#define SHPC_LED_NO 0x0
#define SHPC_LED_ON 0x1
@@ -136,7 +135,7 @@ static int roundup_pow_of_two(int x)
static uint16_t shpc_get_status(SHPCDevice *shpc, int slot, uint16_t msk)
{
uint8_t *status = shpc->config + SHPC_SLOT_STATUS(slot);
- return (pci_get_word(status) & msk) >> (ffs(msk) - 1);
+ return (pci_get_word(status) & msk) >> ctz32(msk);
}
static void shpc_set_status(SHPCDevice *shpc,
@@ -144,7 +143,7 @@ static void shpc_set_status(SHPCDevice *shpc,
{
uint8_t *status = shpc->config + SHPC_SLOT_STATUS(slot);
pci_word_test_and_clear_mask(status, msk);
- pci_word_test_and_set_mask(status, value << (ffs(msk) - 1));
+ pci_word_test_and_set_mask(status, value << ctz32(msk));
}
static void shpc_interrupt_update(PCIDevice *d)
diff --git a/hw/pci/slotid_cap.c b/hw/pci/slotid_cap.c
index 62f7bae2f..1c01d346c 100644
--- a/hw/pci/slotid_cap.c
+++ b/hw/pci/slotid_cap.c
@@ -3,7 +3,7 @@
#include "qemu/error-report.h"
#define SLOTID_CAP_LENGTH 4
-#define SLOTID_NSLOTS_SHIFT (ffs(PCI_SID_ESR_NSLOTS) - 1)
+#define SLOTID_NSLOTS_SHIFT ctz32(PCI_SID_ESR_NSLOTS)
int slotid_cap_init(PCIDevice *d, int nslots,
uint8_t chassis,
diff --git a/hw/pcmcia/pxa2xx.c b/hw/pcmcia/pxa2xx.c
index a7e187743..812716e1c 100644
--- a/hw/pcmcia/pxa2xx.c
+++ b/hw/pcmcia/pxa2xx.c
@@ -163,7 +163,7 @@ static void pxa2xx_pcmcia_initfn(Object *obj)
sysbus_init_mmio(sbd, &s->container_mem);
/* Socket I/O Memory Space */
- memory_region_init_io(&s->iomem, NULL, &pxa2xx_pcmcia_io_ops, s,
+ memory_region_init_io(&s->iomem, obj, &pxa2xx_pcmcia_io_ops, s,
"pxa2xx-pcmcia-io", 0x04000000);
memory_region_add_subregion(&s->container_mem, 0x00000000,
&s->iomem);
@@ -171,13 +171,13 @@ static void pxa2xx_pcmcia_initfn(Object *obj)
/* Then next 64 MB is reserved */
/* Socket Attribute Memory Space */
- memory_region_init_io(&s->attr_iomem, NULL, &pxa2xx_pcmcia_attr_ops, s,
+ memory_region_init_io(&s->attr_iomem, obj, &pxa2xx_pcmcia_attr_ops, s,
"pxa2xx-pcmcia-attribute", 0x04000000);
memory_region_add_subregion(&s->container_mem, 0x08000000,
&s->attr_iomem);
/* Socket Common Memory Space */
- memory_region_init_io(&s->common_iomem, NULL, &pxa2xx_pcmcia_common_ops, s,
+ memory_region_init_io(&s->common_iomem, obj, &pxa2xx_pcmcia_common_ops, s,
"pxa2xx-pcmcia-common", 0x04000000);
memory_region_add_subregion(&s->container_mem, 0x0c000000,
&s->common_iomem);
diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index 437955d1d..c8ab06e7f 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -3,7 +3,7 @@ obj-y += ppc.o ppc_booke.o
# IBM pSeries (sPAPR)
obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o
obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
-obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o
+obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o
ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
obj-y += spapr_pci_vfio.o
endif
diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
index c10e1b57b..d300846c3 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -1030,6 +1030,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
exit(1);
}
}
+ g_free(filename);
/* Reserve space for dtb */
dt_base = (loadaddr + bios_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK;
diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c
index a365bf922..77d5c819e 100644
--- a/hw/ppc/mac_newworld.c
+++ b/hw/ppc/mac_newworld.c
@@ -119,7 +119,7 @@ static const MemoryRegionOps unin_ops = {
static void fw_cfg_boot_set(void *opaque, const char *boot_device,
Error **errp)
{
- fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
+ fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
}
static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
@@ -145,7 +145,6 @@ static void ppc_core99_reset(void *opaque)
static void ppc_core99_init(MachineState *machine)
{
ram_addr_t ram_size = machine->ram_size;
- const char *cpu_model = machine->cpu_model;
const char *kernel_filename = machine->kernel_filename;
const char *kernel_cmdline = machine->kernel_cmdline;
const char *initrd_filename = machine->initrd_filename;
@@ -182,14 +181,15 @@ static void ppc_core99_init(MachineState *machine)
linux_boot = (kernel_filename != NULL);
/* init CPUs */
- if (cpu_model == NULL)
+ if (machine->cpu_model == NULL) {
#ifdef TARGET_PPC64
- cpu_model = "970fx";
+ machine->cpu_model = "970fx";
#else
- cpu_model = "G4";
+ machine->cpu_model = "G4";
#endif
+ }
for (i = 0; i < smp_cpus; i++) {
- cpu = cpu_ppc_init(cpu_model);
+ cpu = cpu_ppc_init(machine->cpu_model);
if (cpu == NULL) {
fprintf(stderr, "Unable to find PowerPC CPU definition\n");
exit(1);
diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c
index f26133ded..06fdbaf58 100644
--- a/hw/ppc/mac_oldworld.c
+++ b/hw/ppc/mac_oldworld.c
@@ -52,7 +52,7 @@
static void fw_cfg_boot_set(void *opaque, const char *boot_device,
Error **errp)
{
- fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
+ fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
}
static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
@@ -75,7 +75,6 @@ static void ppc_heathrow_reset(void *opaque)
static void ppc_heathrow_init(MachineState *machine)
{
ram_addr_t ram_size = machine->ram_size;
- const char *cpu_model = machine->cpu_model;
const char *kernel_filename = machine->kernel_filename;
const char *kernel_cmdline = machine->kernel_cmdline;
const char *initrd_filename = machine->initrd_filename;
@@ -107,10 +106,10 @@ static void ppc_heathrow_init(MachineState *machine)
linux_boot = (kernel_filename != NULL);
/* init CPUs */
- if (cpu_model == NULL)
- cpu_model = "G3";
+ if (machine->cpu_model == NULL)
+ machine->cpu_model = "G3";
for (i = 0; i < smp_cpus; i++) {
- cpu = cpu_ppc_init(cpu_model);
+ cpu = cpu_ppc_init(machine->cpu_model);
if (cpu == NULL) {
fprintf(stderr, "Unable to find PowerPC CPU definition\n");
exit(1);
diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
index 99db56c8d..b77e30357 100644
--- a/hw/ppc/ppc.c
+++ b/hw/ppc/ppc.c
@@ -51,8 +51,6 @@
# define LOG_TB(...) do { } while (0)
#endif
-#define NSEC_PER_SEC 1000000000LL
-
static void cpu_ppc_tb_stop (CPUPPCState *env);
static void cpu_ppc_tb_start (CPUPPCState *env);
@@ -875,8 +873,9 @@ static int timebase_post_load(void *opaque, int version_id)
*/
host_ns = qemu_clock_get_ns(QEMU_CLOCK_HOST);
ns_diff = MAX(0, host_ns - tb_remote->time_of_the_day_ns);
- migration_duration_ns = MIN(NSEC_PER_SEC, ns_diff);
- migration_duration_tb = muldiv64(migration_duration_ns, freq, NSEC_PER_SEC);
+ migration_duration_ns = MIN(NANOSECONDS_PER_SECOND, ns_diff);
+ migration_duration_tb = muldiv64(migration_duration_ns, freq,
+ NANOSECONDS_PER_SECOND);
guest_tb = tb_remote->guest_timebase + MIN(0, migration_duration_tb);
tb_off_adj = guest_tb - cpu_get_real_ticks();
diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c
index 778970aa9..032fa803d 100644
--- a/hw/ppc/ppc440_bamboo.c
+++ b/hw/ppc/ppc440_bamboo.c
@@ -159,7 +159,6 @@ static void main_cpu_reset(void *opaque)
static void bamboo_init(MachineState *machine)
{
ram_addr_t ram_size = machine->ram_size;
- const char *cpu_model = machine->cpu_model;
const char *kernel_filename = machine->kernel_filename;
const char *kernel_cmdline = machine->kernel_cmdline;
const char *initrd_filename = machine->initrd_filename;
@@ -184,10 +183,10 @@ static void bamboo_init(MachineState *machine)
int i;
/* Setup CPU. */
- if (cpu_model == NULL) {
- cpu_model = "440EP";
+ if (machine->cpu_model == NULL) {
+ machine->cpu_model = "440EP";
}
- cpu = cpu_ppc_init(cpu_model);
+ cpu = cpu_ppc_init(machine->cpu_model);
if (cpu == NULL) {
fprintf(stderr, "Unable to initialize CPU!\n");
exit(1);
diff --git a/hw/ppc/ppce500_spin.c b/hw/ppc/ppce500_spin.c
index d49f2b880..a99f7b039 100644
--- a/hw/ppc/ppce500_spin.c
+++ b/hw/ppc/ppce500_spin.c
@@ -74,7 +74,7 @@ static void spin_reset(void *opaque)
/* Create -kernel TLB entries for BookE, linearly spanning 256MB. */
static inline hwaddr booke206_page_size_to_tlb(uint64_t size)
{
- return (ffs(size >> 10) - 1) >> 1;
+ return ctz32(size >> 10) >> 1;
}
static void mmubooke_create_initial_mapping(CPUPPCState *env,
diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c
index 7f52662d7..45b5f62d6 100644
--- a/hw/ppc/prep.c
+++ b/hw/ppc/prep.c
@@ -506,7 +506,6 @@ static int PPC_NVRAM_set_params (Nvram *nvram, uint16_t NVRAM_size,
static void ppc_prep_init(MachineState *machine)
{
ram_addr_t ram_size = machine->ram_size;
- const char *cpu_model = machine->cpu_model;
const char *kernel_filename = machine->kernel_filename;
const char *kernel_cmdline = machine->kernel_cmdline;
const char *initrd_filename = machine->initrd_filename;
@@ -528,7 +527,6 @@ static void ppc_prep_init(MachineState *machine)
PCIDevice *pci;
ISABus *isa_bus;
ISADevice *isa;
- qemu_irq *cpu_exit_irq;
int ppc_boot_device;
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
@@ -537,10 +535,10 @@ static void ppc_prep_init(MachineState *machine)
linux_boot = (kernel_filename != NULL);
/* init CPUs */
- if (cpu_model == NULL)
- cpu_model = "602";
+ if (machine->cpu_model == NULL)
+ machine->cpu_model = "602";
for (i = 0; i < smp_cpus; i++) {
- cpu = cpu_ppc_init(cpu_model);
+ cpu = cpu_ppc_init(machine->cpu_model);
if (cpu == NULL) {
fprintf(stderr, "Unable to find PowerPC CPU definition\n");
exit(1);
@@ -625,11 +623,11 @@ static void ppc_prep_init(MachineState *machine)
/* PCI -> ISA bridge */
pci = pci_create_simple(pci_bus, PCI_DEVFN(1, 0), "i82378");
- cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
cpu = POWERPC_CPU(first_cpu);
qdev_connect_gpio_out(&pci->qdev, 0,
cpu->env.irq_inputs[PPC6xx_INPUT_INT]);
- qdev_connect_gpio_out(&pci->qdev, 1, *cpu_exit_irq);
+ qdev_connect_gpio_out(&pci->qdev, 1,
+ qemu_allocate_irq(cpu_request_exit, NULL, 0));
sysbus_connect_irq(&pcihost->busdev, 0, qdev_get_gpio_in(&pci->qdev, 9));
sysbus_connect_irq(&pcihost->busdev, 1, qdev_get_gpio_in(&pci->qdev, 11));
sysbus_connect_irq(&pcihost->busdev, 2, qdev_get_gpio_in(&pci->qdev, 9));
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 61ddc7994..a6f19473c 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -34,6 +34,7 @@
#include "sysemu/cpus.h"
#include "sysemu/kvm.h"
#include "kvm_ppc.h"
+#include "migration/migration.h"
#include "mmu-hash64.h"
#include "qom/cpu.h"
@@ -90,25 +91,6 @@
#define HTAB_SIZE(spapr) (1ULL << ((spapr)->htab_shift))
-typedef struct sPAPRMachineState sPAPRMachineState;
-
-#define TYPE_SPAPR_MACHINE "spapr-machine"
-#define SPAPR_MACHINE(obj) \
- OBJECT_CHECK(sPAPRMachineState, (obj), TYPE_SPAPR_MACHINE)
-
-/**
- * sPAPRMachineState:
- */
-struct sPAPRMachineState {
- /*< private >*/
- MachineState parent_obj;
-
- /*< public >*/
- char *kvm_type;
-};
-
-sPAPREnvironment *spapr;
-
static XICSState *try_create_xics(const char *type, int nr_servers,
int nr_irqs, Error **errp)
{
@@ -184,7 +166,28 @@ static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu,
return ret;
}
-static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr)
+static int spapr_fixup_cpu_numa_dt(void *fdt, int offset, CPUState *cs)
+{
+ int ret = 0;
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
+ int index = ppc_get_vcpu_dt_id(cpu);
+ uint32_t associativity[] = {cpu_to_be32(0x5),
+ cpu_to_be32(0x0),
+ cpu_to_be32(0x0),
+ cpu_to_be32(0x0),
+ cpu_to_be32(cs->numa_node),
+ cpu_to_be32(index)};
+
+ /* Advertise NUMA via ibm,associativity */
+ if (nb_numa_nodes > 1) {
+ ret = fdt_setprop(fdt, offset, "ibm,associativity", associativity,
+ sizeof(associativity));
+ }
+
+ return ret;
+}
+
+static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr)
{
int ret = 0, offset, cpus_offset;
CPUState *cs;
@@ -196,12 +199,6 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr)
PowerPCCPU *cpu = POWERPC_CPU(cs);
DeviceClass *dc = DEVICE_GET_CLASS(cs);
int index = ppc_get_vcpu_dt_id(cpu);
- uint32_t associativity[] = {cpu_to_be32(0x5),
- cpu_to_be32(0x0),
- cpu_to_be32(0x0),
- cpu_to_be32(0x0),
- cpu_to_be32(cs->numa_node),
- cpu_to_be32(index)};
if ((index % smt) != 0) {
continue;
@@ -225,20 +222,17 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr)
}
}
- 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;
}
+ ret = spapr_fixup_cpu_numa_dt(fdt, offset, cs);
+ if (ret < 0) {
+ return ret;
+ }
+
ret = spapr_fixup_cpu_smt_dt(fdt, offset, cpu,
ppc_get_compat_smt_threads(cpu));
if (ret < 0) {
@@ -284,15 +278,18 @@ static size_t create_page_sizes_prop(CPUPPCState *env, uint32_t *prop,
static hwaddr spapr_node0_size(void)
{
+ MachineState *machine = MACHINE(qdev_get_machine());
+
if (nb_numa_nodes) {
int i;
for (i = 0; i < nb_numa_nodes; ++i) {
if (numa_info[i].node_mem) {
- return MIN(pow2floor(numa_info[i].node_mem), ram_size);
+ return MIN(pow2floor(numa_info[i].node_mem),
+ machine->ram_size);
}
}
}
- return ram_size;
+ return machine->ram_size;
}
#define _FDT(exp) \
@@ -318,18 +315,13 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base,
uint32_t epow_irq)
{
void *fdt;
- CPUState *cs;
uint32_t start_prop = cpu_to_be32(initrd_base);
uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
GString *hypertas = g_string_sized_new(256);
GString *qemu_hypertas = g_string_sized_new(256);
uint32_t refpoints[] = {cpu_to_be32(0x4), cpu_to_be32(0x4)};
- uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)};
- int smt = kvmppc_smt_threads();
+ uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(max_cpus)};
unsigned char vec5[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x80};
- QemuOpts *opts = qemu_opts_find(qemu_find_opts("smp-opts"), NULL);
- unsigned sockets = opts ? qemu_opt_get_number(opts, "sockets", 0) : 0;
- uint32_t cpus_per_socket = sockets ? (smp_cpus / sockets) : 1;
char *buf;
add_str(hypertas, "hcall-pft");
@@ -415,107 +407,6 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base,
_FDT((fdt_end_node(fdt)));
- /* cpus */
- _FDT((fdt_begin_node(fdt, "cpus")));
-
- _FDT((fdt_property_cell(fdt, "#address-cells", 0x1)));
- _FDT((fdt_property_cell(fdt, "#size-cells", 0x0)));
-
- CPU_FOREACH(cs) {
- PowerPCCPU *cpu = POWERPC_CPU(cs);
- CPUPPCState *env = &cpu->env;
- DeviceClass *dc = DEVICE_GET_CLASS(cs);
- PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs);
- int index = ppc_get_vcpu_dt_id(cpu);
- char *nodename;
- uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40),
- 0xffffffff, 0xffffffff};
- uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() : TIMEBASE_FREQ;
- uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000;
- uint32_t page_sizes_prop[64];
- size_t page_sizes_prop_size;
-
- if ((index % smt) != 0) {
- continue;
- }
-
- nodename = g_strdup_printf("%s@%x", dc->fw_name, index);
-
- _FDT((fdt_begin_node(fdt, nodename)));
-
- g_free(nodename);
-
- _FDT((fdt_property_cell(fdt, "reg", index)));
- _FDT((fdt_property_string(fdt, "device_type", "cpu")));
-
- _FDT((fdt_property_cell(fdt, "cpu-version", env->spr[SPR_PVR])));
- _FDT((fdt_property_cell(fdt, "d-cache-block-size",
- env->dcache_line_size)));
- _FDT((fdt_property_cell(fdt, "d-cache-line-size",
- env->dcache_line_size)));
- _FDT((fdt_property_cell(fdt, "i-cache-block-size",
- env->icache_line_size)));
- _FDT((fdt_property_cell(fdt, "i-cache-line-size",
- env->icache_line_size)));
-
- if (pcc->l1_dcache_size) {
- _FDT((fdt_property_cell(fdt, "d-cache-size", pcc->l1_dcache_size)));
- } else {
- fprintf(stderr, "Warning: Unknown L1 dcache size for cpu\n");
- }
- if (pcc->l1_icache_size) {
- _FDT((fdt_property_cell(fdt, "i-cache-size", pcc->l1_icache_size)));
- } else {
- fprintf(stderr, "Warning: Unknown L1 icache size for cpu\n");
- }
-
- _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_string(fdt, "status", "okay")));
- _FDT((fdt_property(fdt, "64-bit", NULL, 0)));
-
- if (env->spr_cb[SPR_PURR].oea_read) {
- _FDT((fdt_property(fdt, "ibm,purr", NULL, 0)));
- }
-
- if (env->mmu_model & POWERPC_MMU_1TSEG) {
- _FDT((fdt_property(fdt, "ibm,processor-segment-sizes",
- segs, sizeof(segs))));
- }
-
- /* Advertise VMX/VSX (vector extensions) if available
- * 0 / no property == no vector extensions
- * 1 == VMX / Altivec available
- * 2 == VSX available */
- if (env->insns_flags & PPC_ALTIVEC) {
- uint32_t vmx = (env->insns_flags2 & PPC2_VSX) ? 2 : 1;
-
- _FDT((fdt_property_cell(fdt, "ibm,vmx", vmx)));
- }
-
- /* Advertise DFP (Decimal Floating Point) if available
- * 0 / no property == no DFP
- * 1 == DFP available */
- if (env->insns_flags2 & PPC2_DFP) {
- _FDT((fdt_property_cell(fdt, "ibm,dfp", 1)));
- }
-
- page_sizes_prop_size = create_page_sizes_prop(env, page_sizes_prop,
- sizeof(page_sizes_prop));
- if (page_sizes_prop_size) {
- _FDT((fdt_property(fdt, "ibm,segment-page-sizes",
- page_sizes_prop, page_sizes_prop_size)));
- }
-
- _FDT((fdt_property_cell(fdt, "ibm,chip-id",
- cs->cpu_index / cpus_per_socket)));
-
- _FDT((fdt_end_node(fdt)));
- }
-
- _FDT((fdt_end_node(fdt)));
-
/* RTAS */
_FDT((fdt_begin_node(fdt, "rtas")));
@@ -533,6 +424,8 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base,
refpoints, sizeof(refpoints))));
_FDT((fdt_property_cell(fdt, "rtas-error-log-max", RTAS_ERROR_LOG_MAX)));
+ _FDT((fdt_property_cell(fdt, "rtas-event-scan-rate",
+ RTAS_EVENT_SCAN_RATE)));
/*
* According to PAPR, rtas ibm,os-term does not guarantee a return
@@ -602,7 +495,8 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base,
return fdt;
}
-int spapr_h_cas_compose_response(target_ulong addr, target_ulong size)
+int spapr_h_cas_compose_response(sPAPRMachineState *spapr,
+ target_ulong addr, target_ulong size)
{
void *fdt, *fdt_skel;
sPAPRDeviceTreeUpdateHeader hdr = { .version_id = 1 };
@@ -663,8 +557,9 @@ static void spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start,
sizeof(associativity))));
}
-static int spapr_populate_memory(sPAPREnvironment *spapr, void *fdt)
+static int spapr_populate_memory(sPAPRMachineState *spapr, void *fdt)
{
+ MachineState *machine = MACHINE(spapr);
hwaddr mem_start, node_size;
int i, nb_nodes = nb_numa_nodes;
NodeInfo *nodes = numa_info;
@@ -673,7 +568,7 @@ static int spapr_populate_memory(sPAPREnvironment *spapr, void *fdt)
/* No NUMA nodes, assume there is just one node with whole RAM */
if (!nb_numa_nodes) {
nb_nodes = 1;
- ramnode.node_mem = ram_size;
+ ramnode.node_mem = machine->ram_size;
nodes = &ramnode;
}
@@ -681,12 +576,12 @@ static int spapr_populate_memory(sPAPREnvironment *spapr, void *fdt)
if (!nodes[i].node_mem) {
continue;
}
- if (mem_start >= ram_size) {
+ if (mem_start >= machine->ram_size) {
node_size = 0;
} else {
node_size = nodes[i].node_mem;
- if (node_size > ram_size - mem_start) {
- node_size = ram_size - mem_start;
+ if (node_size > machine->ram_size - mem_start) {
+ node_size = machine->ram_size - mem_start;
}
}
if (!mem_start) {
@@ -712,7 +607,138 @@ static int spapr_populate_memory(sPAPREnvironment *spapr, void *fdt)
return 0;
}
-static void spapr_finalize_fdt(sPAPREnvironment *spapr,
+static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset,
+ sPAPRMachineState *spapr)
+{
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
+ CPUPPCState *env = &cpu->env;
+ PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs);
+ int index = ppc_get_vcpu_dt_id(cpu);
+ uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40),
+ 0xffffffff, 0xffffffff};
+ uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() : TIMEBASE_FREQ;
+ uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000;
+ uint32_t page_sizes_prop[64];
+ size_t page_sizes_prop_size;
+ QemuOpts *opts = qemu_opts_find(qemu_find_opts("smp-opts"), NULL);
+ unsigned sockets = opts ? qemu_opt_get_number(opts, "sockets", 0) : 0;
+ uint32_t cpus_per_socket = sockets ? (smp_cpus / sockets) : 1;
+ uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)};
+
+ _FDT((fdt_setprop_cell(fdt, offset, "reg", index)));
+ _FDT((fdt_setprop_string(fdt, offset, "device_type", "cpu")));
+
+ _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", env->spr[SPR_PVR])));
+ _FDT((fdt_setprop_cell(fdt, offset, "d-cache-block-size",
+ env->dcache_line_size)));
+ _FDT((fdt_setprop_cell(fdt, offset, "d-cache-line-size",
+ env->dcache_line_size)));
+ _FDT((fdt_setprop_cell(fdt, offset, "i-cache-block-size",
+ env->icache_line_size)));
+ _FDT((fdt_setprop_cell(fdt, offset, "i-cache-line-size",
+ env->icache_line_size)));
+
+ if (pcc->l1_dcache_size) {
+ _FDT((fdt_setprop_cell(fdt, offset, "d-cache-size",
+ pcc->l1_dcache_size)));
+ } else {
+ fprintf(stderr, "Warning: Unknown L1 dcache size for cpu\n");
+ }
+ if (pcc->l1_icache_size) {
+ _FDT((fdt_setprop_cell(fdt, offset, "i-cache-size",
+ pcc->l1_icache_size)));
+ } else {
+ fprintf(stderr, "Warning: Unknown L1 icache size for cpu\n");
+ }
+
+ _FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq)));
+ _FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq)));
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", env->slb_nr)));
+ _FDT((fdt_setprop_string(fdt, offset, "status", "okay")));
+ _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0)));
+
+ if (env->spr_cb[SPR_PURR].oea_read) {
+ _FDT((fdt_setprop(fdt, offset, "ibm,purr", NULL, 0)));
+ }
+
+ if (env->mmu_model & POWERPC_MMU_1TSEG) {
+ _FDT((fdt_setprop(fdt, offset, "ibm,processor-segment-sizes",
+ segs, sizeof(segs))));
+ }
+
+ /* Advertise VMX/VSX (vector extensions) if available
+ * 0 / no property == no vector extensions
+ * 1 == VMX / Altivec available
+ * 2 == VSX available */
+ if (env->insns_flags & PPC_ALTIVEC) {
+ uint32_t vmx = (env->insns_flags2 & PPC2_VSX) ? 2 : 1;
+
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", vmx)));
+ }
+
+ /* Advertise DFP (Decimal Floating Point) if available
+ * 0 / no property == no DFP
+ * 1 == DFP available */
+ if (env->insns_flags2 & PPC2_DFP) {
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,dfp", 1)));
+ }
+
+ page_sizes_prop_size = create_page_sizes_prop(env, page_sizes_prop,
+ sizeof(page_sizes_prop));
+ if (page_sizes_prop_size) {
+ _FDT((fdt_setprop(fdt, offset, "ibm,segment-page-sizes",
+ page_sizes_prop, page_sizes_prop_size)));
+ }
+
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id",
+ cs->cpu_index / cpus_per_socket)));
+
+ _FDT((fdt_setprop(fdt, offset, "ibm,pft-size",
+ pft_size_prop, sizeof(pft_size_prop))));
+
+ _FDT(spapr_fixup_cpu_numa_dt(fdt, offset, cs));
+
+ _FDT(spapr_fixup_cpu_smt_dt(fdt, offset, cpu,
+ ppc_get_compat_smt_threads(cpu)));
+}
+
+static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr)
+{
+ CPUState *cs;
+ int cpus_offset;
+ char *nodename;
+ int smt = kvmppc_smt_threads();
+
+ cpus_offset = fdt_add_subnode(fdt, 0, "cpus");
+ _FDT(cpus_offset);
+ _FDT((fdt_setprop_cell(fdt, cpus_offset, "#address-cells", 0x1)));
+ _FDT((fdt_setprop_cell(fdt, cpus_offset, "#size-cells", 0x0)));
+
+ /*
+ * We walk the CPUs in reverse order to ensure that CPU DT nodes
+ * created by fdt_add_subnode() end up in the right order in FDT
+ * for the guest kernel the enumerate the CPUs correctly.
+ */
+ CPU_FOREACH_REVERSE(cs) {
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
+ int index = ppc_get_vcpu_dt_id(cpu);
+ DeviceClass *dc = DEVICE_GET_CLASS(cs);
+ int offset;
+
+ if ((index % smt) != 0) {
+ continue;
+ }
+
+ nodename = g_strdup_printf("%s@%x", dc->fw_name, index);
+ offset = fdt_add_subnode(fdt, cpus_offset, nodename);
+ g_free(nodename);
+ _FDT(offset);
+ spapr_populate_cpu_dt(cs, fdt, offset, spapr);
+ }
+
+}
+
+static void spapr_finalize_fdt(sPAPRMachineState *spapr,
hwaddr fdt_addr,
hwaddr rtas_addr,
hwaddr rtas_size)
@@ -757,11 +783,8 @@ static void spapr_finalize_fdt(sPAPREnvironment *spapr,
fprintf(stderr, "Couldn't set up RTAS device tree properties\n");
}
- /* Advertise NUMA via ibm,associativity */
- ret = spapr_fixup_cpu_dt(fdt, spapr);
- if (ret < 0) {
- fprintf(stderr, "Couldn't finalize CPU device tree properties\n");
- }
+ /* cpus */
+ spapr_populate_cpus_dt_node(fdt, spapr);
bootlist = get_boot_devices_list(&cb, true);
if (cb && bootlist) {
@@ -794,8 +817,8 @@ static void spapr_finalize_fdt(sPAPREnvironment *spapr,
_FDT((fdt_pack(fdt)));
if (fdt_totalsize(fdt) > FDT_MAX_SIZE) {
- hw_error("FDT too big ! 0x%x bytes (max is 0x%x)\n",
- fdt_totalsize(fdt), FDT_MAX_SIZE);
+ error_report("FDT too big ! 0x%x bytes (max is 0x%x)",
+ fdt_totalsize(fdt), FDT_MAX_SIZE);
exit(1);
}
@@ -828,7 +851,7 @@ static void emulate_spapr_hypercall(PowerPCCPU *cpu)
#define CLEAN_HPTE(_hpte) ((*(uint64_t *)(_hpte)) &= tswap64(~HPTE64_V_HPTE_DIRTY))
#define DIRTY_HPTE(_hpte) ((*(uint64_t *)(_hpte)) |= tswap64(HPTE64_V_HPTE_DIRTY))
-static void spapr_reset_htab(sPAPREnvironment *spapr)
+static void spapr_reset_htab(sPAPRMachineState *spapr)
{
long shift;
int index;
@@ -890,7 +913,7 @@ static int find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque)
* A guest reset will cause spapr->htab_fd to become stale if being used.
* Reopen the file descriptor to make sure the whole HTAB is properly read.
*/
-static int spapr_check_htab_fd(sPAPREnvironment *spapr)
+static int spapr_check_htab_fd(sPAPRMachineState *spapr)
{
int rc = 0;
@@ -899,7 +922,7 @@ static int spapr_check_htab_fd(sPAPREnvironment *spapr)
spapr->htab_fd = kvmppc_get_htab_fd(false);
if (spapr->htab_fd < 0) {
error_report("Unable to open fd for reading hash table from KVM: "
- "%s", strerror(errno));
+ "%s", strerror(errno));
rc = -1;
}
spapr->htab_fd_stale = false;
@@ -910,6 +933,7 @@ static int spapr_check_htab_fd(sPAPREnvironment *spapr)
static void ppc_spapr_reset(void)
{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
PowerPCCPU *first_ppc_cpu;
uint32_t rtas_limit;
@@ -943,12 +967,13 @@ static void ppc_spapr_reset(void)
first_ppc_cpu->env.gpr[3] = spapr->fdt_addr;
first_ppc_cpu->env.gpr[5] = 0;
first_cpu->halted = 0;
- first_ppc_cpu->env.nip = spapr->entry_point;
+ first_ppc_cpu->env.nip = SPAPR_ENTRY_POINT;
}
static void spapr_cpu_reset(void *opaque)
{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
PowerPCCPU *cpu = opaque;
CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
@@ -977,12 +1002,12 @@ static void spapr_cpu_reset(void *opaque)
* We have 8 hpte per group, and each hpte is 16 bytes.
* ie have 128 bytes per hpte entry.
*/
- env->htab_mask = (1ULL << ((spapr)->htab_shift - 7)) - 1;
+ env->htab_mask = (1ULL << (spapr->htab_shift - 7)) - 1;
env->spr[SPR_SDR1] = (target_ulong)(uintptr_t)spapr->htab |
(spapr->htab_shift - 18);
}
-static void spapr_create_nvram(sPAPREnvironment *spapr)
+static void spapr_create_nvram(sPAPRMachineState *spapr)
{
DeviceState *dev = qdev_create(&spapr->vio_bus->bus, "spapr-nvram");
DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0);
@@ -996,7 +1021,7 @@ static void spapr_create_nvram(sPAPREnvironment *spapr)
spapr->nvram = (struct sPAPRNVRAM *)dev;
}
-static void spapr_rtc_create(sPAPREnvironment *spapr)
+static void spapr_rtc_create(sPAPRMachineState *spapr)
{
DeviceState *dev = qdev_create(NULL, TYPE_SPAPR_RTC);
@@ -1026,10 +1051,10 @@ static int spapr_vga_init(PCIBus *pci_bus)
static int spapr_post_load(void *opaque, int version_id)
{
- sPAPREnvironment *spapr = (sPAPREnvironment *)opaque;
+ sPAPRMachineState *spapr = (sPAPRMachineState *)opaque;
int err = 0;
- /* In earlier versions, there was no seperate qdev for the PAPR
+ /* In earlier versions, there was no separate qdev for the PAPR
* RTC, so the RTC offset was stored directly in sPAPREnvironment.
* So when migrating from those versions, poke the incoming offset
* value into the RTC device */
@@ -1055,16 +1080,16 @@ static const VMStateDescription vmstate_spapr = {
VMSTATE_UNUSED_BUFFER(version_before_3, 0, 4),
/* RTC offset */
- VMSTATE_UINT64_TEST(rtc_offset, sPAPREnvironment, version_before_3),
+ VMSTATE_UINT64_TEST(rtc_offset, sPAPRMachineState, version_before_3),
- VMSTATE_PPC_TIMEBASE_V(tb, sPAPREnvironment, 2),
+ VMSTATE_PPC_TIMEBASE_V(tb, sPAPRMachineState, 2),
VMSTATE_END_OF_LIST()
},
};
static int htab_save_setup(QEMUFile *f, void *opaque)
{
- sPAPREnvironment *spapr = opaque;
+ sPAPRMachineState *spapr = opaque;
/* "Iteration" header */
qemu_put_be32(f, spapr->htab_shift);
@@ -1088,7 +1113,7 @@ static int htab_save_setup(QEMUFile *f, void *opaque)
return 0;
}
-static void htab_save_first_pass(QEMUFile *f, sPAPREnvironment *spapr,
+static void htab_save_first_pass(QEMUFile *f, sPAPRMachineState *spapr,
int64_t max_ns)
{
int htabslots = HTAB_SIZE(spapr) / HASH_PTE_SIZE_64;
@@ -1138,7 +1163,7 @@ static void htab_save_first_pass(QEMUFile *f, sPAPREnvironment *spapr,
spapr->htab_save_index = index;
}
-static int htab_save_later_pass(QEMUFile *f, sPAPREnvironment *spapr,
+static int htab_save_later_pass(QEMUFile *f, sPAPRMachineState *spapr,
int64_t max_ns)
{
bool final = max_ns < 0;
@@ -1220,7 +1245,7 @@ static int htab_save_later_pass(QEMUFile *f, sPAPREnvironment *spapr,
static int htab_save_iterate(QEMUFile *f, void *opaque)
{
- sPAPREnvironment *spapr = opaque;
+ sPAPRMachineState *spapr = opaque;
int rc = 0;
/* Iteration header */
@@ -1255,7 +1280,7 @@ static int htab_save_iterate(QEMUFile *f, void *opaque)
static int htab_save_complete(QEMUFile *f, void *opaque)
{
- sPAPREnvironment *spapr = opaque;
+ sPAPRMachineState *spapr = opaque;
/* Iteration header */
qemu_put_be32(f, 0);
@@ -1290,7 +1315,7 @@ static int htab_save_complete(QEMUFile *f, void *opaque)
static int htab_load(QEMUFile *f, void *opaque, int version_id)
{
- sPAPREnvironment *spapr = opaque;
+ sPAPRMachineState *spapr = opaque;
uint32_t section_hdr;
int fd = -1;
@@ -1384,16 +1409,42 @@ static void spapr_boot_set(void *opaque, const char *boot_device,
machine->boot_order = g_strdup(boot_device);
}
+static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu)
+{
+ CPUPPCState *env = &cpu->env;
+
+ /* Set time-base frequency to 512 MHz */
+ cpu_ppc_tb_init(env, TIMEBASE_FREQ);
+
+ /* PAPR always has exception vectors in RAM not ROM. To ensure this,
+ * MSR[IP] should never be set.
+ */
+ env->msr_mask &= ~(1 << 6);
+
+ /* Tell KVM that we're in PAPR mode */
+ if (kvm_enabled()) {
+ kvmppc_set_papr(cpu);
+ }
+
+ if (cpu->max_compat) {
+ if (ppc_set_compat(cpu, cpu->max_compat) < 0) {
+ exit(1);
+ }
+ }
+
+ xics_cpu_setup(spapr->icp, cpu);
+
+ qemu_register_reset(spapr_cpu_reset, cpu);
+}
+
/* pSeries LPAR / sPAPR hardware init */
static void ppc_spapr_init(MachineState *machine)
{
- ram_addr_t ram_size = machine->ram_size;
- const char *cpu_model = machine->cpu_model;
+ sPAPRMachineState *spapr = SPAPR_MACHINE(machine);
const char *kernel_filename = machine->kernel_filename;
const char *kernel_cmdline = machine->kernel_cmdline;
const char *initrd_filename = machine->initrd_filename;
PowerPCCPU *cpu;
- CPUPPCState *env;
PCIHostState *phb;
int i;
MemoryRegion *sysmem = get_system_memory();
@@ -1410,7 +1461,6 @@ static void ppc_spapr_init(MachineState *machine)
msi_supported = true;
- spapr = g_malloc0(sizeof(*spapr));
QLIST_INIT(&spapr->phbs);
cpu_ppc_hypercall = emulate_spapr_hypercall;
@@ -1419,7 +1469,7 @@ static void ppc_spapr_init(MachineState *machine)
rma_alloc_size = kvmppc_alloc_rma(&rma);
if (rma_alloc_size == -1) {
- hw_error("qemu: Unable to create RMA\n");
+ error_report("Unable to create RMA");
exit(1);
}
@@ -1457,7 +1507,7 @@ static void ppc_spapr_init(MachineState *machine)
* 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) {
+ if ((1ULL << (spapr->htab_shift + 7)) >= machine->ram_size) {
break;
}
spapr->htab_shift++;
@@ -1465,49 +1515,31 @@ static void ppc_spapr_init(MachineState *machine)
/* Set up Interrupt Controller before we create the VCPUs */
spapr->icp = xics_system_init(machine,
- smp_cpus * kvmppc_smt_threads() / smp_threads,
+ DIV_ROUND_UP(max_cpus * kvmppc_smt_threads(),
+ smp_threads),
XICS_IRQS);
/* init CPUs */
- if (cpu_model == NULL) {
- cpu_model = kvm_enabled() ? "host" : "POWER7";
+ if (machine->cpu_model == NULL) {
+ machine->cpu_model = kvm_enabled() ? "host" : "POWER7";
}
for (i = 0; i < smp_cpus; i++) {
- cpu = cpu_ppc_init(cpu_model);
+ cpu = cpu_ppc_init(machine->cpu_model);
if (cpu == NULL) {
fprintf(stderr, "Unable to find PowerPC CPU definition\n");
exit(1);
}
- env = &cpu->env;
-
- /* Set time-base frequency to 512 MHz */
- cpu_ppc_tb_init(env, TIMEBASE_FREQ);
-
- /* PAPR always has exception vectors in RAM not ROM. To ensure this,
- * MSR[IP] should never be set.
- */
- env->msr_mask &= ~(1 << 6);
-
- /* Tell KVM that we're in PAPR mode */
- if (kvm_enabled()) {
- kvmppc_set_papr(cpu);
- }
-
- if (cpu->max_compat) {
- if (ppc_set_compat(cpu, cpu->max_compat) < 0) {
- exit(1);
- }
- }
-
- xics_cpu_setup(spapr->icp, cpu);
+ spapr_cpu_init(spapr, cpu);
+ }
- qemu_register_reset(spapr_cpu_reset, cpu);
+ if (kvm_enabled()) {
+ /* Enable H_LOGICAL_CI_* so SLOF can talk to in-kernel devices */
+ kvmppc_enable_logical_ci_hcalls();
}
/* allocate RAM */
- spapr->ram_limit = ram_size;
memory_region_allocate_system_memory(ram, NULL, "ppc_spapr.ram",
- spapr->ram_limit);
+ machine->ram_size);
memory_region_add_subregion(sysmem, 0, ram);
if (rma_alloc_size && rma) {
@@ -1520,18 +1552,18 @@ static void ppc_spapr_init(MachineState *machine)
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "spapr-rtas.bin");
if (!filename) {
- hw_error("Could not find LPAR rtas '%s'\n", "spapr-rtas.bin");
+ error_report("Could not find LPAR rtas '%s'", "spapr-rtas.bin");
exit(1);
}
spapr->rtas_size = get_image_size(filename);
spapr->rtas_blob = g_malloc(spapr->rtas_size);
if (load_image_size(filename, spapr->rtas_blob, spapr->rtas_size) < 0) {
- hw_error("qemu: could not load LPAR rtas '%s'\n", filename);
+ error_report("Could not load LPAR rtas '%s'", filename);
exit(1);
}
if (spapr->rtas_size > RTAS_MAX_SIZE) {
- hw_error("RTAS too big ! 0x%zx bytes (max is 0x%x)\n",
- (size_t)spapr->rtas_size, RTAS_MAX_SIZE);
+ error_report("RTAS too big ! 0x%zx bytes (max is 0x%x)",
+ (size_t)spapr->rtas_size, RTAS_MAX_SIZE);
exit(1);
}
g_free(filename);
@@ -1641,18 +1673,19 @@ static void ppc_spapr_init(MachineState *machine)
}
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
if (!filename) {
- hw_error("Could not find LPAR rtas '%s'\n", bios_name);
+ error_report("Could not find LPAR firmware '%s'", bios_name);
exit(1);
}
fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE);
- if (fw_size < 0) {
- hw_error("qemu: could not load LPAR rtas '%s'\n", filename);
+ if (fw_size <= 0) {
+ error_report("Could not load LPAR firmware '%s'", filename);
exit(1);
}
g_free(filename);
- spapr->entry_point = 0x100;
-
+ /* FIXME: Should register things through the MachineState's qdev
+ * interface, this is a legacy from the sPAPREnvironment structure
+ * which predated MachineState but had a similar function */
vmstate_register(NULL, 0, &vmstate_spapr, spapr);
register_savevm_live(NULL, "spapr/htab", -1, 1,
&savevm_htab_handlers, spapr);
@@ -1660,9 +1693,14 @@ static void ppc_spapr_init(MachineState *machine)
/* Prepare the device tree */
spapr->fdt_skel = spapr_create_fdt_skel(initrd_base, initrd_size,
kernel_size, kernel_le,
- kernel_cmdline, spapr->epow_irq);
+ kernel_cmdline,
+ spapr->check_exception_irq);
assert(spapr->fdt_skel != NULL);
+ /* used by RTAS */
+ QTAILQ_INIT(&spapr->ccs_list);
+ qemu_register_reset(spapr_ccs_reset_hook, spapr);
+
qemu_register_boot_set(spapr_boot_set, spapr);
}
@@ -1743,17 +1781,17 @@ static char *spapr_get_fw_dev_path(FWPathProvider *p, BusState *bus,
static char *spapr_get_kvm_type(Object *obj, Error **errp)
{
- sPAPRMachineState *sm = SPAPR_MACHINE(obj);
+ sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
- return g_strdup(sm->kvm_type);
+ return g_strdup(spapr->kvm_type);
}
static void spapr_set_kvm_type(Object *obj, const char *value, Error **errp)
{
- sPAPRMachineState *sm = SPAPR_MACHINE(obj);
+ sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
- g_free(sm->kvm_type);
- sm->kvm_type = g_strdup(value);
+ g_free(spapr->kvm_type);
+ spapr->kvm_type = g_strdup(value);
}
static void spapr_machine_initfn(Object *obj)
@@ -1794,6 +1832,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
mc->max_cpus = MAX_CPUS;
mc->no_parallel = 1;
mc->default_boot_order = "";
+ mc->default_ram_size = 512 * M_BYTE;
mc->kvm_type = spapr_kvm_type;
mc->has_dynamic_sysbus = true;
@@ -1807,6 +1846,7 @@ static const TypeInfo spapr_machine_info = {
.abstract = true,
.instance_size = sizeof(sPAPRMachineState),
.instance_init = spapr_machine_initfn,
+ .class_size = sizeof(sPAPRMachineClass),
.class_init = spapr_machine_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_FW_PATH_PROVIDER },
@@ -1815,22 +1855,66 @@ static const TypeInfo spapr_machine_info = {
},
};
+#define SPAPR_COMPAT_2_3 \
+ HW_COMPAT_2_3 \
+ {\
+ .driver = "spapr-pci-host-bridge",\
+ .property = "dynamic-reconfiguration",\
+ .value = "off",\
+ },
+
#define SPAPR_COMPAT_2_2 \
+ SPAPR_COMPAT_2_3 \
+ HW_COMPAT_2_2 \
{\
.driver = TYPE_SPAPR_PCI_HOST_BRIDGE,\
.property = "mem_win_size",\
.value = "0x20000000",\
- }
+ },
#define SPAPR_COMPAT_2_1 \
- SPAPR_COMPAT_2_2
+ SPAPR_COMPAT_2_2 \
+ HW_COMPAT_2_1
+
+static void spapr_compat_2_3(Object *obj)
+{
+ savevm_skip_section_footers();
+ global_state_set_optional();
+}
+
+static void spapr_compat_2_2(Object *obj)
+{
+ spapr_compat_2_3(obj);
+}
+
+static void spapr_compat_2_1(Object *obj)
+{
+ spapr_compat_2_2(obj);
+}
+
+static void spapr_machine_2_3_instance_init(Object *obj)
+{
+ spapr_compat_2_3(obj);
+ spapr_machine_initfn(obj);
+}
+
+static void spapr_machine_2_2_instance_init(Object *obj)
+{
+ spapr_compat_2_2(obj);
+ spapr_machine_initfn(obj);
+}
+
+static void spapr_machine_2_1_instance_init(Object *obj)
+{
+ spapr_compat_2_1(obj);
+ spapr_machine_initfn(obj);
+}
static void spapr_machine_2_1_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
static GlobalProperty compat_props[] = {
- HW_COMPAT_2_1,
- SPAPR_COMPAT_2_1,
+ SPAPR_COMPAT_2_1
{ /* end of list */ }
};
@@ -1843,12 +1927,13 @@ static const TypeInfo spapr_machine_2_1_info = {
.name = TYPE_SPAPR_MACHINE "2.1",
.parent = TYPE_SPAPR_MACHINE,
.class_init = spapr_machine_2_1_class_init,
+ .instance_init = spapr_machine_2_1_instance_init,
};
static void spapr_machine_2_2_class_init(ObjectClass *oc, void *data)
{
static GlobalProperty compat_props[] = {
- SPAPR_COMPAT_2_2,
+ SPAPR_COMPAT_2_2
{ /* end of list */ }
};
MachineClass *mc = MACHINE_CLASS(oc);
@@ -1862,22 +1947,43 @@ static const TypeInfo spapr_machine_2_2_info = {
.name = TYPE_SPAPR_MACHINE "2.2",
.parent = TYPE_SPAPR_MACHINE,
.class_init = spapr_machine_2_2_class_init,
+ .instance_init = spapr_machine_2_2_instance_init,
};
static void spapr_machine_2_3_class_init(ObjectClass *oc, void *data)
{
+ static GlobalProperty compat_props[] = {
+ SPAPR_COMPAT_2_3
+ { /* end of list */ }
+ };
MachineClass *mc = MACHINE_CLASS(oc);
mc->name = "pseries-2.3";
mc->desc = "pSeries Logical Partition (PAPR compliant) v2.3";
- mc->alias = "pseries";
- mc->is_default = 1;
+ mc->compat_props = compat_props;
}
static const TypeInfo spapr_machine_2_3_info = {
.name = TYPE_SPAPR_MACHINE "2.3",
.parent = TYPE_SPAPR_MACHINE,
.class_init = spapr_machine_2_3_class_init,
+ .instance_init = spapr_machine_2_3_instance_init,
+};
+
+static void spapr_machine_2_4_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->name = "pseries-2.4";
+ mc->desc = "pSeries Logical Partition (PAPR compliant) v2.4";
+ mc->alias = "pseries";
+ mc->is_default = 1;
+}
+
+static const TypeInfo spapr_machine_2_4_info = {
+ .name = TYPE_SPAPR_MACHINE "2.4",
+ .parent = TYPE_SPAPR_MACHINE,
+ .class_init = spapr_machine_2_4_class_init,
};
static void spapr_machine_register_types(void)
@@ -1886,6 +1992,7 @@ static void spapr_machine_register_types(void)
type_register_static(&spapr_machine_2_1_info);
type_register_static(&spapr_machine_2_2_info);
type_register_static(&spapr_machine_2_3_info);
+ type_register_static(&spapr_machine_2_4_info);
}
type_init(spapr_machine_register_types)
diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
new file mode 100644
index 000000000..ee874326e
--- /dev/null
+++ b/hw/ppc/spapr_drc.c
@@ -0,0 +1,745 @@
+/*
+ * QEMU SPAPR Dynamic Reconfiguration Connector Implementation
+ *
+ * Copyright IBM Corp. 2014
+ *
+ * Authors:
+ * Michael Roth <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "hw/ppc/spapr_drc.h"
+#include "qom/object.h"
+#include "hw/qdev.h"
+#include "qapi/visitor.h"
+#include "qemu/error-report.h"
+
+/* #define DEBUG_SPAPR_DRC */
+
+#ifdef DEBUG_SPAPR_DRC
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#define DPRINTFN(fmt, ...) \
+ do { DPRINTF(fmt, ## __VA_ARGS__); fprintf(stderr, "\n"); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+ do { } while (0)
+#define DPRINTFN(fmt, ...) \
+ do { } while (0)
+#endif
+
+#define DRC_CONTAINER_PATH "/dr-connector"
+#define DRC_INDEX_TYPE_SHIFT 28
+#define DRC_INDEX_ID_MASK (~(~0 << DRC_INDEX_TYPE_SHIFT))
+
+static sPAPRDRConnectorTypeShift get_type_shift(sPAPRDRConnectorType type)
+{
+ uint32_t shift = 0;
+
+ /* make sure this isn't SPAPR_DR_CONNECTOR_TYPE_ANY, or some
+ * other wonky value.
+ */
+ g_assert(is_power_of_2(type));
+
+ while (type != (1 << shift)) {
+ shift++;
+ }
+ return shift;
+}
+
+static uint32_t get_index(sPAPRDRConnector *drc)
+{
+ /* no set format for a drc index: it only needs to be globally
+ * unique. this is how we encode the DRC type on bare-metal
+ * however, so might as well do that here
+ */
+ return (get_type_shift(drc->type) << DRC_INDEX_TYPE_SHIFT) |
+ (drc->id & DRC_INDEX_ID_MASK);
+}
+
+static int set_isolation_state(sPAPRDRConnector *drc,
+ sPAPRDRIsolationState state)
+{
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+
+ DPRINTFN("drc: %x, set_isolation_state: %x", get_index(drc), state);
+
+ drc->isolation_state = state;
+
+ if (drc->isolation_state == SPAPR_DR_ISOLATION_STATE_ISOLATED) {
+ /* if we're awaiting release, but still in an unconfigured state,
+ * it's likely the guest is still in the process of configuring
+ * the device and is transitioning the devices to an ISOLATED
+ * state as a part of that process. so we only complete the
+ * removal when this transition happens for a device in a
+ * configured state, as suggested by the state diagram from
+ * PAPR+ 2.7, 13.4
+ */
+ if (drc->awaiting_release) {
+ if (drc->configured) {
+ DPRINTFN("finalizing device removal");
+ drck->detach(drc, DEVICE(drc->dev), drc->detach_cb,
+ drc->detach_cb_opaque, NULL);
+ } else {
+ DPRINTFN("deferring device removal on unconfigured device\n");
+ }
+ }
+ drc->configured = false;
+ }
+
+ return 0;
+}
+
+static int set_indicator_state(sPAPRDRConnector *drc,
+ sPAPRDRIndicatorState state)
+{
+ DPRINTFN("drc: %x, set_indicator_state: %x", get_index(drc), state);
+ drc->indicator_state = state;
+ return 0;
+}
+
+static int set_allocation_state(sPAPRDRConnector *drc,
+ sPAPRDRAllocationState state)
+{
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+
+ DPRINTFN("drc: %x, set_allocation_state: %x", get_index(drc), state);
+
+ if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI) {
+ drc->allocation_state = state;
+ if (drc->awaiting_release &&
+ drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) {
+ DPRINTFN("finalizing device removal");
+ drck->detach(drc, DEVICE(drc->dev), drc->detach_cb,
+ drc->detach_cb_opaque, NULL);
+ }
+ }
+ return 0;
+}
+
+static uint32_t get_type(sPAPRDRConnector *drc)
+{
+ return drc->type;
+}
+
+static const char *get_name(sPAPRDRConnector *drc)
+{
+ return drc->name;
+}
+
+static const void *get_fdt(sPAPRDRConnector *drc, int *fdt_start_offset)
+{
+ if (fdt_start_offset) {
+ *fdt_start_offset = drc->fdt_start_offset;
+ }
+ return drc->fdt;
+}
+
+static void set_configured(sPAPRDRConnector *drc)
+{
+ DPRINTFN("drc: %x, set_configured", get_index(drc));
+
+ if (drc->isolation_state != SPAPR_DR_ISOLATION_STATE_UNISOLATED) {
+ /* guest should be not configuring an isolated device */
+ DPRINTFN("drc: %x, set_configured: skipping isolated device",
+ get_index(drc));
+ return;
+ }
+ drc->configured = true;
+}
+
+/*
+ * dr-entity-sense sensor value
+ * returned via get-sensor-state RTAS calls
+ * as expected by state diagram in PAPR+ 2.7, 13.4
+ * based on the current allocation/indicator/power states
+ * for the DR connector.
+ */
+static sPAPRDREntitySense entity_sense(sPAPRDRConnector *drc)
+{
+ sPAPRDREntitySense state;
+
+ if (drc->dev) {
+ if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI &&
+ drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) {
+ /* for logical DR, we return a state of UNUSABLE
+ * iff the allocation state UNUSABLE.
+ * Otherwise, report the state as USABLE/PRESENT,
+ * as we would for PCI.
+ */
+ state = SPAPR_DR_ENTITY_SENSE_UNUSABLE;
+ } else {
+ /* this assumes all PCI devices are assigned to
+ * a 'live insertion' power domain, where QEMU
+ * manages power state automatically as opposed
+ * to the guest. present, non-PCI resources are
+ * unaffected by power state.
+ */
+ state = SPAPR_DR_ENTITY_SENSE_PRESENT;
+ }
+ } else {
+ if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) {
+ /* PCI devices, and only PCI devices, use EMPTY
+ * in cases where we'd otherwise use UNUSABLE
+ */
+ state = SPAPR_DR_ENTITY_SENSE_EMPTY;
+ } else {
+ state = SPAPR_DR_ENTITY_SENSE_UNUSABLE;
+ }
+ }
+
+ DPRINTFN("drc: %x, entity_sense: %x", get_index(drc), state);
+ return state;
+}
+
+static void prop_get_index(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ uint32_t value = (uint32_t)drck->get_index(drc);
+ visit_type_uint32(v, &value, name, errp);
+}
+
+static void prop_get_type(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ uint32_t value = (uint32_t)drck->get_type(drc);
+ visit_type_uint32(v, &value, name, errp);
+}
+
+static char *prop_get_name(Object *obj, Error **errp)
+{
+ sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ return g_strdup(drck->get_name(drc));
+}
+
+static void prop_get_entity_sense(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ uint32_t value = (uint32_t)drck->entity_sense(drc);
+ visit_type_uint32(v, &value, name, errp);
+}
+
+static void prop_get_fdt(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
+ int fdt_offset_next, fdt_offset, fdt_depth;
+ void *fdt;
+
+ if (!drc->fdt) {
+ return;
+ }
+
+ fdt = drc->fdt;
+ fdt_offset = drc->fdt_start_offset;
+ fdt_depth = 0;
+
+ do {
+ const char *name = NULL;
+ const struct fdt_property *prop = NULL;
+ int prop_len = 0, name_len = 0;
+ uint32_t tag;
+
+ tag = fdt_next_tag(fdt, fdt_offset, &fdt_offset_next);
+ switch (tag) {
+ case FDT_BEGIN_NODE:
+ fdt_depth++;
+ name = fdt_get_name(fdt, fdt_offset, &name_len);
+ visit_start_struct(v, NULL, NULL, name, 0, NULL);
+ break;
+ case FDT_END_NODE:
+ /* shouldn't ever see an FDT_END_NODE before FDT_BEGIN_NODE */
+ g_assert(fdt_depth > 0);
+ visit_end_struct(v, NULL);
+ fdt_depth--;
+ break;
+ case FDT_PROP: {
+ int i;
+ prop = fdt_get_property_by_offset(fdt, fdt_offset, &prop_len);
+ name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
+ visit_start_list(v, name, NULL);
+ for (i = 0; i < prop_len; i++) {
+ visit_type_uint8(v, (uint8_t *)&prop->data[i], NULL, NULL);
+
+ }
+ visit_end_list(v, NULL);
+ break;
+ }
+ default:
+ error_setg(&error_abort, "device FDT in unexpected state: %d", tag);
+ }
+ fdt_offset = fdt_offset_next;
+ } while (fdt_depth != 0);
+}
+
+static void attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt,
+ int fdt_start_offset, bool coldplug, Error **errp)
+{
+ DPRINTFN("drc: %x, attach", get_index(drc));
+
+ if (drc->isolation_state != SPAPR_DR_ISOLATION_STATE_ISOLATED) {
+ error_setg(errp, "an attached device is still awaiting release");
+ return;
+ }
+ if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) {
+ g_assert(drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_USABLE);
+ }
+ g_assert(fdt || coldplug);
+
+ /* NOTE: setting initial isolation state to UNISOLATED means we can't
+ * detach unless guest has a userspace/kernel that moves this state
+ * back to ISOLATED in response to an unplug event, or this is done
+ * manually by the admin prior. if we force things while the guest
+ * may be accessing the device, we can easily crash the guest, so we
+ * we defer completion of removal in such cases to the reset() hook.
+ */
+ if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) {
+ drc->isolation_state = SPAPR_DR_ISOLATION_STATE_UNISOLATED;
+ }
+ drc->indicator_state = SPAPR_DR_INDICATOR_STATE_ACTIVE;
+
+ drc->dev = d;
+ drc->fdt = fdt;
+ drc->fdt_start_offset = fdt_start_offset;
+ drc->configured = false;
+
+ object_property_add_link(OBJECT(drc), "device",
+ object_get_typename(OBJECT(drc->dev)),
+ (Object **)(&drc->dev),
+ NULL, 0, NULL);
+}
+
+static void detach(sPAPRDRConnector *drc, DeviceState *d,
+ spapr_drc_detach_cb *detach_cb,
+ void *detach_cb_opaque, Error **errp)
+{
+ DPRINTFN("drc: %x, detach", get_index(drc));
+
+ drc->detach_cb = detach_cb;
+ drc->detach_cb_opaque = detach_cb_opaque;
+
+ if (drc->isolation_state != SPAPR_DR_ISOLATION_STATE_ISOLATED) {
+ DPRINTFN("awaiting transition to isolated state before removal");
+ drc->awaiting_release = true;
+ return;
+ }
+
+ if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI &&
+ drc->allocation_state != SPAPR_DR_ALLOCATION_STATE_UNUSABLE) {
+ DPRINTFN("awaiting transition to unusable state before removal");
+ drc->awaiting_release = true;
+ return;
+ }
+
+ drc->indicator_state = SPAPR_DR_INDICATOR_STATE_INACTIVE;
+
+ if (drc->detach_cb) {
+ drc->detach_cb(drc->dev, drc->detach_cb_opaque);
+ }
+
+ drc->awaiting_release = false;
+ g_free(drc->fdt);
+ drc->fdt = NULL;
+ drc->fdt_start_offset = 0;
+ object_property_del(OBJECT(drc), "device", NULL);
+ drc->dev = NULL;
+ drc->detach_cb = NULL;
+ drc->detach_cb_opaque = NULL;
+}
+
+static bool release_pending(sPAPRDRConnector *drc)
+{
+ return drc->awaiting_release;
+}
+
+static void reset(DeviceState *d)
+{
+ sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d);
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+
+ DPRINTFN("drc reset: %x", drck->get_index(drc));
+ /* immediately upon reset we can safely assume DRCs whose devices
+ * are pending removal can be safely removed, and that they will
+ * subsequently be left in an ISOLATED state. move the DRC to this
+ * state in these cases (which will in turn complete any pending
+ * device removals)
+ */
+ if (drc->awaiting_release) {
+ drck->set_isolation_state(drc, SPAPR_DR_ISOLATION_STATE_ISOLATED);
+ /* generally this should also finalize the removal, but if the device
+ * hasn't yet been configured we normally defer removal under the
+ * assumption that this transition is taking place as part of device
+ * configuration. so check if we're still waiting after this, and
+ * force removal if we are
+ */
+ if (drc->awaiting_release) {
+ drck->detach(drc, DEVICE(drc->dev), drc->detach_cb,
+ drc->detach_cb_opaque, NULL);
+ }
+
+ /* non-PCI devices may be awaiting a transition to UNUSABLE */
+ if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI &&
+ drc->awaiting_release) {
+ drck->set_allocation_state(drc, SPAPR_DR_ALLOCATION_STATE_UNUSABLE);
+ }
+ }
+}
+
+static void realize(DeviceState *d, Error **errp)
+{
+ sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d);
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ Object *root_container;
+ char link_name[256];
+ gchar *child_name;
+ Error *err = NULL;
+
+ DPRINTFN("drc realize: %x", drck->get_index(drc));
+ /* NOTE: we do this as part of realize/unrealize due to the fact
+ * that the guest will communicate with the DRC via RTAS calls
+ * referencing the global DRC index. By unlinking the DRC
+ * from DRC_CONTAINER_PATH/<drc_index> we effectively make it
+ * inaccessible by the guest, since lookups rely on this path
+ * existing in the composition tree
+ */
+ root_container = container_get(object_get_root(), DRC_CONTAINER_PATH);
+ snprintf(link_name, sizeof(link_name), "%x", drck->get_index(drc));
+ child_name = object_get_canonical_path_component(OBJECT(drc));
+ DPRINTFN("drc child name: %s", child_name);
+ object_property_add_alias(root_container, link_name,
+ drc->owner, child_name, &err);
+ if (err) {
+ error_report("%s", error_get_pretty(err));
+ error_free(err);
+ object_unref(OBJECT(drc));
+ }
+ g_free(child_name);
+ DPRINTFN("drc realize complete");
+}
+
+static void unrealize(DeviceState *d, Error **errp)
+{
+ sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d);
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ Object *root_container;
+ char name[256];
+ Error *err = NULL;
+
+ DPRINTFN("drc unrealize: %x", drck->get_index(drc));
+ root_container = container_get(object_get_root(), DRC_CONTAINER_PATH);
+ snprintf(name, sizeof(name), "%x", drck->get_index(drc));
+ object_property_del(root_container, name, &err);
+ if (err) {
+ error_report("%s", error_get_pretty(err));
+ error_free(err);
+ object_unref(OBJECT(drc));
+ }
+}
+
+sPAPRDRConnector *spapr_dr_connector_new(Object *owner,
+ sPAPRDRConnectorType type,
+ uint32_t id)
+{
+ sPAPRDRConnector *drc =
+ SPAPR_DR_CONNECTOR(object_new(TYPE_SPAPR_DR_CONNECTOR));
+
+ g_assert(type);
+
+ drc->type = type;
+ drc->id = id;
+ drc->owner = owner;
+ object_property_add_child(owner, "dr-connector[*]", OBJECT(drc), NULL);
+ object_property_set_bool(OBJECT(drc), true, "realized", NULL);
+
+ /* human-readable name for a DRC to encode into the DT
+ * description. this is mainly only used within a guest in place
+ * of the unique DRC index.
+ *
+ * in the case of VIO/PCI devices, it corresponds to a
+ * "location code" that maps a logical device/function (DRC index)
+ * to a physical (or virtual in the case of VIO) location in the
+ * system by chaining together the "location label" for each
+ * encapsulating component.
+ *
+ * since this is more to do with diagnosing physical hardware
+ * issues than guest compatibility, we choose location codes/DRC
+ * names that adhere to the documented format, but avoid encoding
+ * the entire topology information into the label/code, instead
+ * just using the location codes based on the labels for the
+ * endpoints (VIO/PCI adaptor connectors), which is basically
+ * just "C" followed by an integer ID.
+ *
+ * DRC names as documented by PAPR+ v2.7, 13.5.2.4
+ * location codes as documented by PAPR+ v2.7, 12.3.1.5
+ */
+ switch (drc->type) {
+ case SPAPR_DR_CONNECTOR_TYPE_CPU:
+ drc->name = g_strdup_printf("CPU %d", id);
+ break;
+ case SPAPR_DR_CONNECTOR_TYPE_PHB:
+ drc->name = g_strdup_printf("PHB %d", id);
+ break;
+ case SPAPR_DR_CONNECTOR_TYPE_VIO:
+ case SPAPR_DR_CONNECTOR_TYPE_PCI:
+ drc->name = g_strdup_printf("C%d", id);
+ break;
+ case SPAPR_DR_CONNECTOR_TYPE_LMB:
+ drc->name = g_strdup_printf("LMB %d", id);
+ break;
+ default:
+ g_assert(false);
+ }
+
+ /* PCI slot always start in a USABLE state, and stay there */
+ if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) {
+ drc->allocation_state = SPAPR_DR_ALLOCATION_STATE_USABLE;
+ }
+
+ return drc;
+}
+
+static void spapr_dr_connector_instance_init(Object *obj)
+{
+ sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
+
+ object_property_add_uint32_ptr(obj, "isolation-state",
+ &drc->isolation_state, NULL);
+ object_property_add_uint32_ptr(obj, "indicator-state",
+ &drc->indicator_state, NULL);
+ object_property_add_uint32_ptr(obj, "allocation-state",
+ &drc->allocation_state, NULL);
+ object_property_add_uint32_ptr(obj, "id", &drc->id, NULL);
+ object_property_add(obj, "index", "uint32", prop_get_index,
+ NULL, NULL, NULL, NULL);
+ object_property_add(obj, "connector_type", "uint32", prop_get_type,
+ NULL, NULL, NULL, NULL);
+ object_property_add_str(obj, "name", prop_get_name, NULL, NULL);
+ object_property_add(obj, "entity-sense", "uint32", prop_get_entity_sense,
+ NULL, NULL, NULL, NULL);
+ object_property_add(obj, "fdt", "struct", prop_get_fdt,
+ NULL, NULL, NULL, NULL);
+}
+
+static void spapr_dr_connector_class_init(ObjectClass *k, void *data)
+{
+ DeviceClass *dk = DEVICE_CLASS(k);
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k);
+
+ dk->reset = reset;
+ dk->realize = realize;
+ dk->unrealize = unrealize;
+ drck->set_isolation_state = set_isolation_state;
+ drck->set_indicator_state = set_indicator_state;
+ drck->set_allocation_state = set_allocation_state;
+ drck->get_index = get_index;
+ drck->get_type = get_type;
+ drck->get_name = get_name;
+ drck->get_fdt = get_fdt;
+ drck->set_configured = set_configured;
+ drck->entity_sense = entity_sense;
+ drck->attach = attach;
+ drck->detach = detach;
+ drck->release_pending = release_pending;
+}
+
+static const TypeInfo spapr_dr_connector_info = {
+ .name = TYPE_SPAPR_DR_CONNECTOR,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(sPAPRDRConnector),
+ .instance_init = spapr_dr_connector_instance_init,
+ .class_size = sizeof(sPAPRDRConnectorClass),
+ .class_init = spapr_dr_connector_class_init,
+};
+
+static void spapr_drc_register_types(void)
+{
+ type_register_static(&spapr_dr_connector_info);
+}
+
+type_init(spapr_drc_register_types)
+
+/* helper functions for external users */
+
+sPAPRDRConnector *spapr_dr_connector_by_index(uint32_t index)
+{
+ Object *obj;
+ char name[256];
+
+ snprintf(name, sizeof(name), "%s/%x", DRC_CONTAINER_PATH, index);
+ obj = object_resolve_path(name, NULL);
+
+ return !obj ? NULL : SPAPR_DR_CONNECTOR(obj);
+}
+
+sPAPRDRConnector *spapr_dr_connector_by_id(sPAPRDRConnectorType type,
+ uint32_t id)
+{
+ return spapr_dr_connector_by_index(
+ (get_type_shift(type) << DRC_INDEX_TYPE_SHIFT) |
+ (id & DRC_INDEX_ID_MASK));
+}
+
+/* generate a string the describes the DRC to encode into the
+ * device tree.
+ *
+ * as documented by PAPR+ v2.7, 13.5.2.6 and C.6.1
+ */
+static const char *spapr_drc_get_type_str(sPAPRDRConnectorType type)
+{
+ switch (type) {
+ case SPAPR_DR_CONNECTOR_TYPE_CPU:
+ return "CPU";
+ case SPAPR_DR_CONNECTOR_TYPE_PHB:
+ return "PHB";
+ case SPAPR_DR_CONNECTOR_TYPE_VIO:
+ return "SLOT";
+ case SPAPR_DR_CONNECTOR_TYPE_PCI:
+ return "28";
+ case SPAPR_DR_CONNECTOR_TYPE_LMB:
+ return "MEM";
+ default:
+ g_assert(false);
+ }
+
+ return NULL;
+}
+
+/**
+ * spapr_drc_populate_dt
+ *
+ * @fdt: libfdt device tree
+ * @path: path in the DT to generate properties
+ * @owner: parent Object/DeviceState for which to generate DRC
+ * descriptions for
+ * @drc_type_mask: mask of sPAPRDRConnectorType values corresponding
+ * to the types of DRCs to generate entries for
+ *
+ * generate OF properties to describe DRC topology/indices to guests
+ *
+ * as documented in PAPR+ v2.1, 13.5.2
+ */
+int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner,
+ uint32_t drc_type_mask)
+{
+ Object *root_container;
+ ObjectProperty *prop;
+ uint32_t drc_count = 0;
+ GArray *drc_indexes, *drc_power_domains;
+ GString *drc_names, *drc_types;
+ int ret;
+
+ /* the first entry of each properties is a 32-bit integer encoding
+ * the number of elements in the array. we won't know this until
+ * we complete the iteration through all the matching DRCs, but
+ * reserve the space now and set the offsets accordingly so we
+ * can fill them in later.
+ */
+ drc_indexes = g_array_new(false, true, sizeof(uint32_t));
+ drc_indexes = g_array_set_size(drc_indexes, 1);
+ drc_power_domains = g_array_new(false, true, sizeof(uint32_t));
+ drc_power_domains = g_array_set_size(drc_power_domains, 1);
+ drc_names = g_string_set_size(g_string_new(NULL), sizeof(uint32_t));
+ drc_types = g_string_set_size(g_string_new(NULL), sizeof(uint32_t));
+
+ /* aliases for all DRConnector objects will be rooted in QOM
+ * composition tree at DRC_CONTAINER_PATH
+ */
+ root_container = container_get(object_get_root(), DRC_CONTAINER_PATH);
+
+ QTAILQ_FOREACH(prop, &root_container->properties, node) {
+ Object *obj;
+ sPAPRDRConnector *drc;
+ sPAPRDRConnectorClass *drck;
+ uint32_t drc_index, drc_power_domain;
+
+ if (!strstart(prop->type, "link<", NULL)) {
+ continue;
+ }
+
+ obj = object_property_get_link(root_container, prop->name, NULL);
+ drc = SPAPR_DR_CONNECTOR(obj);
+ drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+
+ if (owner && (drc->owner != owner)) {
+ continue;
+ }
+
+ if ((drc->type & drc_type_mask) == 0) {
+ continue;
+ }
+
+ drc_count++;
+
+ /* ibm,drc-indexes */
+ drc_index = cpu_to_be32(drck->get_index(drc));
+ g_array_append_val(drc_indexes, drc_index);
+
+ /* ibm,drc-power-domains */
+ drc_power_domain = cpu_to_be32(-1);
+ g_array_append_val(drc_power_domains, drc_power_domain);
+
+ /* ibm,drc-names */
+ drc_names = g_string_append(drc_names, drck->get_name(drc));
+ drc_names = g_string_insert_len(drc_names, -1, "\0", 1);
+
+ /* ibm,drc-types */
+ drc_types = g_string_append(drc_types,
+ spapr_drc_get_type_str(drc->type));
+ drc_types = g_string_insert_len(drc_types, -1, "\0", 1);
+ }
+
+ /* now write the drc count into the space we reserved at the
+ * beginning of the arrays previously
+ */
+ *(uint32_t *)drc_indexes->data = cpu_to_be32(drc_count);
+ *(uint32_t *)drc_power_domains->data = cpu_to_be32(drc_count);
+ *(uint32_t *)drc_names->str = cpu_to_be32(drc_count);
+ *(uint32_t *)drc_types->str = cpu_to_be32(drc_count);
+
+ ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-indexes",
+ drc_indexes->data,
+ drc_indexes->len * sizeof(uint32_t));
+ if (ret) {
+ fprintf(stderr, "Couldn't create ibm,drc-indexes property\n");
+ goto out;
+ }
+
+ ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-power-domains",
+ drc_power_domains->data,
+ drc_power_domains->len * sizeof(uint32_t));
+ if (ret) {
+ fprintf(stderr, "Couldn't finalize ibm,drc-power-domains property\n");
+ goto out;
+ }
+
+ ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-names",
+ drc_names->str, drc_names->len);
+ if (ret) {
+ fprintf(stderr, "Couldn't finalize ibm,drc-names property\n");
+ goto out;
+ }
+
+ ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-types",
+ drc_types->str, drc_types->len);
+ if (ret) {
+ fprintf(stderr, "Couldn't finalize ibm,drc-types property\n");
+ goto out;
+ }
+
+out:
+ g_array_free(drc_indexes, true);
+ g_array_free(drc_power_domains, true);
+ g_string_free(drc_names, true);
+ g_string_free(drc_types, true);
+
+ return ret;
+}
diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c
index 283e96bca..f626eb7b3 100644
--- a/hw/ppc/spapr_events.c
+++ b/hw/ppc/spapr_events.c
@@ -32,6 +32,9 @@
#include "hw/ppc/spapr.h"
#include "hw/ppc/spapr_vio.h"
+#include "hw/pci/pci.h"
+#include "hw/pci-host/spapr.h"
+#include "hw/ppc/spapr_drc.h"
#include <libfdt.h>
@@ -77,6 +80,7 @@ struct rtas_error_log {
#define RTAS_LOG_TYPE_ECC_UNCORR 0x00000009
#define RTAS_LOG_TYPE_ECC_CORR 0x0000000a
#define RTAS_LOG_TYPE_EPOW 0x00000040
+#define RTAS_LOG_TYPE_HOTPLUG 0x000000e5
uint32_t extended_length;
} QEMU_PACKED;
@@ -166,6 +170,38 @@ struct epow_log_full {
struct rtas_event_log_v6_epow epow;
} QEMU_PACKED;
+struct rtas_event_log_v6_hp {
+#define RTAS_LOG_V6_SECTION_ID_HOTPLUG 0x4850 /* HP */
+ struct rtas_event_log_v6_section_header hdr;
+ uint8_t hotplug_type;
+#define RTAS_LOG_V6_HP_TYPE_CPU 1
+#define RTAS_LOG_V6_HP_TYPE_MEMORY 2
+#define RTAS_LOG_V6_HP_TYPE_SLOT 3
+#define RTAS_LOG_V6_HP_TYPE_PHB 4
+#define RTAS_LOG_V6_HP_TYPE_PCI 5
+ uint8_t hotplug_action;
+#define RTAS_LOG_V6_HP_ACTION_ADD 1
+#define RTAS_LOG_V6_HP_ACTION_REMOVE 2
+ uint8_t hotplug_identifier;
+#define RTAS_LOG_V6_HP_ID_DRC_NAME 1
+#define RTAS_LOG_V6_HP_ID_DRC_INDEX 2
+#define RTAS_LOG_V6_HP_ID_DRC_COUNT 3
+ uint8_t reserved;
+ union {
+ uint32_t index;
+ uint32_t count;
+ char name[1];
+ } drc;
+} QEMU_PACKED;
+
+struct hp_log_full {
+ struct rtas_error_log hdr;
+ struct rtas_event_log_v6 v6hdr;
+ struct rtas_event_log_v6_maina maina;
+ struct rtas_event_log_v6_mainb mainb;
+ struct rtas_event_log_v6_hp hp;
+} QEMU_PACKED;
+
#define EVENT_MASK_INTERNAL_ERRORS 0x80000000
#define EVENT_MASK_EPOW 0x40000000
#define EVENT_MASK_HOTPLUG 0x10000000
@@ -181,67 +217,109 @@ struct epow_log_full {
} \
} while (0)
-void spapr_events_fdt_skel(void *fdt, uint32_t epow_irq)
+void spapr_events_fdt_skel(void *fdt, uint32_t check_exception_irq)
{
- uint32_t epow_irq_ranges[] = {cpu_to_be32(epow_irq), cpu_to_be32(1)};
- uint32_t epow_interrupts[] = {cpu_to_be32(epow_irq), 0};
+ uint32_t irq_ranges[] = {cpu_to_be32(check_exception_irq), cpu_to_be32(1)};
+ uint32_t interrupts[] = {cpu_to_be32(check_exception_irq), 0};
_FDT((fdt_begin_node(fdt, "event-sources")));
_FDT((fdt_property(fdt, "interrupt-controller", NULL, 0)));
_FDT((fdt_property_cell(fdt, "#interrupt-cells", 2)));
_FDT((fdt_property(fdt, "interrupt-ranges",
- epow_irq_ranges, sizeof(epow_irq_ranges))));
+ irq_ranges, sizeof(irq_ranges))));
_FDT((fdt_begin_node(fdt, "epow-events")));
- _FDT((fdt_property(fdt, "interrupts",
- epow_interrupts, sizeof(epow_interrupts))));
+ _FDT((fdt_property(fdt, "interrupts", interrupts, sizeof(interrupts))));
_FDT((fdt_end_node(fdt)));
_FDT((fdt_end_node(fdt)));
}
-static struct epow_log_full *pending_epow;
-static uint32_t next_plid;
+static void rtas_event_log_queue(int log_type, void *data, bool exception)
+{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
+ sPAPREventLogEntry *entry = g_new(sPAPREventLogEntry, 1);
-static void spapr_powerdown_req(Notifier *n, void *opaque)
+ g_assert(data);
+ entry->log_type = log_type;
+ entry->exception = exception;
+ entry->data = data;
+ QTAILQ_INSERT_TAIL(&spapr->pending_events, entry, next);
+}
+
+static sPAPREventLogEntry *rtas_event_log_dequeue(uint32_t event_mask,
+ bool exception)
{
- sPAPREnvironment *spapr = container_of(n, sPAPREnvironment, epow_notifier);
- struct rtas_error_log *hdr;
- struct rtas_event_log_v6 *v6hdr;
- struct rtas_event_log_v6_maina *maina;
- struct rtas_event_log_v6_mainb *mainb;
- struct rtas_event_log_v6_epow *epow;
- struct tm tm;
- int year;
+ sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
+ sPAPREventLogEntry *entry = NULL;
- if (pending_epow) {
- /* For now, we just throw away earlier events if two come
- * along before any are consumed. This is sufficient for our
- * powerdown messages, but we'll need more if we do more
- * general error/event logging */
- g_free(pending_epow);
+ /* we only queue EPOW events atm. */
+ if ((event_mask & EVENT_MASK_EPOW) == 0) {
+ return NULL;
}
- pending_epow = g_malloc0(sizeof(*pending_epow));
- hdr = &pending_epow->hdr;
- v6hdr = &pending_epow->v6hdr;
- maina = &pending_epow->maina;
- mainb = &pending_epow->mainb;
- epow = &pending_epow->epow;
- hdr->summary = cpu_to_be32(RTAS_LOG_VERSION_6
- | RTAS_LOG_SEVERITY_EVENT
- | RTAS_LOG_DISPOSITION_NOT_RECOVERED
- | RTAS_LOG_OPTIONAL_PART_PRESENT
- | RTAS_LOG_TYPE_EPOW);
- hdr->extended_length = cpu_to_be32(sizeof(*pending_epow)
- - sizeof(pending_epow->hdr));
+ QTAILQ_FOREACH(entry, &spapr->pending_events, next) {
+ if (entry->exception != exception) {
+ continue;
+ }
+
+ /* EPOW and hotplug events are surfaced in the same manner */
+ if (entry->log_type == RTAS_LOG_TYPE_EPOW ||
+ entry->log_type == RTAS_LOG_TYPE_HOTPLUG) {
+ break;
+ }
+ }
+
+ if (entry) {
+ QTAILQ_REMOVE(&spapr->pending_events, entry, next);
+ }
+ return entry;
+}
+
+static bool rtas_event_log_contains(uint32_t event_mask, bool exception)
+{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
+ sPAPREventLogEntry *entry = NULL;
+
+ /* we only queue EPOW events atm. */
+ if ((event_mask & EVENT_MASK_EPOW) == 0) {
+ return false;
+ }
+
+ QTAILQ_FOREACH(entry, &spapr->pending_events, next) {
+ if (entry->exception != exception) {
+ continue;
+ }
+
+ /* EPOW and hotplug events are surfaced in the same manner */
+ if (entry->log_type == RTAS_LOG_TYPE_EPOW ||
+ entry->log_type == RTAS_LOG_TYPE_HOTPLUG) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static uint32_t next_plid;
+
+static void spapr_init_v6hdr(struct rtas_event_log_v6 *v6hdr)
+{
v6hdr->b0 = RTAS_LOG_V6_B0_VALID | RTAS_LOG_V6_B0_NEW_LOG
| RTAS_LOG_V6_B0_BIGENDIAN;
v6hdr->b2 = RTAS_LOG_V6_B2_POWERPC_FORMAT
| RTAS_LOG_V6_B2_LOG_FORMAT_PLATFORM_EVENT;
v6hdr->company = cpu_to_be32(RTAS_LOG_V6_COMPANY_IBM);
+}
+
+static void spapr_init_maina(struct rtas_event_log_v6_maina *maina,
+ int section_count)
+{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
+ struct tm tm;
+ int year;
maina->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINA);
maina->hdr.section_length = cpu_to_be16(sizeof(*maina));
@@ -256,8 +334,37 @@ static void spapr_powerdown_req(Notifier *n, void *opaque)
| (to_bcd(tm.tm_min) << 16)
| (to_bcd(tm.tm_sec) << 8));
maina->creator_id = 'H'; /* Hypervisor */
- maina->section_count = 3; /* Main-A, Main-B and EPOW */
+ maina->section_count = section_count;
maina->plid = next_plid++;
+}
+
+static void spapr_powerdown_req(Notifier *n, void *opaque)
+{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
+ struct rtas_error_log *hdr;
+ struct rtas_event_log_v6 *v6hdr;
+ struct rtas_event_log_v6_maina *maina;
+ struct rtas_event_log_v6_mainb *mainb;
+ struct rtas_event_log_v6_epow *epow;
+ struct epow_log_full *new_epow;
+
+ new_epow = g_malloc0(sizeof(*new_epow));
+ hdr = &new_epow->hdr;
+ v6hdr = &new_epow->v6hdr;
+ maina = &new_epow->maina;
+ mainb = &new_epow->mainb;
+ epow = &new_epow->epow;
+
+ hdr->summary = cpu_to_be32(RTAS_LOG_VERSION_6
+ | RTAS_LOG_SEVERITY_EVENT
+ | RTAS_LOG_DISPOSITION_NOT_RECOVERED
+ | RTAS_LOG_OPTIONAL_PART_PRESENT
+ | RTAS_LOG_TYPE_EPOW);
+ hdr->extended_length = cpu_to_be32(sizeof(*new_epow)
+ - sizeof(new_epow->hdr));
+
+ spapr_init_v6hdr(v6hdr);
+ spapr_init_maina(maina, 3 /* Main-A, Main-B and EPOW */);
mainb->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINB);
mainb->hdr.section_length = cpu_to_be16(sizeof(*mainb));
@@ -274,16 +381,92 @@ static void spapr_powerdown_req(Notifier *n, void *opaque)
epow->event_modifier = RTAS_LOG_V6_EPOW_MODIFIER_NORMAL;
epow->extended_modifier = RTAS_LOG_V6_EPOW_XMODIFIER_PARTITION_SPECIFIC;
- qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->epow_irq));
+ rtas_event_log_queue(RTAS_LOG_TYPE_EPOW, new_epow, true);
+
+ qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
+}
+
+static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action)
+{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
+ struct hp_log_full *new_hp;
+ struct rtas_error_log *hdr;
+ struct rtas_event_log_v6 *v6hdr;
+ struct rtas_event_log_v6_maina *maina;
+ struct rtas_event_log_v6_mainb *mainb;
+ struct rtas_event_log_v6_hp *hp;
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ sPAPRDRConnectorType drc_type = drck->get_type(drc);
+
+ new_hp = g_malloc0(sizeof(struct hp_log_full));
+ hdr = &new_hp->hdr;
+ v6hdr = &new_hp->v6hdr;
+ maina = &new_hp->maina;
+ mainb = &new_hp->mainb;
+ hp = &new_hp->hp;
+
+ hdr->summary = cpu_to_be32(RTAS_LOG_VERSION_6
+ | RTAS_LOG_SEVERITY_EVENT
+ | RTAS_LOG_DISPOSITION_NOT_RECOVERED
+ | RTAS_LOG_OPTIONAL_PART_PRESENT
+ | RTAS_LOG_INITIATOR_HOTPLUG
+ | RTAS_LOG_TYPE_HOTPLUG);
+ hdr->extended_length = cpu_to_be32(sizeof(*new_hp)
+ - sizeof(new_hp->hdr));
+
+ spapr_init_v6hdr(v6hdr);
+ spapr_init_maina(maina, 3 /* Main-A, Main-B, HP */);
+
+ mainb->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINB);
+ mainb->hdr.section_length = cpu_to_be16(sizeof(*mainb));
+ mainb->subsystem_id = 0x80; /* External environment */
+ mainb->event_severity = 0x00; /* Informational / non-error */
+ mainb->event_subtype = 0x00; /* Normal shutdown */
+
+ hp->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_HOTPLUG);
+ hp->hdr.section_length = cpu_to_be16(sizeof(*hp));
+ hp->hdr.section_version = 1; /* includes extended modifier */
+ hp->hotplug_action = hp_action;
+
+
+ switch (drc_type) {
+ case SPAPR_DR_CONNECTOR_TYPE_PCI:
+ hp->drc.index = cpu_to_be32(drck->get_index(drc));
+ hp->hotplug_identifier = RTAS_LOG_V6_HP_ID_DRC_INDEX;
+ hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PCI;
+ break;
+ default:
+ /* we shouldn't be signaling hotplug events for resources
+ * that don't support them
+ */
+ g_assert(false);
+ return;
+ }
+
+ rtas_event_log_queue(RTAS_LOG_TYPE_HOTPLUG, new_hp, true);
+
+ qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
}
-static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+void spapr_hotplug_req_add_event(sPAPRDRConnector *drc)
+{
+ spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_ADD);
+}
+
+void spapr_hotplug_req_remove_event(sPAPRDRConnector *drc)
+{
+ spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_REMOVE);
+}
+
+static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
{
- uint32_t mask, buf, len;
+ uint32_t mask, buf, len, event_len;
uint64_t xinfo;
+ sPAPREventLogEntry *event;
+ struct rtas_error_log *hdr;
if ((nargs < 6) || (nargs > 7) || nret != 1) {
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
@@ -298,25 +481,85 @@ static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr,
xinfo |= (uint64_t)rtas_ld(args, 6) << 32;
}
- if ((mask & EVENT_MASK_EPOW) && pending_epow) {
- if (sizeof(*pending_epow) < len) {
- len = sizeof(*pending_epow);
- }
+ event = rtas_event_log_dequeue(mask, true);
+ if (!event) {
+ goto out_no_events;
+ }
+
+ hdr = event->data;
+ event_len = be32_to_cpu(hdr->extended_length) + sizeof(*hdr);
+
+ if (event_len < len) {
+ len = event_len;
+ }
- cpu_physical_memory_write(buf, pending_epow, len);
- g_free(pending_epow);
- pending_epow = NULL;
- rtas_st(rets, 0, RTAS_OUT_SUCCESS);
- } else {
- rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND);
+ cpu_physical_memory_write(buf, event->data, len);
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+ g_free(event->data);
+ g_free(event);
+
+ /* according to PAPR+, the IRQ must be left asserted, or re-asserted, if
+ * there are still pending events to be fetched via check-exception. We
+ * do the latter here, since our code relies on edge-triggered
+ * interrupts.
+ */
+ if (rtas_event_log_contains(mask, true)) {
+ qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
}
+
+ return;
+
+out_no_events:
+ rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND);
+}
+
+static void event_scan(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ uint32_t mask, buf, len, event_len;
+ sPAPREventLogEntry *event;
+ struct rtas_error_log *hdr;
+
+ if (nargs != 4 || nret != 1) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ mask = rtas_ld(args, 0);
+ buf = rtas_ld(args, 2);
+ len = rtas_ld(args, 3);
+
+ event = rtas_event_log_dequeue(mask, false);
+ if (!event) {
+ goto out_no_events;
+ }
+
+ hdr = event->data;
+ event_len = be32_to_cpu(hdr->extended_length) + sizeof(*hdr);
+
+ if (event_len < len) {
+ len = event_len;
+ }
+
+ cpu_physical_memory_write(buf, event->data, len);
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+ g_free(event->data);
+ g_free(event);
+ return;
+
+out_no_events:
+ rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND);
}
-void spapr_events_init(sPAPREnvironment *spapr)
+void spapr_events_init(sPAPRMachineState *spapr)
{
- spapr->epow_irq = xics_alloc(spapr->icp, 0, 0, false);
+ QTAILQ_INIT(&spapr->pending_events);
+ spapr->check_exception_irq = xics_alloc(spapr->icp, 0, 0, false);
spapr->epow_notifier.notify = spapr_powerdown_req;
qemu_register_powerdown_notifier(&spapr->epow_notifier);
spapr_rtas_register(RTAS_CHECK_EXCEPTION, "check-exception",
check_exception);
+ spapr_rtas_register(RTAS_EVENT_SCAN, "event-scan", event_scan);
}
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index 4f76f1cbf..652ddf6e3 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -84,9 +84,10 @@ static inline bool valid_pte_index(CPUPPCState *env, target_ulong pte_index)
return true;
}
-static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_enter(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
+ MachineState *machine = MACHINE(spapr);
CPUPPCState *env = &cpu->env;
target_ulong flags = args[0];
target_ulong pte_index = args[1];
@@ -118,7 +119,7 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr,
raddr = (ptel & HPTE64_R_RPN) & ~((1ULL << page_shift) - 1);
- if (raddr < spapr->ram_limit) {
+ if (raddr < machine->ram_size) {
/* Regular RAM - should have WIMG=0010 */
if ((ptel & HPTE64_R_WIMG) != HPTE64_R_M) {
return H_PARAMETER;
@@ -205,7 +206,7 @@ static RemoveResult remove_hpte(CPUPPCState *env, target_ulong ptex,
return REMOVE_SUCCESS;
}
-static target_ulong h_remove(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
CPUPPCState *env = &cpu->env;
@@ -252,7 +253,7 @@ static target_ulong h_remove(PowerPCCPU *cpu, sPAPREnvironment *spapr,
#define H_BULK_REMOVE_MAX_BATCH 4
-static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
CPUPPCState *env = &cpu->env;
@@ -299,7 +300,7 @@ static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return H_SUCCESS;
}
-static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_protect(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
CPUPPCState *env = &cpu->env;
@@ -337,7 +338,7 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return H_SUCCESS;
}
-static target_ulong h_read(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_read(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
CPUPPCState *env = &cpu->env;
@@ -367,7 +368,7 @@ static target_ulong h_read(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return H_SUCCESS;
}
-static target_ulong h_set_dabr(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_set_dabr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
/* FIXME: actually implement this */
@@ -506,7 +507,7 @@ static target_ulong deregister_dtl(CPUPPCState *env, target_ulong addr)
return H_SUCCESS;
}
-static target_ulong h_register_vpa(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_register_vpa(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
target_ulong flags = args[0];
@@ -551,7 +552,7 @@ static target_ulong h_register_vpa(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return ret;
}
-static target_ulong h_cede(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_cede(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
CPUPPCState *env = &cpu->env;
@@ -567,7 +568,7 @@ static target_ulong h_cede(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return H_SUCCESS;
}
-static target_ulong h_rtas(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_rtas(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
target_ulong rtas_r3 = args[0];
@@ -579,7 +580,7 @@ static target_ulong h_rtas(PowerPCCPU *cpu, sPAPREnvironment *spapr,
nret, rtas_r3 + 12 + 4*nargs);
}
-static target_ulong h_logical_load(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_logical_load(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
CPUState *cs = CPU(cpu);
@@ -603,7 +604,7 @@ static target_ulong h_logical_load(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return H_PARAMETER;
}
-static target_ulong h_logical_store(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_logical_store(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
CPUState *cs = CPU(cpu);
@@ -629,7 +630,7 @@ static target_ulong h_logical_store(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return H_PARAMETER;
}
-static target_ulong h_logical_memop(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_logical_memop(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
CPUState *cs = CPU(cpu);
@@ -698,14 +699,14 @@ static target_ulong h_logical_memop(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return H_SUCCESS;
}
-static target_ulong h_logical_icbi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_logical_icbi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
/* Nothing to do on emulation, KVM will trap this in the kernel */
return H_SUCCESS;
}
-static target_ulong h_logical_dcbf(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_logical_dcbf(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
/* Nothing to do on emulation, KVM will trap this in the kernel */
@@ -788,7 +789,7 @@ static target_ulong h_set_mode_resource_addr_trans_mode(PowerPCCPU *cpu,
return H_SUCCESS;
}
-static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
target_ulong resource = args[1];
@@ -828,7 +829,7 @@ static void do_set_compat(void *arg)
((cpuver) == CPU_POWERPC_LOGICAL_2_07) ? 2070 : 0)
static target_ulong h_client_architecture_support(PowerPCCPU *cpu_,
- sPAPREnvironment *spapr,
+ sPAPRMachineState *spapr,
target_ulong opcode,
target_ulong *args)
{
@@ -921,7 +922,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_,
return H_SUCCESS;
}
- if (spapr_h_cas_compose_response(args[1], args[2])) {
+ if (spapr_h_cas_compose_response(spapr, args[1], args[2])) {
qemu_system_reset_request();
}
@@ -952,6 +953,8 @@ void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn)
target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode,
target_ulong *args)
{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
+
if ((opcode <= MAX_HCALL_OPCODE)
&& ((opcode & 0x3) == 0)) {
spapr_hcall_fn fn = papr_hypercall_table[opcode / 4];
diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c
index f3990fdc3..f61504e0c 100644
--- a/hw/ppc/spapr_iommu.c
+++ b/hw/ppc/spapr_iommu.c
@@ -41,7 +41,7 @@ enum sPAPRTCEAccess {
static QLIST_HEAD(spapr_tce_tables, sPAPRTCETable) spapr_tce_tables;
-static sPAPRTCETable *spapr_tce_find_by_liobn(uint32_t liobn)
+sPAPRTCETable *spapr_tce_find_by_liobn(target_ulong liobn)
{
sPAPRTCETable *tcet;
@@ -52,7 +52,7 @@ static sPAPRTCETable *spapr_tce_find_by_liobn(uint32_t liobn)
}
QLIST_FOREACH(tcet, &spapr_tce_tables, list) {
- if (tcet->liobn == liobn) {
+ if (tcet->liobn == (uint32_t)liobn) {
return tcet;
}
}
@@ -60,6 +60,20 @@ static sPAPRTCETable *spapr_tce_find_by_liobn(uint32_t liobn)
return NULL;
}
+static IOMMUAccessFlags spapr_tce_iommu_access_flags(uint64_t tce)
+{
+ switch (tce & SPAPR_TCE_RW) {
+ case SPAPR_TCE_FAULT:
+ return IOMMU_NONE;
+ case SPAPR_TCE_RO:
+ return IOMMU_RO;
+ case SPAPR_TCE_WO:
+ return IOMMU_WO;
+ default: /* SPAPR_TCE_RW */
+ return IOMMU_RW;
+ }
+}
+
/* Called from RCU critical section */
static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr,
bool is_write)
@@ -82,7 +96,7 @@ static IOMMUTLBEntry spapr_tce_translate_iommu(MemoryRegion *iommu, hwaddr addr,
ret.iova = addr & page_mask;
ret.translated_addr = tce & page_mask;
ret.addr_mask = ~page_mask;
- ret.perm = tce & IOMMU_RW;
+ ret.perm = spapr_tce_iommu_access_flags(tce);
}
trace_spapr_iommu_xlate(tcet->liobn, addr, ret.iova, ret.perm,
ret.addr_mask);
@@ -126,11 +140,11 @@ static MemoryRegionIOMMUOps spapr_iommu_ops = {
static int spapr_tce_table_realize(DeviceState *dev)
{
sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev);
+ uint64_t window_size = (uint64_t)tcet->nb_table << tcet->page_shift;
- if (kvm_enabled()) {
+ if (kvm_enabled() && !(window_size >> 32)) {
tcet->table = kvmppc_create_spapr_tce(tcet->liobn,
- tcet->nb_table <<
- tcet->page_shift,
+ window_size,
&tcet->fd,
tcet->vfio_accel);
}
@@ -161,6 +175,7 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn,
bool vfio_accel)
{
sPAPRTCETable *tcet;
+ char tmp[64];
if (spapr_tce_find_by_liobn(liobn)) {
fprintf(stderr, "Attempted to create TCE table with duplicate"
@@ -179,7 +194,8 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn,
tcet->nb_table = nb_table;
tcet->vfio_accel = vfio_accel;
- object_property_add_child(OBJECT(owner), "tce-table", OBJECT(tcet), NULL);
+ snprintf(tmp, sizeof(tmp), "tce-table-%x", liobn);
+ object_property_add_child(OBJECT(owner), tmp, OBJECT(tcet), NULL);
object_property_set_bool(OBJECT(tcet), true, "realized", NULL);
@@ -231,14 +247,14 @@ static target_ulong put_tce_emu(sPAPRTCETable *tcet, target_ulong ioba,
entry.iova = ioba & page_mask;
entry.translated_addr = tce & page_mask;
entry.addr_mask = ~page_mask;
- entry.perm = tce & IOMMU_RW;
+ entry.perm = spapr_tce_iommu_access_flags(tce);
memory_region_notify_iommu(&tcet->iommu, entry);
return H_SUCCESS;
}
static target_ulong h_put_tce_indirect(PowerPCCPU *cpu,
- sPAPREnvironment *spapr,
+ sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
int i;
@@ -247,7 +263,7 @@ static target_ulong h_put_tce_indirect(PowerPCCPU *cpu,
target_ulong ioba1 = ioba;
target_ulong tce_list = args[2];
target_ulong npages = args[3];
- target_ulong ret = H_PARAMETER;
+ target_ulong ret = H_PARAMETER, tce = 0;
sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn);
CPUState *cs = CPU(cpu);
hwaddr page_mask, page_size;
@@ -265,9 +281,7 @@ static target_ulong h_put_tce_indirect(PowerPCCPU *cpu,
ioba &= page_mask;
for (i = 0; i < npages; ++i, ioba += page_size) {
- target_ulong off = (tce_list & ~SPAPR_TCE_RW) +
- i * sizeof(target_ulong);
- target_ulong tce = ldq_phys(cs->as, off);
+ tce = ldq_be_phys(cs->as, tce_list + i * sizeof(target_ulong));
ret = put_tce_emu(tcet, ioba, tce);
if (ret) {
@@ -277,15 +291,15 @@ static target_ulong h_put_tce_indirect(PowerPCCPU *cpu,
/* Trace last successful or the first problematic entry */
i = i ? (i - 1) : 0;
- trace_spapr_iommu_indirect(liobn, ioba1, tce_list, i,
- ldq_phys(cs->as,
- tce_list + i * sizeof(target_ulong)),
- ret);
-
+ if (SPAPR_IS_PCI_LIOBN(liobn)) {
+ trace_spapr_iommu_pci_indirect(liobn, ioba1, tce_list, i, tce, ret);
+ } else {
+ trace_spapr_iommu_indirect(liobn, ioba1, tce_list, i, tce, ret);
+ }
return ret;
}
-static target_ulong h_stuff_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_stuff_tce(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
int i;
@@ -315,12 +329,16 @@ static target_ulong h_stuff_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr,
break;
}
}
- trace_spapr_iommu_stuff(liobn, ioba, tce_value, npages, ret);
+ if (SPAPR_IS_PCI_LIOBN(liobn)) {
+ trace_spapr_iommu_pci_stuff(liobn, ioba, tce_value, npages, ret);
+ } else {
+ trace_spapr_iommu_stuff(liobn, ioba, tce_value, npages, ret);
+ }
return ret;
}
-static target_ulong h_put_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_put_tce(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
target_ulong liobn = args[0];
@@ -336,7 +354,11 @@ static target_ulong h_put_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr,
ret = put_tce_emu(tcet, ioba, tce);
}
- trace_spapr_iommu_put(liobn, ioba, tce, ret);
+ if (SPAPR_IS_PCI_LIOBN(liobn)) {
+ trace_spapr_iommu_pci_put(liobn, ioba, tce, ret);
+ } else {
+ trace_spapr_iommu_put(liobn, ioba, tce, ret);
+ }
return ret;
}
@@ -357,7 +379,7 @@ static target_ulong get_tce_emu(sPAPRTCETable *tcet, target_ulong ioba,
return H_SUCCESS;
}
-static target_ulong h_get_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_get_tce(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
target_ulong liobn = args[0];
@@ -376,7 +398,11 @@ static target_ulong h_get_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr,
args[0] = tce;
}
}
- trace_spapr_iommu_get(liobn, ioba, ret, tce);
+ if (SPAPR_IS_PCI_LIOBN(liobn)) {
+ trace_spapr_iommu_pci_get(liobn, ioba, ret, tce);
+ } else {
+ trace_spapr_iommu_get(liobn, ioba, ret, tce);
+ }
return ret;
}
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index 05f4faca6..119fa5e46 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -23,6 +23,7 @@
* THE SOFTWARE.
*/
#include "hw/hw.h"
+#include "hw/sysbus.h"
#include "hw/pci/pci.h"
#include "hw/pci/msi.h"
#include "hw/pci/msix.h"
@@ -33,8 +34,12 @@
#include <libfdt.h>
#include "trace.h"
#include "qemu/error-report.h"
+#include "qapi/qmp/qerror.h"
+#include "hw/pci/pci_bridge.h"
#include "hw/pci/pci_bus.h"
+#include "hw/ppc/spapr_drc.h"
+#include "sysemu/device_tree.h"
/* Copied from the kernel arch/powerpc/platforms/pseries/msi.c */
#define RTAS_QUERY_FN 0
@@ -47,7 +52,17 @@
#define RTAS_TYPE_MSI 1
#define RTAS_TYPE_MSIX 2
-static sPAPRPHBState *find_phb(sPAPREnvironment *spapr, uint64_t buid)
+#define FDT_NAME_MAX 128
+
+#define _FDT(exp) \
+ do { \
+ int ret = (exp); \
+ if (ret < 0) { \
+ return ret; \
+ } \
+ } while (0)
+
+sPAPRPHBState *spapr_pci_find_phb(sPAPRMachineState *spapr, uint64_t buid)
{
sPAPRPHBState *sphb;
@@ -61,10 +76,10 @@ static sPAPRPHBState *find_phb(sPAPREnvironment *spapr, uint64_t buid)
return NULL;
}
-static PCIDevice *find_dev(sPAPREnvironment *spapr, uint64_t buid,
- uint32_t config_addr)
+PCIDevice *spapr_pci_find_dev(sPAPRMachineState *spapr, uint64_t buid,
+ uint32_t config_addr)
{
- sPAPRPHBState *sphb = find_phb(spapr, buid);
+ sPAPRPHBState *sphb = spapr_pci_find_phb(spapr, buid);
PCIHostState *phb = PCI_HOST_BRIDGE(sphb);
int bus_num = (config_addr >> 16) & 0xFF;
int devfn = (config_addr >> 8) & 0xFF;
@@ -82,7 +97,7 @@ static uint32_t rtas_pci_cfgaddr(uint32_t arg)
return ((arg >> 20) & 0xf00) | (arg & 0xff);
}
-static void finish_read_pci_config(sPAPREnvironment *spapr, uint64_t buid,
+static void finish_read_pci_config(sPAPRMachineState *spapr, uint64_t buid,
uint32_t addr, uint32_t size,
target_ulong rets)
{
@@ -95,7 +110,7 @@ static void finish_read_pci_config(sPAPREnvironment *spapr, uint64_t buid,
return;
}
- pci_dev = find_dev(spapr, buid, addr);
+ pci_dev = spapr_pci_find_dev(spapr, buid, addr);
addr = rtas_pci_cfgaddr(addr);
if (!pci_dev || (addr % size) || (addr >= pci_config_size(pci_dev))) {
@@ -112,7 +127,7 @@ static void finish_read_pci_config(sPAPREnvironment *spapr, uint64_t buid,
rtas_st(rets, 1, val);
}
-static void rtas_ibm_read_pci_config(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static void rtas_ibm_read_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
@@ -132,7 +147,7 @@ static void rtas_ibm_read_pci_config(PowerPCCPU *cpu, sPAPREnvironment *spapr,
finish_read_pci_config(spapr, buid, addr, size, rets);
}
-static void rtas_read_pci_config(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static void rtas_read_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
@@ -150,7 +165,7 @@ static void rtas_read_pci_config(PowerPCCPU *cpu, sPAPREnvironment *spapr,
finish_read_pci_config(spapr, 0, addr, size, rets);
}
-static void finish_write_pci_config(sPAPREnvironment *spapr, uint64_t buid,
+static void finish_write_pci_config(sPAPRMachineState *spapr, uint64_t buid,
uint32_t addr, uint32_t size,
uint32_t val, target_ulong rets)
{
@@ -162,7 +177,7 @@ static void finish_write_pci_config(sPAPREnvironment *spapr, uint64_t buid,
return;
}
- pci_dev = find_dev(spapr, buid, addr);
+ pci_dev = spapr_pci_find_dev(spapr, buid, addr);
addr = rtas_pci_cfgaddr(addr);
if (!pci_dev || (addr % size) || (addr >= pci_config_size(pci_dev))) {
@@ -178,7 +193,7 @@ static void finish_write_pci_config(sPAPREnvironment *spapr, uint64_t buid,
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
}
-static void rtas_ibm_write_pci_config(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static void rtas_ibm_write_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
@@ -199,7 +214,7 @@ static void rtas_ibm_write_pci_config(PowerPCCPU *cpu, sPAPREnvironment *spapr,
finish_write_pci_config(spapr, buid, addr, size, val, rets);
}
-static void rtas_write_pci_config(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static void rtas_write_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
@@ -248,7 +263,7 @@ static void spapr_msi_setmsg(PCIDevice *pdev, hwaddr addr, bool msix,
}
}
-static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args, uint32_t nret,
target_ulong rets)
@@ -280,9 +295,9 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
}
/* Fins sPAPRPHBState */
- phb = find_phb(spapr, buid);
+ phb = spapr_pci_find_phb(spapr, buid);
if (phb) {
- pdev = find_dev(spapr, buid, config_addr);
+ pdev = spapr_pci_find_dev(spapr, buid, config_addr);
}
if (!phb || !pdev) {
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
@@ -366,7 +381,7 @@ out:
}
static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu,
- sPAPREnvironment *spapr,
+ sPAPRMachineState *spapr,
uint32_t token,
uint32_t nargs,
target_ulong args,
@@ -381,9 +396,9 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu,
spapr_pci_msi *msi;
/* Find sPAPRPHBState */
- phb = find_phb(spapr, buid);
+ phb = spapr_pci_find_phb(spapr, buid);
if (phb) {
- pdev = find_dev(spapr, buid, config_addr);
+ pdev = spapr_pci_find_dev(spapr, buid, config_addr);
}
if (!phb || !pdev) {
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
@@ -407,13 +422,14 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu,
}
static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu,
- sPAPREnvironment *spapr,
+ sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args, uint32_t nret,
target_ulong rets)
{
sPAPRPHBState *sphb;
sPAPRPHBClass *spc;
+ PCIDevice *pdev;
uint32_t addr, option;
uint64_t buid;
int ret;
@@ -426,11 +442,17 @@ static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu,
addr = rtas_ld(args, 0);
option = rtas_ld(args, 3);
- sphb = find_phb(spapr, buid);
+ sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
+ pdev = pci_find_device(PCI_HOST_BRIDGE(sphb)->bus,
+ (addr >> 16) & 0xFF, (addr >> 8) & 0xFF);
+ if (!pdev || !object_dynamic_cast(OBJECT(pdev), "vfio-pci")) {
+ goto param_error_exit;
+ }
+
spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb);
if (!spc->eeh_set_option) {
goto param_error_exit;
@@ -445,7 +467,7 @@ param_error_exit:
}
static void rtas_ibm_get_config_addr_info2(PowerPCCPU *cpu,
- sPAPREnvironment *spapr,
+ sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args, uint32_t nret,
target_ulong rets)
@@ -461,7 +483,7 @@ static void rtas_ibm_get_config_addr_info2(PowerPCCPU *cpu,
}
buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
- sphb = find_phb(spapr, buid);
+ sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
@@ -479,7 +501,7 @@ static void rtas_ibm_get_config_addr_info2(PowerPCCPU *cpu,
switch (option) {
case RTAS_GET_PE_ADDR:
addr = rtas_ld(args, 0);
- pdev = find_dev(spapr, buid, addr);
+ pdev = spapr_pci_find_dev(spapr, buid, addr);
if (!pdev) {
goto param_error_exit;
}
@@ -501,7 +523,7 @@ param_error_exit:
}
static void rtas_ibm_read_slot_reset_state2(PowerPCCPU *cpu,
- sPAPREnvironment *spapr,
+ sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args, uint32_t nret,
target_ulong rets)
@@ -516,7 +538,7 @@ static void rtas_ibm_read_slot_reset_state2(PowerPCCPU *cpu,
}
buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
- sphb = find_phb(spapr, buid);
+ sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
@@ -545,7 +567,7 @@ param_error_exit:
}
static void rtas_ibm_set_slot_reset(PowerPCCPU *cpu,
- sPAPREnvironment *spapr,
+ sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args, uint32_t nret,
target_ulong rets)
@@ -562,7 +584,7 @@ static void rtas_ibm_set_slot_reset(PowerPCCPU *cpu,
buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
option = rtas_ld(args, 3);
- sphb = find_phb(spapr, buid);
+ sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
@@ -581,7 +603,7 @@ param_error_exit:
}
static void rtas_ibm_configure_pe(PowerPCCPU *cpu,
- sPAPREnvironment *spapr,
+ sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args, uint32_t nret,
target_ulong rets)
@@ -596,7 +618,7 @@ static void rtas_ibm_configure_pe(PowerPCCPU *cpu,
}
buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
- sphb = find_phb(spapr, buid);
+ sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
@@ -616,7 +638,7 @@ param_error_exit:
/* To support it later */
static void rtas_ibm_slot_error_detail(PowerPCCPU *cpu,
- sPAPREnvironment *spapr,
+ sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args, uint32_t nret,
target_ulong rets)
@@ -631,7 +653,7 @@ static void rtas_ibm_slot_error_detail(PowerPCCPU *cpu,
}
buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
- sphb = find_phb(spapr, buid);
+ sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
@@ -707,6 +729,7 @@ static PCIINTxRoute spapr_route_intx_pin_to_irq(void *opaque, int pin)
static void spapr_msi_write(void *opaque, hwaddr addr,
uint64_t data, unsigned size)
{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
uint32_t irq = data;
trace_spapr_pci_msi_write(addr, data, irq);
@@ -731,8 +754,472 @@ static AddressSpace *spapr_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn)
return &phb->iommu_as;
}
+static char *spapr_phb_vfio_get_loc_code(sPAPRPHBState *sphb, PCIDevice *pdev)
+{
+ char *path = NULL, *buf = NULL, *host = NULL;
+
+ /* Get the PCI VFIO host id */
+ host = object_property_get_str(OBJECT(pdev), "host", NULL);
+ if (!host) {
+ goto err_out;
+ }
+
+ /* Construct the path of the file that will give us the DT location */
+ path = g_strdup_printf("/sys/bus/pci/devices/%s/devspec", host);
+ g_free(host);
+ if (!path || !g_file_get_contents(path, &buf, NULL, NULL)) {
+ goto err_out;
+ }
+ g_free(path);
+
+ /* Construct and read from host device tree the loc-code */
+ path = g_strdup_printf("/proc/device-tree%s/ibm,loc-code", buf);
+ g_free(buf);
+ if (!path || !g_file_get_contents(path, &buf, NULL, NULL)) {
+ goto err_out;
+ }
+ return buf;
+
+err_out:
+ g_free(path);
+ return NULL;
+}
+
+static char *spapr_phb_get_loc_code(sPAPRPHBState *sphb, PCIDevice *pdev)
+{
+ char *buf;
+ const char *devtype = "qemu";
+ uint32_t busnr = pci_bus_num(PCI_BUS(qdev_get_parent_bus(DEVICE(pdev))));
+
+ if (object_dynamic_cast(OBJECT(pdev), "vfio-pci")) {
+ buf = spapr_phb_vfio_get_loc_code(sphb, pdev);
+ if (buf) {
+ return buf;
+ }
+ devtype = "vfio";
+ }
+ /*
+ * For emulated devices and VFIO-failure case, make up
+ * the loc-code.
+ */
+ buf = g_strdup_printf("%s_%s:%04x:%02x:%02x.%x",
+ devtype, pdev->name, sphb->index, busnr,
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+ return buf;
+}
+
+/* Macros to operate with address in OF binding to PCI */
+#define b_x(x, p, l) (((x) & ((1<<(l))-1)) << (p))
+#define b_n(x) b_x((x), 31, 1) /* 0 if relocatable */
+#define b_p(x) b_x((x), 30, 1) /* 1 if prefetchable */
+#define b_t(x) b_x((x), 29, 1) /* 1 if the address is aliased */
+#define b_ss(x) b_x((x), 24, 2) /* the space code */
+#define b_bbbbbbbb(x) b_x((x), 16, 8) /* bus number */
+#define b_ddddd(x) b_x((x), 11, 5) /* device number */
+#define b_fff(x) b_x((x), 8, 3) /* function number */
+#define b_rrrrrrrr(x) b_x((x), 0, 8) /* register number */
+
+/* for 'reg'/'assigned-addresses' OF properties */
+#define RESOURCE_CELLS_SIZE 2
+#define RESOURCE_CELLS_ADDRESS 3
+
+typedef struct ResourceFields {
+ uint32_t phys_hi;
+ uint32_t phys_mid;
+ uint32_t phys_lo;
+ uint32_t size_hi;
+ uint32_t size_lo;
+} QEMU_PACKED ResourceFields;
+
+typedef struct ResourceProps {
+ ResourceFields reg[8];
+ ResourceFields assigned[7];
+ uint32_t reg_len;
+ uint32_t assigned_len;
+} ResourceProps;
+
+/* fill in the 'reg'/'assigned-resources' OF properties for
+ * a PCI device. 'reg' describes resource requirements for a
+ * device's IO/MEM regions, 'assigned-addresses' describes the
+ * actual resource assignments.
+ *
+ * the properties are arrays of ('phys-addr', 'size') pairs describing
+ * the addressable regions of the PCI device, where 'phys-addr' is a
+ * RESOURCE_CELLS_ADDRESS-tuple of 32-bit integers corresponding to
+ * (phys.hi, phys.mid, phys.lo), and 'size' is a
+ * RESOURCE_CELLS_SIZE-tuple corresponding to (size.hi, size.lo).
+ *
+ * phys.hi = 0xYYXXXXZZ, where:
+ * 0xYY = npt000ss
+ * ||| |
+ * ||| +-- space code
+ * ||| |
+ * ||| + 00 if configuration space
+ * ||| + 01 if IO region,
+ * ||| + 10 if 32-bit MEM region
+ * ||| + 11 if 64-bit MEM region
+ * |||
+ * ||+------ for non-relocatable IO: 1 if aliased
+ * || for relocatable IO: 1 if below 64KB
+ * || for MEM: 1 if below 1MB
+ * |+------- 1 if region is prefetchable
+ * +-------- 1 if region is non-relocatable
+ * 0xXXXX = bbbbbbbb dddddfff, encoding bus, slot, and function
+ * bits respectively
+ * 0xZZ = rrrrrrrr, the register number of the BAR corresponding
+ * to the region
+ *
+ * phys.mid and phys.lo correspond respectively to the hi/lo portions
+ * of the actual address of the region.
+ *
+ * how the phys-addr/size values are used differ slightly between
+ * 'reg' and 'assigned-addresses' properties. namely, 'reg' has
+ * an additional description for the config space region of the
+ * device, and in the case of QEMU has n=0 and phys.mid=phys.lo=0
+ * to describe the region as relocatable, with an address-mapping
+ * that corresponds directly to the PHB's address space for the
+ * resource. 'assigned-addresses' always has n=1 set with an absolute
+ * address assigned for the resource. in general, 'assigned-addresses'
+ * won't be populated, since addresses for PCI devices are generally
+ * unmapped initially and left to the guest to assign.
+ *
+ * note also that addresses defined in these properties are, at least
+ * for PAPR guests, relative to the PHBs IO/MEM windows, and
+ * correspond directly to the addresses in the BARs.
+ *
+ * in accordance with PCI Bus Binding to Open Firmware,
+ * IEEE Std 1275-1994, section 4.1.1, as implemented by PAPR+ v2.7,
+ * Appendix C.
+ */
+static void populate_resource_props(PCIDevice *d, ResourceProps *rp)
+{
+ int bus_num = pci_bus_num(PCI_BUS(qdev_get_parent_bus(DEVICE(d))));
+ uint32_t dev_id = (b_bbbbbbbb(bus_num) |
+ b_ddddd(PCI_SLOT(d->devfn)) |
+ b_fff(PCI_FUNC(d->devfn)));
+ ResourceFields *reg, *assigned;
+ int i, reg_idx = 0, assigned_idx = 0;
+
+ /* config space region */
+ reg = &rp->reg[reg_idx++];
+ reg->phys_hi = cpu_to_be32(dev_id);
+ reg->phys_mid = 0;
+ reg->phys_lo = 0;
+ reg->size_hi = 0;
+ reg->size_lo = 0;
+
+ for (i = 0; i < PCI_NUM_REGIONS; i++) {
+ if (!d->io_regions[i].size) {
+ continue;
+ }
+
+ reg = &rp->reg[reg_idx++];
+
+ reg->phys_hi = cpu_to_be32(dev_id | b_rrrrrrrr(pci_bar(d, i)));
+ if (d->io_regions[i].type & PCI_BASE_ADDRESS_SPACE_IO) {
+ reg->phys_hi |= cpu_to_be32(b_ss(1));
+ } else if (d->io_regions[i].type & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ reg->phys_hi |= cpu_to_be32(b_ss(3));
+ } else {
+ reg->phys_hi |= cpu_to_be32(b_ss(2));
+ }
+ reg->phys_mid = 0;
+ reg->phys_lo = 0;
+ reg->size_hi = cpu_to_be32(d->io_regions[i].size >> 32);
+ reg->size_lo = cpu_to_be32(d->io_regions[i].size);
+
+ if (d->io_regions[i].addr == PCI_BAR_UNMAPPED) {
+ continue;
+ }
+
+ assigned = &rp->assigned[assigned_idx++];
+ assigned->phys_hi = cpu_to_be32(reg->phys_hi | b_n(1));
+ assigned->phys_mid = cpu_to_be32(d->io_regions[i].addr >> 32);
+ assigned->phys_lo = cpu_to_be32(d->io_regions[i].addr);
+ assigned->size_hi = reg->size_hi;
+ assigned->size_lo = reg->size_lo;
+ }
+
+ rp->reg_len = reg_idx * sizeof(ResourceFields);
+ rp->assigned_len = assigned_idx * sizeof(ResourceFields);
+}
+
+static uint32_t spapr_phb_get_pci_drc_index(sPAPRPHBState *phb,
+ PCIDevice *pdev);
+
+static int spapr_populate_pci_child_dt(PCIDevice *dev, void *fdt, int offset,
+ sPAPRPHBState *sphb)
+{
+ ResourceProps rp;
+ bool is_bridge = false;
+ int pci_status, err;
+ char *buf = NULL;
+ uint32_t drc_index = spapr_phb_get_pci_drc_index(sphb, dev);
+ uint32_t max_msi, max_msix;
+
+ if (pci_default_read_config(dev, PCI_HEADER_TYPE, 1) ==
+ PCI_HEADER_TYPE_BRIDGE) {
+ is_bridge = true;
+ }
+
+ /* in accordance with PAPR+ v2.7 13.6.3, Table 181 */
+ _FDT(fdt_setprop_cell(fdt, offset, "vendor-id",
+ pci_default_read_config(dev, PCI_VENDOR_ID, 2)));
+ _FDT(fdt_setprop_cell(fdt, offset, "device-id",
+ pci_default_read_config(dev, PCI_DEVICE_ID, 2)));
+ _FDT(fdt_setprop_cell(fdt, offset, "revision-id",
+ pci_default_read_config(dev, PCI_REVISION_ID, 1)));
+ _FDT(fdt_setprop_cell(fdt, offset, "class-code",
+ pci_default_read_config(dev, PCI_CLASS_PROG, 3)));
+ if (pci_default_read_config(dev, PCI_INTERRUPT_PIN, 1)) {
+ _FDT(fdt_setprop_cell(fdt, offset, "interrupts",
+ pci_default_read_config(dev, PCI_INTERRUPT_PIN, 1)));
+ }
+
+ if (!is_bridge) {
+ _FDT(fdt_setprop_cell(fdt, offset, "min-grant",
+ pci_default_read_config(dev, PCI_MIN_GNT, 1)));
+ _FDT(fdt_setprop_cell(fdt, offset, "max-latency",
+ pci_default_read_config(dev, PCI_MAX_LAT, 1)));
+ }
+
+ if (pci_default_read_config(dev, PCI_SUBSYSTEM_ID, 2)) {
+ _FDT(fdt_setprop_cell(fdt, offset, "subsystem-id",
+ pci_default_read_config(dev, PCI_SUBSYSTEM_ID, 2)));
+ }
+
+ if (pci_default_read_config(dev, PCI_SUBSYSTEM_VENDOR_ID, 2)) {
+ _FDT(fdt_setprop_cell(fdt, offset, "subsystem-vendor-id",
+ pci_default_read_config(dev, PCI_SUBSYSTEM_VENDOR_ID, 2)));
+ }
+
+ _FDT(fdt_setprop_cell(fdt, offset, "cache-line-size",
+ pci_default_read_config(dev, PCI_CACHE_LINE_SIZE, 1)));
+
+ /* the following fdt cells are masked off the pci status register */
+ pci_status = pci_default_read_config(dev, PCI_STATUS, 2);
+ _FDT(fdt_setprop_cell(fdt, offset, "devsel-speed",
+ PCI_STATUS_DEVSEL_MASK & pci_status));
+
+ if (pci_status & PCI_STATUS_FAST_BACK) {
+ _FDT(fdt_setprop(fdt, offset, "fast-back-to-back", NULL, 0));
+ }
+ if (pci_status & PCI_STATUS_66MHZ) {
+ _FDT(fdt_setprop(fdt, offset, "66mhz-capable", NULL, 0));
+ }
+ if (pci_status & PCI_STATUS_UDF) {
+ _FDT(fdt_setprop(fdt, offset, "udf-supported", NULL, 0));
+ }
+
+ /* NOTE: this is normally generated by firmware via path/unit name,
+ * but in our case we must set it manually since it does not get
+ * processed by OF beforehand
+ */
+ _FDT(fdt_setprop_string(fdt, offset, "name", "pci"));
+ buf = spapr_phb_get_loc_code(sphb, dev);
+ if (!buf) {
+ error_report("Failed setting the ibm,loc-code");
+ return -1;
+ }
+
+ err = fdt_setprop_string(fdt, offset, "ibm,loc-code", buf);
+ g_free(buf);
+ if (err < 0) {
+ return err;
+ }
+
+ if (drc_index) {
+ _FDT(fdt_setprop_cell(fdt, offset, "ibm,my-drc-index", drc_index));
+ }
+
+ _FDT(fdt_setprop_cell(fdt, offset, "#address-cells",
+ RESOURCE_CELLS_ADDRESS));
+ _FDT(fdt_setprop_cell(fdt, offset, "#size-cells",
+ RESOURCE_CELLS_SIZE));
+
+ max_msi = msi_nr_vectors_allocated(dev);
+ if (max_msi) {
+ _FDT(fdt_setprop_cell(fdt, offset, "ibm,req#msi", max_msi));
+ }
+ max_msix = dev->msix_entries_nr;
+ if (max_msix) {
+ _FDT(fdt_setprop_cell(fdt, offset, "ibm,req#msi-x", max_msix));
+ }
+
+ populate_resource_props(dev, &rp);
+ _FDT(fdt_setprop(fdt, offset, "reg", (uint8_t *)rp.reg, rp.reg_len));
+ _FDT(fdt_setprop(fdt, offset, "assigned-addresses",
+ (uint8_t *)rp.assigned, rp.assigned_len));
+
+ return 0;
+}
+
+/* create OF node for pci device and required OF DT properties */
+static int spapr_create_pci_child_dt(sPAPRPHBState *phb, PCIDevice *dev,
+ void *fdt, int node_offset)
+{
+ int offset, ret;
+ int slot = PCI_SLOT(dev->devfn);
+ int func = PCI_FUNC(dev->devfn);
+ char nodename[FDT_NAME_MAX];
+
+ if (func != 0) {
+ snprintf(nodename, FDT_NAME_MAX, "pci@%x,%x", slot, func);
+ } else {
+ snprintf(nodename, FDT_NAME_MAX, "pci@%x", slot);
+ }
+ offset = fdt_add_subnode(fdt, node_offset, nodename);
+ ret = spapr_populate_pci_child_dt(dev, fdt, offset, phb);
+
+ g_assert(!ret);
+ if (ret) {
+ return 0;
+ }
+ return offset;
+}
+
+static void spapr_phb_add_pci_device(sPAPRDRConnector *drc,
+ sPAPRPHBState *phb,
+ PCIDevice *pdev,
+ Error **errp)
+{
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ DeviceState *dev = DEVICE(pdev);
+ void *fdt = NULL;
+ int fdt_start_offset = 0, fdt_size;
+
+ if (dev->hotplugged) {
+ fdt = create_device_tree(&fdt_size);
+ fdt_start_offset = spapr_create_pci_child_dt(phb, pdev, fdt, 0);
+ if (!fdt_start_offset) {
+ error_setg(errp, "Failed to create pci child device tree node");
+ goto out;
+ }
+ }
+
+ drck->attach(drc, DEVICE(pdev),
+ fdt, fdt_start_offset, !dev->hotplugged, errp);
+out:
+ if (*errp) {
+ g_free(fdt);
+ }
+}
+
+static void spapr_phb_remove_pci_device_cb(DeviceState *dev, void *opaque)
+{
+ /* some version guests do not wait for completion of a device
+ * cleanup (generally done asynchronously by the kernel) before
+ * signaling to QEMU that the device is safe, but instead sleep
+ * for some 'safe' period of time. unfortunately on a busy host
+ * this sleep isn't guaranteed to be long enough, resulting in
+ * bad things like IRQ lines being left asserted during final
+ * device removal. to deal with this we call reset just prior
+ * to finalizing the device, which will put the device back into
+ * an 'idle' state, as the device cleanup code expects.
+ */
+ pci_device_reset(PCI_DEVICE(dev));
+ object_unparent(OBJECT(dev));
+}
+
+static void spapr_phb_remove_pci_device(sPAPRDRConnector *drc,
+ sPAPRPHBState *phb,
+ PCIDevice *pdev,
+ Error **errp)
+{
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+
+ drck->detach(drc, DEVICE(pdev), spapr_phb_remove_pci_device_cb, phb, errp);
+}
+
+static sPAPRDRConnector *spapr_phb_get_pci_drc(sPAPRPHBState *phb,
+ PCIDevice *pdev)
+{
+ uint32_t busnr = pci_bus_num(PCI_BUS(qdev_get_parent_bus(DEVICE(pdev))));
+ return spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_PCI,
+ (phb->index << 16) |
+ (busnr << 8) |
+ pdev->devfn);
+}
+
+static uint32_t spapr_phb_get_pci_drc_index(sPAPRPHBState *phb,
+ PCIDevice *pdev)
+{
+ sPAPRDRConnector *drc = spapr_phb_get_pci_drc(phb, pdev);
+ sPAPRDRConnectorClass *drck;
+
+ if (!drc) {
+ return 0;
+ }
+
+ drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ return drck->get_index(drc);
+}
+
+static void spapr_phb_hot_plug_child(HotplugHandler *plug_handler,
+ DeviceState *plugged_dev, Error **errp)
+{
+ sPAPRPHBState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler));
+ PCIDevice *pdev = PCI_DEVICE(plugged_dev);
+ sPAPRDRConnector *drc = spapr_phb_get_pci_drc(phb, pdev);
+ Error *local_err = NULL;
+
+ /* if DR is disabled we don't need to do anything in the case of
+ * hotplug or coldplug callbacks
+ */
+ if (!phb->dr_enabled) {
+ /* if this is a hotplug operation initiated by the user
+ * we need to let them know it's not enabled
+ */
+ if (plugged_dev->hotplugged) {
+ error_setg(errp, QERR_BUS_NO_HOTPLUG,
+ object_get_typename(OBJECT(phb)));
+ }
+ return;
+ }
+
+ g_assert(drc);
+
+ spapr_phb_add_pci_device(drc, phb, pdev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ if (plugged_dev->hotplugged) {
+ spapr_hotplug_req_add_event(drc);
+ }
+}
+
+static void spapr_phb_hot_unplug_child(HotplugHandler *plug_handler,
+ DeviceState *plugged_dev, Error **errp)
+{
+ sPAPRPHBState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler));
+ PCIDevice *pdev = PCI_DEVICE(plugged_dev);
+ sPAPRDRConnectorClass *drck;
+ sPAPRDRConnector *drc = spapr_phb_get_pci_drc(phb, pdev);
+ Error *local_err = NULL;
+
+ if (!phb->dr_enabled) {
+ error_setg(errp, QERR_BUS_NO_HOTPLUG,
+ object_get_typename(OBJECT(phb)));
+ return;
+ }
+
+ g_assert(drc);
+
+ drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ if (!drck->release_pending(drc)) {
+ spapr_phb_remove_pci_device(drc, phb, pdev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ spapr_hotplug_req_remove_event(drc);
+ }
+}
+
static void spapr_phb_realize(DeviceState *dev, Error **errp)
{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
SysBusDevice *s = SYS_BUS_DEVICE(dev);
sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(s);
PCIHostState *phb = PCI_HOST_BRIDGE(s);
@@ -742,12 +1229,12 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
PCIBus *bus;
uint64_t msi_window_size = 4096;
- if (sphb->index != -1) {
+ if (sphb->index != (uint32_t)-1) {
hwaddr windows_base;
- if ((sphb->buid != -1) || (sphb->dma_liobn != -1)
- || (sphb->mem_win_addr != -1)
- || (sphb->io_win_addr != -1)) {
+ if ((sphb->buid != (uint64_t)-1) || (sphb->dma_liobn != (uint32_t)-1)
+ || (sphb->mem_win_addr != (hwaddr)-1)
+ || (sphb->io_win_addr != (hwaddr)-1)) {
error_setg(errp, "Either \"index\" or other parameters must"
" be specified for PAPR PHB, not both");
return;
@@ -760,7 +1247,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
}
sphb->buid = SPAPR_PCI_BASE_BUID + sphb->index;
- sphb->dma_liobn = SPAPR_PCI_BASE_LIOBN + sphb->index;
+ sphb->dma_liobn = SPAPR_PCI_LIOBN(sphb->index, 0);
windows_base = SPAPR_PCI_WINDOW_BASE
+ sphb->index * SPAPR_PCI_WINDOW_SPACING;
@@ -768,27 +1255,27 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
sphb->io_win_addr = windows_base + SPAPR_PCI_IO_WIN_OFF;
}
- if (sphb->buid == -1) {
+ if (sphb->buid == (uint64_t)-1) {
error_setg(errp, "BUID not specified for PHB");
return;
}
- if (sphb->dma_liobn == -1) {
+ if (sphb->dma_liobn == (uint32_t)-1) {
error_setg(errp, "LIOBN not specified for PHB");
return;
}
- if (sphb->mem_win_addr == -1) {
+ if (sphb->mem_win_addr == (hwaddr)-1) {
error_setg(errp, "Memory window address not specified for PHB");
return;
}
- if (sphb->io_win_addr == -1) {
+ if (sphb->io_win_addr == (hwaddr)-1) {
error_setg(errp, "IO window address not specified for PHB");
return;
}
- if (find_phb(spapr, sphb->buid)) {
+ if (spapr_pci_find_phb(spapr, sphb->buid)) {
error_setg(errp, "PCI host bridges must have unique BUIDs");
return;
}
@@ -824,6 +1311,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
&sphb->memspace, &sphb->iospace,
PCI_DEVFN(0, 0), PCI_NUM_PINS, TYPE_PCI_BUS);
phb->bus = bus;
+ qbus_set_hotplug_handler(BUS(phb->bus), DEVICE(sphb), NULL);
/*
* Initialize PHB address space.
@@ -880,6 +1368,15 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
sphb->lsi_table[i].irq = irq;
}
+ /* allocate connectors for child PCI devices */
+ if (sphb->dr_enabled) {
+ for (i = 0; i < PCI_SLOT_MAX * 8; i++) {
+ spapr_dr_connector_new(OBJECT(phb),
+ SPAPR_DR_CONNECTOR_TYPE_PCI,
+ (sphb->index << 16) | i);
+ }
+ }
+
if (!info->finish_realize) {
error_setg(errp, "finish_realize not defined");
return;
@@ -893,11 +1390,11 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
static void spapr_phb_finish_realize(sPAPRPHBState *sphb, Error **errp)
{
sPAPRTCETable *tcet;
+ uint32_t nb_table;
+ nb_table = SPAPR_PCI_DMA32_SIZE >> SPAPR_TCE_PAGE_SHIFT;
tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn,
- 0,
- SPAPR_TCE_PAGE_SHIFT,
- 0x40000000 >> SPAPR_TCE_PAGE_SHIFT, false);
+ 0, SPAPR_TCE_PAGE_SHIFT, nb_table, false);
if (!tcet) {
error_setg(errp, "Unable to create TCE table for %s",
sphb->dtbusname);
@@ -936,6 +1433,8 @@ static Property spapr_phb_properties[] = {
DEFINE_PROP_UINT64("io_win_addr", sPAPRPHBState, io_win_addr, -1),
DEFINE_PROP_UINT64("io_win_size", sPAPRPHBState, io_win_size,
SPAPR_PCI_IO_WIN_SIZE),
+ DEFINE_PROP_BOOL("dynamic-reconfiguration", sPAPRPHBState, dr_enabled,
+ true),
DEFINE_PROP_END_OF_LIST(),
};
@@ -962,34 +1461,28 @@ static const VMStateDescription vmstate_spapr_pci_msi = {
},
};
-static void spapr_pci_fill_msi_devs(gpointer key, gpointer value,
- gpointer opaque)
-{
- sPAPRPHBState *sphb = opaque;
-
- sphb->msi_devs[sphb->msi_devs_num].key = *(uint32_t *)key;
- sphb->msi_devs[sphb->msi_devs_num].value = *(spapr_pci_msi *)value;
- sphb->msi_devs_num++;
-}
-
static void spapr_pci_pre_save(void *opaque)
{
sPAPRPHBState *sphb = opaque;
- int msi_devs_num;
+ GHashTableIter iter;
+ gpointer key, value;
+ int i;
if (sphb->msi_devs) {
g_free(sphb->msi_devs);
sphb->msi_devs = NULL;
}
- sphb->msi_devs_num = 0;
- msi_devs_num = g_hash_table_size(sphb->msi);
- if (!msi_devs_num) {
+ sphb->msi_devs_num = g_hash_table_size(sphb->msi);
+ if (!sphb->msi_devs_num) {
return;
}
- sphb->msi_devs = g_malloc(msi_devs_num * sizeof(spapr_pci_msi_mig));
+ sphb->msi_devs = g_malloc(sphb->msi_devs_num * sizeof(spapr_pci_msi_mig));
- g_hash_table_foreach(sphb->msi, spapr_pci_fill_msi_devs, sphb);
- assert(sphb->msi_devs_num == msi_devs_num);
+ g_hash_table_iter_init(&iter, sphb->msi);
+ for (i = 0; g_hash_table_iter_next(&iter, &key, &value); ++i) {
+ sphb->msi_devs[i].key = *(uint32_t *) key;
+ sphb->msi_devs[i].value = *(spapr_pci_msi *) value;
+ }
}
static int spapr_pci_post_load(void *opaque, int version_id)
@@ -1049,6 +1542,7 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data)
PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
sPAPRPHBClass *spc = SPAPR_PCI_HOST_BRIDGE_CLASS(klass);
+ HotplugHandlerClass *hp = HOTPLUG_HANDLER_CLASS(klass);
hc->root_bus_path = spapr_phb_root_bus_path;
dc->realize = spapr_phb_realize;
@@ -1058,6 +1552,8 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data)
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
dc->cannot_instantiate_with_device_add_yet = false;
spc->finish_realize = spapr_phb_finish_realize;
+ hp->plug = spapr_phb_hot_plug_child;
+ hp->unplug = spapr_phb_hot_unplug_child;
}
static const TypeInfo spapr_phb_info = {
@@ -1066,9 +1562,13 @@ static const TypeInfo spapr_phb_info = {
.instance_size = sizeof(sPAPRPHBState),
.class_init = spapr_phb_class_init,
.class_size = sizeof(sPAPRPHBClass),
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_HOTPLUG_HANDLER },
+ { }
+ }
};
-PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index)
+PCIHostState *spapr_create_phb(sPAPRMachineState *spapr, int index)
{
DeviceState *dev;
@@ -1079,46 +1579,90 @@ PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index)
return PCI_HOST_BRIDGE(dev);
}
-/* Macros to operate with address in OF binding to PCI */
-#define b_x(x, p, l) (((x) & ((1<<(l))-1)) << (p))
-#define b_n(x) b_x((x), 31, 1) /* 0 if relocatable */
-#define b_p(x) b_x((x), 30, 1) /* 1 if prefetchable */
-#define b_t(x) b_x((x), 29, 1) /* 1 if the address is aliased */
-#define b_ss(x) b_x((x), 24, 2) /* the space code */
-#define b_bbbbbbbb(x) b_x((x), 16, 8) /* bus number */
-#define b_ddddd(x) b_x((x), 11, 5) /* device number */
-#define b_fff(x) b_x((x), 8, 3) /* function number */
-#define b_rrrrrrrr(x) b_x((x), 0, 8) /* register number */
-
-typedef struct sPAPRTCEDT {
+typedef struct sPAPRFDT {
void *fdt;
int node_off;
-} sPAPRTCEDT;
+ sPAPRPHBState *sphb;
+} sPAPRFDT;
-static int spapr_phb_children_dt(Object *child, void *opaque)
+static void spapr_populate_pci_devices_dt(PCIBus *bus, PCIDevice *pdev,
+ void *opaque)
{
- sPAPRTCEDT *p = opaque;
- sPAPRTCETable *tcet;
+ PCIBus *sec_bus;
+ sPAPRFDT *p = opaque;
+ int offset;
+ sPAPRFDT s_fdt;
+
+ offset = spapr_create_pci_child_dt(p->sphb, pdev, p->fdt, p->node_off);
+ if (!offset) {
+ error_report("Failed to create pci child device tree node");
+ return;
+ }
- tcet = (sPAPRTCETable *) object_dynamic_cast(child, TYPE_SPAPR_TCE_TABLE);
- if (!tcet) {
- return 0;
+ if ((pci_default_read_config(pdev, PCI_HEADER_TYPE, 1) !=
+ PCI_HEADER_TYPE_BRIDGE)) {
+ return;
}
- spapr_dma_dt(p->fdt, p->node_off, "ibm,dma-window",
- tcet->liobn, tcet->bus_offset,
- tcet->nb_table << tcet->page_shift);
- /* Stop after the first window */
+ sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(pdev));
+ if (!sec_bus) {
+ return;
+ }
+
+ s_fdt.fdt = p->fdt;
+ s_fdt.node_off = offset;
+ s_fdt.sphb = p->sphb;
+ pci_for_each_device(sec_bus, pci_bus_num(sec_bus),
+ spapr_populate_pci_devices_dt,
+ &s_fdt);
+}
+
+static void spapr_phb_pci_enumerate_bridge(PCIBus *bus, PCIDevice *pdev,
+ void *opaque)
+{
+ unsigned int *bus_no = opaque;
+ unsigned int primary = *bus_no;
+ unsigned int subordinate = 0xff;
+ PCIBus *sec_bus = NULL;
+
+ if ((pci_default_read_config(pdev, PCI_HEADER_TYPE, 1) !=
+ PCI_HEADER_TYPE_BRIDGE)) {
+ return;
+ }
+
+ (*bus_no)++;
+ pci_default_write_config(pdev, PCI_PRIMARY_BUS, primary, 1);
+ pci_default_write_config(pdev, PCI_SECONDARY_BUS, *bus_no, 1);
+ pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, *bus_no, 1);
+
+ sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(pdev));
+ if (!sec_bus) {
+ return;
+ }
+
+ pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, subordinate, 1);
+ pci_for_each_device(sec_bus, pci_bus_num(sec_bus),
+ spapr_phb_pci_enumerate_bridge, bus_no);
+ pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, *bus_no, 1);
+}
+
+static void spapr_phb_pci_enumerate(sPAPRPHBState *phb)
+{
+ PCIBus *bus = PCI_HOST_BRIDGE(phb)->bus;
+ unsigned int bus_no = 0;
+
+ pci_for_each_device(bus, pci_bus_num(bus),
+ spapr_phb_pci_enumerate_bridge,
+ &bus_no);
- return 1;
}
int spapr_populate_pci_dt(sPAPRPHBState *phb,
uint32_t xics_phandle,
void *fdt)
{
- int bus_off, i, j;
- char nodename[256];
+ int bus_off, i, j, ret;
+ char nodename[FDT_NAME_MAX];
uint32_t bus_range[] = { cpu_to_be32(0), cpu_to_be32(0xff) };
const uint64_t mmiosize = memory_region_size(&phb->memwindow);
const uint64_t w32max = (1ULL << 32) - SPAPR_PCI_MEM_WIN_BUS_OFFSET;
@@ -1151,22 +1695,17 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
uint32_t interrupt_map_mask[] = {
cpu_to_be32(b_ddddd(-1)|b_fff(0)), 0x0, 0x0, cpu_to_be32(-1)};
uint32_t interrupt_map[PCI_SLOT_MAX * PCI_NUM_PINS][7];
+ sPAPRTCETable *tcet;
+ PCIBus *bus = PCI_HOST_BRIDGE(phb)->bus;
+ sPAPRFDT s_fdt;
/* Start populating the FDT */
- sprintf(nodename, "pci@%" PRIx64, phb->buid);
+ snprintf(nodename, FDT_NAME_MAX, "pci@%" PRIx64, phb->buid);
bus_off = fdt_add_subnode(fdt, 0, nodename);
if (bus_off < 0) {
return bus_off;
}
-#define _FDT(exp) \
- do { \
- int ret = (exp); \
- if (ret < 0) { \
- return ret; \
- } \
- } while (0)
-
/* Write PHB properties */
_FDT(fdt_setprop_string(fdt, bus_off, "device_type", "pci"));
_FDT(fdt_setprop_string(fdt, bus_off, "compatible", "IBM,Logical_PHB"));
@@ -1203,8 +1742,28 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
_FDT(fdt_setprop(fdt, bus_off, "interrupt-map", &interrupt_map,
sizeof(interrupt_map)));
- object_child_foreach(OBJECT(phb), spapr_phb_children_dt,
- &((sPAPRTCEDT){ .fdt = fdt, .node_off = bus_off }));
+ tcet = spapr_tce_find_by_liobn(SPAPR_PCI_LIOBN(phb->index, 0));
+ spapr_dma_dt(fdt, bus_off, "ibm,dma-window",
+ tcet->liobn, tcet->bus_offset,
+ tcet->nb_table << tcet->page_shift);
+
+ /* Walk the bridges and program the bus numbers*/
+ spapr_phb_pci_enumerate(phb);
+ _FDT(fdt_setprop_cell(fdt, bus_off, "qemu,phb-enumerated", 0x1));
+
+ /* Populate tree nodes with PCI devices attached */
+ s_fdt.fdt = fdt;
+ s_fdt.node_off = bus_off;
+ s_fdt.sphb = phb;
+ pci_for_each_device(bus, pci_bus_num(bus),
+ spapr_populate_pci_devices_dt,
+ &s_fdt);
+
+ ret = spapr_drc_populate_dt(fdt, bus_off, OBJECT(phb),
+ SPAPR_DR_CONNECTOR_TYPE_PCI);
+ if (ret) {
+ return ret;
+ }
return 0;
}
@@ -1268,6 +1827,7 @@ static int spapr_switch_one_vga(DeviceState *dev, void *opaque)
void spapr_pci_switch_vga(bool big_endian)
{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
sPAPRPHBState *sphb;
/*
diff --git a/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c
index 99a1be511..cca45ed31 100644
--- a/hw/ppc/spapr_pci_vfio.c
+++ b/hw/ppc/spapr_pci_vfio.c
@@ -19,6 +19,7 @@
#include "hw/ppc/spapr.h"
#include "hw/pci-host/spapr.h"
+#include "hw/pci/msix.h"
#include "linux/vfio.h"
#include "hw/vfio/vfio.h"
@@ -71,9 +72,26 @@ static void spapr_phb_vfio_finish_realize(sPAPRPHBState *sphb, Error **errp)
spapr_tce_get_iommu(tcet));
}
+static void spapr_phb_vfio_eeh_reenable(sPAPRPHBVFIOState *svphb)
+{
+ struct vfio_eeh_pe_op op = {
+ .argsz = sizeof(op),
+ .op = VFIO_EEH_PE_ENABLE
+ };
+
+ vfio_container_ioctl(&svphb->phb.iommu_as,
+ svphb->iommugroupid, VFIO_EEH_PE_OP, &op);
+}
+
static void spapr_phb_vfio_reset(DeviceState *qdev)
{
- /* Do nothing */
+ /*
+ * The PE might be in frozen state. To reenable the EEH
+ * functionality on it will clean the frozen state, which
+ * ensures that the contained PCI devices will work properly
+ * after reboot.
+ */
+ spapr_phb_vfio_eeh_reenable(SPAPR_PCI_VFIO_HOST_BRIDGE(qdev));
}
static int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb,
@@ -142,6 +160,49 @@ static int spapr_phb_vfio_eeh_get_state(sPAPRPHBState *sphb, int *state)
return RTAS_OUT_SUCCESS;
}
+static void spapr_phb_vfio_eeh_clear_dev_msix(PCIBus *bus,
+ PCIDevice *pdev,
+ void *opaque)
+{
+ /* Check if the device is VFIO PCI device */
+ if (!object_dynamic_cast(OBJECT(pdev), "vfio-pci")) {
+ return;
+ }
+
+ /*
+ * The MSIx table will be cleaned out by reset. We need
+ * disable it so that it can be reenabled properly. Also,
+ * the cached MSIx table should be cleared as it's not
+ * reflecting the contents in hardware.
+ */
+ if (msix_enabled(pdev)) {
+ uint16_t flags;
+
+ flags = pci_host_config_read_common(pdev,
+ pdev->msix_cap + PCI_MSIX_FLAGS,
+ pci_config_size(pdev), 2);
+ flags &= ~PCI_MSIX_FLAGS_ENABLE;
+ pci_host_config_write_common(pdev,
+ pdev->msix_cap + PCI_MSIX_FLAGS,
+ pci_config_size(pdev), flags, 2);
+ }
+
+ msix_reset(pdev);
+}
+
+static void spapr_phb_vfio_eeh_clear_bus_msix(PCIBus *bus, void *opaque)
+{
+ pci_for_each_device(bus, pci_bus_num(bus),
+ spapr_phb_vfio_eeh_clear_dev_msix, NULL);
+}
+
+static void spapr_phb_vfio_eeh_pre_reset(sPAPRPHBState *sphb)
+{
+ PCIHostState *phb = PCI_HOST_BRIDGE(sphb);
+
+ pci_for_each_bus(phb->bus, spapr_phb_vfio_eeh_clear_bus_msix, NULL);
+}
+
static int spapr_phb_vfio_eeh_reset(sPAPRPHBState *sphb, int option)
{
sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb);
@@ -153,9 +214,11 @@ static int spapr_phb_vfio_eeh_reset(sPAPRPHBState *sphb, int option)
op.op = VFIO_EEH_PE_RESET_DEACTIVATE;
break;
case RTAS_SLOT_RESET_HOT:
+ spapr_phb_vfio_eeh_pre_reset(sphb);
op.op = VFIO_EEH_PE_RESET_HOT;
break;
case RTAS_SLOT_RESET_FUNDAMENTAL:
+ spapr_phb_vfio_eeh_pre_reset(sphb);
op.op = VFIO_EEH_PE_RESET_FUNDAMENTAL;
break;
default:
diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c
index 0f1ae5582..2986f94f0 100644
--- a/hw/ppc/spapr_rtas.c
+++ b/hw/ppc/spapr_rtas.c
@@ -29,14 +29,64 @@
#include "sysemu/char.h"
#include "hw/qdev.h"
#include "sysemu/device_tree.h"
+#include "sysemu/cpus.h"
#include "hw/ppc/spapr.h"
#include "hw/ppc/spapr_vio.h"
#include "qapi-event.h"
#include <libfdt.h>
+#include "hw/ppc/spapr_drc.h"
-static void rtas_display_character(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+/* #define DEBUG_SPAPR */
+
+#ifdef DEBUG_SPAPR
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+ do { } while (0)
+#endif
+
+static sPAPRConfigureConnectorState *spapr_ccs_find(sPAPRMachineState *spapr,
+ uint32_t drc_index)
+{
+ sPAPRConfigureConnectorState *ccs = NULL;
+
+ QTAILQ_FOREACH(ccs, &spapr->ccs_list, next) {
+ if (ccs->drc_index == drc_index) {
+ break;
+ }
+ }
+
+ return ccs;
+}
+
+static void spapr_ccs_add(sPAPRMachineState *spapr,
+ sPAPRConfigureConnectorState *ccs)
+{
+ g_assert(!spapr_ccs_find(spapr, ccs->drc_index));
+ QTAILQ_INSERT_HEAD(&spapr->ccs_list, ccs, next);
+}
+
+static void spapr_ccs_remove(sPAPRMachineState *spapr,
+ sPAPRConfigureConnectorState *ccs)
+{
+ QTAILQ_REMOVE(&spapr->ccs_list, ccs, next);
+ g_free(ccs);
+}
+
+void spapr_ccs_reset_hook(void *opaque)
+{
+ sPAPRMachineState *spapr = opaque;
+ sPAPRConfigureConnectorState *ccs, *ccs_tmp;
+
+ QTAILQ_FOREACH_SAFE(ccs, &spapr->ccs_list, next, ccs_tmp) {
+ spapr_ccs_remove(spapr, ccs);
+ }
+}
+
+static void rtas_display_character(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
@@ -52,7 +102,7 @@ static void rtas_display_character(PowerPCCPU *cpu, sPAPREnvironment *spapr,
}
}
-static void rtas_power_off(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static void rtas_power_off(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs, target_ulong args,
uint32_t nret, target_ulong rets)
{
@@ -64,7 +114,7 @@ static void rtas_power_off(PowerPCCPU *cpu, sPAPREnvironment *spapr,
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
}
-static void rtas_system_reboot(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static void rtas_system_reboot(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
@@ -78,7 +128,7 @@ static void rtas_system_reboot(PowerPCCPU *cpu, sPAPREnvironment *spapr,
}
static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_,
- sPAPREnvironment *spapr,
+ sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
@@ -108,7 +158,7 @@ static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_,
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
}
-static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr,
+static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
@@ -155,7 +205,7 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr,
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
}
-static void rtas_stop_self(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static void rtas_stop_self(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
@@ -178,7 +228,7 @@ static void rtas_stop_self(PowerPCCPU *cpu, sPAPREnvironment *spapr,
}
static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu,
- sPAPREnvironment *spapr,
+ sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
@@ -213,7 +263,7 @@ static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu,
}
static void rtas_ibm_set_system_parameter(PowerPCCPU *cpu,
- sPAPREnvironment *spapr,
+ sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
@@ -233,7 +283,7 @@ static void rtas_ibm_set_system_parameter(PowerPCCPU *cpu,
}
static void rtas_ibm_os_term(PowerPCCPU *cpu,
- sPAPREnvironment *spapr,
+ sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
@@ -245,12 +295,314 @@ static void rtas_ibm_os_term(PowerPCCPU *cpu,
rtas_st(rets, 0, ret);
}
+static void rtas_set_power_level(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args, uint32_t nret,
+ target_ulong rets)
+{
+ int32_t power_domain;
+
+ if (nargs != 2 || nret != 2) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ /* we currently only use a single, "live insert" powerdomain for
+ * hotplugged/dlpar'd resources, so the power is always live/full (100)
+ */
+ power_domain = rtas_ld(args, 0);
+ if (power_domain != -1) {
+ rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED);
+ return;
+ }
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+ rtas_st(rets, 1, 100);
+}
+
+static void rtas_get_power_level(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args, uint32_t nret,
+ target_ulong rets)
+{
+ int32_t power_domain;
+
+ if (nargs != 1 || nret != 2) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ /* we currently only use a single, "live insert" powerdomain for
+ * hotplugged/dlpar'd resources, so the power is always live/full (100)
+ */
+ power_domain = rtas_ld(args, 0);
+ if (power_domain != -1) {
+ rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED);
+ return;
+ }
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+ rtas_st(rets, 1, 100);
+}
+
+static bool sensor_type_is_dr(uint32_t sensor_type)
+{
+ switch (sensor_type) {
+ case RTAS_SENSOR_TYPE_ISOLATION_STATE:
+ case RTAS_SENSOR_TYPE_DR:
+ case RTAS_SENSOR_TYPE_ALLOCATION_STATE:
+ return true;
+ }
+
+ return false;
+}
+
+static void rtas_set_indicator(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args, uint32_t nret,
+ target_ulong rets)
+{
+ uint32_t sensor_type;
+ uint32_t sensor_index;
+ uint32_t sensor_state;
+ sPAPRDRConnector *drc;
+ sPAPRDRConnectorClass *drck;
+
+ if (nargs != 3 || nret != 1) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ sensor_type = rtas_ld(args, 0);
+ sensor_index = rtas_ld(args, 1);
+ sensor_state = rtas_ld(args, 2);
+
+ if (!sensor_type_is_dr(sensor_type)) {
+ goto out_unimplemented;
+ }
+
+ /* if this is a DR sensor we can assume sensor_index == drc_index */
+ drc = spapr_dr_connector_by_index(sensor_index);
+ if (!drc) {
+ DPRINTF("rtas_set_indicator: invalid sensor/DRC index: %xh\n",
+ sensor_index);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+ drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+
+ switch (sensor_type) {
+ case RTAS_SENSOR_TYPE_ISOLATION_STATE:
+ /* if the guest is configuring a device attached to this
+ * DRC, we should reset the configuration state at this
+ * point since it may no longer be reliable (guest released
+ * device and needs to start over, or unplug occurred so
+ * the FDT is no longer valid)
+ */
+ if (sensor_state == SPAPR_DR_ISOLATION_STATE_ISOLATED) {
+ sPAPRConfigureConnectorState *ccs = spapr_ccs_find(spapr,
+ sensor_index);
+ if (ccs) {
+ spapr_ccs_remove(spapr, ccs);
+ }
+ }
+ drck->set_isolation_state(drc, sensor_state);
+ break;
+ case RTAS_SENSOR_TYPE_DR:
+ drck->set_indicator_state(drc, sensor_state);
+ break;
+ case RTAS_SENSOR_TYPE_ALLOCATION_STATE:
+ drck->set_allocation_state(drc, sensor_state);
+ break;
+ default:
+ goto out_unimplemented;
+ }
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+ return;
+
+out_unimplemented:
+ /* currently only DR-related sensors are implemented */
+ DPRINTF("rtas_set_indicator: sensor/indicator not implemented: %d\n",
+ sensor_type);
+ rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED);
+}
+
+static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args, uint32_t nret,
+ target_ulong rets)
+{
+ uint32_t sensor_type;
+ uint32_t sensor_index;
+ sPAPRDRConnector *drc;
+ sPAPRDRConnectorClass *drck;
+ uint32_t entity_sense;
+
+ if (nargs != 2 || nret != 2) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ sensor_type = rtas_ld(args, 0);
+ sensor_index = rtas_ld(args, 1);
+
+ if (sensor_type != RTAS_SENSOR_TYPE_ENTITY_SENSE) {
+ /* currently only DR-related sensors are implemented */
+ DPRINTF("rtas_get_sensor_state: sensor/indicator not implemented: %d\n",
+ sensor_type);
+ rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED);
+ return;
+ }
+
+ drc = spapr_dr_connector_by_index(sensor_index);
+ if (!drc) {
+ DPRINTF("rtas_get_sensor_state: invalid sensor/DRC index: %xh\n",
+ sensor_index);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+ drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ entity_sense = drck->entity_sense(drc);
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+ rtas_st(rets, 1, entity_sense);
+}
+
+/* configure-connector work area offsets, int32_t units for field
+ * indexes, bytes for field offset/len values.
+ *
+ * as documented by PAPR+ v2.7, 13.5.3.5
+ */
+#define CC_IDX_NODE_NAME_OFFSET 2
+#define CC_IDX_PROP_NAME_OFFSET 2
+#define CC_IDX_PROP_LEN 3
+#define CC_IDX_PROP_DATA_OFFSET 4
+#define CC_VAL_DATA_OFFSET ((CC_IDX_PROP_DATA_OFFSET + 1) * 4)
+#define CC_WA_LEN 4096
+
+static void rtas_ibm_configure_connector(PowerPCCPU *cpu,
+ sPAPRMachineState *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args, uint32_t nret,
+ target_ulong rets)
+{
+ uint64_t wa_addr;
+ uint64_t wa_offset;
+ uint32_t drc_index;
+ sPAPRDRConnector *drc;
+ sPAPRDRConnectorClass *drck;
+ sPAPRConfigureConnectorState *ccs;
+ sPAPRDRCCResponse resp = SPAPR_DR_CC_RESPONSE_CONTINUE;
+ int rc;
+ const void *fdt;
+
+ if (nargs != 2 || nret != 1) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ wa_addr = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 0);
+
+ drc_index = rtas_ld(wa_addr, 0);
+ drc = spapr_dr_connector_by_index(drc_index);
+ if (!drc) {
+ DPRINTF("rtas_ibm_configure_connector: invalid DRC index: %xh\n",
+ drc_index);
+ rc = RTAS_OUT_PARAM_ERROR;
+ goto out;
+ }
+
+ drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ fdt = drck->get_fdt(drc, NULL);
+
+ ccs = spapr_ccs_find(spapr, drc_index);
+ if (!ccs) {
+ ccs = g_new0(sPAPRConfigureConnectorState, 1);
+ (void)drck->get_fdt(drc, &ccs->fdt_offset);
+ ccs->drc_index = drc_index;
+ spapr_ccs_add(spapr, ccs);
+ }
+
+ do {
+ uint32_t tag;
+ const char *name;
+ const struct fdt_property *prop;
+ int fdt_offset_next, prop_len;
+
+ tag = fdt_next_tag(fdt, ccs->fdt_offset, &fdt_offset_next);
+
+ switch (tag) {
+ case FDT_BEGIN_NODE:
+ ccs->fdt_depth++;
+ name = fdt_get_name(fdt, ccs->fdt_offset, NULL);
+
+ /* provide the name of the next OF node */
+ wa_offset = CC_VAL_DATA_OFFSET;
+ rtas_st(wa_addr, CC_IDX_NODE_NAME_OFFSET, wa_offset);
+ rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset,
+ (uint8_t *)name, strlen(name) + 1);
+ resp = SPAPR_DR_CC_RESPONSE_NEXT_CHILD;
+ break;
+ case FDT_END_NODE:
+ ccs->fdt_depth--;
+ if (ccs->fdt_depth == 0) {
+ /* done sending the device tree, don't need to track
+ * the state anymore
+ */
+ drck->set_configured(drc);
+ spapr_ccs_remove(spapr, ccs);
+ ccs = NULL;
+ resp = SPAPR_DR_CC_RESPONSE_SUCCESS;
+ } else {
+ resp = SPAPR_DR_CC_RESPONSE_PREV_PARENT;
+ }
+ break;
+ case FDT_PROP:
+ prop = fdt_get_property_by_offset(fdt, ccs->fdt_offset,
+ &prop_len);
+ name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
+
+ /* provide the name of the next OF property */
+ wa_offset = CC_VAL_DATA_OFFSET;
+ rtas_st(wa_addr, CC_IDX_PROP_NAME_OFFSET, wa_offset);
+ rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset,
+ (uint8_t *)name, strlen(name) + 1);
+
+ /* provide the length and value of the OF property. data gets
+ * placed immediately after NULL terminator of the OF property's
+ * name string
+ */
+ wa_offset += strlen(name) + 1,
+ rtas_st(wa_addr, CC_IDX_PROP_LEN, prop_len);
+ rtas_st(wa_addr, CC_IDX_PROP_DATA_OFFSET, wa_offset);
+ rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset,
+ (uint8_t *)((struct fdt_property *)prop)->data,
+ prop_len);
+ resp = SPAPR_DR_CC_RESPONSE_NEXT_PROPERTY;
+ break;
+ case FDT_END:
+ resp = SPAPR_DR_CC_RESPONSE_ERROR;
+ default:
+ /* keep seeking for an actionable tag */
+ break;
+ }
+ if (ccs) {
+ ccs->fdt_offset = fdt_offset_next;
+ }
+ } while (resp == SPAPR_DR_CC_RESPONSE_CONTINUE);
+
+ rc = resp;
+out:
+ rtas_st(rets, 0, rc);
+}
+
static struct rtas_call {
const char *name;
spapr_rtas_fn fn;
} rtas_table[RTAS_TOKEN_MAX - RTAS_TOKEN_BASE];
-target_ulong spapr_rtas_call(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+target_ulong spapr_rtas_call(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs, target_ulong args,
uint32_t nret, target_ulong rets)
{
@@ -300,6 +652,8 @@ int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr,
{
int ret;
int i;
+ uint32_t lrdr_capacity[5];
+ MachineState *machine = MACHINE(qdev_get_machine());
ret = fdt_add_mem_rsv(fdt, rtas_addr, rtas_size);
if (ret < 0) {
@@ -348,6 +702,19 @@ int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr,
}
}
+
+ lrdr_capacity[0] = cpu_to_be32(((uint64_t)machine->maxram_size) >> 32);
+ lrdr_capacity[1] = cpu_to_be32(machine->maxram_size & 0xffffffff);
+ lrdr_capacity[2] = 0;
+ lrdr_capacity[3] = cpu_to_be32(SPAPR_MEMORY_BLOCK_SIZE);
+ lrdr_capacity[4] = cpu_to_be32(max_cpus/smp_threads);
+ ret = qemu_fdt_setprop(fdt, "/rtas", "ibm,lrdr-capacity", lrdr_capacity,
+ sizeof(lrdr_capacity));
+ if (ret < 0) {
+ fprintf(stderr, "Couldn't add ibm,lrdr-capacity rtas property\n");
+ return ret;
+ }
+
return 0;
}
@@ -370,6 +737,16 @@ static void core_rtas_register_types(void)
rtas_ibm_set_system_parameter);
spapr_rtas_register(RTAS_IBM_OS_TERM, "ibm,os-term",
rtas_ibm_os_term);
+ spapr_rtas_register(RTAS_SET_POWER_LEVEL, "set-power-level",
+ rtas_set_power_level);
+ spapr_rtas_register(RTAS_GET_POWER_LEVEL, "get-power-level",
+ rtas_get_power_level);
+ spapr_rtas_register(RTAS_SET_INDICATOR, "set-indicator",
+ rtas_set_indicator);
+ spapr_rtas_register(RTAS_GET_SENSOR_STATE, "get-sensor-state",
+ rtas_get_sensor_state);
+ spapr_rtas_register(RTAS_IBM_CONFIGURE_CONNECTOR, "ibm,configure-connector",
+ rtas_ibm_configure_connector);
}
type_init(core_rtas_register_types)
diff --git a/hw/ppc/spapr_rtc.c b/hw/ppc/spapr_rtc.c
index 83eb7c186..34b27db70 100644
--- a/hw/ppc/spapr_rtc.c
+++ b/hw/ppc/spapr_rtc.c
@@ -26,6 +26,7 @@
*
*/
#include "cpu.h"
+#include "qemu/timer.h"
#include "sysemu/sysemu.h"
#include "hw/ppc/spapr.h"
#include "qapi-event.h"
@@ -40,8 +41,6 @@ struct sPAPRRTCState {
int64_t ns_offset;
};
-#define NSEC_PER_SEC 1000000000LL
-
void spapr_rtc_read(DeviceState *dev, struct tm *tm, uint32_t *ns)
{
sPAPRRTCState *rtc = SPAPR_RTC(dev);
@@ -52,7 +51,7 @@ void spapr_rtc_read(DeviceState *dev, struct tm *tm, uint32_t *ns)
assert(rtc);
guest_ns = host_ns + rtc->ns_offset;
- guest_s = guest_ns / NSEC_PER_SEC;
+ guest_s = guest_ns / NANOSECONDS_PER_SECOND;
if (tm) {
gmtime_r(&guest_s, tm);
@@ -72,12 +71,12 @@ int spapr_rtc_import_offset(DeviceState *dev, int64_t legacy_offset)
rtc = SPAPR_RTC(dev);
- rtc->ns_offset = legacy_offset * NSEC_PER_SEC;
+ rtc->ns_offset = legacy_offset * NANOSECONDS_PER_SECOND;
return 0;
}
-static void rtas_get_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static void rtas_get_time_of_day(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
@@ -107,7 +106,7 @@ static void rtas_get_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr,
rtas_st(rets, 7, ns);
}
-static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
uint32_t nret, target_ulong rets)
@@ -147,7 +146,7 @@ static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr,
host_ns = qemu_clock_get_ns(rtc_clock);
- rtc->ns_offset = (new_s * NSEC_PER_SEC) - host_ns;
+ rtc->ns_offset = (new_s * NANOSECONDS_PER_SECOND) - host_ns;
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
}
@@ -169,7 +168,7 @@ static void spapr_rtc_realize(DeviceState *dev, Error **errp)
qemu_get_timedate(&tm, 0);
host_s = mktimegm(&tm);
rtc_ns = qemu_clock_get_ns(rtc_clock);
- rtc->ns_offset = host_s * NSEC_PER_SEC - rtc_ns;
+ rtc->ns_offset = host_s * NANOSECONDS_PER_SECOND - rtc_ns;
object_property_add_tm(OBJECT(rtc), "date", spapr_rtc_qom_date, NULL);
}
diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c
index 1360b97ab..c51eb8e24 100644
--- a/hw/ppc/spapr_vio.c
+++ b/hw/ppc/spapr_vio.c
@@ -22,7 +22,6 @@
#include "hw/hw.h"
#include "sysemu/sysemu.h"
#include "hw/boards.h"
-#include "monitor/monitor.h"
#include "hw/loader.h"
#include "elf.h"
#include "hw/sysbus.h"
@@ -161,7 +160,7 @@ static int vio_make_devnode(VIOsPAPRDevice *dev,
/*
* CRQ handling
*/
-static target_ulong h_reg_crq(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_reg_crq(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
target_ulong reg = args[0];
@@ -219,7 +218,7 @@ static target_ulong free_crq(VIOsPAPRDevice *dev)
return H_SUCCESS;
}
-static target_ulong h_free_crq(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_free_crq(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
target_ulong reg = args[0];
@@ -233,7 +232,7 @@ static target_ulong h_free_crq(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return free_crq(dev);
}
-static target_ulong h_send_crq(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_send_crq(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
target_ulong reg = args[0];
@@ -256,7 +255,7 @@ static target_ulong h_send_crq(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return H_HARDWARE;
}
-static target_ulong h_enable_crq(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_enable_crq(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
target_ulong reg = args[0];
@@ -334,7 +333,7 @@ void spapr_vio_set_bypass(VIOsPAPRDevice *dev, bool bypass)
dev->tcet->bypass = bypass;
}
-static void rtas_set_tce_bypass(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static void rtas_set_tce_bypass(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t token,
uint32_t nargs, target_ulong args,
uint32_t nret, target_ulong rets)
@@ -365,7 +364,7 @@ static void rtas_set_tce_bypass(PowerPCCPU *cpu, sPAPREnvironment *spapr,
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
}
-static void rtas_quiesce(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static void rtas_quiesce(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t token,
uint32_t nargs, target_ulong args,
uint32_t nret, target_ulong rets)
@@ -427,6 +426,7 @@ static void spapr_vio_busdev_reset(DeviceState *qdev)
static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp)
{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev;
VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
char *id;
@@ -469,7 +469,7 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp)
}
if (pc->rtce_window_size) {
- uint32_t liobn = SPAPR_VIO_BASE_LIOBN | dev->reg;
+ uint32_t liobn = SPAPR_VIO_LIOBN(dev->reg);
memory_region_init(&dev->mrroot, OBJECT(dev), "iommu-spapr-root",
ram_size);
@@ -492,7 +492,7 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp)
pc->realize(dev, errp);
}
-static target_ulong h_vio_signal(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+static target_ulong h_vio_signal(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode,
target_ulong *args)
{
diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c
index 6ebd5bee8..de86f7c64 100644
--- a/hw/ppc/virtex_ml507.c
+++ b/hw/ppc/virtex_ml507.c
@@ -32,6 +32,7 @@
#include "sysemu/device_tree.h"
#include "hw/loader.h"
#include "elf.h"
+#include "qemu/error-report.h"
#include "qemu/log.h"
#include "exec/address-spaces.h"
@@ -40,7 +41,6 @@
#include "ppc405.h"
#include "sysemu/block-backend.h"
-#include "qapi/qmp/qerror.h"
#define EPAPR_MAGIC (0x45504150)
#define FLASH_SIZE (16 * 1024 * 1024)
@@ -197,7 +197,6 @@ static int xilinx_load_device_tree(hwaddr addr,
static void virtex_init(MachineState *machine)
{
ram_addr_t ram_size = machine->ram_size;
- const char *cpu_model = machine->cpu_model;
const char *kernel_filename = machine->kernel_filename;
const char *kernel_cmdline = machine->kernel_cmdline;
hwaddr initrd_base = 0;
@@ -214,11 +213,11 @@ static void virtex_init(MachineState *machine)
int i;
/* init CPUs */
- if (cpu_model == NULL) {
- cpu_model = "440-Xilinx";
+ if (machine->cpu_model == NULL) {
+ machine->cpu_model = "440-Xilinx";
}
- cpu = ppc440_init_xilinx(&ram_size, 1, cpu_model, 400000000);
+ cpu = ppc440_init_xilinx(&ram_size, 1, machine->cpu_model, 400000000);
env = &cpu->env;
qemu_register_reset(main_cpu_reset, cpu);
diff --git a/hw/s390x/css.c b/hw/s390x/css.c
index 9a13b006d..97d93d56d 100644
--- a/hw/s390x/css.c
+++ b/hw/s390x/css.c
@@ -392,6 +392,8 @@ static void sch_handle_start_func(SubchDev *sch, ORB *orb)
path = 0x80;
if (!(s->ctrl & SCSW_ACTL_SUSP)) {
+ s->cstat = 0;
+ s->dstat = 0;
/* Look at the orb and try to execute the channel program. */
assert(orb != NULL); /* resume does not pass an orb */
p->intparm = orb->intparm;
@@ -588,6 +590,7 @@ int css_do_msch(SubchDev *sch, const SCHIB *orig_schib)
{
SCSW *s = &sch->curr_status.scsw;
PMCW *p = &sch->curr_status.pmcw;
+ uint16_t oldflags;
int ret;
SCHIB schib;
@@ -610,6 +613,7 @@ int css_do_msch(SubchDev *sch, const SCHIB *orig_schib)
copy_schib_from_guest(&schib, orig_schib);
/* Only update the program-modifiable fields. */
p->intparm = schib.pmcw.intparm;
+ oldflags = p->flags;
p->flags &= ~(PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA |
PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME |
PMCW_FLAGS_MASK_MP);
@@ -625,6 +629,12 @@ int css_do_msch(SubchDev *sch, const SCHIB *orig_schib)
(PMCW_CHARS_MASK_MBFC | PMCW_CHARS_MASK_CSENSE);
sch->curr_status.mba = schib.mba;
+ /* Has the channel been disabled? */
+ if (sch->disable_cb && (oldflags & PMCW_FLAGS_MASK_ENA) != 0
+ && (p->flags & PMCW_FLAGS_MASK_ENA) == 0) {
+ sch->disable_cb(sch);
+ }
+
ret = 0;
out:
@@ -745,20 +755,27 @@ static void css_update_chnmon(SubchDev *sch)
/* Format 1, per-subchannel area. */
uint32_t count;
- count = ldl_phys(&address_space_memory, sch->curr_status.mba);
+ count = address_space_ldl(&address_space_memory,
+ sch->curr_status.mba,
+ MEMTXATTRS_UNSPECIFIED,
+ NULL);
count++;
- stl_phys(&address_space_memory, sch->curr_status.mba, count);
+ address_space_stl(&address_space_memory, sch->curr_status.mba, count,
+ MEMTXATTRS_UNSPECIFIED, NULL);
} else {
/* Format 0, global area. */
uint32_t offset;
uint16_t count;
offset = sch->curr_status.pmcw.mbi << 5;
- count = lduw_phys(&address_space_memory,
- channel_subsys->chnmon_area + offset);
+ count = address_space_lduw(&address_space_memory,
+ channel_subsys->chnmon_area + offset,
+ MEMTXATTRS_UNSPECIFIED,
+ NULL);
count++;
- stw_phys(&address_space_memory,
- channel_subsys->chnmon_area + offset, count);
+ address_space_stw(&address_space_memory,
+ channel_subsys->chnmon_area + offset, count,
+ MEMTXATTRS_UNSPECIFIED, NULL);
}
}
@@ -1457,6 +1474,21 @@ int subch_device_load(SubchDev *s, QEMUFile *f)
}
s->ccw_fmt_1 = qemu_get_byte(f);
s->ccw_no_data_cnt = qemu_get_byte(f);
+ /*
+ * Hack alert. We don't migrate the channel subsystem status (no
+ * device!), but we need to find out if the guest enabled mss/mcss-e.
+ * If the subchannel is enabled, it certainly was able to access it,
+ * so adjust the max_ssid/max_cssid values for relevant ssid/cssid
+ * values. This is not watertight, but better than nothing.
+ */
+ if (s->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA) {
+ if (s->ssid) {
+ channel_subsys->max_ssid = MAX_SSID;
+ }
+ if (s->cssid != channel_subsys->default_cssid) {
+ channel_subsys->max_cssid = MAX_CSSID;
+ }
+ }
return 0;
}
@@ -1476,6 +1508,10 @@ void css_reset_sch(SubchDev *sch)
{
PMCW *p = &sch->curr_status.pmcw;
+ if ((p->flags & PMCW_FLAGS_MASK_ENA) != 0 && sch->disable_cb) {
+ sch->disable_cb(sch);
+ }
+
p->intparm = 0;
p->flags &= ~(PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA |
PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME |
diff --git a/hw/s390x/css.h b/hw/s390x/css.h
index 7e5314870..a09bb1f87 100644
--- a/hw/s390x/css.h
+++ b/hw/s390x/css.h
@@ -81,6 +81,7 @@ struct SubchDev {
uint8_t ccw_no_data_cnt;
/* transport-provided data: */
int (*ccw_cb) (SubchDev *, CCW1);
+ void (*disable_cb)(SubchDev *);
SenseId id;
void *driver_data;
};
diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c
index 78da71836..0c700effb 100644
--- a/hw/s390x/event-facility.c
+++ b/hw/s390x/event-facility.c
@@ -15,7 +15,6 @@
*
*/
-#include "monitor/monitor.h"
#include "sysemu/sysemu.h"
#include "hw/s390x/sclp.h"
@@ -362,6 +361,7 @@ static void init_event_facility_class(ObjectClass *klass, void *data)
dc->reset = reset_event_facility;
dc->vmsd = &vmstate_event_facility;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
k->init = init_event_facility;
k->command_handler = command_handler;
k->event_pending = event_pending;
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 754fb1947..2e0a8b6e0 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -315,6 +315,7 @@ static void s390_ipl_class_init(ObjectClass *klass, void *data)
dc->props = s390_ipl_properties;
dc->reset = s390_ipl_reset;
dc->vmsd = &vmstate_ipl;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
static const TypeInfo s390_ipl_info = {
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
index 3c086f615..560b66a50 100644
--- a/hw/s390x/s390-pci-bus.c
+++ b/hw/s390x/s390-pci-bus.c
@@ -278,7 +278,8 @@ static uint64_t s390_guest_io_table_walk(uint64_t guest_iota,
px = calc_px(guest_dma_address);
sto_a = guest_iota + rtx * sizeof(uint64_t);
- sto = ldq_phys(&address_space_memory, sto_a);
+ sto = address_space_ldq(&address_space_memory, sto_a,
+ MEMTXATTRS_UNSPECIFIED, NULL);
sto = get_rt_sto(sto);
if (!sto) {
pte = 0;
@@ -286,7 +287,8 @@ static uint64_t s390_guest_io_table_walk(uint64_t guest_iota,
}
pto_a = sto + sx * sizeof(uint64_t);
- pto = ldq_phys(&address_space_memory, pto_a);
+ pto = address_space_ldq(&address_space_memory, pto_a,
+ MEMTXATTRS_UNSPECIFIED, NULL);
pto = get_st_pto(pto);
if (!pto) {
pte = 0;
@@ -294,7 +296,8 @@ static uint64_t s390_guest_io_table_walk(uint64_t guest_iota,
}
px_a = pto + px * sizeof(uint64_t);
- pte = ldq_phys(&address_space_memory, px_a);
+ pte = address_space_ldq(&address_space_memory, px_a,
+ MEMTXATTRS_UNSPECIFIED, NULL);
out:
return pte;
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index 08d8aa6b4..f9151a9af 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -155,7 +155,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2)
return 0;
}
- if (s390_cpu_virt_mem_read(cpu, env->regs[r2], buffer, sizeof(*reqh))) {
+ if (s390_cpu_virt_mem_read(cpu, env->regs[r2], r2, buffer, sizeof(*reqh))) {
return 0;
}
reqh = (ClpReqHdr *)buffer;
@@ -165,7 +165,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2)
return 0;
}
- if (s390_cpu_virt_mem_read(cpu, env->regs[r2], buffer,
+ if (s390_cpu_virt_mem_read(cpu, env->regs[r2], r2, buffer,
req_len + sizeof(*resh))) {
return 0;
}
@@ -180,7 +180,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2)
return 0;
}
- if (s390_cpu_virt_mem_read(cpu, env->regs[r2], buffer,
+ if (s390_cpu_virt_mem_read(cpu, env->regs[r2], r2, buffer,
req_len + res_len)) {
return 0;
}
@@ -277,7 +277,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2)
}
out:
- if (s390_cpu_virt_mem_write(cpu, env->regs[r2], buffer,
+ if (s390_cpu_virt_mem_write(cpu, env->regs[r2], r2, buffer,
req_len + res_len)) {
return 0;
}
@@ -331,7 +331,8 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
return 0;
}
MemoryRegion *mr = pbdev->pdev->io_regions[pcias].memory;
- io_mem_read(mr, offset, &data, len);
+ memory_region_dispatch_read(mr, offset, &data, len,
+ MEMTXATTRS_UNSPECIFIED);
} else if (pcias == 15) {
if ((4 - (offset & 0x3)) < len) {
program_interrupt(env, PGM_OPERAND, 4);
@@ -456,7 +457,8 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
mr = pbdev->pdev->io_regions[pcias].memory;
}
- io_mem_write(mr, offset, data, len);
+ memory_region_dispatch_write(mr, offset, data, len,
+ MEMTXATTRS_UNSPECIFIED);
} else if (pcias == 15) {
if ((4 - (offset & 0x3)) < len) {
program_interrupt(env, PGM_OPERAND, 4);
@@ -544,7 +546,8 @@ out:
return 0;
}
-int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr)
+int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr,
+ uint8_t ar)
{
CPUS390XState *env = &cpu->env;
S390PCIBusDevice *pbdev;
@@ -601,12 +604,14 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr)
return 0;
}
- if (s390_cpu_virt_mem_read(cpu, gaddr, buffer, len)) {
+ if (s390_cpu_virt_mem_read(cpu, gaddr, ar, buffer, len)) {
return 0;
}
for (i = 0; i < len / 8; i++) {
- io_mem_write(mr, env->regs[r3] + i * 8, ldq_p(buffer + i * 8), 8);
+ memory_region_dispatch_write(mr, env->regs[r3] + i * 8,
+ ldq_p(buffer + i * 8), 8,
+ MEMTXATTRS_UNSPECIFIED);
}
setcc(cpu, ZPCI_PCI_LS_OK);
@@ -694,7 +699,7 @@ static void dereg_ioat(S390PCIBusDevice *pbdev)
pbdev->g_iota = 0;
}
-int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba)
+int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
{
CPUS390XState *env = &cpu->env;
uint8_t oc;
@@ -723,7 +728,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba)
return 0;
}
- if (s390_cpu_virt_mem_read(cpu, fiba, (uint8_t *)&fib, sizeof(fib))) {
+ if (s390_cpu_virt_mem_read(cpu, fiba, ar, (uint8_t *)&fib, sizeof(fib))) {
return 0;
}
@@ -769,7 +774,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba)
return 0;
}
-int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba)
+int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
{
CPUS390XState *env = &cpu->env;
uint32_t fh;
@@ -825,7 +830,7 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba)
fib.fc |= 0x10;
}
- if (s390_cpu_virt_mem_write(cpu, fiba, (uint8_t *)&fib, sizeof(fib))) {
+ if (s390_cpu_virt_mem_write(cpu, fiba, ar, (uint8_t *)&fib, sizeof(fib))) {
return 0;
}
diff --git a/hw/s390x/s390-pci-inst.h b/hw/s390x/s390-pci-inst.h
index 7e6c80473..70fa71395 100644
--- a/hw/s390x/s390-pci-inst.h
+++ b/hw/s390x/s390-pci-inst.h
@@ -281,8 +281,9 @@ int clp_service_call(S390CPU *cpu, uint8_t r2);
int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2);
int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2);
int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2);
-int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr);
-int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba);
-int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba);
+int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr,
+ uint8_t ar);
+int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar);
+int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar);
#endif
diff --git a/hw/s390x/s390-virtio-bus.c b/hw/s390x/s390-virtio-bus.c
index 047c96369..77aec8a5b 100644
--- a/hw/s390x/s390-virtio-bus.c
+++ b/hw/s390x/s390-virtio-bus.c
@@ -21,7 +21,6 @@
#include "sysemu/block-backend.h"
#include "sysemu/sysemu.h"
#include "hw/boards.h"
-#include "monitor/monitor.h"
#include "hw/loader.h"
#include "elf.h"
#include "hw/virtio/virtio.h"
@@ -45,6 +44,8 @@
do { } while (0)
#endif
+#define VIRTIO_S390_QUEUE_MAX 64
+
static void virtio_s390_bus_new(VirtioBusState *bus, size_t bus_size,
VirtIOS390Device *dev);
@@ -75,10 +76,20 @@ void s390_virtio_reset_idx(VirtIOS390Device *dev)
for (i = 0; i < num_vq; i++) {
idx_addr = virtio_queue_get_avail_addr(dev->vdev, i) +
VIRTIO_VRING_AVAIL_IDX_OFFS;
- stw_phys(&address_space_memory, idx_addr, 0);
+ address_space_stw(&address_space_memory, idx_addr, 0,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ idx_addr = virtio_queue_get_avail_addr(dev->vdev, i) +
+ virtio_queue_get_avail_size(dev->vdev, i);
+ address_space_stw(&address_space_memory, idx_addr, 0,
+ MEMTXATTRS_UNSPECIFIED, NULL);
idx_addr = virtio_queue_get_used_addr(dev->vdev, i) +
VIRTIO_VRING_USED_IDX_OFFS;
- stw_phys(&address_space_memory, idx_addr, 0);
+ address_space_stw(&address_space_memory, idx_addr, 0,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ idx_addr = virtio_queue_get_used_addr(dev->vdev, i) +
+ virtio_queue_get_used_size(dev->vdev, i);
+ address_space_stw(&address_space_memory, idx_addr, 0,
+ MEMTXATTRS_UNSPECIFIED, NULL);
}
}
@@ -129,8 +140,6 @@ static void s390_virtio_device_init(VirtIOS390Device *dev,
bus->dev_offs += dev_len;
- dev->host_features = virtio_bus_get_vdev_features(&dev->bus,
- dev->host_features);
s390_virtio_device_sync(dev);
s390_virtio_reset_idx(dev);
if (dev->qdev.hotplugged) {
@@ -145,7 +154,6 @@ static void s390_virtio_net_realize(VirtIOS390Device *s390_dev, Error **errp)
DeviceState *vdev = DEVICE(&dev->vdev);
Error *err = NULL;
- virtio_net_set_config_size(&dev->vdev, s390_dev->host_features);
virtio_net_set_netclient_name(&dev->vdev, qdev->id,
object_get_typename(OBJECT(qdev)));
qdev_set_parent_bus(vdev, BUS(&s390_dev->bus));
@@ -336,7 +344,8 @@ static uint64_t s390_virtio_device_vq_token(VirtIOS390Device *dev, int vq)
(vq * VIRTIO_VQCONFIG_LEN) +
VIRTIO_VQCONFIG_OFFS_TOKEN;
- return ldq_be_phys(&address_space_memory, token_off);
+ return address_space_ldq_be(&address_space_memory, token_off,
+ MEMTXATTRS_UNSPECIFIED, NULL);
}
static ram_addr_t s390_virtio_device_num_vq(VirtIOS390Device *dev)
@@ -344,7 +353,7 @@ static ram_addr_t s390_virtio_device_num_vq(VirtIOS390Device *dev)
VirtIODevice *vdev = dev->vdev;
int num_vq;
- for (num_vq = 0; num_vq < VIRTIO_PCI_QUEUE_MAX; num_vq++) {
+ for (num_vq = 0; num_vq < VIRTIO_S390_QUEUE_MAX; num_vq++) {
if (!virtio_queue_get_num(vdev, num_vq)) {
break;
}
@@ -371,21 +380,33 @@ void s390_virtio_device_sync(VirtIOS390Device *dev)
virtio_reset(dev->vdev);
/* Sync dev space */
- stb_phys(&address_space_memory,
- dev->dev_offs + VIRTIO_DEV_OFFS_TYPE, dev->vdev->device_id);
-
- stb_phys(&address_space_memory,
- dev->dev_offs + VIRTIO_DEV_OFFS_NUM_VQ,
- s390_virtio_device_num_vq(dev));
- stb_phys(&address_space_memory,
- dev->dev_offs + VIRTIO_DEV_OFFS_FEATURE_LEN, dev->feat_len);
-
- stb_phys(&address_space_memory,
- dev->dev_offs + VIRTIO_DEV_OFFS_CONFIG_LEN, dev->vdev->config_len);
+ address_space_stb(&address_space_memory,
+ dev->dev_offs + VIRTIO_DEV_OFFS_TYPE,
+ dev->vdev->device_id,
+ MEMTXATTRS_UNSPECIFIED,
+ NULL);
+
+ address_space_stb(&address_space_memory,
+ dev->dev_offs + VIRTIO_DEV_OFFS_NUM_VQ,
+ s390_virtio_device_num_vq(dev),
+ MEMTXATTRS_UNSPECIFIED,
+ NULL);
+ address_space_stb(&address_space_memory,
+ dev->dev_offs + VIRTIO_DEV_OFFS_FEATURE_LEN,
+ dev->feat_len,
+ MEMTXATTRS_UNSPECIFIED,
+ NULL);
+
+ address_space_stb(&address_space_memory,
+ dev->dev_offs + VIRTIO_DEV_OFFS_CONFIG_LEN,
+ dev->vdev->config_len,
+ MEMTXATTRS_UNSPECIFIED,
+ NULL);
num_vq = s390_virtio_device_num_vq(dev);
- stb_phys(&address_space_memory,
- dev->dev_offs + VIRTIO_DEV_OFFS_NUM_VQ, num_vq);
+ address_space_stb(&address_space_memory,
+ dev->dev_offs + VIRTIO_DEV_OFFS_NUM_VQ, num_vq,
+ MEMTXATTRS_UNSPECIFIED, NULL);
/* Sync virtqueues */
for (i = 0; i < num_vq; i++) {
@@ -396,11 +417,14 @@ void s390_virtio_device_sync(VirtIOS390Device *dev)
vring = s390_virtio_next_ring(bus);
virtio_queue_set_addr(dev->vdev, i, vring);
virtio_queue_set_vector(dev->vdev, i, i);
- stq_be_phys(&address_space_memory,
- vq + VIRTIO_VQCONFIG_OFFS_ADDRESS, vring);
- stw_be_phys(&address_space_memory,
- vq + VIRTIO_VQCONFIG_OFFS_NUM,
- virtio_queue_get_num(dev->vdev, i));
+ address_space_stq_be(&address_space_memory,
+ vq + VIRTIO_VQCONFIG_OFFS_ADDRESS, vring,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ address_space_stw_be(&address_space_memory,
+ vq + VIRTIO_VQCONFIG_OFFS_NUM,
+ virtio_queue_get_num(dev->vdev, i),
+ MEMTXATTRS_UNSPECIFIED,
+ NULL);
}
cur_offs = dev->dev_offs;
@@ -408,7 +432,9 @@ void s390_virtio_device_sync(VirtIOS390Device *dev)
cur_offs += num_vq * VIRTIO_VQCONFIG_LEN;
/* Sync feature bitmap */
- stl_le_phys(&address_space_memory, cur_offs, dev->host_features);
+ address_space_stl_le(&address_space_memory, cur_offs,
+ dev->vdev->host_features,
+ MEMTXATTRS_UNSPECIFIED, NULL);
dev->feat_offs = cur_offs + dev->feat_len;
cur_offs += dev->feat_len * 2;
@@ -426,12 +452,16 @@ void s390_virtio_device_update_status(VirtIOS390Device *dev)
VirtIODevice *vdev = dev->vdev;
uint32_t features;
- virtio_set_status(vdev, ldub_phys(&address_space_memory,
- dev->dev_offs + VIRTIO_DEV_OFFS_STATUS));
+ virtio_set_status(vdev,
+ address_space_ldub(&address_space_memory,
+ dev->dev_offs + VIRTIO_DEV_OFFS_STATUS,
+ MEMTXATTRS_UNSPECIFIED, NULL));
/* Update guest supported feature bitmap */
- features = bswap32(ldl_be_phys(&address_space_memory, dev->feat_offs));
+ features = bswap32(address_space_ldl_be(&address_space_memory,
+ dev->feat_offs,
+ MEMTXATTRS_UNSPECIFIED, NULL));
virtio_set_features(vdev, features);
}
@@ -446,7 +476,7 @@ VirtIOS390Device *s390_virtio_bus_find_vring(VirtIOS390Bus *bus,
QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
VirtIOS390Device *dev = (VirtIOS390Device *)kid->child;
- for(i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) {
+ for (i = 0; i < VIRTIO_S390_QUEUE_MAX; i++) {
if (!virtio_queue_get_addr(dev->vdev, i))
break;
if (virtio_queue_get_addr(dev->vdev, i) == mem) {
@@ -498,27 +528,28 @@ static void virtio_s390_notify(DeviceState *d, uint16_t vector)
s390_virtio_irq(0, token);
}
-static unsigned virtio_s390_get_features(DeviceState *d)
+static void virtio_s390_device_plugged(DeviceState *d, Error **errp)
{
VirtIOS390Device *dev = to_virtio_s390_device(d);
- return dev->host_features;
+ VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
+ int n = virtio_get_num_queues(vdev);
+
+ if (n > VIRTIO_S390_QUEUE_MAX) {
+ error_setg(errp, "The nubmer of virtqueues %d "
+ "exceeds s390 limit %d", n,
+ VIRTIO_S390_QUEUE_MAX);
+ }
}
/**************** S390 Virtio Bus Device Descriptions *******************/
-static Property s390_virtio_net_properties[] = {
- DEFINE_VIRTIO_COMMON_FEATURES(VirtIOS390Device, host_features),
- DEFINE_VIRTIO_NET_FEATURES(VirtIOS390Device, host_features),
- DEFINE_PROP_END_OF_LIST(),
-};
-
static void s390_virtio_net_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
k->realize = s390_virtio_net_realize;
- dc->props = s390_virtio_net_properties;
+ set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
}
static const TypeInfo s390_virtio_net = {
@@ -532,8 +563,10 @@ static const TypeInfo s390_virtio_net = {
static void s390_virtio_blk_class_init(ObjectClass *klass, void *data)
{
VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
k->realize = s390_virtio_blk_realize;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
}
static const TypeInfo s390_virtio_blk = {
@@ -555,6 +588,7 @@ static void s390_virtio_serial_class_init(ObjectClass *klass, void *data)
k->realize = s390_virtio_serial_realize;
dc->props = s390_virtio_serial_properties;
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
}
static const TypeInfo s390_virtio_serial = {
@@ -565,18 +599,13 @@ static const TypeInfo s390_virtio_serial = {
.class_init = s390_virtio_serial_class_init,
};
-static Property s390_virtio_rng_properties[] = {
- DEFINE_VIRTIO_COMMON_FEATURES(VirtIOS390Device, host_features),
- DEFINE_PROP_END_OF_LIST(),
-};
-
static void s390_virtio_rng_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
k->realize = s390_virtio_rng_realize;
- dc->props = s390_virtio_rng_properties;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
static const TypeInfo s390_virtio_rng = {
@@ -622,19 +651,13 @@ static const TypeInfo virtio_s390_device_info = {
.abstract = true,
};
-static Property s390_virtio_scsi_properties[] = {
- DEFINE_VIRTIO_COMMON_FEATURES(VirtIOS390Device, host_features),
- DEFINE_VIRTIO_SCSI_FEATURES(VirtIOS390Device, host_features),
- DEFINE_PROP_END_OF_LIST(),
-};
-
static void s390_virtio_scsi_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
k->realize = s390_virtio_scsi_realize;
- dc->props = s390_virtio_scsi_properties;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
}
static const TypeInfo s390_virtio_scsi = {
@@ -646,18 +669,13 @@ static const TypeInfo s390_virtio_scsi = {
};
#ifdef CONFIG_VHOST_SCSI
-static Property s390_vhost_scsi_properties[] = {
- DEFINE_VIRTIO_COMMON_FEATURES(VirtIOS390Device, host_features),
- DEFINE_PROP_END_OF_LIST(),
-};
-
static void s390_vhost_scsi_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
k->realize = s390_vhost_scsi_realize;
- dc->props = s390_vhost_scsi_properties;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
}
static const TypeInfo s390_vhost_scsi = {
@@ -681,8 +699,10 @@ static int s390_virtio_bridge_init(SysBusDevice *dev)
static void s390_virtio_bridge_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
k->init = s390_virtio_bridge_init;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
static const TypeInfo s390_virtio_bridge_info = {
@@ -714,7 +734,7 @@ static void virtio_s390_bus_class_init(ObjectClass *klass, void *data)
BusClass *bus_class = BUS_CLASS(klass);
bus_class->max_dev = 1;
k->notify = virtio_s390_notify;
- k->get_features = virtio_s390_get_features;
+ k->device_plugged = virtio_s390_device_plugged;
}
static const TypeInfo virtio_s390_bus_info = {
diff --git a/hw/s390x/s390-virtio-bus.h b/hw/s390x/s390-virtio-bus.h
index 96b1890b4..7ad295e68 100644
--- a/hw/s390x/s390-virtio-bus.h
+++ b/hw/s390x/s390-virtio-bus.h
@@ -92,7 +92,6 @@ struct VirtIOS390Device {
ram_addr_t feat_offs;
uint8_t feat_len;
VirtIODevice *vdev;
- uint32_t host_features;
VirtioBusState bus;
};
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index afb539ade..4c51d1a5b 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -36,7 +36,7 @@ typedef struct S390CcwMachineState {
void io_subsystem_reset(void)
{
- DeviceState *css, *sclp, *flic;
+ DeviceState *css, *sclp, *flic, *diag288;
css = DEVICE(object_resolve_path_type("", "virtual-css-bridge", NULL));
if (css) {
@@ -51,6 +51,10 @@ void io_subsystem_reset(void)
if (flic) {
qdev_reset_all(flic);
}
+ diag288 = DEVICE(object_resolve_path_type("", "diag288", NULL));
+ if (diag288) {
+ qdev_reset_all(diag288);
+ }
}
static int virtio_ccw_hcall_notify(const uint64_t *args)
@@ -67,7 +71,7 @@ static int virtio_ccw_hcall_notify(const uint64_t *args)
if (!sch || !css_subch_visible(sch)) {
return -EINVAL;
}
- if (queue >= VIRTIO_PCI_QUEUE_MAX) {
+ if (queue >= VIRTIO_CCW_QUEUE_MAX) {
return -EINVAL;
}
virtio_queue_notify(virtio_ccw_get_vdev(sch), queue);
@@ -204,9 +208,6 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data)
MachineClass *mc = MACHINE_CLASS(oc);
NMIClass *nc = NMI_CLASS(oc);
- mc->name = "s390-ccw-virtio";
- mc->alias = "s390-ccw";
- mc->desc = "VirtIO-ccw based S390 machine";
mc->init = ccw_init;
mc->block_default_type = IF_VIRTIO;
mc->no_cdrom = 1;
@@ -271,6 +272,7 @@ static inline void s390_machine_initfn(Object *obj)
static const TypeInfo ccw_machine_info = {
.name = TYPE_S390_CCW_MACHINE,
.parent = TYPE_MACHINE,
+ .abstract = true,
.instance_size = sizeof(S390CcwMachineState),
.instance_init = s390_machine_initfn,
.class_init = ccw_machine_class_init,
@@ -280,9 +282,26 @@ static const TypeInfo ccw_machine_info = {
},
};
+static void ccw_machine_2_4_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->name = "s390-ccw-virtio-2.4";
+ mc->alias = "s390-ccw-virtio";
+ mc->desc = "VirtIO-ccw based S390 machine v2.4";
+ mc->is_default = 1;
+}
+
+static const TypeInfo ccw_machine_2_4_info = {
+ .name = TYPE_S390_CCW_MACHINE "2.4",
+ .parent = TYPE_S390_CCW_MACHINE,
+ .class_init = ccw_machine_2_4_class_init,
+};
+
static void ccw_machine_register_types(void)
{
type_register_static(&ccw_machine_info);
+ type_register_static(&ccw_machine_2_4_info);
}
type_init(ccw_machine_register_types)
diff --git a/hw/s390x/s390-virtio.c b/hw/s390x/s390-virtio.c
index bdb538859..1284e77b2 100644
--- a/hw/s390x/s390-virtio.c
+++ b/hw/s390x/s390-virtio.c
@@ -22,12 +22,12 @@
*/
#include "hw/hw.h"
+#include "qapi/qmp/qerror.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
#include "sysemu/sysemu.h"
#include "net/net.h"
#include "hw/boards.h"
-#include "monitor/monitor.h"
#include "hw/loader.h"
#include "hw/virtio/virtio.h"
#include "hw/sysbus.h"
@@ -77,6 +77,16 @@ static int s390_virtio_hcall_notify(const uint64_t *args)
if (mem > ram_size) {
VirtIOS390Device *dev = s390_virtio_bus_find_vring(s390_bus, mem, &i);
if (dev) {
+ /*
+ * Older kernels will use the virtqueue before setting DRIVER_OK.
+ * In this case the feature bits are not yet up to date, meaning
+ * that several funny things can happen, e.g. the guest thinks
+ * EVENT_IDX is on and QEMU thinks it is off. Let's force a feature
+ * and status sync.
+ */
+ if (!(dev->vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
+ s390_virtio_device_update_status(dev);
+ }
virtio_queue_notify(dev->vdev, i);
} else {
r = -EINVAL;
@@ -97,7 +107,9 @@ static int s390_virtio_hcall_reset(const uint64_t *args)
return -EINVAL;
}
virtio_reset(dev->vdev);
- stb_phys(&address_space_memory, dev->dev_offs + VIRTIO_DEV_OFFS_STATUS, 0);
+ address_space_stb(&address_space_memory,
+ dev->dev_offs + VIRTIO_DEV_OFFS_STATUS, 0,
+ MEMTXATTRS_UNSPECIFIED, NULL);
s390_virtio_device_sync(dev);
s390_virtio_reset_idx(dev);
@@ -312,7 +324,7 @@ void s390_nmi(NMIState *n, int cpu_index, Error **errp)
CPUState *cs = qemu_get_cpu(cpu_index);
if (s390_cpu_restart(S390_CPU(cs))) {
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
}
}
@@ -333,7 +345,6 @@ static void s390_machine_class_init(ObjectClass *oc, void *data)
mc->no_floppy = 1;
mc->no_cdrom = 1;
mc->no_sdcard = 1;
- mc->is_default = 1;
nc->nmi_monitor_handler = s390_nmi;
}
diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c
index a969975a7..b3a6c5e5a 100644
--- a/hw/s390x/sclp.c
+++ b/hw/s390x/sclp.c
@@ -457,10 +457,19 @@ sclpMemoryHotplugDev *get_sclp_memory_hotplug_dev(void)
TYPE_SCLP_MEMORY_HOTPLUG_DEV, NULL));
}
+static void sclp_memory_hotplug_dev_class_init(ObjectClass *klass,
+ void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+}
+
static TypeInfo sclp_memory_hotplug_dev_info = {
.name = TYPE_SCLP_MEMORY_HOTPLUG_DEV,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(sclpMemoryHotplugDev),
+ .class_init = sclp_memory_hotplug_dev_class_init,
};
static void register_types(void)
diff --git a/hw/s390x/sclpcpu.c b/hw/s390x/sclpcpu.c
index 3600fe231..2fe8b5aa4 100644
--- a/hw/s390x/sclpcpu.c
+++ b/hw/s390x/sclpcpu.c
@@ -88,12 +88,14 @@ static int irq_cpu_hotplug_init(SCLPEvent *event)
static void cpu_class_init(ObjectClass *oc, void *data)
{
SCLPEventClass *k = SCLP_EVENT_CLASS(oc);
+ DeviceClass *dc = DEVICE_CLASS(oc);
k->init = irq_cpu_hotplug_init;
k->get_send_mask = send_mask;
k->get_receive_mask = receive_mask;
k->read_event_data = read_event_data;
k->write_event_data = NULL;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
static const TypeInfo sclp_cpu_info = {
diff --git a/hw/s390x/sclpquiesce.c b/hw/s390x/sclpquiesce.c
index 1a399bd1f..ffa555313 100644
--- a/hw/s390x/sclpquiesce.c
+++ b/hw/s390x/sclpquiesce.c
@@ -116,6 +116,7 @@ static void quiesce_class_init(ObjectClass *klass, void *data)
dc->reset = quiesce_reset;
dc->vmsd = &vmstate_sclpquiesce;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
k->init = quiesce_init;
k->get_send_mask = send_mask;
diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
index d8fde77d9..d36373e88 100644
--- a/hw/s390x/virtio-ccw.c
+++ b/hw/s390x/virtio-ccw.c
@@ -1,8 +1,9 @@
/*
* virtio ccw target implementation
*
- * Copyright 2012,2014 IBM Corp.
+ * Copyright 2012,2015 IBM Corp.
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ * Pierre Morel <pmorel@linux.vnet.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or (at
* your option) any later version. See the COPYING file in the top-level
@@ -14,12 +15,13 @@
#include "sysemu/blockdev.h"
#include "sysemu/sysemu.h"
#include "net/net.h"
-#include "monitor/monitor.h"
#include "hw/virtio/virtio.h"
#include "hw/virtio/virtio-serial.h"
#include "hw/virtio/virtio-net.h"
#include "hw/sysbus.h"
#include "qemu/bitops.h"
+#include "qemu/error-report.h"
+#include "hw/virtio/virtio-access.h"
#include "hw/virtio/virtio-bus.h"
#include "hw/s390x/adapter.h"
#include "hw/s390x/s390_flic.h"
@@ -170,7 +172,7 @@ static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev)
return;
}
vdev = virtio_bus_get_device(&dev->bus);
- for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
+ for (n = 0; n < VIRTIO_CCW_QUEUE_MAX; n++) {
if (!virtio_queue_get_num(vdev, n)) {
continue;
}
@@ -205,7 +207,7 @@ static void virtio_ccw_stop_ioeventfd(VirtioCcwDevice *dev)
return;
}
vdev = virtio_bus_get_device(&dev->bus);
- for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
+ for (n = 0; n < VIRTIO_CCW_QUEUE_MAX; n++) {
if (!virtio_queue_get_num(vdev, n)) {
continue;
}
@@ -236,11 +238,20 @@ VirtualCssBus *virtual_css_bus_init(void)
}
/* Communication blocks used by several channel commands. */
-typedef struct VqInfoBlock {
+typedef struct VqInfoBlockLegacy {
uint64_t queue;
uint32_t align;
uint16_t index;
uint16_t num;
+} QEMU_PACKED VqInfoBlockLegacy;
+
+typedef struct VqInfoBlock {
+ uint64_t desc;
+ uint32_t res0;
+ uint16_t index;
+ uint16_t num;
+ uint64_t avail;
+ uint64_t used;
} QEMU_PACKED VqInfoBlock;
typedef struct VqConfigBlock {
@@ -260,18 +271,27 @@ typedef struct VirtioThinintInfo {
uint8_t isc;
} QEMU_PACKED VirtioThinintInfo;
+typedef struct VirtioRevInfo {
+ uint16_t revision;
+ uint16_t length;
+ uint8_t data[0];
+} QEMU_PACKED VirtioRevInfo;
+
/* Specify where the virtqueues for the subchannel are in guest memory. */
-static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align,
- uint16_t index, uint16_t num)
+static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info,
+ VqInfoBlockLegacy *linfo)
{
VirtIODevice *vdev = virtio_ccw_get_vdev(sch);
+ uint16_t index = info ? info->index : linfo->index;
+ uint16_t num = info ? info->num : linfo->num;
+ uint64_t desc = info ? info->desc : linfo->queue;
- if (index >= VIRTIO_PCI_QUEUE_MAX) {
+ if (index >= VIRTIO_CCW_QUEUE_MAX) {
return -EINVAL;
}
/* Current code in virtio.c relies on 4K alignment. */
- if (addr && (align != 4096)) {
+ if (linfo && desc && (linfo->align != 4096)) {
return -EINVAL;
}
@@ -279,9 +299,13 @@ static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align,
return -EINVAL;
}
- virtio_queue_set_addr(vdev, index, addr);
- if (!addr) {
- virtio_queue_set_vector(vdev, index, 0);
+ if (info) {
+ virtio_queue_set_rings(vdev, index, desc, info->avail, info->used);
+ } else {
+ virtio_queue_set_addr(vdev, index, desc);
+ }
+ if (!desc) {
+ virtio_queue_set_vector(vdev, index, VIRTIO_NO_VECTOR);
} else {
/* Fail if we don't have a big enough queue. */
/* TODO: Add interface to handle vring.num changing */
@@ -291,7 +315,7 @@ static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align,
virtio_queue_set_vector(vdev, index, index);
}
/* tell notify handler in case of config change */
- vdev->config_vector = VIRTIO_PCI_QUEUE_MAX;
+ vdev->config_vector = VIRTIO_CCW_QUEUE_MAX;
return 0;
}
@@ -314,10 +338,79 @@ static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev, VirtIODevice *vdev)
dev->sch->thinint_active = false;
}
-static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
+static int virtio_ccw_handle_set_vq(SubchDev *sch, CCW1 ccw, bool check_len,
+ bool is_legacy)
{
int ret;
VqInfoBlock info;
+ VqInfoBlockLegacy linfo;
+ size_t info_len = is_legacy ? sizeof(linfo) : sizeof(info);
+
+ if (check_len) {
+ if (ccw.count != info_len) {
+ return -EINVAL;
+ }
+ } else if (ccw.count < info_len) {
+ /* Can't execute command. */
+ return -EINVAL;
+ }
+ if (!ccw.cda) {
+ return -EFAULT;
+ }
+ if (is_legacy) {
+ linfo.queue = address_space_ldq_be(&address_space_memory, ccw.cda,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ linfo.align = address_space_ldl_be(&address_space_memory,
+ ccw.cda + sizeof(linfo.queue),
+ MEMTXATTRS_UNSPECIFIED,
+ NULL);
+ linfo.index = address_space_lduw_be(&address_space_memory,
+ ccw.cda + sizeof(linfo.queue)
+ + sizeof(linfo.align),
+ MEMTXATTRS_UNSPECIFIED,
+ NULL);
+ linfo.num = address_space_lduw_be(&address_space_memory,
+ ccw.cda + sizeof(linfo.queue)
+ + sizeof(linfo.align)
+ + sizeof(linfo.index),
+ MEMTXATTRS_UNSPECIFIED,
+ NULL);
+ ret = virtio_ccw_set_vqs(sch, NULL, &linfo);
+ } else {
+ info.desc = address_space_ldq_be(&address_space_memory, ccw.cda,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ info.index = address_space_lduw_be(&address_space_memory,
+ ccw.cda + sizeof(info.desc)
+ + sizeof(info.res0),
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ info.num = address_space_lduw_be(&address_space_memory,
+ ccw.cda + sizeof(info.desc)
+ + sizeof(info.res0)
+ + sizeof(info.index),
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ info.avail = address_space_ldq_be(&address_space_memory,
+ ccw.cda + sizeof(info.desc)
+ + sizeof(info.res0)
+ + sizeof(info.index)
+ + sizeof(info.num),
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ info.used = address_space_ldq_be(&address_space_memory,
+ ccw.cda + sizeof(info.desc)
+ + sizeof(info.res0)
+ + sizeof(info.index)
+ + sizeof(info.num)
+ + sizeof(info.avail),
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ ret = virtio_ccw_set_vqs(sch, &info, NULL);
+ }
+ sch->curr_status.scsw.count = 0;
+ return ret;
+}
+
+static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
+{
+ int ret;
+ VirtioRevInfo revinfo;
uint8_t status;
VirtioFeatDesc features;
void *config;
@@ -341,33 +434,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
/* Look at the command. */
switch (ccw.cmd_code) {
case CCW_CMD_SET_VQ:
- if (check_len) {
- if (ccw.count != sizeof(info)) {
- ret = -EINVAL;
- break;
- }
- } else if (ccw.count < sizeof(info)) {
- /* Can't execute command. */
- ret = -EINVAL;
- break;
- }
- if (!ccw.cda) {
- ret = -EFAULT;
- } else {
- info.queue = ldq_phys(&address_space_memory, ccw.cda);
- info.align = ldl_phys(&address_space_memory,
- ccw.cda + sizeof(info.queue));
- info.index = lduw_phys(&address_space_memory,
- ccw.cda + sizeof(info.queue)
- + sizeof(info.align));
- info.num = lduw_phys(&address_space_memory,
- ccw.cda + sizeof(info.queue)
- + sizeof(info.align)
- + sizeof(info.index));
- ret = virtio_ccw_set_vqs(sch, info.queue, info.align, info.index,
- info.num);
- sch->curr_status.scsw.count = 0;
- }
+ ret = virtio_ccw_handle_set_vq(sch, ccw, check_len, dev->revision < 1);
break;
case CCW_CMD_VDEV_RESET:
virtio_ccw_reset_virtio(dev, vdev);
@@ -387,15 +454,29 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
if (!ccw.cda) {
ret = -EFAULT;
} else {
- features.index = ldub_phys(&address_space_memory,
- ccw.cda + sizeof(features.features));
- if (features.index < ARRAY_SIZE(dev->host_features)) {
- features.features = dev->host_features[features.index];
+ features.index = address_space_ldub(&address_space_memory,
+ ccw.cda
+ + sizeof(features.features),
+ MEMTXATTRS_UNSPECIFIED,
+ NULL);
+ if (features.index == 0) {
+ features.features = (uint32_t)vdev->host_features;
+ } else if (features.index == 1) {
+ features.features = (uint32_t)(vdev->host_features >> 32);
+ /*
+ * Don't offer version 1 to the guest if it did not
+ * negotiate at least revision 1.
+ */
+ if (dev->revision <= 0) {
+ features.features &= ~(1 << (VIRTIO_F_VERSION_1 - 32));
+ }
} else {
/* Return zeroes if the guest supports more feature bits. */
features.features = 0;
}
- stl_le_phys(&address_space_memory, ccw.cda, features.features);
+ address_space_stl_le(&address_space_memory, ccw.cda,
+ features.features, MEMTXATTRS_UNSPECIFIED,
+ NULL);
sch->curr_status.scsw.count = ccw.count - sizeof(features);
ret = 0;
}
@@ -414,11 +495,30 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
if (!ccw.cda) {
ret = -EFAULT;
} else {
- features.index = ldub_phys(&address_space_memory,
- ccw.cda + sizeof(features.features));
- features.features = ldl_le_phys(&address_space_memory, ccw.cda);
- if (features.index < ARRAY_SIZE(dev->host_features)) {
- virtio_set_features(vdev, features.features);
+ features.index = address_space_ldub(&address_space_memory,
+ ccw.cda
+ + sizeof(features.features),
+ MEMTXATTRS_UNSPECIFIED,
+ NULL);
+ features.features = address_space_ldl_le(&address_space_memory,
+ ccw.cda,
+ MEMTXATTRS_UNSPECIFIED,
+ NULL);
+ if (features.index == 0) {
+ virtio_set_features(vdev,
+ (vdev->guest_features & 0xffffffff00000000ULL) |
+ features.features);
+ } else if (features.index == 1) {
+ /*
+ * The guest should not set version 1 if it didn't
+ * negotiate a revision >= 1.
+ */
+ if (dev->revision <= 0) {
+ features.features &= ~(1 << (VIRTIO_F_VERSION_1 - 32));
+ }
+ virtio_set_features(vdev,
+ (vdev->guest_features & 0x00000000ffffffffULL) |
+ ((uint64_t)features.features << 32));
} else {
/*
* If the guest supports more feature bits, assert that it
@@ -492,19 +592,24 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
if (!ccw.cda) {
ret = -EFAULT;
} else {
- status = ldub_phys(&address_space_memory, ccw.cda);
+ status = address_space_ldub(&address_space_memory, ccw.cda,
+ MEMTXATTRS_UNSPECIFIED, NULL);
if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) {
virtio_ccw_stop_ioeventfd(dev);
}
- virtio_set_status(vdev, status);
- if (vdev->status == 0) {
- virtio_ccw_reset_virtio(dev, vdev);
- }
- if (status & VIRTIO_CONFIG_S_DRIVER_OK) {
- virtio_ccw_start_ioeventfd(dev);
+ if (virtio_set_status(vdev, status) == 0) {
+ if (vdev->status == 0) {
+ virtio_ccw_reset_virtio(dev, vdev);
+ }
+ if (status & VIRTIO_CONFIG_S_DRIVER_OK) {
+ virtio_ccw_start_ioeventfd(dev);
+ }
+ sch->curr_status.scsw.count = ccw.count - sizeof(status);
+ ret = 0;
+ } else {
+ /* Trigger a command reject. */
+ ret = -ENOSYS;
}
- sch->curr_status.scsw.count = ccw.count - sizeof(status);
- ret = 0;
}
break;
case CCW_CMD_SET_IND:
@@ -526,7 +631,8 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
if (!ccw.cda) {
ret = -EFAULT;
} else {
- indicators = ldq_be_phys(&address_space_memory, ccw.cda);
+ indicators = address_space_ldq_be(&address_space_memory, ccw.cda,
+ MEMTXATTRS_UNSPECIFIED, NULL);
dev->indicators = get_indicator(indicators, sizeof(uint64_t));
sch->curr_status.scsw.count = ccw.count - sizeof(indicators);
ret = 0;
@@ -546,7 +652,8 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
if (!ccw.cda) {
ret = -EFAULT;
} else {
- indicators = ldq_be_phys(&address_space_memory, ccw.cda);
+ indicators = address_space_ldq_be(&address_space_memory, ccw.cda,
+ MEMTXATTRS_UNSPECIFIED, NULL);
dev->indicators2 = get_indicator(indicators, sizeof(uint64_t));
sch->curr_status.scsw.count = ccw.count - sizeof(indicators);
ret = 0;
@@ -566,15 +673,21 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
if (!ccw.cda) {
ret = -EFAULT;
} else {
- vq_config.index = lduw_be_phys(&address_space_memory, ccw.cda);
- if (vq_config.index >= VIRTIO_PCI_QUEUE_MAX) {
+ vq_config.index = address_space_lduw_be(&address_space_memory,
+ ccw.cda,
+ MEMTXATTRS_UNSPECIFIED,
+ NULL);
+ if (vq_config.index >= VIRTIO_CCW_QUEUE_MAX) {
ret = -EINVAL;
break;
}
vq_config.num_max = virtio_queue_get_num(vdev,
vq_config.index);
- stw_be_phys(&address_space_memory,
- ccw.cda + sizeof(vq_config.index), vq_config.num_max);
+ address_space_stw_be(&address_space_memory,
+ ccw.cda + sizeof(vq_config.index),
+ vq_config.num_max,
+ MEMTXATTRS_UNSPECIFIED,
+ NULL);
sch->curr_status.scsw.count = ccw.count - sizeof(vq_config);
ret = 0;
}
@@ -626,6 +739,40 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
}
}
break;
+ case CCW_CMD_SET_VIRTIO_REV:
+ len = sizeof(revinfo);
+ if (ccw.count < len) {
+ ret = -EINVAL;
+ break;
+ }
+ if (!ccw.cda) {
+ ret = -EFAULT;
+ break;
+ }
+ revinfo.revision =
+ address_space_lduw_be(&address_space_memory, ccw.cda,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ revinfo.length =
+ address_space_lduw_be(&address_space_memory,
+ ccw.cda + sizeof(revinfo.revision),
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ if (ccw.count < len + revinfo.length ||
+ (check_len && ccw.count > len + revinfo.length)) {
+ ret = -EINVAL;
+ break;
+ }
+ /*
+ * Once we start to support revisions with additional data, we'll
+ * need to fetch it here. Nothing to do for now, though.
+ */
+ if (dev->revision >= 0 ||
+ revinfo.revision > virtio_ccw_rev_max(vdev)) {
+ ret = -ENOSYS;
+ break;
+ }
+ ret = 0;
+ dev->revision = revinfo.revision;
+ break;
default:
ret = -ENOSYS;
break;
@@ -633,8 +780,14 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
return ret;
}
-static void virtio_ccw_device_realize(VirtioCcwDevice *dev,
- VirtIODevice *vdev, Error **errp)
+static void virtio_sch_disable_cb(SubchDev *sch)
+{
+ VirtioCcwDevice *dev = sch->driver_data;
+
+ dev->revision = -1;
+}
+
+static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp)
{
unsigned int cssid = 0;
unsigned int ssid = 0;
@@ -644,7 +797,8 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev,
bool found = false;
SubchDev *sch;
int num;
- DeviceState *parent = DEVICE(dev);
+ Error *err = NULL;
+ VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
sch = g_malloc0(sizeof(SubchDev));
@@ -752,22 +906,24 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev,
css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE);
sch->ccw_cb = virtio_ccw_cb;
+ sch->disable_cb = virtio_sch_disable_cb;
/* Build senseid data. */
memset(&sch->id, 0, sizeof(SenseId));
sch->id.reserved = 0xff;
sch->id.cu_type = VIRTIO_CCW_CU_TYPE;
- sch->id.cu_model = vdev->device_id;
- /* Only the first 32 feature bits are used. */
- dev->host_features[0] = virtio_bus_get_vdev_features(&dev->bus,
- dev->host_features[0]);
+ dev->revision = -1;
- virtio_add_feature(&dev->host_features[0], VIRTIO_F_NOTIFY_ON_EMPTY);
- virtio_add_feature(&dev->host_features[0], VIRTIO_F_BAD_FEATURE);
+ if (k->realize) {
+ k->realize(dev, &err);
+ }
+ if (err) {
+ error_propagate(errp, err);
+ css_subch_assign(cssid, ssid, schid, devno, NULL);
+ goto out_err;
+ }
- css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid,
- parent->hotplugged, 1);
return;
out_err:
@@ -797,17 +953,13 @@ static void virtio_ccw_net_realize(VirtioCcwDevice *ccw_dev, Error **errp)
DeviceState *vdev = DEVICE(&dev->vdev);
Error *err = NULL;
- virtio_net_set_config_size(&dev->vdev, ccw_dev->host_features[0]);
virtio_net_set_netclient_name(&dev->vdev, qdev->id,
object_get_typename(OBJECT(qdev)));
qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
object_property_set_bool(OBJECT(vdev), true, "realized", &err);
if (err) {
error_propagate(errp, err);
- return;
}
-
- virtio_ccw_device_realize(ccw_dev, VIRTIO_DEVICE(vdev), errp);
}
static void virtio_ccw_net_instance_init(Object *obj)
@@ -830,10 +982,7 @@ static void virtio_ccw_blk_realize(VirtioCcwDevice *ccw_dev, Error **errp)
object_property_set_bool(OBJECT(vdev), true, "realized", &err);
if (err) {
error_propagate(errp, err);
- return;
}
-
- virtio_ccw_device_realize(ccw_dev, VIRTIO_DEVICE(vdev), errp);
}
static void virtio_ccw_blk_instance_init(Object *obj)
@@ -870,10 +1019,7 @@ static void virtio_ccw_serial_realize(VirtioCcwDevice *ccw_dev, Error **errp)
object_property_set_bool(OBJECT(vdev), true, "realized", &err);
if (err) {
error_propagate(errp, err);
- return;
}
-
- virtio_ccw_device_realize(ccw_dev, VIRTIO_DEVICE(vdev), errp);
}
@@ -895,50 +1041,20 @@ static void virtio_ccw_balloon_realize(VirtioCcwDevice *ccw_dev, Error **errp)
object_property_set_bool(OBJECT(vdev), true, "realized", &err);
if (err) {
error_propagate(errp, err);
- return;
}
-
- virtio_ccw_device_realize(ccw_dev, VIRTIO_DEVICE(vdev), errp);
-}
-
-static void balloon_ccw_stats_get_all(Object *obj, struct Visitor *v,
- void *opaque, const char *name,
- Error **errp)
-{
- VirtIOBalloonCcw *dev = opaque;
- object_property_get(OBJECT(&dev->vdev), v, "guest-stats", errp);
-}
-
-static void balloon_ccw_stats_get_poll_interval(Object *obj, struct Visitor *v,
- void *opaque, const char *name,
- Error **errp)
-{
- VirtIOBalloonCcw *dev = opaque;
- object_property_get(OBJECT(&dev->vdev), v, "guest-stats-polling-interval",
- errp);
-}
-
-static void balloon_ccw_stats_set_poll_interval(Object *obj, struct Visitor *v,
- void *opaque, const char *name,
- Error **errp)
-{
- VirtIOBalloonCcw *dev = opaque;
- object_property_set(OBJECT(&dev->vdev), v, "guest-stats-polling-interval",
- errp);
}
static void virtio_ccw_balloon_instance_init(Object *obj)
{
VirtIOBalloonCcw *dev = VIRTIO_BALLOON_CCW(obj);
+
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
TYPE_VIRTIO_BALLOON);
- object_property_add(obj, "guest-stats", "guest statistics",
- balloon_ccw_stats_get_all, NULL, NULL, dev, NULL);
-
- object_property_add(obj, "guest-stats-polling-interval", "int",
- balloon_ccw_stats_get_poll_interval,
- balloon_ccw_stats_set_poll_interval,
- NULL, dev, NULL);
+ object_property_add_alias(obj, "guest-stats", OBJECT(&dev->vdev),
+ "guest-stats", &error_abort);
+ object_property_add_alias(obj, "guest-stats-polling-interval",
+ OBJECT(&dev->vdev),
+ "guest-stats-polling-interval", &error_abort);
}
static void virtio_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp)
@@ -963,10 +1079,7 @@ static void virtio_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp)
object_property_set_bool(OBJECT(vdev), true, "realized", &err);
if (err) {
error_propagate(errp, err);
- return;
}
-
- virtio_ccw_device_realize(ccw_dev, VIRTIO_DEVICE(vdev), errp);
}
static void virtio_ccw_scsi_instance_init(Object *obj)
@@ -990,10 +1103,7 @@ static void vhost_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp)
object_property_set_bool(OBJECT(vdev), true, "realized", &err);
if (err) {
error_propagate(errp, err);
- return;
}
-
- virtio_ccw_device_realize(ccw_dev, VIRTIO_DEVICE(vdev), errp);
}
static void vhost_ccw_scsi_instance_init(Object *obj)
@@ -1021,8 +1131,6 @@ static void virtio_ccw_rng_realize(VirtioCcwDevice *ccw_dev, Error **errp)
object_property_set_link(OBJECT(dev),
OBJECT(dev->vdev.conf.rng), "rng",
NULL);
-
- virtio_ccw_device_realize(ccw_dev, VIRTIO_DEVICE(vdev), errp);
}
/* DeviceState to VirtioCcwDevice. Note: used on datapath,
@@ -1065,7 +1173,7 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector)
return;
}
- if (vector < VIRTIO_PCI_QUEUE_MAX) {
+ if (vector < VIRTIO_CCW_QUEUE_MAX) {
if (!dev->indicators) {
return;
}
@@ -1086,9 +1194,13 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector)
css_adapter_interrupt(dev->thinint_isc);
}
} else {
- indicators = ldq_phys(&address_space_memory, dev->indicators->addr);
+ indicators = address_space_ldq(&address_space_memory,
+ dev->indicators->addr,
+ MEMTXATTRS_UNSPECIFIED,
+ NULL);
indicators |= 1ULL << vector;
- stq_phys(&address_space_memory, dev->indicators->addr, indicators);
+ address_space_stq(&address_space_memory, dev->indicators->addr,
+ indicators, MEMTXATTRS_UNSPECIFIED, NULL);
css_conditional_io_interrupt(sch);
}
} else {
@@ -1096,21 +1208,17 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector)
return;
}
vector = 0;
- indicators = ldq_phys(&address_space_memory, dev->indicators2->addr);
+ indicators = address_space_ldq(&address_space_memory,
+ dev->indicators2->addr,
+ MEMTXATTRS_UNSPECIFIED,
+ NULL);
indicators |= 1ULL << vector;
- stq_phys(&address_space_memory, dev->indicators2->addr, indicators);
+ address_space_stq(&address_space_memory, dev->indicators2->addr,
+ indicators, MEMTXATTRS_UNSPECIFIED, NULL);
css_conditional_io_interrupt(sch);
}
}
-static unsigned virtio_ccw_get_features(DeviceState *d)
-{
- VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
-
- /* Only the first 32 feature bits are used. */
- return dev->host_features[0];
-}
-
static void virtio_ccw_reset(DeviceState *d)
{
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
@@ -1208,8 +1316,8 @@ static int virtio_ccw_add_irqfd(VirtioCcwDevice *dev, int n)
VirtQueue *vq = virtio_get_queue(vdev, n);
EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
- return kvm_irqchip_add_irqfd_notifier(kvm_state, notifier, NULL,
- dev->routes.gsi[n]);
+ return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, notifier, NULL,
+ dev->routes.gsi[n]);
}
static void virtio_ccw_remove_irqfd(VirtioCcwDevice *dev, int n)
@@ -1219,8 +1327,8 @@ static void virtio_ccw_remove_irqfd(VirtioCcwDevice *dev, int n)
EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
int ret;
- ret = kvm_irqchip_remove_irqfd_notifier(kvm_state, notifier,
- dev->routes.gsi[n]);
+ ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, notifier,
+ dev->routes.gsi[n]);
assert(ret == 0);
}
@@ -1337,6 +1445,7 @@ static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f)
{
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
SubchDev *s = dev->sch;
+ VirtIODevice *vdev = virtio_ccw_get_vdev(s);
subch_device_save(s, f);
if (dev->indicators != NULL) {
@@ -1360,14 +1469,17 @@ static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f)
qemu_put_be32(f, 0);
qemu_put_be64(f, 0UL);
}
+ qemu_put_be16(f, vdev->config_vector);
qemu_put_be64(f, dev->routes.adapter.ind_offset);
qemu_put_byte(f, dev->thinint_isc);
+ qemu_put_be32(f, dev->revision);
}
static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f)
{
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
SubchDev *s = dev->sch;
+ VirtIODevice *vdev = virtio_ccw_get_vdev(s);
int len;
s->driver_data = dev;
@@ -1393,8 +1505,10 @@ static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f)
qemu_get_be64(f);
dev->summary_indicator = NULL;
}
+ qemu_get_be16s(f, &vdev->config_vector);
dev->routes.adapter.ind_offset = qemu_get_be64(f);
dev->thinint_isc = qemu_get_byte(f);
+ dev->revision = qemu_get_be32(f);
if (s->thinint_active) {
return css_register_io_adapter(CSS_IO_ADAPTER_VIRTIO,
dev->thinint_isc, true, false,
@@ -1404,11 +1518,41 @@ static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f)
return 0;
}
+/* This is called by virtio-bus just after the device is plugged. */
+static void virtio_ccw_device_plugged(DeviceState *d, Error **errp)
+{
+ VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
+ VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
+ SubchDev *sch = dev->sch;
+ int n = virtio_get_num_queues(vdev);
+
+ if (virtio_get_num_queues(vdev) > VIRTIO_CCW_QUEUE_MAX) {
+ error_setg(errp, "The nubmer of virtqueues %d "
+ "exceeds ccw limit %d", n,
+ VIRTIO_CCW_QUEUE_MAX);
+ return;
+ }
+
+ if (!kvm_eventfds_enabled()) {
+ dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD;
+ }
+
+ sch->id.cu_model = virtio_bus_get_vdev_id(&dev->bus);
+
+ css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid,
+ d->hotplugged, 1);
+}
+
+static void virtio_ccw_device_unplugged(DeviceState *d)
+{
+ VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
+
+ virtio_ccw_stop_ioeventfd(dev);
+}
/**************** Virtio-ccw Bus Device Descriptions *******************/
static Property virtio_ccw_net_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
- DEFINE_VIRTIO_NET_FEATURES(VirtioCcwDevice, host_features[0]),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
DEFINE_PROP_END_OF_LIST(),
@@ -1423,6 +1567,7 @@ static void virtio_ccw_net_class_init(ObjectClass *klass, void *data)
k->exit = virtio_ccw_exit;
dc->reset = virtio_ccw_reset;
dc->props = virtio_ccw_net_properties;
+ set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
}
static const TypeInfo virtio_ccw_net = {
@@ -1449,6 +1594,7 @@ static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data)
k->exit = virtio_ccw_exit;
dc->reset = virtio_ccw_reset;
dc->props = virtio_ccw_blk_properties;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
}
static const TypeInfo virtio_ccw_blk = {
@@ -1475,6 +1621,7 @@ static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data)
k->exit = virtio_ccw_exit;
dc->reset = virtio_ccw_reset;
dc->props = virtio_ccw_serial_properties;
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
}
static const TypeInfo virtio_ccw_serial = {
@@ -1501,6 +1648,7 @@ static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data)
k->exit = virtio_ccw_exit;
dc->reset = virtio_ccw_reset;
dc->props = virtio_ccw_balloon_properties;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
static const TypeInfo virtio_ccw_balloon = {
@@ -1513,7 +1661,6 @@ static const TypeInfo virtio_ccw_balloon = {
static Property virtio_ccw_scsi_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
- DEFINE_VIRTIO_SCSI_FEATURES(VirtioCcwDevice, host_features[0]),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
DEFINE_PROP_END_OF_LIST(),
@@ -1528,6 +1675,7 @@ static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data)
k->exit = virtio_ccw_exit;
dc->reset = virtio_ccw_reset;
dc->props = virtio_ccw_scsi_properties;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
}
static const TypeInfo virtio_ccw_scsi = {
@@ -1553,6 +1701,7 @@ static void vhost_ccw_scsi_class_init(ObjectClass *klass, void *data)
k->exit = virtio_ccw_exit;
dc->reset = virtio_ccw_reset;
dc->props = vhost_ccw_scsi_properties;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
}
static const TypeInfo vhost_ccw_scsi = {
@@ -1590,6 +1739,7 @@ static void virtio_ccw_rng_class_init(ObjectClass *klass, void *data)
k->exit = virtio_ccw_exit;
dc->reset = virtio_ccw_reset;
dc->props = virtio_ccw_rng_properties;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
static const TypeInfo virtio_ccw_rng = {
@@ -1603,10 +1753,9 @@ static const TypeInfo virtio_ccw_rng = {
static void virtio_ccw_busdev_realize(DeviceState *dev, Error **errp)
{
VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev;
- VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
virtio_ccw_bus_new(&_dev->bus, sizeof(_dev->bus), _dev);
- _info->realize(_dev, errp);
+ virtio_ccw_device_realize(_dev, errp);
}
static int virtio_ccw_busdev_exit(DeviceState *dev)
@@ -1639,16 +1788,10 @@ static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev,
object_unparent(OBJECT(dev));
}
-static Property virtio_ccw_properties[] = {
- DEFINE_VIRTIO_COMMON_FEATURES(VirtioCcwDevice, host_features[0]),
- DEFINE_PROP_END_OF_LIST(),
-};
-
static void virtio_ccw_device_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- dc->props = virtio_ccw_properties;
dc->realize = virtio_ccw_busdev_realize;
dc->exit = virtio_ccw_busdev_exit;
dc->bus_type = TYPE_VIRTUAL_CSS_BUS;
@@ -1676,9 +1819,11 @@ static void virtual_css_bridge_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
k->init = virtual_css_bridge_init;
hc->unplug = virtio_ccw_busdev_unplug;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
static const TypeInfo virtual_css_bridge_info = {
@@ -1711,7 +1856,6 @@ static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data)
bus_class->max_dev = 1;
k->notify = virtio_ccw_notify;
- k->get_features = virtio_ccw_get_features;
k->vmstate_change = virtio_ccw_vmstate_change;
k->query_guest_notifiers = virtio_ccw_query_guest_notifiers;
k->set_host_notifier = virtio_ccw_set_host_notifier;
@@ -1720,6 +1864,8 @@ static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data)
k->load_queue = virtio_ccw_load_queue;
k->save_config = virtio_ccw_save_config;
k->load_config = virtio_ccw_load_config;
+ k->device_plugged = virtio_ccw_device_plugged;
+ k->device_unplugged = virtio_ccw_device_unplugged;
}
static const TypeInfo virtio_ccw_bus_info = {
@@ -1729,6 +1875,56 @@ static const TypeInfo virtio_ccw_bus_info = {
.class_init = virtio_ccw_bus_class_init,
};
+#ifdef CONFIG_VIRTFS
+static Property virtio_ccw_9p_properties[] = {
+ DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
+ DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
+ VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_ccw_9p_realize(VirtioCcwDevice *ccw_dev, Error **errp)
+{
+ V9fsCCWState *dev = VIRTIO_9P_CCW(ccw_dev);
+ DeviceState *vdev = DEVICE(&dev->vdev);
+ Error *err = NULL;
+
+ qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
+ object_property_set_bool(OBJECT(vdev), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ }
+}
+
+static void virtio_ccw_9p_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
+
+ k->exit = virtio_ccw_exit;
+ k->realize = virtio_ccw_9p_realize;
+ dc->reset = virtio_ccw_reset;
+ dc->props = virtio_ccw_9p_properties;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
+}
+
+static void virtio_ccw_9p_instance_init(Object *obj)
+{
+ V9fsCCWState *dev = VIRTIO_9P_CCW(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VIRTIO_9P);
+}
+
+static const TypeInfo virtio_ccw_9p_info = {
+ .name = TYPE_VIRTIO_9P_CCW,
+ .parent = TYPE_VIRTIO_CCW_DEVICE,
+ .instance_size = sizeof(V9fsCCWState),
+ .instance_init = virtio_ccw_9p_instance_init,
+ .class_init = virtio_ccw_9p_class_init,
+};
+#endif
+
static void virtio_ccw_register(void)
{
type_register_static(&virtio_ccw_bus_info);
@@ -1744,6 +1940,9 @@ static void virtio_ccw_register(void)
#endif
type_register_static(&virtio_ccw_rng);
type_register_static(&virtual_css_bridge_info);
+#ifdef CONFIG_VIRTFS
+ type_register_static(&virtio_ccw_9p_info);
+#endif
}
type_init(virtio_ccw_register)
diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h
index 4fceda735..692ddd731 100644
--- a/hw/s390x/virtio-ccw.h
+++ b/hw/s390x/virtio-ccw.h
@@ -1,8 +1,9 @@
/*
* virtio ccw target definitions
*
- * Copyright 2012 IBM Corp.
+ * Copyright 2012,2015 IBM Corp.
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ * Pierre Morel <pmorel@linux.vnet.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or (at
* your option) any later version. See the COPYING file in the top-level
@@ -40,6 +41,7 @@
#define CCW_CMD_SET_CONF_IND 0x53
#define CCW_CMD_READ_VQ_CONF 0x32
#define CCW_CMD_SET_IND_ADAPTER 0x73
+#define CCW_CMD_SET_VIRTIO_REV 0x83
#define TYPE_VIRTIO_CCW_DEVICE "virtio-ccw-device"
#define VIRTIO_CCW_DEVICE(obj) \
@@ -68,9 +70,6 @@ typedef struct VirtIOCCWDeviceClass {
int (*exit)(VirtioCcwDevice *dev);
} VirtIOCCWDeviceClass;
-/* Change here if we want to support more feature bits. */
-#define VIRTIO_CCW_FEATURE_SIZE 1
-
/* Performance improves when virtqueue kick processing is decoupled from the
* vcpu thread using ioeventfd for some devices. */
#define VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT 1
@@ -88,7 +87,7 @@ struct VirtioCcwDevice {
DeviceState parent_obj;
SubchDev *sch;
char *bus_id;
- uint32_t host_features[VIRTIO_CCW_FEATURE_SIZE];
+ int revision;
VirtioBusState bus;
bool ioeventfd_started;
bool ioeventfd_disabled;
@@ -102,6 +101,12 @@ struct VirtioCcwDevice {
uint64_t ind_bit;
};
+/* The maximum virtio revision we support. */
+static inline int virtio_ccw_rev_max(VirtIODevice *vdev)
+{
+ return 0;
+}
+
/* virtual css bus type */
typedef struct VirtualCssBus {
BusState parent_obj;
@@ -193,4 +198,19 @@ typedef struct VirtIORNGCcw {
VirtualCssBus *virtual_css_bus_init(void);
void virtio_ccw_device_update_status(SubchDev *sch);
VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch);
+
+#ifdef CONFIG_VIRTFS
+#include "hw/9pfs/virtio-9p.h"
+
+#define TYPE_VIRTIO_9P_CCW "virtio-9p-ccw"
+#define VIRTIO_9P_CCW(obj) \
+ OBJECT_CHECK(V9fsCCWState, (obj), TYPE_VIRTIO_9P_CCW)
+
+typedef struct V9fsCCWState {
+ VirtioCcwDevice parent_obj;
+ V9fsState vdev;
+} V9fsCCWState;
+
+#endif /* CONFIG_VIRTFS */
+
#endif
diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c
index ad7317bfe..a04369c5a 100644
--- a/hw/scsi/megasas.c
+++ b/hw/scsi/megasas.c
@@ -804,7 +804,7 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd)
MFI_INFO_LDOPS_READ_POLICY);
info.max_strips_per_io = cpu_to_le16(s->fw_sge);
info.stripe_sz_ops.min = 3;
- info.stripe_sz_ops.max = ffs(MEGASAS_MAX_SECTORS + 1) - 1;
+ info.stripe_sz_ops.max = ctz32(MEGASAS_MAX_SECTORS + 1);
info.properties.pred_fail_poll_interval = cpu_to_le16(300);
info.properties.intr_throttle_cnt = cpu_to_le16(16);
info.properties.intr_throttle_timeout = cpu_to_le16(50);
@@ -2202,8 +2202,15 @@ static uint64_t megasas_queue_read(void *opaque, hwaddr addr,
return 0;
}
+static void megasas_queue_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ return;
+}
+
static const MemoryRegionOps megasas_queue_ops = {
.read = megasas_queue_read,
+ .write = megasas_queue_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.impl = {
.min_access_size = 8,
@@ -2407,13 +2414,6 @@ static void megasas_scsi_realize(PCIDevice *dev, Error **errp)
}
}
-static void
-megasas_write_config(PCIDevice *pci, uint32_t addr, uint32_t val, int len)
-{
- pci_default_write_config(pci, addr, val, len);
- msi_write_config(pci, addr, val, len);
-}
-
static Property megasas_properties_gen1[] = {
DEFINE_PROP_UINT32("max_sge", MegasasState, fw_sge,
MEGASAS_DEFAULT_SGE),
@@ -2516,7 +2516,6 @@ static void megasas_class_init(ObjectClass *oc, void *data)
dc->vmsd = info->vmsd;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
dc->desc = info->desc;
- pc->config_write = megasas_write_config;
}
static const TypeInfo megasas_info = {
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
index 0c506dbe9..f0ae4625f 100644
--- a/hw/scsi/scsi-bus.c
+++ b/hw/scsi/scsi-bus.c
@@ -1973,6 +1973,7 @@ static const VMStateDescription vmstate_scsi_sense_state = {
.name = "SCSIDevice/sense",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = scsi_sense_state_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8_SUB_ARRAY(sense, SCSIDevice,
SCSI_SENSE_BUF_SIZE_OLD,
@@ -2003,13 +2004,9 @@ const VMStateDescription vmstate_scsi_device = {
},
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection []) {
- {
- .vmsd = &vmstate_scsi_sense_state,
- .needed = scsi_sense_state_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_scsi_sense_state,
+ NULL
}
};
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index 54d71f4c0..0e0bc6440 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -765,6 +765,9 @@ static inline bool media_is_dvd(SCSIDiskState *s)
if (!blk_is_inserted(s->qdev.conf.blk)) {
return false;
}
+ if (s->tray_open) {
+ return false;
+ }
blk_get_geometry(s->qdev.conf.blk, &nb_sectors);
return nb_sectors > CD_MAX_SECTORS;
}
@@ -778,6 +781,9 @@ static inline bool media_is_cd(SCSIDiskState *s)
if (!blk_is_inserted(s->qdev.conf.blk)) {
return false;
}
+ if (s->tray_open) {
+ return false;
+ }
blk_get_geometry(s->qdev.conf.blk, &nb_sectors);
return nb_sectors <= CD_MAX_SECTORS;
}
@@ -975,7 +981,15 @@ static int scsi_get_configuration(SCSIDiskState *s, uint8_t *outbuf)
if (s->qdev.type != TYPE_ROM) {
return -1;
}
- current = media_is_dvd(s) ? MMC_PROFILE_DVD_ROM : MMC_PROFILE_CD_ROM;
+
+ if (media_is_dvd(s)) {
+ current = MMC_PROFILE_DVD_ROM;
+ } else if (media_is_cd(s)) {
+ current = MMC_PROFILE_CD_ROM;
+ } else {
+ current = MMC_PROFILE_NONE;
+ }
+
memset(outbuf, 0, 40);
stl_be_p(&outbuf[0], 36); /* Bytes after the data length field */
stw_be_p(&outbuf[6], current);
@@ -1669,6 +1683,10 @@ static void scsi_write_same_complete(void *opaque, int ret)
if (data->iov.iov_len) {
block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct,
data->iov.iov_len, BLOCK_ACCT_WRITE);
+ /* blk_aio_write doesn't like the qiov size being different from
+ * nb_sectors, make sure they match.
+ */
+ qemu_iovec_init_external(&data->qiov, &data->iov, 1);
r->req.aiocb = blk_aio_writev(s->qdev.conf.blk, data->sector,
&data->qiov, data->iov.iov_len / 512,
scsi_write_same_complete, data);
diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c
index 335f4429a..62d91003b 100644
--- a/hw/scsi/vhost-scsi.c
+++ b/hw/scsi/vhost-scsi.c
@@ -16,6 +16,7 @@
#include <sys/ioctl.h>
#include "config.h"
+#include "qemu/error-report.h"
#include "qemu/queue.h"
#include "monitor/monitor.h"
#include "migration/migration.h"
@@ -151,8 +152,9 @@ static void vhost_scsi_stop(VHostSCSI *s)
vhost_dev_disable_notifiers(&s->dev, vdev);
}
-static uint32_t vhost_scsi_get_features(VirtIODevice *vdev,
- uint32_t features)
+static uint64_t vhost_scsi_get_features(VirtIODevice *vdev,
+ uint64_t features,
+ Error **errp)
{
VHostSCSI *s = VHOST_SCSI(vdev);
@@ -246,7 +248,7 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp)
s->dev.backend_features = 0;
ret = vhost_dev_init(&s->dev, (void *)(uintptr_t)vhostfd,
- VHOST_BACKEND_TYPE_KERNEL, true);
+ VHOST_BACKEND_TYPE_KERNEL);
if (ret < 0) {
error_setg(errp, "vhost-scsi: vhost initialization failed: %s",
strerror(-ret));
@@ -289,12 +291,19 @@ static char *vhost_scsi_get_fw_dev_path(FWPathProvider *p, BusState *bus,
{
VHostSCSI *s = VHOST_SCSI(dev);
/* format: channel@channel/vhost-scsi@target,lun */
- return g_strdup_printf("channel@%x/%s@%x,%x", s->channel,
+ return g_strdup_printf("/channel@%x/%s@%x,%x", s->channel,
qdev_fw_name(dev), s->target, s->lun);
}
static Property vhost_scsi_properties[] = {
- DEFINE_VHOST_SCSI_PROPERTIES(VHostSCSI, parent_obj.conf),
+ DEFINE_PROP_STRING("vhostfd", VHostSCSI, parent_obj.conf.vhostfd),
+ DEFINE_PROP_STRING("wwpn", VHostSCSI, parent_obj.conf.wwpn),
+ DEFINE_PROP_UINT32("boot_tpgt", VHostSCSI, parent_obj.conf.boot_tpgt, 0),
+ DEFINE_PROP_UINT32("num_queues", VHostSCSI, parent_obj.conf.num_queues, 1),
+ DEFINE_PROP_UINT32("max_sectors", VHostSCSI, parent_obj.conf.max_sectors,
+ 0xFFFF),
+ DEFINE_PROP_UINT32("cmd_per_lun", VHostSCSI, parent_obj.conf.cmd_per_lun,
+ 128),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index c9bea067e..93c33e11f 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -145,7 +145,7 @@ static int virtio_scsi_parse_req(VirtIOSCSIReq *req,
*
* TODO: always disable this workaround for virtio 1.0 devices.
*/
- if (!virtio_has_feature(vdev, VIRTIO_F_ANY_LAYOUT)) {
+ if (!virtio_vdev_has_feature(vdev, VIRTIO_F_ANY_LAYOUT)) {
if (req->elem.out_num) {
req_size = req->elem.out_sg[0].iov_len;
}
@@ -628,9 +628,14 @@ static void virtio_scsi_set_config(VirtIODevice *vdev,
vs->cdb_size = virtio_ldl_p(vdev, &scsiconf->cdb_size);
}
-static uint32_t virtio_scsi_get_features(VirtIODevice *vdev,
- uint32_t requested_features)
+static uint64_t virtio_scsi_get_features(VirtIODevice *vdev,
+ uint64_t requested_features,
+ Error **errp)
{
+ VirtIOSCSI *s = VIRTIO_SCSI(vdev);
+
+ /* Firstly sync all virtio-scsi possible supported features */
+ requested_features |= s->host_features;
return requested_features;
}
@@ -657,6 +662,11 @@ static void virtio_scsi_reset(VirtIODevice *vdev)
static void virtio_scsi_save(QEMUFile *f, void *opaque)
{
VirtIODevice *vdev = VIRTIO_DEVICE(opaque);
+ VirtIOSCSI *s = VIRTIO_SCSI(vdev);
+
+ if (s->dataplane_started) {
+ virtio_scsi_dataplane_stop(s);
+ }
virtio_save(vdev, f);
}
@@ -749,7 +759,7 @@ static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense)
VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
VirtIODevice *vdev = VIRTIO_DEVICE(s);
- if (virtio_has_feature(vdev, VIRTIO_SCSI_F_CHANGE) &&
+ if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_CHANGE) &&
dev->type != TYPE_ROM) {
virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_PARAM_CHANGE,
sense.asc | (sense.ascq << 8));
@@ -773,7 +783,7 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev,
aio_context_release(s->ctx);
}
- if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) {
+ if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) {
virtio_scsi_push_event(s, sd,
VIRTIO_SCSI_T_TRANSPORT_RESET,
VIRTIO_SCSI_EVT_RESET_RESCAN);
@@ -787,7 +797,7 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev,
VirtIOSCSI *s = VIRTIO_SCSI(vdev);
SCSIDevice *sd = SCSI_DEVICE(dev);
- if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) {
+ if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) {
virtio_scsi_push_event(s, sd,
VIRTIO_SCSI_T_TRANSPORT_RESET,
VIRTIO_SCSI_EVT_RESET_REMOVED);
@@ -826,10 +836,10 @@ void virtio_scsi_common_realize(DeviceState *dev, Error **errp,
sizeof(VirtIOSCSIConfig));
if (s->conf.num_queues == 0 ||
- s->conf.num_queues > VIRTIO_PCI_QUEUE_MAX - 2) {
+ s->conf.num_queues > VIRTIO_QUEUE_MAX - 2) {
error_setg(errp, "Invalid number of queues (= %" PRIu32 "), "
"must be a positive integer less than %d.",
- s->conf.num_queues, VIRTIO_PCI_QUEUE_MAX - 2);
+ s->conf.num_queues, VIRTIO_QUEUE_MAX - 2);
virtio_cleanup(vdev);
return;
}
@@ -944,7 +954,15 @@ static void virtio_scsi_device_unrealize(DeviceState *dev, Error **errp)
}
static Property virtio_scsi_properties[] = {
- DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOSCSI, parent_obj.conf),
+ DEFINE_PROP_UINT32("num_queues", VirtIOSCSI, parent_obj.conf.num_queues, 1),
+ DEFINE_PROP_UINT32("max_sectors", VirtIOSCSI, parent_obj.conf.max_sectors,
+ 0xFFFF),
+ DEFINE_PROP_UINT32("cmd_per_lun", VirtIOSCSI, parent_obj.conf.cmd_per_lun,
+ 128),
+ DEFINE_PROP_BIT("hotplug", VirtIOSCSI, host_features,
+ VIRTIO_SCSI_F_HOTPLUG, true),
+ DEFINE_PROP_BIT("param_change", VirtIOSCSI, host_features,
+ VIRTIO_SCSI_F_CHANGE, true),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c
index c6148d380..9c71f31fe 100644
--- a/hw/scsi/vmw_pvscsi.c
+++ b/hw/scsi/vmw_pvscsi.c
@@ -1174,13 +1174,6 @@ static const VMStateDescription vmstate_pvscsi = {
}
};
-static void
-pvscsi_write_config(PCIDevice *pci, uint32_t addr, uint32_t val, int len)
-{
- pci_default_write_config(pci, addr, val, len);
- msi_write_config(pci, addr, val, len);
-}
-
static Property pvscsi_properties[] = {
DEFINE_PROP_UINT8("use_msg", PVSCSIState, use_msg, 1),
DEFINE_PROP_END_OF_LIST(),
@@ -1202,7 +1195,6 @@ static void pvscsi_class_init(ObjectClass *klass, void *data)
dc->vmsd = &vmstate_pvscsi;
dc->props = pvscsi_properties;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
- k->config_write = pvscsi_write_config;
hc->unplug = pvscsi_hot_unplug;
hc->plug = pvscsi_hotplug;
}
diff --git a/hw/sd/pxa2xx_mmci.c b/hw/sd/pxa2xx_mmci.c
index ac3ab39be..d1fe6d58e 100644
--- a/hw/sd/pxa2xx_mmci.c
+++ b/hw/sd/pxa2xx_mmci.c
@@ -48,7 +48,6 @@ struct PXA2xxMMCIState {
int resp_len;
int cmdreq;
- int ac_width;
};
#define MMC_STRPCL 0x00 /* MMC Clock Start/Stop register */
@@ -215,7 +214,7 @@ static void pxa2xx_mmci_wakequeues(PXA2xxMMCIState *s)
pxa2xx_mmci_fifo_update(s);
}
-static uint32_t pxa2xx_mmci_read(void *opaque, hwaddr offset)
+static uint64_t pxa2xx_mmci_read(void *opaque, hwaddr offset, unsigned size)
{
PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
uint32_t ret;
@@ -257,8 +256,8 @@ static uint32_t pxa2xx_mmci_read(void *opaque, hwaddr offset)
return 0;
case MMC_RXFIFO:
ret = 0;
- while (s->ac_width -- && s->rx_len) {
- ret |= s->rx_fifo[s->rx_start ++] << (s->ac_width << 3);
+ while (size-- && s->rx_len) {
+ ret |= s->rx_fifo[s->rx_start++] << (size << 3);
s->rx_start &= 0x1f;
s->rx_len --;
}
@@ -277,7 +276,7 @@ static uint32_t pxa2xx_mmci_read(void *opaque, hwaddr offset)
}
static void pxa2xx_mmci_write(void *opaque,
- hwaddr offset, uint32_t value)
+ hwaddr offset, uint64_t value, unsigned size)
{
PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
@@ -370,9 +369,9 @@ static void pxa2xx_mmci_write(void *opaque,
break;
case MMC_TXFIFO:
- while (s->ac_width -- && s->tx_len < 0x20)
+ while (size-- && s->tx_len < 0x20)
s->tx_fifo[(s->tx_start + (s->tx_len ++)) & 0x1f] =
- (value >> (s->ac_width << 3)) & 0xff;
+ (value >> (size << 3)) & 0xff;
s->intreq &= ~INT_TXFIFO_REQ;
pxa2xx_mmci_fifo_update(s);
break;
@@ -386,60 +385,9 @@ static void pxa2xx_mmci_write(void *opaque,
}
}
-static uint32_t pxa2xx_mmci_readb(void *opaque, hwaddr offset)
-{
- PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
- s->ac_width = 1;
- return pxa2xx_mmci_read(opaque, offset);
-}
-
-static uint32_t pxa2xx_mmci_readh(void *opaque, hwaddr offset)
-{
- PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
- s->ac_width = 2;
- return pxa2xx_mmci_read(opaque, offset);
-}
-
-static uint32_t pxa2xx_mmci_readw(void *opaque, hwaddr offset)
-{
- PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
- s->ac_width = 4;
- return pxa2xx_mmci_read(opaque, offset);
-}
-
-static void pxa2xx_mmci_writeb(void *opaque,
- hwaddr offset, uint32_t value)
-{
- PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
- s->ac_width = 1;
- pxa2xx_mmci_write(opaque, offset, value);
-}
-
-static void pxa2xx_mmci_writeh(void *opaque,
- hwaddr offset, uint32_t value)
-{
- PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
- s->ac_width = 2;
- pxa2xx_mmci_write(opaque, offset, value);
-}
-
-static void pxa2xx_mmci_writew(void *opaque,
- hwaddr offset, uint32_t value)
-{
- PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
- s->ac_width = 4;
- pxa2xx_mmci_write(opaque, offset, value);
-}
-
static const MemoryRegionOps pxa2xx_mmci_ops = {
- .old_mmio = {
- .read = { pxa2xx_mmci_readb,
- pxa2xx_mmci_readh,
- pxa2xx_mmci_readw, },
- .write = { pxa2xx_mmci_writeb,
- pxa2xx_mmci_writeh,
- pxa2xx_mmci_writew, },
- },
+ .read = pxa2xx_mmci_read,
+ .write = pxa2xx_mmci_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index f955265f7..a1ff465a6 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -389,13 +389,13 @@ static inline uint64_t sd_addr_to_wpnum(uint64_t addr)
return addr >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT);
}
-static void sd_reset(SDState *sd, BlockBackend *blk)
+static void sd_reset(SDState *sd)
{
uint64_t size;
uint64_t sect;
- if (blk) {
- blk_get_geometry(blk, &sect);
+ if (sd->blk) {
+ blk_get_geometry(sd->blk, &sect);
} else {
sect = 0;
}
@@ -412,11 +412,9 @@ static void sd_reset(SDState *sd, BlockBackend *blk)
sd_set_cardstatus(sd);
sd_set_sdstatus(sd);
- sd->blk = blk;
-
if (sd->wp_groups)
g_free(sd->wp_groups);
- sd->wp_switch = blk ? blk_is_read_only(blk) : false;
+ sd->wp_switch = sd->blk ? blk_is_read_only(sd->blk) : false;
sd->wpgrps_size = sect;
sd->wp_groups = bitmap_new(sd->wpgrps_size);
memset(sd->function_group, 0, sizeof(sd->function_group));
@@ -434,7 +432,7 @@ static void sd_cardchange(void *opaque, bool load)
qemu_set_irq(sd->inserted_cb, blk_is_inserted(sd->blk));
if (blk_is_inserted(sd->blk)) {
- sd_reset(sd, sd->blk);
+ sd_reset(sd);
qemu_set_irq(sd->readonly_cb, sd->wp_switch);
}
}
@@ -492,7 +490,8 @@ SDState *sd_init(BlockBackend *blk, bool is_spi)
sd->buf = blk_blockalign(blk, 512);
sd->spi = is_spi;
sd->enable = true;
- sd_reset(sd, blk);
+ sd->blk = blk;
+ sd_reset(sd);
if (sd->blk) {
blk_attach_dev_nofail(sd->blk, sd);
blk_set_dev_ops(sd->blk, &sd_block_ops, sd);
@@ -680,7 +679,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
default:
sd->state = sd_idle_state;
- sd_reset(sd, sd->blk);
+ sd_reset(sd);
return sd->spi ? sd_r1 : sd_r0;
}
break;
@@ -796,8 +795,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
sd->vhs = 0;
/* No response if not exactly one VHS bit is set. */
- if (!(req.arg >> 8) || (req.arg >> ffs(req.arg & ~0xff)))
+ if (!(req.arg >> 8) || (req.arg >> (ctz32(req.arg & ~0xff) + 1))) {
return sd->spi ? sd_r7 : sd_r0;
+ }
/* Accept. */
sd->vhs = req.arg;
diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c
index d1d0847ba..5e22ed79b 100644
--- a/hw/sh4/r2d.c
+++ b/hw/sh4/r2d.c
@@ -127,7 +127,7 @@ static void r2d_fpga_irq_set(void *opaque, int n, int level)
update_irl(fpga);
}
-static uint32_t r2d_fpga_read(void *opaque, hwaddr addr)
+static uint64_t r2d_fpga_read(void *opaque, hwaddr addr, unsigned int size)
{
r2d_fpga_t *s = opaque;
@@ -146,7 +146,7 @@ static uint32_t r2d_fpga_read(void *opaque, hwaddr addr)
}
static void
-r2d_fpga_write(void *opaque, hwaddr addr, uint32_t value)
+r2d_fpga_write(void *opaque, hwaddr addr, uint64_t value, unsigned int size)
{
r2d_fpga_t *s = opaque;
@@ -170,10 +170,10 @@ r2d_fpga_write(void *opaque, hwaddr addr, uint32_t value)
}
static const MemoryRegionOps r2d_fpga_ops = {
- .old_mmio = {
- .read = { r2d_fpga_read, r2d_fpga_read, NULL, },
- .write = { r2d_fpga_write, r2d_fpga_write, NULL, },
- },
+ .read = r2d_fpga_read,
+ .write = r2d_fpga_write,
+ .impl.min_access_size = 2,
+ .impl.max_access_size = 2,
.endianness = DEVICE_NATIVE_ENDIAN,
};
@@ -318,8 +318,10 @@ static void r2d_init(MachineState *machine)
}
/* initialization which should be done by firmware */
- stl_phys(&address_space_memory, SH7750_BCR1, 1<<3); /* cs3 SDRAM */
- stw_phys(&address_space_memory, SH7750_BCR2, 3<<(3*2)); /* cs3 32bit */
+ address_space_stl(&address_space_memory, SH7750_BCR1, 1 << 3,
+ MEMTXATTRS_UNSPECIFIED, NULL); /* cs3 SDRAM */
+ address_space_stw(&address_space_memory, SH7750_BCR2, 3 << (3 * 2),
+ MEMTXATTRS_UNSPECIFIED, NULL); /* cs3 32bit */
reset_info->vector = (SDRAM_BASE + LINUX_LOAD_OFFSET) | 0xa0000000; /* Start from P2 area */
}
diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c
index a69bf2da4..68ac4d8bb 100644
--- a/hw/sparc/sun4m.c
+++ b/hw/sparc/sun4m.c
@@ -124,7 +124,7 @@ void DMA_register_channel (int nchan,
static void fw_cfg_boot_set(void *opaque, const char *boot_device,
Error **errp)
{
- fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
+ fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
}
static void nvram_init(Nvram *nvram, uint8_t *macaddr,
@@ -897,7 +897,6 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef,
espdma_irq, ledma_irq;
qemu_irq esp_reset, dma_enable;
qemu_irq fdc_tc;
- qemu_irq *cpu_halt;
unsigned long kernel_size;
DriveInfo *fd[MAX_FD];
FWCfgState *fw_cfg;
@@ -1024,9 +1023,8 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef,
escc_init(hwdef->serial_base, slavio_irq[15], slavio_irq[15],
serial_hds[0], serial_hds[1], ESCC_CLOCK, 1);
- cpu_halt = qemu_allocate_irqs(cpu_halt_signal, NULL, 1);
if (hwdef->apc_base) {
- apc_init(hwdef->apc_base, cpu_halt[0]);
+ apc_init(hwdef->apc_base, qemu_allocate_irq(cpu_halt_signal, NULL, 0));
}
if (hwdef->fd_base) {
@@ -1036,7 +1034,7 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef,
sun4m_fdctrl_init(slavio_irq[22], hwdef->fd_base, fd,
&fdc_tc);
} else {
- fdc_tc = *qemu_allocate_irqs(dummy_fdc_tc, NULL, 1);
+ fdc_tc = qemu_allocate_irq(dummy_fdc_tc, NULL, 0);
}
slavio_misc_init(hwdef->slavio_base, hwdef->aux1_base, hwdef->aux2_base,
diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c
index 6f34e8793..30cfa0e0a 100644
--- a/hw/sparc64/sun4u.c
+++ b/hw/sparc64/sun4u.c
@@ -127,7 +127,7 @@ void DMA_register_channel (int nchan,
static void fw_cfg_boot_set(void *opaque, const char *boot_device,
Error **errp)
{
- fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
+ fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
}
static int sun4u_NVRAM_set_params(Nvram *nvram, uint16_t NVRAM_size,
diff --git a/hw/timer/arm_mptimer.c b/hw/timer/arm_mptimer.c
index 8b93b3c1a..3e59c2a28 100644
--- a/hw/timer/arm_mptimer.c
+++ b/hw/timer/arm_mptimer.c
@@ -38,7 +38,7 @@ static inline int get_current_cpu(ARMMPTimerState *s)
static inline void timerblock_update_irq(TimerBlock *tb)
{
- qemu_set_irq(tb->irq, tb->status);
+ qemu_set_irq(tb->irq, tb->status && (tb->control & 4));
}
/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */
@@ -122,11 +122,18 @@ static void timerblock_write(void *opaque, hwaddr addr,
case 8: /* Control. */
old = tb->control;
tb->control = value;
- if (((old & 1) == 0) && (value & 1)) {
- if (tb->count == 0 && (tb->control & 2)) {
+ if (value & 1) {
+ if ((old & 1) && (tb->count != 0)) {
+ /* Do nothing if timer is ticking right now. */
+ break;
+ }
+ if (tb->control & 2) {
tb->count = tb->load;
}
timerblock_reload(tb, 1);
+ } else if (old & 1) {
+ /* Shutdown the timer. */
+ timer_del(tb->timer);
}
break;
case 12: /* Interrupt status. */
diff --git a/hw/timer/arm_timer.c b/hw/timer/arm_timer.c
index 145291016..d53f39ad6 100644
--- a/hw/timer/arm_timer.c
+++ b/hw/timer/arm_timer.c
@@ -280,14 +280,12 @@ static int sp804_init(SysBusDevice *sbd)
{
DeviceState *dev = DEVICE(sbd);
SP804State *s = SP804(dev);
- qemu_irq *qi;
- qi = qemu_allocate_irqs(sp804_set_irq, s, 2);
sysbus_init_irq(sbd, &s->irq);
s->timer[0] = arm_timer_init(s->freq0);
s->timer[1] = arm_timer_init(s->freq1);
- s->timer[0]->irq = qi[0];
- s->timer[1]->irq = qi[1];
+ s->timer[0]->irq = qemu_allocate_irq(sp804_set_irq, s, 0);
+ s->timer[1]->irq = qemu_allocate_irq(sp804_set_irq, s, 1);
memory_region_init_io(&s->iomem, OBJECT(s), &sp804_ops, s,
"sp804", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
diff --git a/hw/timer/cadence_ttc.c b/hw/timer/cadence_ttc.c
index d46db3c0e..35bc88033 100644
--- a/hw/timer/cadence_ttc.c
+++ b/hw/timer/cadence_ttc.c
@@ -208,15 +208,14 @@ static void cadence_timer_sync(CadenceTimerState *s)
s->reg_intr |= (2 << i);
}
}
+ if ((x < 0) || (x >= interval)) {
+ s->reg_intr |= (s->reg_count & COUNTER_CTRL_INT) ?
+ COUNTER_INTR_IV : COUNTER_INTR_OV;
+ }
while (x < 0) {
x += interval;
}
s->reg_value = (uint32_t)(x % interval);
-
- if (s->reg_value != x) {
- s->reg_intr |= (s->reg_count & COUNTER_CTRL_INT) ?
- COUNTER_INTR_IV : COUNTER_INTR_OV;
- }
cadence_timer_update(s);
}
diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c
index 78d86be91..2bb62211c 100644
--- a/hw/timer/hpet.c
+++ b/hw/timer/hpet.c
@@ -27,6 +27,7 @@
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "ui/console.h"
+#include "qemu/error-report.h"
#include "qemu/timer.h"
#include "hw/timer/hpet.h"
#include "hw/sysbus.h"
@@ -206,8 +207,9 @@ static void update_irq(struct HPETTimer *timer, int set)
}
}
} else if (timer_fsb_route(timer)) {
- stl_le_phys(&address_space_memory,
- timer->fsb >> 32, timer->fsb & 0xffffffff);
+ address_space_stl_le(&address_space_memory, timer->fsb >> 32,
+ timer->fsb & 0xffffffff, MEMTXATTRS_UNSPECIFIED,
+ NULL);
} else if (timer->config & HPET_TN_TYPE_LEVEL) {
s->isr |= mask;
/* fold the ICH PIRQ# pin's internal inversion logic into hpet */
@@ -282,6 +284,7 @@ static const VMStateDescription vmstate_hpet_rtc_irq_level = {
.name = "hpet/rtc_irq_level",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = hpet_rtc_irq_level_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8(rtc_irq_level, HPETState),
VMSTATE_END_OF_LIST()
@@ -321,13 +324,9 @@ static const VMStateDescription vmstate_hpet = {
vmstate_hpet_timer, HPETTimer),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_hpet_rtc_irq_level,
- .needed = hpet_rtc_irq_level_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_hpet_rtc_irq_level,
+ NULL
}
};
diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c
index f2b77fa11..a9f0efd5e 100644
--- a/hw/timer/mc146818rtc.c
+++ b/hw/timer/mc146818rtc.c
@@ -48,7 +48,6 @@
# define DPRINTF_C(format, ...) do { } while (0)
#endif
-#define NSEC_PER_SEC 1000000000LL
#define SEC_PER_MIN 60
#define MIN_PER_HOUR 60
#define SEC_PER_HOUR 3600
@@ -57,7 +56,7 @@
#define RTC_REINJECT_ON_ACK_COUNT 20
#define RTC_CLOCK_RATE 32768
-#define UIP_HOLD_LENGTH (8 * NSEC_PER_SEC / 32768)
+#define UIP_HOLD_LENGTH (8 * NANOSECONDS_PER_SECOND / 32768)
#define MC146818_RTC(obj) OBJECT_CHECK(RTCState, (obj), TYPE_MC146818_RTC)
@@ -106,7 +105,7 @@ static uint64_t get_guest_rtc_ns(RTCState *s)
uint64_t guest_rtc;
uint64_t guest_clock = qemu_clock_get_ns(rtc_clock);
- guest_rtc = s->base_rtc * NSEC_PER_SEC
+ guest_rtc = s->base_rtc * NANOSECONDS_PER_SECOND
+ guest_clock - s->last_update + s->offset;
return guest_rtc;
}
@@ -232,16 +231,17 @@ static void check_update_timer(RTCState *s)
return;
}
- guest_nsec = get_guest_rtc_ns(s) % NSEC_PER_SEC;
+ guest_nsec = get_guest_rtc_ns(s) % NANOSECONDS_PER_SECOND;
/* if UF is clear, reprogram to next second */
next_update_time = qemu_clock_get_ns(rtc_clock)
- + NSEC_PER_SEC - guest_nsec;
+ + NANOSECONDS_PER_SECOND - guest_nsec;
/* Compute time of next alarm. One second is already accounted
* for in next_update_time.
*/
next_alarm_sec = get_next_alarm(s);
- s->next_alarm_time = next_update_time + (next_alarm_sec - 1) * NSEC_PER_SEC;
+ s->next_alarm_time = next_update_time +
+ (next_alarm_sec - 1) * NANOSECONDS_PER_SECOND;
if (s->cmos_data[RTC_REG_C] & REG_C_UF) {
/* UF is set, but AF is clear. Program the timer to target
@@ -457,7 +457,7 @@ static void cmos_ioport_write(void *opaque, hwaddr addr,
/* if disabling set mode, update the time */
if ((s->cmos_data[RTC_REG_B] & REG_B_SET) &&
(s->cmos_data[RTC_REG_A] & 0x70) <= 0x20) {
- s->offset = get_guest_rtc_ns(s) % NSEC_PER_SEC;
+ s->offset = get_guest_rtc_ns(s) % NANOSECONDS_PER_SECOND;
rtc_set_time(s);
}
}
@@ -581,7 +581,7 @@ static void rtc_update_time(RTCState *s)
int64_t guest_nsec;
guest_nsec = get_guest_rtc_ns(s);
- guest_sec = guest_nsec / NSEC_PER_SEC;
+ guest_sec = guest_nsec / NANOSECONDS_PER_SECOND;
gmtime_r(&guest_sec, &ret);
/* Is SET flag of Register B disabled? */
@@ -609,7 +609,8 @@ static int update_in_progress(RTCState *s)
guest_nsec = get_guest_rtc_ns(s);
/* UIP bit will be set at last 244us of every second. */
- if ((guest_nsec % NSEC_PER_SEC) >= (NSEC_PER_SEC - UIP_HOLD_LENGTH)) {
+ if ((guest_nsec % NANOSECONDS_PER_SECOND) >=
+ (NANOSECONDS_PER_SECOND - UIP_HOLD_LENGTH)) {
return 1;
}
return 0;
@@ -723,6 +724,12 @@ static int rtc_post_load(void *opaque, int version_id)
check_update_timer(s);
}
+ uint64_t now = qemu_clock_get_ns(rtc_clock);
+ if (now < s->next_periodic_time ||
+ now > (s->next_periodic_time + get_max_clock_jump())) {
+ periodic_timer_update(s, qemu_clock_get_ns(rtc_clock));
+ }
+
#ifdef TARGET_I386
if (version_id >= 2) {
if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) {
@@ -733,22 +740,23 @@ static int rtc_post_load(void *opaque, int version_id)
return 0;
}
+static bool rtc_irq_reinject_on_ack_count_needed(void *opaque)
+{
+ RTCState *s = (RTCState *)opaque;
+ return s->irq_reinject_on_ack_count != 0;
+}
+
static const VMStateDescription vmstate_rtc_irq_reinject_on_ack_count = {
.name = "mc146818rtc/irq_reinject_on_ack_count",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = rtc_irq_reinject_on_ack_count_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT16(irq_reinject_on_ack_count, RTCState),
VMSTATE_END_OF_LIST()
}
};
-static bool rtc_irq_reinject_on_ack_count_needed(void *opaque)
-{
- RTCState *s = (RTCState *)opaque;
- return s->irq_reinject_on_ack_count != 0;
-}
-
static const VMStateDescription vmstate_rtc = {
.name = "mc146818rtc",
.version_id = 3,
@@ -770,13 +778,9 @@ static const VMStateDescription vmstate_rtc = {
VMSTATE_UINT64_V(next_alarm_time, RTCState, 3),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_rtc_irq_reinject_on_ack_count,
- .needed = rtc_irq_reinject_on_ack_count_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_rtc_irq_reinject_on_ack_count,
+ NULL
}
};
diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs
index 99f598314..64cecc3b6 100644
--- a/hw/tpm/Makefile.objs
+++ b/hw/tpm/Makefile.objs
@@ -1,2 +1,2 @@
common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
-common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
+common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o tpm_util.o
diff --git a/hw/tpm/tpm_int.h b/hw/tpm/tpm_int.h
index 2b35fe21e..f2f285b3c 100644
--- a/hw/tpm/tpm_int.h
+++ b/hw/tpm/tpm_int.h
@@ -29,6 +29,7 @@ struct TPMState {
char *backend;
TPMBackend *be_driver;
+ TPMVersion be_tpm_version;
};
#define TPM(obj) OBJECT_CHECK(TPMState, (obj), TYPE_TPM_TIS)
@@ -65,4 +66,10 @@ struct tpm_resp_hdr {
#define TPM_ORD_ContinueSelfTest 0x53
#define TPM_ORD_GetTicks 0xf1
+
+/* TPM2 defines */
+#define TPM2_ST_NO_SESSIONS 0x8001
+
+#define TPM2_CC_ReadClock 0x00000181
+
#endif /* TPM_TPM_INT_H */
diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c
index 2a45071e3..79a8f98af 100644
--- a/hw/tpm/tpm_passthrough.c
+++ b/hw/tpm/tpm_passthrough.c
@@ -26,6 +26,7 @@
#include "qemu-common.h"
#include "qapi/error.h"
+#include "qemu/error-report.h"
#include "qemu/sockets.h"
#include "sysemu/tpm_backend.h"
#include "tpm_int.h"
@@ -33,16 +34,15 @@
#include "hw/i386/pc.h"
#include "sysemu/tpm_backend_int.h"
#include "tpm_tis.h"
+#include "tpm_util.h"
-/* #define DEBUG_TPM */
+#define DEBUG_TPM 0
-#ifdef DEBUG_TPM
-#define DPRINTF(fmt, ...) \
- do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) \
- do { } while (0)
-#endif
+#define DPRINTF(fmt, ...) do { \
+ if (DEBUG_TPM) { \
+ fprintf(stderr, fmt, ## __VA_ARGS__); \
+ } \
+} while (0);
#define TYPE_TPM_PASSTHROUGH "tpm-passthrough"
#define TPM_PASSTHROUGH(obj) \
@@ -71,6 +71,8 @@ struct TPMPassthruState {
bool tpm_op_canceled;
int cancel_fd;
bool had_startup_error;
+
+ TPMVersion tpm_version;
};
typedef struct TPMPassthruState TPMPassthruState;
@@ -269,6 +271,13 @@ static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb)
return false;
}
+static int tpm_passthrough_reset_tpm_established_flag(TPMBackend *tb,
+ uint8_t locty)
+{
+ /* only a TPM 2.0 will support this */
+ return 0;
+}
+
static bool tpm_passthrough_get_startup_error(TPMBackend *tb)
{
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
@@ -326,56 +335,11 @@ static const char *tpm_passthrough_create_desc(void)
return "Passthrough TPM backend driver";
}
-/*
- * A basic test of a TPM device. We expect a well formatted response header
- * (error response is fine) within one second.
- */
-static int tpm_passthrough_test_tpmdev(int fd)
+static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb)
{
- struct tpm_req_hdr req = {
- .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND),
- .len = cpu_to_be32(sizeof(req)),
- .ordinal = cpu_to_be32(TPM_ORD_GetTicks),
- };
- struct tpm_resp_hdr *resp;
- fd_set readfds;
- int n;
- struct timeval tv = {
- .tv_sec = 1,
- .tv_usec = 0,
- };
- unsigned char buf[1024];
-
- n = write(fd, &req, sizeof(req));
- if (n < 0) {
- return errno;
- }
- if (n != sizeof(req)) {
- return EFAULT;
- }
-
- FD_ZERO(&readfds);
- FD_SET(fd, &readfds);
-
- /* wait for a second */
- n = select(fd + 1, &readfds, NULL, NULL, &tv);
- if (n != 1) {
- return errno;
- }
-
- n = read(fd, &buf, sizeof(buf));
- if (n < sizeof(struct tpm_resp_hdr)) {
- return EFAULT;
- }
-
- resp = (struct tpm_resp_hdr *)buf;
- /* check the header */
- if (be16_to_cpu(resp->tag) != TPM_TAG_RSP_COMMAND ||
- be32_to_cpu(resp->len) != n) {
- return EBADMSG;
- }
+ TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
- return 0;
+ return tpm_pt->tpm_version;
}
/*
@@ -445,7 +409,7 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb)
goto err_free_parameters;
}
- if (tpm_passthrough_test_tpmdev(tpm_pt->tpm_fd)) {
+ if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) {
error_report("'%s' is not a TPM device.",
tpm_pt->tpm_dev);
goto err_close_tpmdev;
@@ -542,6 +506,8 @@ static const TPMDriverOps tpm_passthrough_driver = {
.deliver_request = tpm_passthrough_deliver_request,
.cancel_cmd = tpm_passthrough_cancel_cmd,
.get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag,
+ .reset_tpm_established_flag = tpm_passthrough_reset_tpm_established_flag,
+ .get_tpm_version = tpm_passthrough_get_tpm_version,
};
static void tpm_passthrough_inst_init(Object *obj)
diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c
index 815c8eace..0806b5f82 100644
--- a/hw/tpm/tpm_tis.c
+++ b/hw/tpm/tpm_tis.c
@@ -17,6 +17,9 @@
* supports version 1.3, 21 March 2013
* In the developers menu choose the PC Client section then find the TIS
* specification.
+ *
+ * TPM TIS for TPM 2 implementation following TCG PC Client Platform
+ * TPM Profile (PTP) Specification, Familiy 2.0, Revision 00.43
*/
#include "sysemu/tpm_backend.h"
@@ -29,16 +32,15 @@
#include "tpm_tis.h"
#include "qemu-common.h"
#include "qemu/main-loop.h"
+#include "sysemu/tpm_backend.h"
-/*#define DEBUG_TIS */
+#define DEBUG_TIS 0
-#ifdef DEBUG_TIS
-#define DPRINTF(fmt, ...) \
- do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) \
- do { } while (0)
-#endif
+#define DPRINTF(fmt, ...) do { \
+ if (DEBUG_TIS) { \
+ printf(fmt, ## __VA_ARGS__); \
+ } \
+} while (0);
/* whether the STS interrupt is supported */
#define RAISE_STS_IRQ
@@ -51,6 +53,7 @@
#define TPM_TIS_REG_INTF_CAPABILITY 0x14
#define TPM_TIS_REG_STS 0x18
#define TPM_TIS_REG_DATA_FIFO 0x24
+#define TPM_TIS_REG_INTERFACE_ID 0x30
#define TPM_TIS_REG_DATA_XFIFO 0x80
#define TPM_TIS_REG_DATA_XFIFO_END 0xbc
#define TPM_TIS_REG_DID_VID 0xf00
@@ -59,6 +62,12 @@
/* vendor-specific registers */
#define TPM_TIS_REG_DEBUG 0xf90
+#define TPM_TIS_STS_TPM_FAMILY_MASK (0x3 << 26)/* TPM 2.0 */
+#define TPM_TIS_STS_TPM_FAMILY1_2 (0 << 26) /* TPM 2.0 */
+#define TPM_TIS_STS_TPM_FAMILY2_0 (1 << 26) /* TPM 2.0 */
+#define TPM_TIS_STS_RESET_ESTABLISHMENT_BIT (1 << 25) /* TPM 2.0 */
+#define TPM_TIS_STS_COMMAND_CANCEL (1 << 24) /* TPM 2.0 */
+
#define TPM_TIS_STS_VALID (1 << 7)
#define TPM_TIS_STS_COMMAND_READY (1 << 6)
#define TPM_TIS_STS_TPM_GO (1 << 5)
@@ -104,15 +113,42 @@
#endif
#define TPM_TIS_CAP_INTERFACE_VERSION1_3 (2 << 28)
+#define TPM_TIS_CAP_INTERFACE_VERSION1_3_FOR_TPM2_0 (3 << 28)
#define TPM_TIS_CAP_DATA_TRANSFER_64B (3 << 9)
#define TPM_TIS_CAP_DATA_TRANSFER_LEGACY (0 << 9)
#define TPM_TIS_CAP_BURST_COUNT_DYNAMIC (0 << 8)
#define TPM_TIS_CAP_INTERRUPT_LOW_LEVEL (1 << 4) /* support is mandatory */
-#define TPM_TIS_CAPABILITIES_SUPPORTED (TPM_TIS_CAP_INTERRUPT_LOW_LEVEL | \
- TPM_TIS_CAP_BURST_COUNT_DYNAMIC | \
- TPM_TIS_CAP_DATA_TRANSFER_64B | \
- TPM_TIS_CAP_INTERFACE_VERSION1_3 | \
- TPM_TIS_INTERRUPTS_SUPPORTED)
+#define TPM_TIS_CAPABILITIES_SUPPORTED1_3 \
+ (TPM_TIS_CAP_INTERRUPT_LOW_LEVEL | \
+ TPM_TIS_CAP_BURST_COUNT_DYNAMIC | \
+ TPM_TIS_CAP_DATA_TRANSFER_64B | \
+ TPM_TIS_CAP_INTERFACE_VERSION1_3 | \
+ TPM_TIS_INTERRUPTS_SUPPORTED)
+
+#define TPM_TIS_CAPABILITIES_SUPPORTED2_0 \
+ (TPM_TIS_CAP_INTERRUPT_LOW_LEVEL | \
+ TPM_TIS_CAP_BURST_COUNT_DYNAMIC | \
+ TPM_TIS_CAP_DATA_TRANSFER_64B | \
+ TPM_TIS_CAP_INTERFACE_VERSION1_3_FOR_TPM2_0 | \
+ TPM_TIS_INTERRUPTS_SUPPORTED)
+
+#define TPM_TIS_IFACE_ID_INTERFACE_TIS1_3 (0xf) /* TPM 2.0 */
+#define TPM_TIS_IFACE_ID_INTERFACE_FIFO (0x0) /* TPM 2.0 */
+#define TPM_TIS_IFACE_ID_INTERFACE_VER_FIFO (0 << 4) /* TPM 2.0 */
+#define TPM_TIS_IFACE_ID_CAP_5_LOCALITIES (1 << 8) /* TPM 2.0 */
+#define TPM_TIS_IFACE_ID_CAP_TIS_SUPPORTED (1 << 13) /* TPM 2.0 */
+#define TPM_TIS_IFACE_ID_INT_SEL_LOCK (1 << 19) /* TPM 2.0 */
+
+#define TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3 \
+ (TPM_TIS_IFACE_ID_INTERFACE_TIS1_3 | \
+ (~0 << 4)/* all of it is don't care */)
+
+/* if backend was a TPM 2.0: */
+#define TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0 \
+ (TPM_TIS_IFACE_ID_INTERFACE_FIFO | \
+ TPM_TIS_IFACE_ID_INTERFACE_VER_FIFO | \
+ TPM_TIS_IFACE_ID_CAP_5_LOCALITIES | \
+ TPM_TIS_IFACE_ID_CAP_TIS_SUPPORTED)
#define TPM_TIS_TPM_DID 0x0001
#define TPM_TIS_TPM_VID PCI_VENDOR_ID_IBM
@@ -156,7 +192,8 @@ static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
/*
* Set the given flags in the STS register by clearing the register but
- * preserving the SELFTEST_DONE flag and then setting the new flags.
+ * preserving the SELFTEST_DONE and TPM_FAMILY_MASK flags and then setting
+ * the new flags.
*
* The SELFTEST_DONE flag is acquired from the backend that determines it by
* peeking into TPM commands.
@@ -168,7 +205,7 @@ static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
*/
static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags)
{
- l->sts &= TPM_TIS_STS_SELFTEST_DONE;
+ l->sts &= TPM_TIS_STS_SELFTEST_DONE | TPM_TIS_STS_TPM_FAMILY_MASK;
l->sts |= flags;
}
@@ -421,7 +458,7 @@ static void tpm_tis_dump_state(void *opaque, hwaddr addr)
for (idx = 0; regs[idx] != 0xfff; idx++) {
DPRINTF("tpm_tis: 0x%04x : 0x%08x\n", regs[idx],
- (uint32_t)tpm_tis_mmio_read(opaque, base + regs[idx], 4));
+ (int)tpm_tis_mmio_read(opaque, base + regs[idx], 4));
}
DPRINTF("tpm_tis: read offset : %d\n"
@@ -491,7 +528,17 @@ static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
val = tis->loc[locty].ints;
break;
case TPM_TIS_REG_INTF_CAPABILITY:
- val = TPM_TIS_CAPABILITIES_SUPPORTED;
+ switch (s->be_tpm_version) {
+ case TPM_VERSION_UNSPEC:
+ val = 0;
+ break;
+ case TPM_VERSION_1_2:
+ val = TPM_TIS_CAPABILITIES_SUPPORTED1_3;
+ break;
+ case TPM_VERSION_2_0:
+ val = TPM_TIS_CAPABILITIES_SUPPORTED2_0;
+ break;
+ }
break;
case TPM_TIS_REG_STS:
if (tis->active_locty == locty) {
@@ -538,6 +585,9 @@ static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
shift = 0; /* no more adjustments */
}
break;
+ case TPM_TIS_REG_INTERFACE_ID:
+ val = tis->loc[locty].iface_id;
+ break;
case TPM_TIS_REG_DID_VID:
val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID;
break;
@@ -555,7 +605,7 @@ static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
val >>= shift;
}
- DPRINTF("tpm_tis: read.%u(%08x) = %08x\n", size, (int)addr, (uint32_t)val);
+ DPRINTF("tpm_tis: read.%u(%08x) = %08x\n", size, (int)addr, (int)val);
return val;
}
@@ -578,7 +628,7 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr,
uint16_t len;
uint32_t mask = (size == 1) ? 0xff : ((size == 2) ? 0xffff : ~0);
- DPRINTF("tpm_tis: write.%u(%08x) = %08x\n", size, (int)addr, (uint32_t)val);
+ DPRINTF("tpm_tis: write.%u(%08x) = %08x\n", size, (int)addr, (int)val);
if (locty == 4 && !hw_access) {
DPRINTF("tpm_tis: Access to locality 4 only allowed from hardware\n");
@@ -738,6 +788,25 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr,
break;
}
+ if (s->be_tpm_version == TPM_VERSION_2_0) {
+ /* some flags that are only supported for TPM 2 */
+ if (val & TPM_TIS_STS_COMMAND_CANCEL) {
+ if (tis->loc[locty].state == TPM_TIS_STATE_EXECUTION) {
+ /*
+ * request the backend to cancel. Some backends may not
+ * support it
+ */
+ tpm_backend_cancel_cmd(s->be_driver);
+ }
+ }
+
+ if (val & TPM_TIS_STS_RESET_ESTABLISHMENT_BIT) {
+ if (locty == 3 || locty == 4) {
+ tpm_backend_reset_tpm_established_flag(s->be_driver, locty);
+ }
+ }
+ }
+
val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO |
TPM_TIS_STS_RESPONSE_RETRY);
@@ -815,7 +884,7 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr,
/* drop the byte */
} else {
DPRINTF("tpm_tis: Data to send to TPM: %08x (size=%d)\n",
- val, size);
+ (int)val, size);
if (tis->loc[locty].state == TPM_TIS_STATE_READY) {
tis->loc[locty].state = TPM_TIS_STATE_RECEPTION;
tpm_tis_sts_set(&tis->loc[locty],
@@ -844,7 +913,7 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr,
(tis->loc[locty].sts & TPM_TIS_STS_EXPECT)) {
/* we have a packet length - see if we have all of it */
#ifdef RAISE_STS_IRQ
- bool needIrq = !(tis->loc[locty].sts & TPM_TIS_STS_VALID);
+ bool need_irq = !(tis->loc[locty].sts & TPM_TIS_STS_VALID);
#endif
len = tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer);
if (len > tis->loc[locty].w_offset) {
@@ -855,13 +924,20 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr,
tpm_tis_sts_set(&tis->loc[locty], TPM_TIS_STS_VALID);
}
#ifdef RAISE_STS_IRQ
- if (needIrq) {
+ if (need_irq) {
tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
}
#endif
}
}
break;
+ case TPM_TIS_REG_INTERFACE_ID:
+ if (val & TPM_TIS_IFACE_ID_INT_SEL_LOCK) {
+ for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
+ tis->loc[l].iface_id |= TPM_TIS_IFACE_ID_INT_SEL_LOCK;
+ }
+ }
+ break;
}
}
@@ -887,6 +963,16 @@ static int tpm_tis_do_startup_tpm(TPMState *s)
}
/*
+ * Get the TPMVersion of the backend device being used
+ */
+TPMVersion tpm_tis_get_tpm_version(Object *obj)
+{
+ TPMState *s = TPM(obj);
+
+ return tpm_backend_get_tpm_version(s->be_driver);
+}
+
+/*
* This function is called when the machine starts, resets or due to
* S3 resume.
*/
@@ -896,6 +982,8 @@ static void tpm_tis_reset(DeviceState *dev)
TPMTISEmuState *tis = &s->s.tis;
int c;
+ s->be_tpm_version = tpm_backend_get_tpm_version(s->be_driver);
+
tpm_backend_reset(s->be_driver);
tis->active_locty = TPM_TIS_NO_LOCALITY;
@@ -904,7 +992,18 @@ static void tpm_tis_reset(DeviceState *dev)
for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) {
tis->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS;
- tis->loc[c].sts = 0;
+ switch (s->be_tpm_version) {
+ case TPM_VERSION_UNSPEC:
+ break;
+ case TPM_VERSION_1_2:
+ tis->loc[c].sts = TPM_TIS_STS_TPM_FAMILY1_2;
+ tis->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3;
+ break;
+ case TPM_VERSION_2_0:
+ tis->loc[c].sts = TPM_TIS_STS_TPM_FAMILY2_0;
+ tis->loc[c].iface_id = TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0;
+ break;
+ }
tis->loc[c].inte = TPM_TIS_INT_POLARITY_LOW_LEVEL;
tis->loc[c].ints = 0;
tis->loc[c].state = TPM_TIS_STATE_IDLE;
diff --git a/hw/tpm/tpm_tis.h b/hw/tpm/tpm_tis.h
index db78d51e4..a1df41fa2 100644
--- a/hw/tpm/tpm_tis.h
+++ b/hw/tpm/tpm_tis.h
@@ -42,6 +42,7 @@ typedef struct TPMLocality {
TPMTISState state;
uint8_t access;
uint32_t sts;
+ uint32_t iface_id;
uint32_t inte;
uint32_t ints;
diff --git a/hw/tpm/tpm_util.c b/hw/tpm/tpm_util.c
new file mode 100644
index 000000000..4ace5852e
--- /dev/null
+++ b/hw/tpm/tpm_util.c
@@ -0,0 +1,126 @@
+/*
+ * TPM utility functions
+ *
+ * Copyright (c) 2010 - 2015 IBM Corporation
+ * Authors:
+ * Stefan Berger <stefanb@us.ibm.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "tpm_util.h"
+#include "tpm_int.h"
+
+/*
+ * A basic test of a TPM device. We expect a well formatted response header
+ * (error response is fine) within one second.
+ */
+static int tpm_util_test(int fd,
+ unsigned char *request,
+ size_t requestlen,
+ uint16_t *return_tag)
+{
+ struct tpm_resp_hdr *resp;
+ fd_set readfds;
+ int n;
+ struct timeval tv = {
+ .tv_sec = 1,
+ .tv_usec = 0,
+ };
+ unsigned char buf[1024];
+
+ n = write(fd, request, requestlen);
+ if (n < 0) {
+ return errno;
+ }
+ if (n != requestlen) {
+ return EFAULT;
+ }
+
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+
+ /* wait for a second */
+ n = select(fd + 1, &readfds, NULL, NULL, &tv);
+ if (n != 1) {
+ return errno;
+ }
+
+ n = read(fd, &buf, sizeof(buf));
+ if (n < sizeof(struct tpm_resp_hdr)) {
+ return EFAULT;
+ }
+
+ resp = (struct tpm_resp_hdr *)buf;
+ /* check the header */
+ if (be32_to_cpu(resp->len) != n) {
+ return EBADMSG;
+ }
+
+ *return_tag = be16_to_cpu(resp->tag);
+
+ return 0;
+}
+
+/*
+ * Probe for the TPM device in the back
+ * Returns 0 on success with the version of the probed TPM set, 1 on failure.
+ */
+int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version)
+{
+ /*
+ * Sending a TPM1.2 command to a TPM2 should return a TPM1.2
+ * header (tag = 0xc4) and error code (TPM_BADTAG = 0x1e)
+ *
+ * Sending a TPM2 command to a TPM 2 will give a TPM 2 tag in the
+ * header.
+ * Sending a TPM2 command to a TPM 1.2 will give a TPM 1.2 tag
+ * in the header and an error code.
+ */
+ const struct tpm_req_hdr test_req = {
+ .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND),
+ .len = cpu_to_be32(sizeof(test_req)),
+ .ordinal = cpu_to_be32(TPM_ORD_GetTicks),
+ };
+
+ const struct tpm_req_hdr test_req_tpm2 = {
+ .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+ .len = cpu_to_be32(sizeof(test_req_tpm2)),
+ .ordinal = cpu_to_be32(TPM2_CC_ReadClock),
+ };
+ uint16_t return_tag;
+ int ret;
+
+ /* Send TPM 2 command */
+ ret = tpm_util_test(tpm_fd, (unsigned char *)&test_req_tpm2,
+ sizeof(test_req_tpm2), &return_tag);
+ /* TPM 2 would respond with a tag of TPM2_ST_NO_SESSIONS */
+ if (!ret && return_tag == TPM2_ST_NO_SESSIONS) {
+ *tpm_version = TPM_VERSION_2_0;
+ return 0;
+ }
+
+ /* Send TPM 1.2 command */
+ ret = tpm_util_test(tpm_fd, (unsigned char *)&test_req,
+ sizeof(test_req), &return_tag);
+ if (!ret && return_tag == TPM_TAG_RSP_COMMAND) {
+ *tpm_version = TPM_VERSION_1_2;
+ /* this is a TPM 1.2 */
+ return 0;
+ }
+
+ *tpm_version = TPM_VERSION_UNSPEC;
+
+ return 1;
+}
diff --git a/hw/tpm/tpm_util.h b/hw/tpm/tpm_util.h
new file mode 100644
index 000000000..e7f354a52
--- /dev/null
+++ b/hw/tpm/tpm_util.h
@@ -0,0 +1,28 @@
+/*
+ * TPM utility functions
+ *
+ * Copyright (c) 2010 - 2015 IBM Corporation
+ * Authors:
+ * Stefan Berger <stefanb@us.ibm.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+#ifndef TPM_TPM_UTILS_H
+#define TPM_TPM_UTILS_H
+
+#include "sysemu/tpm_backend.h"
+
+int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version);
+
+#endif /* TPM_TPM_UTILS_H */
diff --git a/hw/unicore32/puv3.c b/hw/unicore32/puv3.c
index cc9a21a71..703e29d6d 100644
--- a/hw/unicore32/puv3.c
+++ b/hw/unicore32/puv3.c
@@ -40,15 +40,15 @@ static void puv3_intc_cpu_handler(void *opaque, int irq, int level)
static void puv3_soc_init(CPUUniCore32State *env)
{
- qemu_irq *cpu_intc, irqs[PUV3_IRQS_NR];
+ qemu_irq cpu_intc, irqs[PUV3_IRQS_NR];
DeviceState *dev;
MemoryRegion *i8042 = g_new(MemoryRegion, 1);
int i;
/* Initialize interrupt controller */
- cpu_intc = qemu_allocate_irqs(puv3_intc_cpu_handler,
- uc32_env_get_cpu(env), 1);
- dev = sysbus_create_simple("puv3_intc", PUV3_INTC_BASE, *cpu_intc);
+ cpu_intc = qemu_allocate_irq(puv3_intc_cpu_handler,
+ uc32_env_get_cpu(env), 0);
+ dev = sysbus_create_simple("puv3_intc", PUV3_INTC_BASE, cpu_intc);
for (i = 0; i < PUV3_IRQS_NR; i++) {
irqs[i] = qdev_get_gpio_in(dev, i);
}
diff --git a/hw/usb/bus.c b/hw/usb/bus.c
index 375167573..5f39e1e3a 100644
--- a/hw/usb/bus.c
+++ b/hw/usb/bus.c
@@ -1,6 +1,7 @@
#include "hw/hw.h"
#include "hw/usb.h"
#include "hw/qdev.h"
+#include "qemu/error-report.h"
#include "sysemu/sysemu.h"
#include "monitor/monitor.h"
#include "trace.h"
diff --git a/hw/usb/ccid-card-emulated.c b/hw/usb/ccid-card-emulated.c
index aa1c37aab..72329ed7d 100644
--- a/hw/usb/ccid-card-emulated.c
+++ b/hw/usb/ccid-card-emulated.c
@@ -33,7 +33,6 @@
#include "qemu/thread.h"
#include "sysemu/char.h"
-#include "monitor/monitor.h"
#include "ccid.h"
#define DPRINTF(card, lvl, fmt, ...) \
diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c
index 10f1d309a..85a4fc3e5 100644
--- a/hw/usb/ccid-card-passthru.c
+++ b/hw/usb/ccid-card-passthru.c
@@ -9,8 +9,8 @@
*/
#include "sysemu/char.h"
+#include "qemu/error-report.h"
#include "qemu/sockets.h"
-#include "monitor/monitor.h"
#include "ccid.h"
#include "libcacard/vscard_common.h"
diff --git a/hw/usb/core.c b/hw/usb/core.c
index cf34755bb..d0025db60 100644
--- a/hw/usb/core.c
+++ b/hw/usb/core.c
@@ -331,23 +331,6 @@ void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p)
usb_packet_complete(s, p);
}
-/* XXX: fix overflow */
-int set_usb_string(uint8_t *buf, const char *str)
-{
- int len, i;
- uint8_t *q;
-
- q = buf;
- len = strlen(str);
- *q++ = 2 * len + 2;
- *q++ = 3;
- for(i = 0; i < len; i++) {
- *q++ = str[i];
- *q++ = 0;
- }
- return q - buf;
-}
-
USBDevice *usb_find_device(USBPort *port, uint8_t addr)
{
USBDevice *dev = port->dev;
@@ -749,12 +732,6 @@ void usb_ep_set_type(USBDevice *dev, int pid, int ep, uint8_t type)
uep->type = type;
}
-uint8_t usb_ep_get_ifnum(USBDevice *dev, int pid, int ep)
-{
- struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
- return uep->ifnum;
-}
-
void usb_ep_set_ifnum(USBDevice *dev, int pid, int ep, uint8_t ifnum)
{
struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
@@ -782,12 +759,6 @@ void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep,
uep->max_packet_size = size * microframes;
}
-int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep)
-{
- struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
- return uep->max_packet_size;
-}
-
void usb_ep_set_max_streams(USBDevice *dev, int pid, int ep, uint8_t raw)
{
struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
@@ -801,18 +772,6 @@ void usb_ep_set_max_streams(USBDevice *dev, int pid, int ep, uint8_t raw)
}
}
-int usb_ep_get_max_streams(USBDevice *dev, int pid, int ep)
-{
- struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
- return uep->max_streams;
-}
-
-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;
-}
-
void usb_ep_set_halted(USBDevice *dev, int pid, int ep, bool halted)
{
struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c
index 67deffebc..f092bb849 100644
--- a/hw/usb/dev-audio.c
+++ b/hw/usb/dev-audio.c
@@ -361,6 +361,9 @@ typedef struct USBAudioState {
uint32_t buffer;
} USBAudioState;
+#define TYPE_USB_AUDIO "usb-audio"
+#define USB_AUDIO(obj) OBJECT_CHECK(USBAudioState, (obj), TYPE_USB_AUDIO)
+
static void output_callback(void *opaque, int avail)
{
USBAudioState *s = opaque;
@@ -506,7 +509,7 @@ static void usb_audio_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index,
int length, uint8_t *data)
{
- USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+ USBAudioState *s = USB_AUDIO(dev);
int ret = 0;
if (s->debug) {
@@ -565,7 +568,7 @@ fail:
static void usb_audio_set_interface(USBDevice *dev, int iface,
int old, int value)
{
- USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+ USBAudioState *s = USB_AUDIO(dev);
if (iface == 1) {
usb_audio_set_output_altset(s, value);
@@ -574,7 +577,7 @@ static void usb_audio_set_interface(USBDevice *dev, int iface,
static void usb_audio_handle_reset(USBDevice *dev)
{
- USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+ USBAudioState *s = USB_AUDIO(dev);
if (s->debug) {
fprintf(stderr, "usb-audio: reset\n");
@@ -615,7 +618,7 @@ static void usb_audio_handle_data(USBDevice *dev, USBPacket *p)
static void usb_audio_handle_destroy(USBDevice *dev)
{
- USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+ USBAudioState *s = USB_AUDIO(dev);
if (s->debug) {
fprintf(stderr, "usb-audio: destroy\n");
@@ -630,12 +633,12 @@ static void usb_audio_handle_destroy(USBDevice *dev)
static void usb_audio_realize(USBDevice *dev, Error **errp)
{
- USBAudioState *s = DO_UPCAST(USBAudioState, dev, dev);
+ USBAudioState *s = USB_AUDIO(dev);
usb_desc_create_serial(dev);
usb_desc_init(dev);
s->dev.opaque = s;
- AUD_register_card("usb-audio", &s->card);
+ AUD_register_card(TYPE_USB_AUDIO, &s->card);
s->out.altset = ALTSET_OFF;
s->out.mute = false;
@@ -647,14 +650,14 @@ static void usb_audio_realize(USBDevice *dev, Error **errp)
s->out.as.endianness = 0;
streambuf_init(&s->out.buf, s->buffer);
- s->out.voice = AUD_open_out(&s->card, s->out.voice, "usb-audio",
+ s->out.voice = AUD_open_out(&s->card, s->out.voice, TYPE_USB_AUDIO,
s, output_callback, &s->out.as);
AUD_set_volume_out(s->out.voice, s->out.mute, s->out.vol[0], s->out.vol[1]);
AUD_set_active_out(s->out.voice, 0);
}
static const VMStateDescription vmstate_usb_audio = {
- .name = "usb-audio",
+ .name = TYPE_USB_AUDIO,
.unmigratable = 1,
};
@@ -684,7 +687,7 @@ static void usb_audio_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo usb_audio_info = {
- .name = "usb-audio",
+ .name = TYPE_USB_AUDIO,
.parent = TYPE_USB_DEVICE,
.instance_size = sizeof(USBAudioState),
.class_init = usb_audio_class_init,
@@ -693,7 +696,7 @@ static const TypeInfo usb_audio_info = {
static void usb_audio_register_types(void)
{
type_register_static(&usb_audio_info);
- usb_legacy_register("usb-audio", "audio", NULL);
+ usb_legacy_register(TYPE_USB_AUDIO, "audio", NULL);
}
type_init(usb_audio_register_types)
diff --git a/hw/usb/dev-bluetooth.c b/hw/usb/dev-bluetooth.c
index 9bf673057..b19ec76b0 100644
--- a/hw/usb/dev-bluetooth.c
+++ b/hw/usb/dev-bluetooth.c
@@ -49,6 +49,9 @@ struct USBBtState {
} outcmd, outacl, outsco;
};
+#define TYPE_USB_BT "usb-bt-dongle"
+#define USB_BT(obj) OBJECT_CHECK(struct USBBtState, (obj), TYPE_USB_BT)
+
#define USB_EVT_EP 1
#define USB_ACL_EP 2
#define USB_SCO_EP 3
@@ -503,7 +506,7 @@ static void usb_bt_handle_destroy(USBDevice *dev)
static void usb_bt_realize(USBDevice *dev, Error **errp)
{
- struct USBBtState *s = DO_UPCAST(struct USBBtState, dev, dev);
+ struct USBBtState *s = USB_BT(dev);
usb_desc_create_serial(dev);
usb_desc_init(dev);
@@ -523,7 +526,7 @@ static USBDevice *usb_bt_init(USBBus *bus, const char *cmdline)
USBDevice *dev;
struct USBBtState *s;
HCIInfo *hci;
- const char *name = "usb-bt-dongle";
+ const char *name = TYPE_USB_BT;
if (*cmdline) {
hci = hci_init(cmdline);
@@ -534,7 +537,7 @@ static USBDevice *usb_bt_init(USBBus *bus, const char *cmdline)
return NULL;
dev = usb_create(bus, name);
- s = DO_UPCAST(struct USBBtState, dev, dev);
+ s = USB_BT(dev);
s->hci = hci;
return dev;
}
@@ -561,7 +564,7 @@ static void usb_bt_class_initfn(ObjectClass *klass, void *data)
}
static const TypeInfo bt_info = {
- .name = "usb-bt-dongle",
+ .name = TYPE_USB_BT,
.parent = TYPE_USB_DEVICE,
.instance_size = sizeof(struct USBBtState),
.class_init = usb_bt_class_initfn,
@@ -570,7 +573,7 @@ static const TypeInfo bt_info = {
static void usb_bt_register_types(void)
{
type_register_static(&bt_info);
- usb_legacy_register("usb-bt-dongle", "bt", usb_bt_init);
+ usb_legacy_register(TYPE_USB_BT, "bt", usb_bt_init);
}
type_init(usb_bt_register_types)
diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c
index 507c9663c..2e7dcd96c 100644
--- a/hw/usb/dev-hid.c
+++ b/hw/usb/dev-hid.c
@@ -51,6 +51,9 @@ typedef struct USBHIDState {
uint32_t head;
} USBHIDState;
+#define TYPE_USB_HID "usb-hid"
+#define USB_HID(obj) OBJECT_CHECK(USBHIDState, (obj), TYPE_USB_HID)
+
enum {
STR_MANUFACTURER = 1,
STR_PRODUCT_MOUSE,
@@ -564,7 +567,7 @@ static void usb_hid_changed(HIDState *hs)
static void usb_hid_handle_reset(USBDevice *dev)
{
- USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
+ USBHIDState *us = USB_HID(dev);
hid_reset(&us->hid);
}
@@ -572,7 +575,7 @@ static void usb_hid_handle_reset(USBDevice *dev)
static void usb_hid_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data)
{
- USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
+ USBHIDState *us = USB_HID(dev);
HIDState *hs = &us->hid;
int ret;
@@ -651,7 +654,7 @@ static void usb_hid_handle_control(USBDevice *dev, USBPacket *p,
static void usb_hid_handle_data(USBDevice *dev, USBPacket *p)
{
- USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
+ USBHIDState *us = USB_HID(dev);
HIDState *hs = &us->hid;
uint8_t buf[p->iov.size];
int len = 0;
@@ -687,7 +690,7 @@ static void usb_hid_handle_data(USBDevice *dev, USBPacket *p)
static void usb_hid_handle_destroy(USBDevice *dev)
{
- USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
+ USBHIDState *us = USB_HID(dev);
hid_free(&us->hid);
}
@@ -696,7 +699,7 @@ static void usb_hid_initfn(USBDevice *dev, int kind,
const USBDesc *usb1, const USBDesc *usb2,
Error **errp)
{
- USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
+ USBHIDState *us = USB_HID(dev);
switch (us->usb_version) {
case 1:
dev->usb_desc = usb1;
@@ -784,6 +787,14 @@ static void usb_hid_class_initfn(ObjectClass *klass, void *data)
uc->handle_attach = usb_desc_attach;
}
+static const TypeInfo usb_hid_type_info = {
+ .name = TYPE_USB_HID,
+ .parent = TYPE_USB_DEVICE,
+ .instance_size = sizeof(USBHIDState),
+ .abstract = true,
+ .class_init = usb_hid_class_initfn,
+};
+
static Property usb_tablet_properties[] = {
DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2),
DEFINE_PROP_STRING("display", USBHIDState, display),
@@ -796,7 +807,6 @@ static void usb_tablet_class_initfn(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
- usb_hid_class_initfn(klass, data);
uc->realize = usb_tablet_realize;
uc->product_desc = "QEMU USB Tablet";
dc->vmsd = &vmstate_usb_ptr;
@@ -806,8 +816,7 @@ static void usb_tablet_class_initfn(ObjectClass *klass, void *data)
static const TypeInfo usb_tablet_info = {
.name = "usb-tablet",
- .parent = TYPE_USB_DEVICE,
- .instance_size = sizeof(USBHIDState),
+ .parent = TYPE_USB_HID,
.class_init = usb_tablet_class_initfn,
};
@@ -821,7 +830,6 @@ static void usb_mouse_class_initfn(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
- usb_hid_class_initfn(klass, data);
uc->realize = usb_mouse_realize;
uc->product_desc = "QEMU USB Mouse";
dc->vmsd = &vmstate_usb_ptr;
@@ -831,8 +839,7 @@ static void usb_mouse_class_initfn(ObjectClass *klass, void *data)
static const TypeInfo usb_mouse_info = {
.name = "usb-mouse",
- .parent = TYPE_USB_DEVICE,
- .instance_size = sizeof(USBHIDState),
+ .parent = TYPE_USB_HID,
.class_init = usb_mouse_class_initfn,
};
@@ -847,7 +854,6 @@ static void usb_keyboard_class_initfn(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
- usb_hid_class_initfn(klass, data);
uc->realize = usb_keyboard_realize;
uc->product_desc = "QEMU USB Keyboard";
dc->vmsd = &vmstate_usb_kbd;
@@ -857,13 +863,13 @@ static void usb_keyboard_class_initfn(ObjectClass *klass, void *data)
static const TypeInfo usb_keyboard_info = {
.name = "usb-kbd",
- .parent = TYPE_USB_DEVICE,
- .instance_size = sizeof(USBHIDState),
+ .parent = TYPE_USB_HID,
.class_init = usb_keyboard_class_initfn,
};
static void usb_hid_register_types(void)
{
+ type_register_static(&usb_hid_type_info);
type_register_static(&usb_tablet_info);
usb_legacy_register("usb-tablet", "tablet", NULL);
type_register_static(&usb_mouse_info);
diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c
index 0482f5871..c8c685550 100644
--- a/hw/usb/dev-hub.c
+++ b/hw/usb/dev-hub.c
@@ -41,6 +41,9 @@ typedef struct USBHubState {
USBHubPort ports[NUM_PORTS];
} USBHubState;
+#define TYPE_USB_HUB "usb-hub"
+#define USB_HUB(obj) OBJECT_CHECK(USBHubState, (obj), TYPE_USB_HUB)
+
#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE)
#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE)
#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR)
@@ -227,7 +230,7 @@ static void usb_hub_complete(USBPort *port, USBPacket *packet)
static USBDevice *usb_hub_find_device(USBDevice *dev, uint8_t addr)
{
- USBHubState *s = DO_UPCAST(USBHubState, dev, dev);
+ USBHubState *s = USB_HUB(dev);
USBHubPort *port;
USBDevice *downstream;
int i;
@@ -247,7 +250,7 @@ static USBDevice *usb_hub_find_device(USBDevice *dev, uint8_t addr)
static void usb_hub_handle_reset(USBDevice *dev)
{
- USBHubState *s = DO_UPCAST(USBHubState, dev, dev);
+ USBHubState *s = USB_HUB(dev);
USBHubPort *port;
int i;
@@ -513,7 +516,7 @@ static USBPortOps usb_hub_port_ops = {
static void usb_hub_realize(USBDevice *dev, Error **errp)
{
- USBHubState *s = DO_UPCAST(USBHubState, dev, dev);
+ USBHubState *s = USB_HUB(dev);
USBHubPort *port;
int i;
@@ -577,7 +580,7 @@ static void usb_hub_class_initfn(ObjectClass *klass, void *data)
}
static const TypeInfo hub_info = {
- .name = "usb-hub",
+ .name = TYPE_USB_HUB,
.parent = TYPE_USB_DEVICE,
.instance_size = sizeof(USBHubState),
.class_init = usb_hub_class_initfn,
diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c
index 108ece819..809b1cb11 100644
--- a/hw/usb/dev-mtp.c
+++ b/hw/usb/dev-mtp.c
@@ -130,6 +130,9 @@ struct MTPState {
QTAILQ_HEAD(, MTPObject) objects;
};
+#define TYPE_USB_MTP "usb-mtp"
+#define USB_MTP(obj) OBJECT_CHECK(MTPState, (obj), TYPE_USB_MTP)
+
#define QEMU_STORAGE_ID 0x00010001
#define MTP_FLAG_WRITABLE 0
@@ -878,7 +881,7 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
static void usb_mtp_handle_reset(USBDevice *dev)
{
- MTPState *s = DO_UPCAST(MTPState, dev, dev);
+ MTPState *s = USB_MTP(dev);
trace_usb_mtp_reset(s->dev.addr);
@@ -914,7 +917,7 @@ static void usb_mtp_cancel_packet(USBDevice *dev, USBPacket *p)
static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p)
{
- MTPState *s = DO_UPCAST(MTPState, dev, dev);
+ MTPState *s = USB_MTP(dev);
MTPControl cmd;
mtp_container container;
uint32_t params[5];
@@ -1062,12 +1065,16 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p)
static void usb_mtp_realize(USBDevice *dev, Error **errp)
{
- MTPState *s = DO_UPCAST(MTPState, dev, dev);
+ MTPState *s = USB_MTP(dev);
usb_desc_create_serial(dev);
usb_desc_init(dev);
QTAILQ_INIT(&s->objects);
if (s->desc == NULL) {
+ if (s->root == NULL) {
+ error_setg(errp, "usb-mtp: x-root property must be configured");
+ return;
+ }
s->desc = strrchr(s->root, '/');
if (s->desc && s->desc[0]) {
s->desc = g_strdup(s->desc + 1);
@@ -1113,7 +1120,7 @@ static void usb_mtp_class_initfn(ObjectClass *klass, void *data)
}
static TypeInfo mtp_info = {
- .name = "usb-mtp",
+ .name = TYPE_USB_MTP,
.parent = TYPE_USB_DEVICE,
.instance_size = sizeof(MTPState),
.class_init = usb_mtp_class_initfn,
diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c
index 9be3a6400..7800ceea5 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -648,6 +648,9 @@ typedef struct USBNetState {
QTAILQ_HEAD(rndis_resp_head, rndis_response) rndis_resp;
} USBNetState;
+#define TYPE_USB_NET "usb-net"
+#define USB_NET(obj) OBJECT_CHECK(USBNetState, (obj), TYPE_USB_NET)
+
static int is_rndis(USBNetState *s)
{
return s->dev.config->bConfigurationValue == DEV_RNDIS_CONFIG_VALUE;
@@ -1265,6 +1268,10 @@ static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t siz
uint8_t *in_buf = s->in_buf;
size_t total_size = size;
+ if (!s->dev.config) {
+ return -1;
+ }
+
if (is_rndis(s)) {
if (s->rndis_state != RNDIS_DATA_INITIALIZED) {
return -1;
@@ -1306,21 +1313,6 @@ static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t siz
return size;
}
-static int usbnet_can_receive(NetClientState *nc)
-{
- USBNetState *s = qemu_get_nic_opaque(nc);
-
- if (!s->dev.config) {
- return 0;
- }
-
- if (is_rndis(s) && s->rndis_state != RNDIS_DATA_INITIALIZED) {
- return 1;
- }
-
- return !s->in_len;
-}
-
static void usbnet_cleanup(NetClientState *nc)
{
USBNetState *s = qemu_get_nic_opaque(nc);
@@ -1340,14 +1332,13 @@ static void usb_net_handle_destroy(USBDevice *dev)
static NetClientInfo net_usbnet_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = usbnet_can_receive,
.receive = usbnet_receive,
.cleanup = usbnet_cleanup,
};
static void usb_net_realize(USBDevice *dev, Error **errrp)
{
- USBNetState *s = DO_UPCAST(USBNetState, dev, dev);
+ USBNetState *s = USB_NET(dev);
usb_desc_create_serial(dev);
usb_desc_init(dev);
@@ -1380,7 +1371,7 @@ static void usb_net_realize(USBDevice *dev, Error **errrp)
static void usb_net_instance_init(Object *obj)
{
USBDevice *dev = USB_DEVICE(obj);
- USBNetState *s = DO_UPCAST(USBNetState, dev, dev);
+ USBNetState *s = USB_NET(dev);
device_add_bootindex_property(obj, &s->conf.bootindex,
"bootindex", "/ethernet-phy@0",
@@ -1394,7 +1385,7 @@ static USBDevice *usb_net_init(USBBus *bus, const char *cmdline)
QemuOpts *opts;
int idx;
- opts = qemu_opts_parse(qemu_find_opts("net"), cmdline, 0);
+ opts = qemu_opts_parse_noisily(qemu_find_opts("net"), cmdline, false);
if (!opts) {
return NULL;
}
@@ -1441,7 +1432,7 @@ static void usb_net_class_initfn(ObjectClass *klass, void *data)
}
static const TypeInfo net_info = {
- .name = "usb-net",
+ .name = TYPE_USB_NET,
.parent = TYPE_USB_DEVICE,
.instance_size = sizeof(USBNetState),
.class_init = usb_net_class_initfn,
@@ -1451,7 +1442,7 @@ static const TypeInfo net_info = {
static void usb_net_register_types(void)
{
type_register_static(&net_info);
- usb_legacy_register("usb-net", "net", usb_net_init);
+ usb_legacy_register(TYPE_USB_NET, "net", usb_net_init);
}
type_init(usb_net_register_types)
diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c
index 67c2072ce..a6a66008e 100644
--- a/hw/usb/dev-serial.c
+++ b/hw/usb/dev-serial.c
@@ -9,7 +9,7 @@
*/
#include "qemu-common.h"
-#include "monitor/monitor.h"
+#include "qemu/error-report.h"
#include "hw/usb.h"
#include "hw/usb/desc.h"
#include "sysemu/char.h"
@@ -103,6 +103,9 @@ typedef struct {
CharDriverState *cs;
} USBSerialState;
+#define TYPE_USB_SERIAL "usb-serial-dev"
+#define USB_SERIAL_DEV(obj) OBJECT_CHECK(USBSerialState, (obj), TYPE_USB_SERIAL)
+
enum {
STR_MANUFACTURER = 1,
STR_PRODUCT_SERIAL,
@@ -473,7 +476,7 @@ static void usb_serial_event(void *opaque, int event)
static void usb_serial_realize(USBDevice *dev, Error **errp)
{
- USBSerialState *s = DO_UPCAST(USBSerialState, dev, dev);
+ USBSerialState *s = USB_SERIAL_DEV(dev);
Error *local_err = NULL;
usb_desc_create_serial(dev);
@@ -576,26 +579,40 @@ static Property serial_properties[] = {
DEFINE_PROP_END_OF_LIST(),
};
-static void usb_serial_class_initfn(ObjectClass *klass, void *data)
+static void usb_serial_dev_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
- uc->realize = usb_serial_realize;
- uc->product_desc = "QEMU USB Serial";
- uc->usb_desc = &desc_serial;
+ uc->realize = usb_serial_realize;
uc->handle_reset = usb_serial_handle_reset;
uc->handle_control = usb_serial_handle_control;
uc->handle_data = usb_serial_handle_data;
dc->vmsd = &vmstate_usb_serial;
- dc->props = serial_properties;
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
}
+static const TypeInfo usb_serial_dev_type_info = {
+ .name = TYPE_USB_SERIAL,
+ .parent = TYPE_USB_DEVICE,
+ .instance_size = sizeof(USBSerialState),
+ .abstract = true,
+ .class_init = usb_serial_dev_class_init,
+};
+
+static void usb_serial_class_initfn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+ uc->product_desc = "QEMU USB Serial";
+ uc->usb_desc = &desc_serial;
+ dc->props = serial_properties;
+}
+
static const TypeInfo serial_info = {
.name = "usb-serial",
- .parent = TYPE_USB_DEVICE,
- .instance_size = sizeof(USBSerialState),
+ .parent = TYPE_USB_SERIAL,
.class_init = usb_serial_class_initfn,
};
@@ -609,26 +626,20 @@ static void usb_braille_class_initfn(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
- uc->realize = usb_serial_realize;
uc->product_desc = "QEMU USB Braille";
uc->usb_desc = &desc_braille;
- uc->handle_reset = usb_serial_handle_reset;
- uc->handle_control = usb_serial_handle_control;
- uc->handle_data = usb_serial_handle_data;
- dc->vmsd = &vmstate_usb_serial;
dc->props = braille_properties;
- set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
}
static const TypeInfo braille_info = {
.name = "usb-braille",
- .parent = TYPE_USB_DEVICE,
- .instance_size = sizeof(USBSerialState),
+ .parent = TYPE_USB_SERIAL,
.class_init = usb_braille_class_initfn,
};
static void usb_serial_register_types(void)
{
+ type_register_static(&usb_serial_dev_type_info);
type_register_static(&serial_info);
usb_legacy_register("usb-serial", "serial", usb_serial_init);
type_register_static(&braille_info);
diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c
index 78ce68167..8952efffa 100644
--- a/hw/usb/dev-smartcard-reader.c
+++ b/hw/usb/dev-smartcard-reader.c
@@ -38,7 +38,6 @@
#include "qemu/error-report.h"
#include "hw/usb.h"
#include "hw/usb/desc.h"
-#include "monitor/monitor.h"
#include "ccid.h"
@@ -55,7 +54,7 @@ do { \
#define D_VERBOSE 4
#define CCID_DEV_NAME "usb-ccid"
-
+#define USB_CCID_DEV(obj) OBJECT_CHECK(USBCCIDState, (obj), CCID_DEV_NAME)
/*
* The two options for variable sized buffers:
* make them constant size, for large enough constant,
@@ -284,6 +283,7 @@ typedef struct CCIDBus {
typedef struct USBCCIDState {
USBDevice dev;
USBEndpoint *intr;
+ USBEndpoint *bulk;
CCIDBus bus;
CCIDCardState *card;
BulkIn bulk_in_pending[BULK_IN_PENDING_NUM]; /* circular */
@@ -649,7 +649,7 @@ static void ccid_detach(USBCCIDState *s)
static void ccid_handle_reset(USBDevice *dev)
{
- USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
+ USBCCIDState *s = USB_CCID_DEV(dev);
DPRINTF(s, 1, "Reset\n");
@@ -692,7 +692,7 @@ static const char *ccid_control_to_str(USBCCIDState *s, int request)
static void ccid_handle_control(USBDevice *dev, USBPacket *p, int request,
int value, int index, int length, uint8_t *data)
{
- USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
+ USBCCIDState *s = USB_CCID_DEV(dev);
int ret;
DPRINTF(s, 1, "%s: got control %s (%x), value %x\n", __func__,
@@ -770,6 +770,7 @@ static void ccid_write_slot_status(USBCCIDState *s, CCID_Header *recv)
h->b.bError = s->bError;
h->bClockStatus = CLOCK_STATUS_RUNNING;
ccid_reset_error_status(s);
+ usb_wakeup(s->bulk, 0);
}
static void ccid_write_parameters(USBCCIDState *s, CCID_Header *recv)
@@ -790,6 +791,7 @@ static void ccid_write_parameters(USBCCIDState *s, CCID_Header *recv)
h->bProtocolNum = s->bProtocolNum;
h->abProtocolDataStructure = s->abProtocolDataStructure;
ccid_reset_error_status(s);
+ usb_wakeup(s->bulk, 0);
}
static void ccid_write_data_block(USBCCIDState *s, uint8_t slot, uint8_t seq,
@@ -811,6 +813,7 @@ static void ccid_write_data_block(USBCCIDState *s, uint8_t slot, uint8_t seq,
}
memcpy(p->abData, data, len);
ccid_reset_error_status(s);
+ usb_wakeup(s->bulk, 0);
}
static void ccid_report_error_failed(USBCCIDState *s, uint8_t error)
@@ -1104,7 +1107,7 @@ static void ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p)
static void ccid_handle_data(USBDevice *dev, USBPacket *p)
{
- USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
+ USBCCIDState *s = USB_CCID_DEV(dev);
uint8_t buf[2];
switch (p->pid) {
@@ -1148,7 +1151,7 @@ static void ccid_handle_data(USBDevice *dev, USBPacket *p)
static void ccid_handle_destroy(USBDevice *dev)
{
- USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
+ USBCCIDState *s = USB_CCID_DEV(dev);
ccid_bulk_in_clear(s);
}
@@ -1184,8 +1187,9 @@ static const TypeInfo ccid_bus_info = {
void ccid_card_send_apdu_to_guest(CCIDCardState *card,
uint8_t *apdu, uint32_t len)
{
- USBCCIDState *s = DO_UPCAST(USBCCIDState, dev.qdev,
- card->qdev.parent_bus->parent);
+ DeviceState *qdev = DEVICE(card);
+ USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent);
+ USBCCIDState *s = USB_CCID_DEV(dev);
Answer *answer;
if (!ccid_has_pending_answers(s)) {
@@ -1206,8 +1210,9 @@ void ccid_card_send_apdu_to_guest(CCIDCardState *card,
void ccid_card_card_removed(CCIDCardState *card)
{
- USBCCIDState *s =
- DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
+ DeviceState *qdev = DEVICE(card);
+ USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent);
+ USBCCIDState *s = USB_CCID_DEV(dev);
ccid_on_slot_change(s, false);
ccid_flush_pending_answers(s);
@@ -1216,8 +1221,9 @@ void ccid_card_card_removed(CCIDCardState *card)
int ccid_card_ccid_attach(CCIDCardState *card)
{
- USBCCIDState *s =
- DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
+ DeviceState *qdev = DEVICE(card);
+ USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent);
+ USBCCIDState *s = USB_CCID_DEV(dev);
DPRINTF(s, 1, "CCID Attach\n");
if (s->migration_state == MIGRATION_MIGRATED) {
@@ -1228,8 +1234,9 @@ int ccid_card_ccid_attach(CCIDCardState *card)
void ccid_card_ccid_detach(CCIDCardState *card)
{
- USBCCIDState *s =
- DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
+ DeviceState *qdev = DEVICE(card);
+ USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent);
+ USBCCIDState *s = USB_CCID_DEV(dev);
DPRINTF(s, 1, "CCID Detach\n");
if (ccid_card_inserted(s)) {
@@ -1240,8 +1247,9 @@ void ccid_card_ccid_detach(CCIDCardState *card)
void ccid_card_card_error(CCIDCardState *card, uint64_t error)
{
- USBCCIDState *s =
- DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
+ DeviceState *qdev = DEVICE(card);
+ USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent);
+ USBCCIDState *s = USB_CCID_DEV(dev);
s->bmCommandStatus = COMMAND_STATUS_FAILED;
s->last_answer_error = error;
@@ -1258,8 +1266,9 @@ void ccid_card_card_error(CCIDCardState *card, uint64_t error)
void ccid_card_card_inserted(CCIDCardState *card)
{
- USBCCIDState *s =
- DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
+ DeviceState *qdev = DEVICE(card);
+ USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent);
+ USBCCIDState *s = USB_CCID_DEV(dev);
s->bmCommandStatus = COMMAND_STATUS_NO_ERROR;
ccid_flush_pending_answers(s);
@@ -1270,8 +1279,8 @@ static int ccid_card_exit(DeviceState *qdev)
{
int ret = 0;
CCIDCardState *card = CCID_CARD(qdev);
- USBCCIDState *s =
- DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
+ USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent);
+ USBCCIDState *s = USB_CCID_DEV(dev);
if (ccid_card_inserted(s)) {
ccid_card_card_removed(card);
@@ -1284,8 +1293,8 @@ static int ccid_card_exit(DeviceState *qdev)
static int ccid_card_init(DeviceState *qdev)
{
CCIDCardState *card = CCID_CARD(qdev);
- USBCCIDState *s =
- DO_UPCAST(USBCCIDState, dev.qdev, card->qdev.parent_bus->parent);
+ USBDevice *dev = USB_DEVICE(qdev->parent_bus->parent);
+ USBCCIDState *s = USB_CCID_DEV(dev);
int ret = 0;
if (card->slot != 0) {
@@ -1306,7 +1315,7 @@ static int ccid_card_init(DeviceState *qdev)
static void ccid_realize(USBDevice *dev, Error **errp)
{
- USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
+ USBCCIDState *s = USB_CCID_DEV(dev);
usb_desc_create_serial(dev);
usb_desc_init(dev);
@@ -1314,6 +1323,7 @@ static void ccid_realize(USBDevice *dev, Error **errp)
NULL);
qbus_set_hotplug_handler(BUS(&s->bus), DEVICE(dev), &error_abort);
s->intr = usb_ep_get(dev, USB_TOKEN_IN, CCID_INT_IN_EP);
+ s->bulk = usb_ep_get(dev, USB_TOKEN_IN, CCID_BULK_IN_EP);
s->card = NULL;
s->migration_state = MIGRATION_NONE;
s->migration_target_ip = 0;
diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
index ae8d40dc7..9a4e7dc0c 100644
--- a/hw/usb/dev-storage.c
+++ b/hw/usb/dev-storage.c
@@ -8,6 +8,7 @@
*/
#include "qemu-common.h"
+#include "qemu/error-report.h"
#include "qemu/option.h"
#include "qemu/config-file.h"
#include "hw/usb.h"
@@ -64,6 +65,9 @@ typedef struct {
SCSIDevice *scsi_dev;
} MSDState;
+#define TYPE_USB_STORAGE "usb-storage-dev"
+#define USB_STORAGE_DEV(obj) OBJECT_CHECK(MSDState, (obj), TYPE_USB_STORAGE)
+
struct usb_msd_cbw {
uint32_t sig;
uint32_t tag;
@@ -385,7 +389,7 @@ static void usb_msd_handle_control(USBDevice *dev, USBPacket *p,
static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p)
{
- MSDState *s = DO_UPCAST(MSDState, dev, dev);
+ MSDState *s = USB_STORAGE_DEV(dev);
assert(s->packet == p);
s->packet = NULL;
@@ -599,7 +603,7 @@ static const struct SCSIBusInfo usb_msd_scsi_info_bot = {
static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
{
- MSDState *s = DO_UPCAST(MSDState, dev, dev);
+ MSDState *s = USB_STORAGE_DEV(dev);
BlockBackend *blk = s->conf.blk;
SCSIDevice *scsi_dev;
Error *err = NULL;
@@ -658,7 +662,7 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
static void usb_msd_realize_bot(USBDevice *dev, Error **errp)
{
- MSDState *s = DO_UPCAST(MSDState, dev, dev);
+ MSDState *s = USB_STORAGE_DEV(dev);
usb_desc_create_serial(dev);
usb_desc_init(dev);
@@ -748,7 +752,7 @@ static Property msd_properties[] = {
DEFINE_PROP_END_OF_LIST(),
};
-static void usb_msd_class_initfn_common(ObjectClass *klass)
+static void usb_msd_class_initfn_common(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
@@ -772,14 +776,13 @@ static void usb_msd_class_initfn_storage(ObjectClass *klass, void *data)
uc->realize = usb_msd_realize_storage;
dc->props = msd_properties;
- usb_msd_class_initfn_common(klass);
}
static void usb_msd_get_bootindex(Object *obj, Visitor *v, void *opaque,
const char *name, Error **errp)
{
USBDevice *dev = USB_DEVICE(obj);
- MSDState *s = DO_UPCAST(MSDState, dev, dev);
+ MSDState *s = USB_STORAGE_DEV(dev);
visit_type_int32(v, &s->conf.bootindex, name, errp);
}
@@ -788,7 +791,7 @@ static void usb_msd_set_bootindex(Object *obj, Visitor *v, void *opaque,
const char *name, Error **errp)
{
USBDevice *dev = USB_DEVICE(obj);
- MSDState *s = DO_UPCAST(MSDState, dev, dev);
+ MSDState *s = USB_STORAGE_DEV(dev);
int32_t boot_index;
Error *local_err = NULL;
@@ -815,6 +818,14 @@ out:
}
}
+static const TypeInfo usb_storage_dev_type_info = {
+ .name = TYPE_USB_STORAGE,
+ .parent = TYPE_USB_DEVICE,
+ .instance_size = sizeof(MSDState),
+ .abstract = true,
+ .class_init = usb_msd_class_initfn_common,
+};
+
static void usb_msd_instance_init(Object *obj)
{
object_property_add(obj, "bootindex", "int32",
@@ -829,27 +840,25 @@ static void usb_msd_class_initfn_bot(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
uc->realize = usb_msd_realize_bot;
- usb_msd_class_initfn_common(klass);
dc->hotpluggable = false;
}
static const TypeInfo msd_info = {
.name = "usb-storage",
- .parent = TYPE_USB_DEVICE,
- .instance_size = sizeof(MSDState),
+ .parent = TYPE_USB_STORAGE,
.class_init = usb_msd_class_initfn_storage,
.instance_init = usb_msd_instance_init,
};
static const TypeInfo bot_info = {
.name = "usb-bot",
- .parent = TYPE_USB_DEVICE,
- .instance_size = sizeof(MSDState),
+ .parent = TYPE_USB_STORAGE,
.class_init = usb_msd_class_initfn_bot,
};
static void usb_msd_register_types(void)
{
+ type_register_static(&usb_storage_dev_type_info);
type_register_static(&msd_info);
type_register_static(&bot_info);
usb_legacy_register("usb-storage", "disk", usb_msd_init);
diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c
index 04fc515db..38b26c586 100644
--- a/hw/usb/dev-uas.c
+++ b/hw/usb/dev-uas.c
@@ -127,6 +127,9 @@ struct UASDevice {
USBPacket *status3[UAS_MAX_STREAMS + 1];
};
+#define TYPE_USB_UAS "usb-uas"
+#define USB_UAS(obj) OBJECT_CHECK(UASDevice, (obj), TYPE_USB_UAS)
+
struct UASRequest {
uint16_t tag;
uint64_t lun;
@@ -626,7 +629,7 @@ static const struct SCSIBusInfo usb_uas_scsi_info = {
static void usb_uas_handle_reset(USBDevice *dev)
{
- UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
+ UASDevice *uas = USB_UAS(dev);
UASRequest *req, *nreq;
UASStatus *st, *nst;
@@ -655,7 +658,7 @@ static void usb_uas_handle_control(USBDevice *dev, USBPacket *p,
static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p)
{
- UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
+ UASDevice *uas = USB_UAS(dev);
UASRequest *req, *nreq;
int i;
@@ -797,7 +800,7 @@ incorrect_lun:
static void usb_uas_handle_data(USBDevice *dev, USBPacket *p)
{
- UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
+ UASDevice *uas = USB_UAS(dev);
uas_iu iu;
UASStatus *st;
UASRequest *req;
@@ -888,14 +891,14 @@ static void usb_uas_handle_data(USBDevice *dev, USBPacket *p)
static void usb_uas_handle_destroy(USBDevice *dev)
{
- UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
+ UASDevice *uas = USB_UAS(dev);
qemu_bh_delete(uas->status_bh);
}
static void usb_uas_realize(USBDevice *dev, Error **errp)
{
- UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
+ UASDevice *uas = USB_UAS(dev);
usb_desc_create_serial(dev);
usb_desc_init(dev);
@@ -943,7 +946,7 @@ static void usb_uas_class_initfn(ObjectClass *klass, void *data)
}
static const TypeInfo uas_info = {
- .name = "usb-uas",
+ .name = TYPE_USB_UAS,
.parent = TYPE_USB_DEVICE,
.instance_size = sizeof(UASDevice),
.class_init = usb_uas_class_initfn,
diff --git a/hw/usb/dev-wacom.c b/hw/usb/dev-wacom.c
index 844eafadf..c2450e729 100644
--- a/hw/usb/dev-wacom.c
+++ b/hw/usb/dev-wacom.c
@@ -56,6 +56,9 @@ typedef struct USBWacomState {
int changed;
} USBWacomState;
+#define TYPE_USB_WACOM "usb-wacom-tablet"
+#define USB_WACOM(obj) OBJECT_CHECK(USBWacomState, (obj), TYPE_USB_WACOM)
+
enum {
STR_MANUFACTURER = 1,
STR_PRODUCT,
@@ -337,7 +340,7 @@ static void usb_wacom_handle_destroy(USBDevice *dev)
static void usb_wacom_realize(USBDevice *dev, Error **errp)
{
- USBWacomState *s = DO_UPCAST(USBWacomState, dev, dev);
+ USBWacomState *s = USB_WACOM(dev);
usb_desc_create_serial(dev);
usb_desc_init(dev);
s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
@@ -367,7 +370,7 @@ static void usb_wacom_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo wacom_info = {
- .name = "usb-wacom-tablet",
+ .name = TYPE_USB_WACOM,
.parent = TYPE_USB_DEVICE,
.instance_size = sizeof(USBWacomState),
.class_init = usb_wacom_class_init,
@@ -376,7 +379,7 @@ static const TypeInfo wacom_info = {
static void usb_wacom_register_types(void)
{
type_register_static(&wacom_info);
- usb_legacy_register("usb-wacom-tablet", "wacom-tablet", NULL);
+ usb_legacy_register(TYPE_USB_WACOM, "wacom-tablet", NULL);
}
type_init(usb_wacom_register_types)
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index d4d754765..64a54c6e8 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -32,7 +32,7 @@
#include "trace.h"
#define FRAME_TIMER_FREQ 1000
-#define FRAME_TIMER_NS (1000000000 / FRAME_TIMER_FREQ)
+#define FRAME_TIMER_NS (NANOSECONDS_PER_SECOND / FRAME_TIMER_FREQ)
#define UFRAME_TIMER_NS (FRAME_TIMER_NS / 8)
#define NB_MAXINTRATE 8 // Max rate at which controller issues ints
diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h
index 87b240f70..30218423c 100644
--- a/hw/usb/hcd-ehci.h
+++ b/hw/usb/hcd-ehci.h
@@ -20,7 +20,6 @@
#include "hw/hw.h"
#include "qemu/timer.h"
#include "hw/usb.h"
-#include "monitor/monitor.h"
#include "sysemu/dma.h"
#include "sysemu/sysemu.h"
#include "hw/pci/pci.h"
diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c
index 1a22c9c0c..7d6581806 100644
--- a/hw/usb/hcd-ohci.c
+++ b/hw/usb/hcd-ohci.c
@@ -2034,6 +2034,7 @@ static const VMStateDescription vmstate_ohci_eof_timer = {
.version_id = 1,
.minimum_version_id = 1,
.pre_load = ohci_eof_timer_pre_load,
+ .needed = ohci_eof_timer_needed,
.fields = (VMStateField[]) {
VMSTATE_TIMER_PTR(eof_timer, OHCIState),
VMSTATE_END_OF_LIST()
@@ -2081,13 +2082,9 @@ static const VMStateDescription vmstate_ohci_state = {
VMSTATE_BOOL(async_complete, OHCIState),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection []) {
- {
- .vmsd = &vmstate_ohci_eof_timer,
- .needed = ohci_eof_timer_needed,
- } , {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_ohci_eof_timer,
+ NULL
}
};
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index 327f26da7..3f0ed6268 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -154,6 +154,9 @@ static void uhci_async_cancel(UHCIAsync *async);
static void uhci_queue_fill(UHCIQueue *q, UHCI_TD *td);
static void uhci_resume(void *opaque);
+#define TYPE_UHCI "pci-uhci-usb"
+#define UHCI(obj) OBJECT_CHECK(UHCIState, (obj), TYPE_UHCI)
+
static inline int32_t uhci_queue_token(UHCI_TD *td)
{
if ((td->token & (0xf << 15)) == 0) {
@@ -351,7 +354,7 @@ static void uhci_update_irq(UHCIState *s)
static void uhci_reset(DeviceState *dev)
{
PCIDevice *d = PCI_DEVICE(dev);
- UHCIState *s = DO_UPCAST(UHCIState, dev, d);
+ UHCIState *s = UHCI(d);
uint8_t *pci_conf;
int i;
UHCIPort *port;
@@ -363,7 +366,7 @@ static void uhci_reset(DeviceState *dev)
pci_conf[0x6a] = 0x01; /* usb clock */
pci_conf[0x6b] = 0x00;
s->cmd = 0;
- s->status = 0;
+ s->status = UHCI_STS_HCHALTED;
s->status2 = 0;
s->intr = 0;
s->fl_base_addr = 0;
@@ -1196,7 +1199,7 @@ static void usb_uhci_common_realize(PCIDevice *dev, Error **errp)
Error *err = NULL;
PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
UHCIPCIDeviceClass *u = container_of(pc, UHCIPCIDeviceClass, parent_class);
- UHCIState *s = DO_UPCAST(UHCIState, dev, dev);
+ UHCIState *s = UHCI(dev);
uint8_t *pci_conf = s->dev.config;
int i;
@@ -1241,7 +1244,7 @@ static void usb_uhci_common_realize(PCIDevice *dev, Error **errp)
static void usb_uhci_vt82c686b_realize(PCIDevice *dev, Error **errp)
{
- UHCIState *s = DO_UPCAST(UHCIState, dev, dev);
+ UHCIState *s = UHCI(dev);
uint8_t *pci_conf = s->dev.config;
/* USB misc control 1/2 */
@@ -1256,7 +1259,7 @@ static void usb_uhci_vt82c686b_realize(PCIDevice *dev, Error **errp)
static void usb_uhci_exit(PCIDevice *dev)
{
- UHCIState *s = DO_UPCAST(UHCIState, dev, dev);
+ UHCIState *s = UHCI(dev);
trace_usb_uhci_exit();
@@ -1294,6 +1297,26 @@ static void uhci_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->class_id = PCI_CLASS_SERIAL_USB;
+ dc->vmsd = &vmstate_uhci;
+ dc->reset = uhci_reset;
+ set_bit(DEVICE_CATEGORY_USB, dc->categories);
+}
+
+static const TypeInfo uhci_pci_type_info = {
+ .name = TYPE_UHCI,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(UHCIState),
+ .class_size = sizeof(UHCIPCIDeviceClass),
+ .abstract = true,
+ .class_init = uhci_class_init,
+};
+
+static void uhci_data_class_init(ObjectClass *klass, void *data)
+{
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
UHCIPCIDeviceClass *u = container_of(k, UHCIPCIDeviceClass, parent_class);
UHCIInfo *info = data;
@@ -1302,9 +1325,6 @@ static void uhci_class_init(ObjectClass *klass, void *data)
k->vendor_id = info->vendor_id;
k->device_id = info->device_id;
k->revision = info->revision;
- k->class_id = PCI_CLASS_SERIAL_USB;
- dc->vmsd = &vmstate_uhci;
- dc->reset = uhci_reset;
if (!info->unplug) {
/* uhci controllers in companion setups can't be hotplugged */
dc->hotpluggable = false;
@@ -1312,7 +1332,6 @@ static void uhci_class_init(ObjectClass *klass, void *data)
} else {
dc->props = uhci_properties_standalone;
}
- set_bit(DEVICE_CATEGORY_USB, dc->categories);
u->info = *info;
}
@@ -1387,13 +1406,13 @@ static UHCIInfo uhci_info[] = {
static void uhci_register_types(void)
{
TypeInfo uhci_type_info = {
- .parent = TYPE_PCI_DEVICE,
- .instance_size = sizeof(UHCIState),
- .class_size = sizeof(UHCIPCIDeviceClass),
- .class_init = uhci_class_init,
+ .parent = TYPE_UHCI,
+ .class_init = uhci_data_class_init,
};
int i;
+ type_register_static(&uhci_pci_type_info);
+
for (i = 0; i < ARRAY_SIZE(uhci_info); i++) {
uhci_type_info.name = uhci_info[i].name;
uhci_type_info.class_data = uhci_info + i;
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index ba15ae001..c673bed4c 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -1767,18 +1767,9 @@ static void xhci_xfer_report(XHCITransfer *xfer)
break;
}
- /*
- * XHCI 1.1, 4.11.3.1 Transfer Event TRB -- "each Transfer TRB
- * encountered with its IOC flag set to '1' shall generate a Transfer
- * Event."
- *
- * Otherwise, longer transfers can have multiple data TRBs (for scatter
- * gather). Short transfers and errors should be reported once per
- * transfer only.
- */
- if ((trb->control & TRB_TR_IOC) ||
- (!reported && ((shortpkt && (trb->control & TRB_TR_ISP)) ||
- (xfer->status != CC_SUCCESS && left == 0)))) {
+ if (!reported && ((trb->control & TRB_TR_IOC) ||
+ (shortpkt && (trb->control & TRB_TR_ISP)) ||
+ (xfer->status != CC_SUCCESS && left == 0))) {
event.slotid = xfer->slotid;
event.epid = xfer->epid;
event.length = (trb->status & 0x1ffff) - chunk;
@@ -1802,6 +1793,14 @@ static void xhci_xfer_report(XHCITransfer *xfer)
return;
}
}
+
+ switch (TRB_TYPE(*trb)) {
+ case TR_SETUP:
+ reported = 0;
+ shortpkt = 0;
+ break;
+ }
+
}
}
@@ -2204,7 +2203,6 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
if (epid == 1) {
if (xhci_fire_ctl_transfer(xhci, xfer) >= 0) {
epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
- ep = xfer->packet.ep;
} else {
DPRINTF("xhci: error firing CTL transfer\n");
}
diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c
index 10f4735dd..11429f5e7 100644
--- a/hw/usb/host-libusb.c
+++ b/hw/usb/host-libusb.c
@@ -38,6 +38,7 @@
#include "qemu-common.h"
#include "monitor/monitor.h"
+#include "qemu/error-report.h"
#include "sysemu/sysemu.h"
#include "trace.h"
@@ -888,6 +889,9 @@ static int usb_host_open(USBHostDevice *s, libusb_device *dev)
fail:
trace_usb_host_open_failure(bus_num, addr);
if (s->dh != NULL) {
+ usb_host_release_interfaces(s);
+ libusb_reset_device(s->dh);
+ usb_host_attach_kernel(s);
libusb_close(s->dh);
s->dh = NULL;
s->dev = NULL;
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index 2416de83e..25df25fd0 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -27,8 +27,9 @@
#include "qemu-common.h"
#include "qemu/timer.h"
-#include "monitor/monitor.h"
#include "sysemu/sysemu.h"
+#include "qapi/qmp/qerror.h"
+#include "qemu/error-report.h"
#include "qemu/iov.h"
#include "sysemu/char.h"
@@ -130,6 +131,9 @@ struct USBRedirDevice {
int compatible_speedmask;
};
+#define TYPE_USB_REDIR "usb-redir"
+#define USB_REDIRECT(obj) OBJECT_CHECK(USBRedirDevice, (obj), TYPE_USB_REDIR)
+
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);
@@ -360,7 +364,7 @@ static void packet_id_queue_empty(struct PacketIdQueue *q)
static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p)
{
- USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+ USBRedirDevice *dev = USB_REDIRECT(udev);
int i = USBEP2I(p->ep);
if (p->combined) {
@@ -500,7 +504,7 @@ static void usbredir_free_bufpq(USBRedirDevice *dev, uint8_t ep)
static void usbredir_handle_reset(USBDevice *udev)
{
- USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+ USBRedirDevice *dev = USB_REDIRECT(udev);
DPRINTF("reset device\n");
usbredirparser_send_reset(dev->parser);
@@ -907,7 +911,7 @@ static void usbredir_stop_interrupt_receiving(USBRedirDevice *dev,
static void usbredir_handle_data(USBDevice *udev, USBPacket *p)
{
- USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+ USBRedirDevice *dev = USB_REDIRECT(udev);
uint8_t ep;
ep = p->ep->nr;
@@ -976,7 +980,7 @@ static void usbredir_stop_ep(USBRedirDevice *dev, int i)
static void usbredir_ep_stopped(USBDevice *udev, USBEndpoint *uep)
{
- USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+ USBRedirDevice *dev = USB_REDIRECT(udev);
usbredir_stop_ep(dev, USBEP2I(uep));
usbredirparser_do_write(dev->parser);
@@ -1046,7 +1050,7 @@ static void usbredir_get_interface(USBRedirDevice *dev, USBPacket *p,
static void usbredir_handle_control(USBDevice *udev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data)
{
- USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+ USBRedirDevice *dev = USB_REDIRECT(udev);
struct usb_redir_control_packet_header control_packet;
if (usbredir_already_in_flight(dev, p->id)) {
@@ -1101,7 +1105,7 @@ static void usbredir_handle_control(USBDevice *udev, USBPacket *p,
static int usbredir_alloc_streams(USBDevice *udev, USBEndpoint **eps,
int nr_eps, int streams)
{
- USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+ USBRedirDevice *dev = USB_REDIRECT(udev);
#if USBREDIR_VERSION >= 0x000700
struct usb_redir_alloc_bulk_streams_header alloc_streams;
int i;
@@ -1140,7 +1144,7 @@ static void usbredir_free_streams(USBDevice *udev, USBEndpoint **eps,
int nr_eps)
{
#if USBREDIR_VERSION >= 0x000700
- USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+ USBRedirDevice *dev = USB_REDIRECT(udev);
struct usb_redir_free_bulk_streams_header free_streams;
int i;
@@ -1362,11 +1366,11 @@ static void usbredir_init_endpoints(USBRedirDevice *dev)
static void usbredir_realize(USBDevice *udev, Error **errp)
{
- USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+ USBRedirDevice *dev = USB_REDIRECT(udev);
int i;
if (dev->cs == NULL) {
- error_set(errp, QERR_MISSING_PARAMETER, "chardev");
+ error_setg(errp, QERR_MISSING_PARAMETER, "chardev");
return;
}
@@ -1375,8 +1379,8 @@ static void usbredir_realize(USBDevice *udev, Error **errp)
&dev->filter_rules,
&dev->filter_rules_count);
if (i) {
- error_set(errp, QERR_INVALID_PARAMETER_VALUE, "filter",
- "a usb device filter string");
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "filter",
+ "a usb device filter string");
return;
}
}
@@ -1415,7 +1419,7 @@ static void usbredir_cleanup_device_queues(USBRedirDevice *dev)
static void usbredir_handle_destroy(USBDevice *udev)
{
- USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+ USBRedirDevice *dev = USB_REDIRECT(udev);
qemu_chr_delete(dev->cs);
dev->cs = NULL;
@@ -2254,40 +2258,42 @@ static const VMStateInfo usbredir_ep_bufpq_vmstate_info = {
/* For endp_data migration */
+static bool usbredir_bulk_receiving_needed(void *priv)
+{
+ struct endp_data *endp = priv;
+
+ return endp->bulk_receiving_started;
+}
+
static const VMStateDescription usbredir_bulk_receiving_vmstate = {
.name = "usb-redir-ep/bulk-receiving",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = usbredir_bulk_receiving_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8(bulk_receiving_started, struct endp_data),
VMSTATE_END_OF_LIST()
}
};
-static bool usbredir_bulk_receiving_needed(void *priv)
+static bool usbredir_stream_needed(void *priv)
{
struct endp_data *endp = priv;
- return endp->bulk_receiving_started;
+ return endp->max_streams;
}
static const VMStateDescription usbredir_stream_vmstate = {
.name = "usb-redir-ep/stream-state",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = usbredir_stream_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT32(max_streams, struct endp_data),
VMSTATE_END_OF_LIST()
}
};
-static bool usbredir_stream_needed(void *priv)
-{
- struct endp_data *endp = priv;
-
- return endp->max_streams;
-}
-
static const VMStateDescription usbredir_ep_vmstate = {
.name = "usb-redir-ep",
.version_id = 1,
@@ -2315,16 +2321,10 @@ static const VMStateDescription usbredir_ep_vmstate = {
VMSTATE_INT32(bufpq_target_size, struct endp_data),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &usbredir_bulk_receiving_vmstate,
- .needed = usbredir_bulk_receiving_needed,
- }, {
- .vmsd = &usbredir_stream_vmstate,
- .needed = usbredir_stream_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &usbredir_bulk_receiving_vmstate,
+ &usbredir_stream_vmstate,
+ NULL
}
};
@@ -2496,7 +2496,7 @@ static void usbredir_class_initfn(ObjectClass *klass, void *data)
static void usbredir_instance_init(Object *obj)
{
USBDevice *udev = USB_DEVICE(obj);
- USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+ USBRedirDevice *dev = USB_REDIRECT(udev);
device_add_bootindex_property(obj, &dev->bootindex,
"bootindex", NULL,
@@ -2504,7 +2504,7 @@ static void usbredir_instance_init(Object *obj)
}
static const TypeInfo usbredir_dev_info = {
- .name = "usb-redir",
+ .name = TYPE_USB_REDIR,
.parent = TYPE_USB_DEVICE,
.instance_size = sizeof(USBRedirDevice),
.class_init = usbredir_class_initfn,
diff --git a/hw/vfio/Makefile.objs b/hw/vfio/Makefile.objs
index e31f30ec0..d540c9d14 100644
--- a/hw/vfio/Makefile.objs
+++ b/hw/vfio/Makefile.objs
@@ -1,4 +1,6 @@
ifeq ($(CONFIG_LINUX), y)
obj-$(CONFIG_SOFTMMU) += common.o
obj-$(CONFIG_PCI) += pci.o
+obj-$(CONFIG_SOFTMMU) += platform.o
+obj-$(CONFIG_SOFTMMU) += calxeda-xgmac.o
endif
diff --git a/hw/vfio/calxeda-xgmac.c b/hw/vfio/calxeda-xgmac.c
new file mode 100644
index 000000000..eb914f0d0
--- /dev/null
+++ b/hw/vfio/calxeda-xgmac.c
@@ -0,0 +1,55 @@
+/*
+ * calxeda xgmac VFIO device
+ *
+ * Copyright Linaro Limited, 2014
+ *
+ * Authors:
+ * Eric Auger <eric.auger@linaro.org>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "hw/vfio/vfio-calxeda-xgmac.h"
+
+static void calxeda_xgmac_realize(DeviceState *dev, Error **errp)
+{
+ VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev);
+ VFIOCalxedaXgmacDeviceClass *k = VFIO_CALXEDA_XGMAC_DEVICE_GET_CLASS(dev);
+
+ vdev->compat = g_strdup("calxeda,hb-xgmac");
+
+ k->parent_realize(dev, errp);
+}
+
+static const VMStateDescription vfio_platform_calxeda_xgmac_vmstate = {
+ .name = TYPE_VFIO_CALXEDA_XGMAC,
+ .unmigratable = 1,
+};
+
+static void vfio_calxeda_xgmac_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VFIOCalxedaXgmacDeviceClass *vcxc =
+ VFIO_CALXEDA_XGMAC_DEVICE_CLASS(klass);
+ vcxc->parent_realize = dc->realize;
+ dc->realize = calxeda_xgmac_realize;
+ dc->desc = "VFIO Calxeda XGMAC";
+ dc->vmsd = &vfio_platform_calxeda_xgmac_vmstate;
+}
+
+static const TypeInfo vfio_calxeda_xgmac_dev_info = {
+ .name = TYPE_VFIO_CALXEDA_XGMAC,
+ .parent = TYPE_VFIO_PLATFORM,
+ .instance_size = sizeof(VFIOCalxedaXgmacDevice),
+ .class_init = vfio_calxeda_xgmac_class_init,
+ .class_size = sizeof(VFIOCalxedaXgmacDeviceClass),
+};
+
+static void register_calxeda_xgmac_dev_type(void)
+{
+ type_register_static(&vfio_calxeda_xgmac_dev_info);
+}
+
+type_init(register_calxeda_xgmac_dev_type)
diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index b01262063..85ee9b005 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -270,13 +270,14 @@ static void vfio_iommu_map_notify(Notifier *n, void *data)
* this IOMMU to its immediate target. We need to translate
* it the rest of the way through to memory.
*/
+ rcu_read_lock();
mr = address_space_translate(&address_space_memory,
iotlb->translated_addr,
&xlat, &len, iotlb->perm & IOMMU_WO);
if (!memory_region_is_ram(mr)) {
error_report("iommu map to non memory area %"HWADDR_PRIx"",
xlat);
- return;
+ goto out;
}
/*
* Translation truncates length to the IOMMU page size,
@@ -284,7 +285,7 @@ static void vfio_iommu_map_notify(Notifier *n, void *data)
*/
if (len & iotlb->addr_mask) {
error_report("iommu has granularity incompatible with target AS");
- return;
+ goto out;
}
if ((iotlb->perm & IOMMU_RW) != IOMMU_NONE) {
@@ -307,6 +308,8 @@ static void vfio_iommu_map_notify(Notifier *n, void *data)
iotlb->addr_mask + 1, ret);
}
}
+out:
+ rcu_read_unlock();
}
static void vfio_listener_region_add(MemoryListener *listener,
@@ -769,11 +772,19 @@ static void vfio_disconnect_container(VFIOGroup *group)
if (QLIST_EMPTY(&container->group_list)) {
VFIOAddressSpace *space = container->space;
+ VFIOGuestIOMMU *giommu, *tmp;
if (container->iommu_data.release) {
container->iommu_data.release(container);
}
QLIST_REMOVE(container, next);
+
+ QLIST_FOREACH_SAFE(giommu, &container->giommu_list, giommu_next, tmp) {
+ memory_region_unregister_iommu_notifier(&giommu->n);
+ QLIST_REMOVE(giommu, giommu_next);
+ g_free(giommu);
+ }
+
trace_vfio_disconnect_container(container->fd);
close(container->fd);
g_free(container);
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index beaa306e9..4023d8e82 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -154,6 +154,7 @@ typedef struct VFIOPCIDevice {
PCIHostDeviceAddress host;
EventNotifier err_notifier;
EventNotifier req_notifier;
+ int (*resetfn)(struct VFIOPCIDevice *);
uint32_t features;
#define VFIO_FEATURE_ENABLE_VGA_BIT 0
#define VFIO_FEATURE_ENABLE_VGA (1 << VFIO_FEATURE_ENABLE_VGA_BIT)
@@ -596,7 +597,7 @@ static void vfio_add_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage *msg,
return;
}
- if (kvm_irqchip_add_irqfd_notifier(kvm_state, &vector->kvm_interrupt,
+ if (kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, &vector->kvm_interrupt,
NULL, virq) < 0) {
kvm_irqchip_release_virq(kvm_state, virq);
event_notifier_cleanup(&vector->kvm_interrupt);
@@ -608,8 +609,8 @@ static void vfio_add_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage *msg,
static void vfio_remove_kvm_msi_virq(VFIOMSIVector *vector)
{
- kvm_irqchip_remove_irqfd_notifier(kvm_state, &vector->kvm_interrupt,
- vector->virq);
+ kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, &vector->kvm_interrupt,
+ vector->virq);
kvm_irqchip_release_virq(kvm_state, vector->virq);
vector->virq = -1;
event_notifier_cleanup(&vector->kvm_interrupt);
@@ -938,7 +939,7 @@ static void vfio_pci_load_rom(VFIOPCIDevice *vdev)
};
uint64_t size;
off_t off = 0;
- size_t bytes;
+ ssize_t bytes;
if (ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_REGION_INFO, &reg_info)) {
error_report("vfio: Error getting ROM info: %m");
@@ -1531,9 +1532,12 @@ static uint64_t vfio_rtl8168_window_quirk_read(void *opaque,
return 0;
}
- io_mem_read(&vdev->pdev.msix_table_mmio,
- (hwaddr)(quirk->data.address_match & 0xfff),
- &val, size);
+ memory_region_dispatch_read(&vdev->pdev.msix_table_mmio,
+ (hwaddr)(quirk->data.address_match
+ & 0xfff),
+ &val,
+ size,
+ MEMTXATTRS_UNSPECIFIED);
return val;
}
}
@@ -1561,10 +1565,10 @@ static void vfio_rtl8168_window_quirk_write(void *opaque, hwaddr addr,
memory_region_name(&quirk->mem),
vdev->vbasedev.name);
- io_mem_write(&vdev->pdev.msix_table_mmio,
- (hwaddr)(data & 0xfff),
- (uint64_t)quirk->data.address_mask,
- size);
+ memory_region_dispatch_write(&vdev->pdev.msix_table_mmio,
+ (hwaddr)(data & 0xfff),
+ (uint64_t)quirk->data.address_mask,
+ size, MEMTXATTRS_UNSPECIFIED);
}
quirk->data.flags = 1;
@@ -2246,6 +2250,33 @@ static int vfio_early_setup_msix(VFIOPCIDevice *vdev)
vdev->msix->pba_offset = pba & ~PCI_MSIX_FLAGS_BIRMASK;
vdev->msix->entries = (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1;
+ /*
+ * Test the size of the pba_offset variable and catch if it extends outside
+ * of the specified BAR. If it is the case, we need to apply a hardware
+ * specific quirk if the device is known or we have a broken configuration.
+ */
+ if (vdev->msix->pba_offset >=
+ vdev->bars[vdev->msix->pba_bar].region.size) {
+
+ PCIDevice *pdev = &vdev->pdev;
+ uint16_t vendor = pci_get_word(pdev->config + PCI_VENDOR_ID);
+ uint16_t device = pci_get_word(pdev->config + PCI_DEVICE_ID);
+
+ /*
+ * Chelsio T5 Virtual Function devices are encoded as 0x58xx for T5
+ * adapters. The T5 hardware returns an incorrect value of 0x8000 for
+ * the VF PBA offset while the BAR itself is only 8k. The correct value
+ * is 0x1000, so we hard code that here.
+ */
+ if (vendor == PCI_VENDOR_ID_CHELSIO && (device & 0xff00) == 0x5800) {
+ vdev->msix->pba_offset = 0x1000;
+ } else {
+ error_report("vfio: Hardware reports invalid configuration, "
+ "MSIX PBA outside of specified BAR");
+ return -EINVAL;
+ }
+ }
+
trace_vfio_early_setup_msix(vdev->vbasedev.name, pos,
vdev->msix->table_bar,
vdev->msix->table_offset,
@@ -2382,7 +2413,7 @@ static void vfio_map_bar(VFIOPCIDevice *vdev, int nr)
* potentially insert a direct-mapped subregion before and after it.
*/
if (vdev->msix && vdev->msix->table_bar == nr) {
- size = vdev->msix->table_offset & qemu_host_page_mask;
+ size = vdev->msix->table_offset & qemu_real_host_page_mask;
}
strncat(name, " mmap", sizeof(name) - strlen(name) - 1);
@@ -2395,8 +2426,9 @@ static void vfio_map_bar(VFIOPCIDevice *vdev, int nr)
if (vdev->msix && vdev->msix->table_bar == nr) {
uint64_t start;
- start = HOST_PAGE_ALIGN(vdev->msix->table_offset +
- (vdev->msix->entries * PCI_MSIX_ENTRY_SIZE));
+ start = REAL_HOST_PAGE_ALIGN((uint64_t)vdev->msix->table_offset +
+ (vdev->msix->entries *
+ PCI_MSIX_ENTRY_SIZE));
size = start < bar->region.size ? bar->region.size - start : 0;
strncat(name, " msix-hi", sizeof(name) - strlen(name) - 1);
@@ -3320,6 +3352,162 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev)
vdev->req_enabled = false;
}
+/*
+ * AMD Radeon PCI config reset, based on Linux:
+ * drivers/gpu/drm/radeon/ci_smc.c:ci_is_smc_running()
+ * drivers/gpu/drm/radeon/radeon_device.c:radeon_pci_config_reset
+ * drivers/gpu/drm/radeon/ci_smc.c:ci_reset_smc()
+ * drivers/gpu/drm/radeon/ci_smc.c:ci_stop_smc_clock()
+ * IDs: include/drm/drm_pciids.h
+ * Registers: http://cgit.freedesktop.org/~agd5f/linux/commit/?id=4e2aa447f6f0
+ *
+ * Bonaire and Hawaii GPUs do not respond to a bus reset. This is a bug in the
+ * hardware that should be fixed on future ASICs. The symptom of this is that
+ * once the accerlated driver loads, Windows guests will bsod on subsequent
+ * attmpts to load the driver, such as after VM reset or shutdown/restart. To
+ * work around this, we do an AMD specific PCI config reset, followed by an SMC
+ * reset. The PCI config reset only works if SMC firmware is running, so we
+ * have a dependency on the state of the device as to whether this reset will
+ * be effective. There are still cases where we won't be able to kick the
+ * device into working, but this greatly improves the usability overall. The
+ * config reset magic is relatively common on AMD GPUs, but the setup and SMC
+ * poking is largely ASIC specific.
+ */
+static bool vfio_radeon_smc_is_running(VFIOPCIDevice *vdev)
+{
+ uint32_t clk, pc_c;
+
+ /*
+ * Registers 200h and 204h are index and data registers for acessing
+ * indirect configuration registers within the device.
+ */
+ vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000004, 4);
+ clk = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
+ vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000370, 4);
+ pc_c = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
+
+ return (!(clk & 1) && (0x20100 <= pc_c));
+}
+
+/*
+ * The scope of a config reset is controlled by a mode bit in the misc register
+ * and a fuse, exposed as a bit in another register. The fuse is the default
+ * (0 = GFX, 1 = whole GPU), the misc bit is a toggle, with the forumula
+ * scope = !(misc ^ fuse), where the resulting scope is defined the same as
+ * the fuse. A truth table therefore tells us that if misc == fuse, we need
+ * to flip the value of the bit in the misc register.
+ */
+static void vfio_radeon_set_gfx_only_reset(VFIOPCIDevice *vdev)
+{
+ uint32_t misc, fuse;
+ bool a, b;
+
+ vfio_region_write(&vdev->bars[5].region, 0x200, 0xc00c0000, 4);
+ fuse = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
+ b = fuse & 64;
+
+ vfio_region_write(&vdev->bars[5].region, 0x200, 0xc0000010, 4);
+ misc = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
+ a = misc & 2;
+
+ if (a == b) {
+ vfio_region_write(&vdev->bars[5].region, 0x204, misc ^ 2, 4);
+ vfio_region_read(&vdev->bars[5].region, 0x204, 4); /* flush */
+ }
+}
+
+static int vfio_radeon_reset(VFIOPCIDevice *vdev)
+{
+ PCIDevice *pdev = &vdev->pdev;
+ int i, ret = 0;
+ uint32_t data;
+
+ /* Defer to a kernel implemented reset */
+ if (vdev->vbasedev.reset_works) {
+ return -ENODEV;
+ }
+
+ /* Enable only memory BAR access */
+ vfio_pci_write_config(pdev, PCI_COMMAND, PCI_COMMAND_MEMORY, 2);
+
+ /* Reset only works if SMC firmware is loaded and running */
+ if (!vfio_radeon_smc_is_running(vdev)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Make sure only the GFX function is reset */
+ vfio_radeon_set_gfx_only_reset(vdev);
+
+ /* AMD PCI config reset */
+ vfio_pci_write_config(pdev, 0x7c, 0x39d5e86b, 4);
+ usleep(100);
+
+ /* Read back the memory size to make sure we're out of reset */
+ for (i = 0; i < 100000; i++) {
+ if (vfio_region_read(&vdev->bars[5].region, 0x5428, 4) != 0xffffffff) {
+ break;
+ }
+ usleep(1);
+ }
+
+ /* Reset SMC */
+ vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000000, 4);
+ data = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
+ data |= 1;
+ vfio_region_write(&vdev->bars[5].region, 0x204, data, 4);
+
+ /* Disable SMC clock */
+ vfio_region_write(&vdev->bars[5].region, 0x200, 0x80000004, 4);
+ data = vfio_region_read(&vdev->bars[5].region, 0x204, 4);
+ data |= 1;
+ vfio_region_write(&vdev->bars[5].region, 0x204, data, 4);
+
+out:
+ /* Restore PCI command register */
+ vfio_pci_write_config(pdev, PCI_COMMAND, 0, 2);
+
+ return ret;
+}
+
+static void vfio_setup_resetfn(VFIOPCIDevice *vdev)
+{
+ PCIDevice *pdev = &vdev->pdev;
+ uint16_t vendor, device;
+
+ vendor = pci_get_word(pdev->config + PCI_VENDOR_ID);
+ device = pci_get_word(pdev->config + PCI_DEVICE_ID);
+
+ switch (vendor) {
+ case 0x1002:
+ switch (device) {
+ /* Bonaire */
+ case 0x6649: /* Bonaire [FirePro W5100] */
+ case 0x6650:
+ case 0x6651:
+ case 0x6658: /* Bonaire XTX [Radeon R7 260X] */
+ case 0x665c: /* Bonaire XT [Radeon HD 7790/8770 / R9 260 OEM] */
+ case 0x665d: /* Bonaire [Radeon R7 200 Series] */
+ /* Hawaii */
+ case 0x67A0: /* Hawaii XT GL [FirePro W9100] */
+ case 0x67A1: /* Hawaii PRO GL [FirePro W8100] */
+ case 0x67A2:
+ case 0x67A8:
+ case 0x67A9:
+ case 0x67AA:
+ case 0x67B0: /* Hawaii XT [Radeon R9 290X] */
+ case 0x67B1: /* Hawaii PRO [Radeon R9 290] */
+ case 0x67B8:
+ case 0x67B9:
+ case 0x67BA:
+ case 0x67BE:
+ vdev->resetfn = vfio_radeon_reset;
+ break;
+ }
+ break;
+ }
+}
+
static int vfio_initfn(PCIDevice *pdev)
{
VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev);
@@ -3353,7 +3541,7 @@ static int vfio_initfn(PCIDevice *pdev)
len = readlink(path, iommu_group_path, sizeof(path));
if (len <= 0 || len >= sizeof(path)) {
error_report("vfio: error no iommu_group for device");
- return len < 0 ? -errno : ENAMETOOLONG;
+ return len < 0 ? -errno : -ENAMETOOLONG;
}
iommu_group_path[len] = 0;
@@ -3468,6 +3656,7 @@ static int vfio_initfn(PCIDevice *pdev)
vfio_register_err_notifier(vdev);
vfio_register_req_notifier(vdev);
+ vfio_setup_resetfn(vdev);
return 0;
@@ -3515,6 +3704,10 @@ static void vfio_pci_reset(DeviceState *dev)
vfio_pci_pre_reset(vdev);
+ if (vdev->resetfn && !vdev->resetfn(vdev)) {
+ goto post_reset;
+ }
+
if (vdev->vbasedev.reset_works &&
(vdev->has_flr || !vdev->has_pm_reset) &&
!ioctl(vdev->vbasedev.fd, VFIO_DEVICE_RESET)) {
diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c
new file mode 100644
index 000000000..60365d127
--- /dev/null
+++ b/hw/vfio/platform.c
@@ -0,0 +1,715 @@
+/*
+ * vfio based device assignment support - platform devices
+ *
+ * Copyright Linaro Limited, 2014
+ *
+ * Authors:
+ * Kim Phillips <kim.phillips@linaro.org>
+ * Eric Auger <eric.auger@linaro.org>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ * Based on vfio based PCI device assignment support:
+ * Copyright Red Hat, Inc. 2012
+ */
+
+#include <sys/ioctl.h>
+#include <linux/vfio.h>
+
+#include "hw/vfio/vfio-platform.h"
+#include "qemu/error-report.h"
+#include "qemu/range.h"
+#include "sysemu/sysemu.h"
+#include "exec/memory.h"
+#include "qemu/queue.h"
+#include "hw/sysbus.h"
+#include "trace.h"
+#include "hw/platform-bus.h"
+#include "sysemu/kvm.h"
+
+/*
+ * Functions used whatever the injection method
+ */
+
+/**
+ * vfio_init_intp - allocate, initialize the IRQ struct pointer
+ * and add it into the list of IRQs
+ * @vbasedev: the VFIO device handle
+ * @info: irq info struct retrieved from VFIO driver
+ */
+static VFIOINTp *vfio_init_intp(VFIODevice *vbasedev,
+ struct vfio_irq_info info)
+{
+ int ret;
+ VFIOPlatformDevice *vdev =
+ container_of(vbasedev, VFIOPlatformDevice, vbasedev);
+ SysBusDevice *sbdev = SYS_BUS_DEVICE(vdev);
+ VFIOINTp *intp;
+
+ intp = g_malloc0(sizeof(*intp));
+ intp->vdev = vdev;
+ intp->pin = info.index;
+ intp->flags = info.flags;
+ intp->state = VFIO_IRQ_INACTIVE;
+ intp->kvm_accel = false;
+
+ sysbus_init_irq(sbdev, &intp->qemuirq);
+
+ /* Get an eventfd for trigger */
+ ret = event_notifier_init(&intp->interrupt, 0);
+ if (ret) {
+ g_free(intp);
+ error_report("vfio: Error: trigger event_notifier_init failed ");
+ return NULL;
+ }
+ /* Get an eventfd for resample/unmask */
+ ret = event_notifier_init(&intp->unmask, 0);
+ if (ret) {
+ g_free(intp);
+ error_report("vfio: Error: resamplefd event_notifier_init failed");
+ return NULL;
+ }
+
+ QLIST_INSERT_HEAD(&vdev->intp_list, intp, next);
+ return intp;
+}
+
+/**
+ * vfio_set_trigger_eventfd - set VFIO eventfd handling
+ *
+ * @intp: IRQ struct handle
+ * @handler: handler to be called on eventfd signaling
+ *
+ * Setup VFIO signaling and attach an optional user-side handler
+ * to the eventfd
+ */
+static int vfio_set_trigger_eventfd(VFIOINTp *intp,
+ eventfd_user_side_handler_t handler)
+{
+ VFIODevice *vbasedev = &intp->vdev->vbasedev;
+ struct vfio_irq_set *irq_set;
+ int argsz, ret;
+ int32_t *pfd;
+
+ argsz = sizeof(*irq_set) + sizeof(*pfd);
+ 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 = intp->pin;
+ irq_set->start = 0;
+ irq_set->count = 1;
+ pfd = (int32_t *)&irq_set->data;
+ *pfd = event_notifier_get_fd(&intp->interrupt);
+ qemu_set_fd_handler(*pfd, (IOHandler *)handler, NULL, intp);
+ ret = ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irq_set);
+ g_free(irq_set);
+ if (ret < 0) {
+ error_report("vfio: Failed to set trigger eventfd: %m");
+ qemu_set_fd_handler(*pfd, NULL, NULL, NULL);
+ }
+ return ret;
+}
+
+/*
+ * Functions only used when eventfds are handled on user-side
+ * ie. without irqfd
+ */
+
+/**
+ * vfio_mmap_set_enabled - enable/disable the fast path mode
+ * @vdev: the VFIO platform device
+ * @enabled: the target mmap state
+ *
+ * enabled = true ~ fast path = MMIO region is mmaped (no KVM TRAP);
+ * enabled = false ~ slow path = MMIO region is trapped and region callbacks
+ * are called; slow path enables to trap the device IRQ status register reset
+*/
+
+static void vfio_mmap_set_enabled(VFIOPlatformDevice *vdev, bool enabled)
+{
+ int i;
+
+ trace_vfio_platform_mmap_set_enabled(enabled);
+
+ for (i = 0; i < vdev->vbasedev.num_regions; i++) {
+ VFIORegion *region = vdev->regions[i];
+
+ memory_region_set_enabled(&region->mmap_mem, enabled);
+ }
+}
+
+/**
+ * vfio_intp_mmap_enable - timer function, restores the fast path
+ * if there is no more active IRQ
+ * @opaque: actually points to the VFIO platform device
+ *
+ * Called on mmap timer timout, this function checks whether the
+ * IRQ is still active and if not, restores the fast path.
+ * by construction a single eventfd is handled at a time.
+ * if the IRQ is still active, the timer is re-programmed.
+ */
+static void vfio_intp_mmap_enable(void *opaque)
+{
+ VFIOINTp *tmp;
+ VFIOPlatformDevice *vdev = (VFIOPlatformDevice *)opaque;
+
+ qemu_mutex_lock(&vdev->intp_mutex);
+ QLIST_FOREACH(tmp, &vdev->intp_list, next) {
+ if (tmp->state == VFIO_IRQ_ACTIVE) {
+ trace_vfio_platform_intp_mmap_enable(tmp->pin);
+ /* re-program the timer to check active status later */
+ timer_mod(vdev->mmap_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
+ vdev->mmap_timeout);
+ qemu_mutex_unlock(&vdev->intp_mutex);
+ return;
+ }
+ }
+ vfio_mmap_set_enabled(vdev, true);
+ qemu_mutex_unlock(&vdev->intp_mutex);
+}
+
+/**
+ * vfio_intp_inject_pending_lockheld - Injects a pending IRQ
+ * @opaque: opaque pointer, in practice the VFIOINTp handle
+ *
+ * The function is called on a previous IRQ completion, from
+ * vfio_platform_eoi, while the intp_mutex is locked.
+ * Also in such situation, the slow path already is set and
+ * the mmap timer was already programmed.
+ */
+static void vfio_intp_inject_pending_lockheld(VFIOINTp *intp)
+{
+ trace_vfio_platform_intp_inject_pending_lockheld(intp->pin,
+ event_notifier_get_fd(&intp->interrupt));
+
+ intp->state = VFIO_IRQ_ACTIVE;
+
+ /* trigger the virtual IRQ */
+ qemu_set_irq(intp->qemuirq, 1);
+}
+
+/**
+ * vfio_intp_interrupt - The user-side eventfd handler
+ * @opaque: opaque pointer which in practice is the VFIOINTp handle
+ *
+ * the function is entered in event handler context:
+ * the vIRQ is injected into the guest if there is no other active
+ * or pending IRQ.
+ */
+static void vfio_intp_interrupt(VFIOINTp *intp)
+{
+ int ret;
+ VFIOINTp *tmp;
+ VFIOPlatformDevice *vdev = intp->vdev;
+ bool delay_handling = false;
+
+ qemu_mutex_lock(&vdev->intp_mutex);
+ if (intp->state == VFIO_IRQ_INACTIVE) {
+ QLIST_FOREACH(tmp, &vdev->intp_list, next) {
+ if (tmp->state == VFIO_IRQ_ACTIVE ||
+ tmp->state == VFIO_IRQ_PENDING) {
+ delay_handling = true;
+ break;
+ }
+ }
+ }
+ if (delay_handling) {
+ /*
+ * the new IRQ gets a pending status and is pushed in
+ * the pending queue
+ */
+ intp->state = VFIO_IRQ_PENDING;
+ trace_vfio_intp_interrupt_set_pending(intp->pin);
+ QSIMPLEQ_INSERT_TAIL(&vdev->pending_intp_queue,
+ intp, pqnext);
+ ret = event_notifier_test_and_clear(&intp->interrupt);
+ qemu_mutex_unlock(&vdev->intp_mutex);
+ return;
+ }
+
+ trace_vfio_platform_intp_interrupt(intp->pin,
+ event_notifier_get_fd(&intp->interrupt));
+
+ ret = event_notifier_test_and_clear(&intp->interrupt);
+ if (!ret) {
+ error_report("Error when clearing fd=%d (ret = %d)\n",
+ event_notifier_get_fd(&intp->interrupt), ret);
+ }
+
+ intp->state = VFIO_IRQ_ACTIVE;
+
+ /* sets slow path */
+ vfio_mmap_set_enabled(vdev, false);
+
+ /* trigger the virtual IRQ */
+ qemu_set_irq(intp->qemuirq, 1);
+
+ /*
+ * Schedule the mmap timer which will restore fastpath when no IRQ
+ * is active anymore
+ */
+ if (vdev->mmap_timeout) {
+ timer_mod(vdev->mmap_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
+ vdev->mmap_timeout);
+ }
+ qemu_mutex_unlock(&vdev->intp_mutex);
+}
+
+/**
+ * vfio_platform_eoi - IRQ completion routine
+ * @vbasedev: the VFIO device handle
+ *
+ * De-asserts the active virtual IRQ and unmasks the physical IRQ
+ * (effective for level sensitive IRQ auto-masked by the VFIO driver).
+ * Then it handles next pending IRQ if any.
+ * eoi function is called on the first access to any MMIO region
+ * after an IRQ was triggered, trapped since slow path was set.
+ * It is assumed this access corresponds to the IRQ status
+ * register reset. With such a mechanism, a single IRQ can be
+ * handled at a time since there is no way to know which IRQ
+ * was completed by the guest (we would need additional details
+ * about the IRQ status register mask).
+ */
+static void vfio_platform_eoi(VFIODevice *vbasedev)
+{
+ VFIOINTp *intp;
+ VFIOPlatformDevice *vdev =
+ container_of(vbasedev, VFIOPlatformDevice, vbasedev);
+
+ qemu_mutex_lock(&vdev->intp_mutex);
+ QLIST_FOREACH(intp, &vdev->intp_list, next) {
+ if (intp->state == VFIO_IRQ_ACTIVE) {
+ trace_vfio_platform_eoi(intp->pin,
+ event_notifier_get_fd(&intp->interrupt));
+ intp->state = VFIO_IRQ_INACTIVE;
+
+ /* deassert the virtual IRQ */
+ qemu_set_irq(intp->qemuirq, 0);
+
+ if (intp->flags & VFIO_IRQ_INFO_AUTOMASKED) {
+ /* unmasks the physical level-sensitive IRQ */
+ vfio_unmask_single_irqindex(vbasedev, intp->pin);
+ }
+
+ /* a single IRQ can be active at a time */
+ break;
+ }
+ }
+ /* in case there are pending IRQs, handle the first one */
+ if (!QSIMPLEQ_EMPTY(&vdev->pending_intp_queue)) {
+ intp = QSIMPLEQ_FIRST(&vdev->pending_intp_queue);
+ vfio_intp_inject_pending_lockheld(intp);
+ QSIMPLEQ_REMOVE_HEAD(&vdev->pending_intp_queue, pqnext);
+ }
+ qemu_mutex_unlock(&vdev->intp_mutex);
+}
+
+/**
+ * vfio_start_eventfd_injection - starts the virtual IRQ injection using
+ * user-side handled eventfds
+ * @intp: the IRQ struct pointer
+ */
+
+static int vfio_start_eventfd_injection(VFIOINTp *intp)
+{
+ int ret;
+
+ ret = vfio_set_trigger_eventfd(intp, vfio_intp_interrupt);
+ if (ret) {
+ error_report("vfio: Error: Failed to pass IRQ fd to the driver: %m");
+ }
+ return ret;
+}
+
+/*
+ * Functions used for irqfd
+ */
+
+/**
+ * vfio_set_resample_eventfd - sets the resamplefd for an IRQ
+ * @intp: the IRQ struct handle
+ * programs the VFIO driver to unmask this IRQ when the
+ * intp->unmask eventfd is triggered
+ */
+static int vfio_set_resample_eventfd(VFIOINTp *intp)
+{
+ VFIODevice *vbasedev = &intp->vdev->vbasedev;
+ struct vfio_irq_set *irq_set;
+ int argsz, ret;
+ int32_t *pfd;
+
+ argsz = sizeof(*irq_set) + sizeof(*pfd);
+ irq_set = g_malloc0(argsz);
+ irq_set->argsz = argsz;
+ irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_UNMASK;
+ irq_set->index = intp->pin;
+ irq_set->start = 0;
+ irq_set->count = 1;
+ pfd = (int32_t *)&irq_set->data;
+ *pfd = event_notifier_get_fd(&intp->unmask);
+ qemu_set_fd_handler(*pfd, NULL, NULL, NULL);
+ ret = ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irq_set);
+ g_free(irq_set);
+ if (ret < 0) {
+ error_report("vfio: Failed to set resample eventfd: %m");
+ }
+ return ret;
+}
+
+static void vfio_start_irqfd_injection(SysBusDevice *sbdev, qemu_irq irq)
+{
+ VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev);
+ VFIOINTp *intp;
+
+ if (!kvm_irqfds_enabled() || !kvm_resamplefds_enabled() ||
+ !vdev->irqfd_allowed) {
+ return;
+ }
+
+ QLIST_FOREACH(intp, &vdev->intp_list, next) {
+ if (intp->qemuirq == irq) {
+ break;
+ }
+ }
+ assert(intp);
+
+ /* Get to a known interrupt state */
+ qemu_set_fd_handler(event_notifier_get_fd(&intp->interrupt),
+ NULL, NULL, vdev);
+
+ vfio_mask_single_irqindex(&vdev->vbasedev, intp->pin);
+ qemu_set_irq(intp->qemuirq, 0);
+
+ if (kvm_irqchip_add_irqfd_notifier(kvm_state, &intp->interrupt,
+ &intp->unmask, irq) < 0) {
+ goto fail_irqfd;
+ }
+
+ if (vfio_set_trigger_eventfd(intp, NULL) < 0) {
+ goto fail_vfio;
+ }
+ if (vfio_set_resample_eventfd(intp) < 0) {
+ goto fail_vfio;
+ }
+
+ /* Let's resume injection with irqfd setup */
+ vfio_unmask_single_irqindex(&vdev->vbasedev, intp->pin);
+
+ intp->kvm_accel = true;
+
+ trace_vfio_platform_start_irqfd_injection(intp->pin,
+ event_notifier_get_fd(&intp->interrupt),
+ event_notifier_get_fd(&intp->unmask));
+ return;
+fail_vfio:
+ kvm_irqchip_remove_irqfd_notifier(kvm_state, &intp->interrupt, irq);
+fail_irqfd:
+ vfio_start_eventfd_injection(intp);
+ vfio_unmask_single_irqindex(&vdev->vbasedev, intp->pin);
+ return;
+}
+
+/* VFIO skeleton */
+
+static void vfio_platform_compute_needs_reset(VFIODevice *vbasedev)
+{
+ vbasedev->needs_reset = true;
+}
+
+/* not implemented yet */
+static int vfio_platform_hot_reset_multi(VFIODevice *vbasedev)
+{
+ return -1;
+}
+
+/**
+ * vfio_populate_device - Allocate and populate MMIO region
+ * and IRQ structs according to driver returned information
+ * @vbasedev: the VFIO device handle
+ *
+ */
+static int vfio_populate_device(VFIODevice *vbasedev)
+{
+ VFIOINTp *intp, *tmp;
+ int i, ret = -1;
+ VFIOPlatformDevice *vdev =
+ container_of(vbasedev, VFIOPlatformDevice, vbasedev);
+
+ if (!(vbasedev->flags & VFIO_DEVICE_FLAGS_PLATFORM)) {
+ error_report("vfio: Um, this isn't a platform device");
+ return ret;
+ }
+
+ vdev->regions = g_new0(VFIORegion *, vbasedev->num_regions);
+
+ for (i = 0; i < vbasedev->num_regions; i++) {
+ struct vfio_region_info reg_info = { .argsz = sizeof(reg_info) };
+ VFIORegion *ptr;
+
+ vdev->regions[i] = g_malloc0(sizeof(VFIORegion));
+ ptr = vdev->regions[i];
+ reg_info.index = i;
+ ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, &reg_info);
+ if (ret) {
+ error_report("vfio: Error getting region %d info: %m", i);
+ goto reg_error;
+ }
+ ptr->flags = reg_info.flags;
+ ptr->size = reg_info.size;
+ ptr->fd_offset = reg_info.offset;
+ ptr->nr = i;
+ ptr->vbasedev = vbasedev;
+
+ trace_vfio_platform_populate_regions(ptr->nr,
+ (unsigned long)ptr->flags,
+ (unsigned long)ptr->size,
+ ptr->vbasedev->fd,
+ (unsigned long)ptr->fd_offset);
+ }
+
+ vdev->mmap_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
+ vfio_intp_mmap_enable, vdev);
+
+ QSIMPLEQ_INIT(&vdev->pending_intp_queue);
+
+ for (i = 0; i < vbasedev->num_irqs; i++) {
+ struct vfio_irq_info irq = { .argsz = sizeof(irq) };
+
+ irq.index = i;
+ ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_IRQ_INFO, &irq);
+ if (ret) {
+ error_printf("vfio: error getting device %s irq info",
+ vbasedev->name);
+ goto irq_err;
+ } else {
+ trace_vfio_platform_populate_interrupts(irq.index,
+ irq.count,
+ irq.flags);
+ intp = vfio_init_intp(vbasedev, irq);
+ if (!intp) {
+ error_report("vfio: Error installing IRQ %d up", i);
+ goto irq_err;
+ }
+ }
+ }
+ return 0;
+irq_err:
+ timer_del(vdev->mmap_timer);
+ QLIST_FOREACH_SAFE(intp, &vdev->intp_list, next, tmp) {
+ QLIST_REMOVE(intp, next);
+ g_free(intp);
+ }
+reg_error:
+ for (i = 0; i < vbasedev->num_regions; i++) {
+ g_free(vdev->regions[i]);
+ }
+ g_free(vdev->regions);
+ return ret;
+}
+
+/* specialized functions for VFIO Platform devices */
+static VFIODeviceOps vfio_platform_ops = {
+ .vfio_compute_needs_reset = vfio_platform_compute_needs_reset,
+ .vfio_hot_reset_multi = vfio_platform_hot_reset_multi,
+ .vfio_eoi = vfio_platform_eoi,
+};
+
+/**
+ * vfio_base_device_init - perform preliminary VFIO setup
+ * @vbasedev: the VFIO device handle
+ *
+ * Implement the VFIO command sequence that allows to discover
+ * assigned device resources: group extraction, device
+ * fd retrieval, resource query.
+ * Precondition: the device name must be initialized
+ */
+static int vfio_base_device_init(VFIODevice *vbasedev)
+{
+ VFIOGroup *group;
+ VFIODevice *vbasedev_iter;
+ char path[PATH_MAX], iommu_group_path[PATH_MAX], *group_name;
+ ssize_t len;
+ struct stat st;
+ int groupid;
+ int ret;
+
+ /* name must be set prior to the call */
+ if (!vbasedev->name || strchr(vbasedev->name, '/')) {
+ return -EINVAL;
+ }
+
+ /* Check that the host device exists */
+ g_snprintf(path, sizeof(path), "/sys/bus/platform/devices/%s/",
+ vbasedev->name);
+
+ if (stat(path, &st) < 0) {
+ error_report("vfio: error: no such host device: %s", path);
+ return -errno;
+ }
+
+ g_strlcat(path, "iommu_group", sizeof(path));
+ len = readlink(path, iommu_group_path, sizeof(iommu_group_path));
+ if (len < 0 || len >= sizeof(iommu_group_path)) {
+ error_report("vfio: error no iommu_group for device");
+ return len < 0 ? -errno : -ENAMETOOLONG;
+ }
+
+ 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", path);
+ return -errno;
+ }
+
+ trace_vfio_platform_base_device_init(vbasedev->name, groupid);
+
+ group = vfio_get_group(groupid, &address_space_memory);
+ if (!group) {
+ error_report("vfio: failed to get group %d", groupid);
+ return -ENOENT;
+ }
+
+ g_snprintf(path, sizeof(path), "%s", vbasedev->name);
+
+ QLIST_FOREACH(vbasedev_iter, &group->device_list, next) {
+ if (strcmp(vbasedev_iter->name, vbasedev->name) == 0) {
+ error_report("vfio: error: device %s is already attached", path);
+ vfio_put_group(group);
+ return -EBUSY;
+ }
+ }
+ ret = vfio_get_device(group, path, vbasedev);
+ if (ret) {
+ error_report("vfio: failed to get device %s", path);
+ vfio_put_group(group);
+ return ret;
+ }
+
+ ret = vfio_populate_device(vbasedev);
+ if (ret) {
+ error_report("vfio: failed to populate device %s", path);
+ vfio_put_group(group);
+ }
+
+ return ret;
+}
+
+/**
+ * vfio_map_region - initialize the 2 memory regions for a given
+ * MMIO region index
+ * @vdev: the VFIO platform device handle
+ * @nr: the index of the region
+ *
+ * Init the top memory region and the mmapped memory region beneath
+ * VFIOPlatformDevice is used since VFIODevice is not a QOM Object
+ * and could not be passed to memory region functions
+*/
+static void vfio_map_region(VFIOPlatformDevice *vdev, int nr)
+{
+ VFIORegion *region = vdev->regions[nr];
+ uint64_t size = region->size;
+ char name[64];
+
+ if (!size) {
+ return;
+ }
+
+ g_snprintf(name, sizeof(name), "VFIO %s region %d",
+ vdev->vbasedev.name, nr);
+
+ /* A "slow" read/write mapping underlies all regions */
+ memory_region_init_io(&region->mem, OBJECT(vdev), &vfio_region_ops,
+ region, name, size);
+
+ g_strlcat(name, " mmap", sizeof(name));
+
+ if (vfio_mmap_region(OBJECT(vdev), region, &region->mem,
+ &region->mmap_mem, &region->mmap, size, 0, name)) {
+ error_report("%s unsupported. Performance may be slow", name);
+ }
+}
+
+/**
+ * vfio_platform_realize - the device realize function
+ * @dev: device state pointer
+ * @errp: error
+ *
+ * initialize the device, its memory regions and IRQ structures
+ * IRQ are started separately
+ */
+static void vfio_platform_realize(DeviceState *dev, Error **errp)
+{
+ VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev);
+ SysBusDevice *sbdev = SYS_BUS_DEVICE(dev);
+ VFIODevice *vbasedev = &vdev->vbasedev;
+ VFIOINTp *intp;
+ int i, ret;
+
+ vbasedev->type = VFIO_DEVICE_TYPE_PLATFORM;
+ vbasedev->ops = &vfio_platform_ops;
+
+ trace_vfio_platform_realize(vbasedev->name, vdev->compat);
+
+ ret = vfio_base_device_init(vbasedev);
+ if (ret) {
+ error_setg(errp, "vfio: vfio_base_device_init failed for %s",
+ vbasedev->name);
+ return;
+ }
+
+ for (i = 0; i < vbasedev->num_regions; i++) {
+ vfio_map_region(vdev, i);
+ sysbus_init_mmio(sbdev, &vdev->regions[i]->mem);
+ }
+
+ QLIST_FOREACH(intp, &vdev->intp_list, next) {
+ vfio_start_eventfd_injection(intp);
+ }
+}
+
+static const VMStateDescription vfio_platform_vmstate = {
+ .name = TYPE_VFIO_PLATFORM,
+ .unmigratable = 1,
+};
+
+static Property vfio_platform_dev_properties[] = {
+ DEFINE_PROP_STRING("host", VFIOPlatformDevice, vbasedev.name),
+ DEFINE_PROP_BOOL("x-mmap", VFIOPlatformDevice, vbasedev.allow_mmap, true),
+ DEFINE_PROP_UINT32("mmap-timeout-ms", VFIOPlatformDevice,
+ mmap_timeout, 1100),
+ DEFINE_PROP_BOOL("x-irqfd", VFIOPlatformDevice, irqfd_allowed, true),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vfio_platform_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
+
+ dc->realize = vfio_platform_realize;
+ dc->props = vfio_platform_dev_properties;
+ dc->vmsd = &vfio_platform_vmstate;
+ dc->desc = "VFIO-based platform device assignment";
+ sbc->connect_irq_notifier = vfio_start_irqfd_injection;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+}
+
+static const TypeInfo vfio_platform_dev_info = {
+ .name = TYPE_VFIO_PLATFORM,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(VFIOPlatformDevice),
+ .class_init = vfio_platform_class_init,
+ .class_size = sizeof(VFIOPlatformDeviceClass),
+ .abstract = true,
+};
+
+static void register_vfio_platform_dev_type(void)
+{
+ type_register_static(&vfio_platform_dev_info);
+}
+
+type_init(register_vfio_platform_dev_type)
diff --git a/hw/virtio/dataplane/vring.c b/hw/virtio/dataplane/vring.c
index 5c7b8c20f..68f199443 100644
--- a/hw/virtio/dataplane/vring.c
+++ b/hw/virtio/dataplane/vring.c
@@ -42,7 +42,7 @@ static void *vring_map(MemoryRegion **mr, hwaddr phys, hwaddr len,
}
/* Ignore regions with dirty logging, we cannot mark them dirty */
- if (memory_region_is_logging(section.mr)) {
+ if (memory_region_get_dirty_log_mask(section.mr)) {
goto out;
}
@@ -67,22 +67,53 @@ static void vring_unmap(void *buffer, bool is_write)
/* Map the guest's vring to host memory */
bool vring_setup(Vring *vring, VirtIODevice *vdev, int n)
{
- hwaddr vring_addr = virtio_queue_get_ring_addr(vdev, n);
- hwaddr vring_size = virtio_queue_get_ring_size(vdev, n);
- void *vring_ptr;
+ struct vring *vr = &vring->vr;
+ hwaddr addr;
+ hwaddr size;
+ void *ptr;
vring->broken = false;
-
- vring_ptr = vring_map(&vring->mr, vring_addr, vring_size, true);
- if (!vring_ptr) {
- error_report("Failed to map vring "
- "addr %#" HWADDR_PRIx " size %" HWADDR_PRIu,
- vring_addr, vring_size);
- vring->broken = true;
- return false;
+ vr->num = virtio_queue_get_num(vdev, n);
+
+ addr = virtio_queue_get_desc_addr(vdev, n);
+ size = virtio_queue_get_desc_size(vdev, n);
+ /* Map the descriptor area as read only */
+ ptr = vring_map(&vring->mr_desc, addr, size, false);
+ if (!ptr) {
+ error_report("Failed to map 0x%" HWADDR_PRIx " byte for vring desc "
+ "at 0x%" HWADDR_PRIx,
+ size, addr);
+ goto out_err_desc;
}
-
- vring_init(&vring->vr, virtio_queue_get_num(vdev, n), vring_ptr, 4096);
+ vr->desc = ptr;
+
+ addr = virtio_queue_get_avail_addr(vdev, n);
+ size = virtio_queue_get_avail_size(vdev, n);
+ /* Add the size of the used_event_idx */
+ size += sizeof(uint16_t);
+ /* Map the driver area as read only */
+ ptr = vring_map(&vring->mr_avail, addr, size, false);
+ if (!ptr) {
+ error_report("Failed to map 0x%" HWADDR_PRIx " byte for vring avail "
+ "at 0x%" HWADDR_PRIx,
+ size, addr);
+ goto out_err_avail;
+ }
+ vr->avail = ptr;
+
+ addr = virtio_queue_get_used_addr(vdev, n);
+ size = virtio_queue_get_used_size(vdev, n);
+ /* Add the size of the avail_event_idx */
+ size += sizeof(uint16_t);
+ /* Map the device area as read-write */
+ ptr = vring_map(&vring->mr_used, addr, size, true);
+ if (!ptr) {
+ error_report("Failed to map 0x%" HWADDR_PRIx " byte for vring used "
+ "at 0x%" HWADDR_PRIx,
+ size, addr);
+ goto out_err_used;
+ }
+ vr->used = ptr;
vring->last_avail_idx = virtio_queue_get_last_avail_idx(vdev, n);
vring->last_used_idx = vring_get_used_idx(vdev, vring);
@@ -92,6 +123,14 @@ bool vring_setup(Vring *vring, VirtIODevice *vdev, int n)
trace_vring_setup(virtio_queue_get_ring_addr(vdev, n),
vring->vr.desc, vring->vr.avail, vring->vr.used);
return true;
+
+out_err_used:
+ memory_region_unref(vring->mr_avail);
+out_err_avail:
+ memory_region_unref(vring->mr_desc);
+out_err_desc:
+ vring->broken = true;
+ return false;
}
void vring_teardown(Vring *vring, VirtIODevice *vdev, int n)
@@ -99,13 +138,15 @@ void vring_teardown(Vring *vring, VirtIODevice *vdev, int n)
virtio_queue_set_last_avail_idx(vdev, n, vring->last_avail_idx);
virtio_queue_invalidate_signalled_used(vdev, n);
- memory_region_unref(vring->mr);
+ memory_region_unref(vring->mr_desc);
+ memory_region_unref(vring->mr_avail);
+ memory_region_unref(vring->mr_used);
}
/* Disable guest->host notifies */
void vring_disable_notification(VirtIODevice *vdev, Vring *vring)
{
- if (!virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
+ if (!virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
vring_set_used_flags(vdev, vring, VRING_USED_F_NO_NOTIFY);
}
}
@@ -116,7 +157,7 @@ void vring_disable_notification(VirtIODevice *vdev, Vring *vring)
*/
bool vring_enable_notification(VirtIODevice *vdev, Vring *vring)
{
- if (virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
+ if (virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
vring_avail_event(&vring->vr) = vring->vr.avail->idx;
} else {
vring_clear_used_flags(vdev, vring, VRING_USED_F_NO_NOTIFY);
@@ -135,12 +176,12 @@ bool vring_should_notify(VirtIODevice *vdev, Vring *vring)
* interrupts. */
smp_mb();
- if (virtio_has_feature(vdev, VIRTIO_F_NOTIFY_ON_EMPTY) &&
+ if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFY_ON_EMPTY) &&
unlikely(!vring_more_avail(vdev, vring))) {
return true;
}
- if (!virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
+ if (!virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
return !(vring_get_avail_flags(vdev, vring) &
VRING_AVAIL_F_NO_INTERRUPT);
}
@@ -153,7 +194,8 @@ bool vring_should_notify(VirtIODevice *vdev, Vring *vring)
return true;
}
- return vring_need_event(vring_used_event(&vring->vr), new, old);
+ return vring_need_event(virtio_tswap16(vdev, vring_used_event(&vring->vr)),
+ new, old);
}
@@ -401,8 +443,9 @@ int vring_pop(VirtIODevice *vdev, Vring *vring,
/* On success, increment avail index. */
vring->last_avail_idx++;
- if (virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
- vring_avail_event(&vring->vr) = vring->last_avail_idx;
+ if (virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
+ vring_avail_event(&vring->vr) =
+ virtio_tswap16(vdev, vring->last_avail_idx);
}
return head;
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index aefe0bbaa..e7ab8293d 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -128,7 +128,7 @@ static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg)
r = qemu_chr_fe_read_all(chr, p, size);
if (r != size) {
- error_report("Failed to read msg header. Read %d instead of %d.\n", r,
+ error_report("Failed to read msg header. Read %d instead of %d.", r,
size);
goto fail;
}
@@ -136,7 +136,7 @@ static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg)
/* validate received flags */
if (msg->flags != (VHOST_USER_REPLY_MASK | VHOST_USER_VERSION)) {
error_report("Failed to read msg header."
- " Flags 0x%x instead of 0x%x.\n", msg->flags,
+ " Flags 0x%x instead of 0x%x.", msg->flags,
VHOST_USER_REPLY_MASK | VHOST_USER_VERSION);
goto fail;
}
@@ -144,7 +144,7 @@ static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg)
/* validate message size is sane */
if (msg->size > VHOST_USER_PAYLOAD_SIZE) {
error_report("Failed to read msg header."
- " Size %d exceeds the maximum %zu.\n", msg->size,
+ " Size %d exceeds the maximum %zu.", msg->size,
VHOST_USER_PAYLOAD_SIZE);
goto fail;
}
@@ -155,7 +155,7 @@ static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg)
r = qemu_chr_fe_read_all(chr, p, size);
if (r != size) {
error_report("Failed to read msg payload."
- " Read %d instead of %d.\n", r, msg->size);
+ " Read %d instead of %d.", r, msg->size);
goto fail;
}
}
@@ -235,8 +235,8 @@ static int vhost_user_call(struct vhost_dev *dev, unsigned long int request,
msg.memory.nregions = fd_num;
if (!fd_num) {
- error_report("Failed initializing vhost-user memory map\n"
- "consider using -object memory-backend-file share=on\n");
+ error_report("Failed initializing vhost-user memory map, "
+ "consider using -object memory-backend-file share=on");
return -1;
}
@@ -280,7 +280,7 @@ static int vhost_user_call(struct vhost_dev *dev, unsigned long int request,
}
break;
default:
- error_report("vhost-user trying to send unhandled ioctl\n");
+ error_report("vhost-user trying to send unhandled ioctl");
return -1;
break;
}
@@ -296,27 +296,27 @@ static int vhost_user_call(struct vhost_dev *dev, unsigned long int request,
if (msg_request != msg.request) {
error_report("Received unexpected msg type."
- " Expected %d received %d\n", msg_request, msg.request);
+ " Expected %d received %d", msg_request, msg.request);
return -1;
}
switch (msg_request) {
case VHOST_USER_GET_FEATURES:
if (msg.size != sizeof(m.u64)) {
- error_report("Received bad msg size.\n");
+ error_report("Received bad msg size.");
return -1;
}
*((__u64 *) arg) = msg.u64;
break;
case VHOST_USER_GET_VRING_BASE:
if (msg.size != sizeof(m.state)) {
- error_report("Received bad msg size.\n");
+ error_report("Received bad msg size.");
return -1;
}
memcpy(arg, &msg.state, sizeof(struct vhost_vring_state));
break;
default:
- error_report("Received unexpected msg type.\n");
+ error_report("Received unexpected msg type.");
return -1;
break;
}
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index a7858d34a..a08c36bb4 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -17,20 +17,26 @@
#include "hw/hw.h"
#include "qemu/atomic.h"
#include "qemu/range.h"
+#include "qemu/error-report.h"
#include <linux/vhost.h>
#include "exec/address-spaces.h"
#include "hw/virtio/virtio-bus.h"
+#include "hw/virtio/virtio-access.h"
#include "migration/migration.h"
+static struct vhost_log *vhost_log;
+
static void vhost_dev_sync_region(struct vhost_dev *dev,
MemoryRegionSection *section,
uint64_t mfirst, uint64_t mlast,
uint64_t rfirst, uint64_t rlast)
{
+ vhost_log_chunk_t *log = dev->log->log;
+
uint64_t start = MAX(mfirst, rfirst);
uint64_t end = MIN(mlast, rlast);
- vhost_log_chunk_t *from = dev->log + start / VHOST_LOG_CHUNK;
- vhost_log_chunk_t *to = dev->log + end / VHOST_LOG_CHUNK + 1;
+ vhost_log_chunk_t *from = log + start / VHOST_LOG_CHUNK;
+ vhost_log_chunk_t *to = log + end / VHOST_LOG_CHUNK + 1;
uint64_t addr = (start / VHOST_LOG_CHUNK) * VHOST_LOG_CHUNK;
if (end < start) {
@@ -280,22 +286,57 @@ static uint64_t vhost_get_log_size(struct vhost_dev *dev)
}
return log_size;
}
+static struct vhost_log *vhost_log_alloc(uint64_t size)
+{
+ struct vhost_log *log = g_malloc0(sizeof *log + size * sizeof(*(log->log)));
+
+ log->size = size;
+ log->refcnt = 1;
+
+ return log;
+}
+
+static struct vhost_log *vhost_log_get(uint64_t size)
+{
+ if (!vhost_log || vhost_log->size != size) {
+ vhost_log = vhost_log_alloc(size);
+ } else {
+ ++vhost_log->refcnt;
+ }
+
+ return vhost_log;
+}
+
+static void vhost_log_put(struct vhost_dev *dev, bool sync)
+{
+ struct vhost_log *log = dev->log;
+
+ if (!log) {
+ return;
+ }
+
+ --log->refcnt;
+ if (log->refcnt == 0) {
+ /* Sync only the range covered by the old log */
+ if (dev->log_size && sync) {
+ vhost_log_sync_range(dev, 0, dev->log_size * VHOST_LOG_CHUNK - 1);
+ }
+ if (vhost_log == log) {
+ vhost_log = NULL;
+ }
+ g_free(log);
+ }
+}
static inline void vhost_dev_log_resize(struct vhost_dev* dev, uint64_t size)
{
- vhost_log_chunk_t *log;
- uint64_t log_base;
+ struct vhost_log *log = vhost_log_get(size);
+ uint64_t log_base = (uintptr_t)log->log;
int r;
- log = g_malloc0(size * sizeof *log);
- log_base = (uintptr_t)log;
r = dev->vhost_ops->vhost_call(dev, VHOST_SET_LOG_BASE, &log_base);
assert(r >= 0);
- /* Sync only the range covered by the old log */
- if (dev->log_size) {
- vhost_log_sync_range(dev, 0, dev->log_size * VHOST_LOG_CHUNK - 1);
- }
- g_free(dev->log);
+ vhost_log_put(dev, true);
dev->log = log;
dev->log_size = size;
}
@@ -377,7 +418,8 @@ static void vhost_set_memory(MemoryListener *listener,
memory_listener);
hwaddr start_addr = section->offset_within_address_space;
ram_addr_t size = int128_get64(section->size);
- bool log_dirty = memory_region_is_logging(section->mr);
+ bool log_dirty =
+ memory_region_get_dirty_log_mask(section->mr) & ~(1 << DIRTY_MEMORY_MIGRATION);
int s = offsetof(struct vhost_memory, regions) +
(dev->mem->nregions + 1) * sizeof dev->mem->regions[0];
void *ram;
@@ -551,7 +593,7 @@ static int vhost_dev_set_features(struct vhost_dev *dev, bool enable_log)
uint64_t features = dev->acked_features;
int r;
if (enable_log) {
- features |= 0x1 << VHOST_F_LOG_ALL;
+ features |= 0x1ULL << VHOST_F_LOG_ALL;
}
r = dev->vhost_ops->vhost_call(dev, VHOST_SET_FEATURES, &features);
return r < 0 ? -errno : 0;
@@ -601,7 +643,7 @@ static int vhost_migration_log(MemoryListener *listener, int enable)
if (r < 0) {
return r;
}
- g_free(dev->log);
+ vhost_log_put(dev, false);
dev->log = NULL;
dev->log_size = 0;
} else {
@@ -636,17 +678,40 @@ static void vhost_log_global_stop(MemoryListener *listener)
}
static void vhost_log_start(MemoryListener *listener,
- MemoryRegionSection *section)
+ MemoryRegionSection *section,
+ int old, int new)
{
/* FIXME: implement */
}
static void vhost_log_stop(MemoryListener *listener,
- MemoryRegionSection *section)
+ MemoryRegionSection *section,
+ int old, int new)
{
/* FIXME: implement */
}
+static int vhost_virtqueue_set_vring_endian_legacy(struct vhost_dev *dev,
+ bool is_big_endian,
+ int vhost_vq_index)
+{
+ struct vhost_vring_state s = {
+ .index = vhost_vq_index,
+ .num = is_big_endian
+ };
+
+ if (!dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_ENDIAN, &s)) {
+ return 0;
+ }
+
+ if (errno == ENOTTY) {
+ error_report("vhost does not support cross-endian");
+ return -ENOSYS;
+ }
+
+ return -errno;
+}
+
static int vhost_virtqueue_start(struct vhost_dev *dev,
struct VirtIODevice *vdev,
struct vhost_virtqueue *vq,
@@ -677,6 +742,16 @@ static int vhost_virtqueue_start(struct vhost_dev *dev,
return -errno;
}
+ if (!virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1) &&
+ virtio_legacy_is_cross_endian(vdev)) {
+ r = vhost_virtqueue_set_vring_endian_legacy(dev,
+ virtio_is_big_endian(vdev),
+ vhost_vq_index);
+ if (r) {
+ return -errno;
+ }
+ }
+
s = l = virtio_queue_get_desc_size(vdev, idx);
a = virtio_queue_get_desc_addr(vdev, idx);
vq->desc = cpu_physical_memory_map(a, &l, 0);
@@ -747,8 +822,9 @@ static void vhost_virtqueue_stop(struct vhost_dev *dev,
struct vhost_virtqueue *vq,
unsigned idx)
{
+ int vhost_vq_index = idx - dev->vq_index;
struct vhost_vring_state state = {
- .index = idx - dev->vq_index
+ .index = vhost_vq_index,
};
int r;
assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs);
@@ -759,6 +835,20 @@ static void vhost_virtqueue_stop(struct vhost_dev *dev,
}
virtio_queue_set_last_avail_idx(vdev, idx, state.num);
virtio_queue_invalidate_signalled_used(vdev, idx);
+
+ /* In the cross-endian case, we need to reset the vring endianness to
+ * native as legacy devices expect so by default.
+ */
+ if (!virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1) &&
+ virtio_legacy_is_cross_endian(vdev)) {
+ r = vhost_virtqueue_set_vring_endian_legacy(dev,
+ !virtio_is_big_endian(vdev),
+ vhost_vq_index);
+ if (r < 0) {
+ error_report("failed to reset vring endianness");
+ }
+ }
+
assert (r >= 0);
cpu_physical_memory_unmap(vq->ring, virtio_queue_get_ring_size(vdev, idx),
0, virtio_queue_get_ring_size(vdev, idx));
@@ -811,7 +901,7 @@ static void vhost_virtqueue_cleanup(struct vhost_virtqueue *vq)
}
int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
- VhostBackendType backend_type, bool force)
+ VhostBackendType backend_type)
{
uint64_t features;
int i, r;
@@ -860,7 +950,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
.priority = 10
};
hdev->migration_blocker = NULL;
- if (!(hdev->features & (0x1 << VHOST_F_LOG_ALL))) {
+ if (!(hdev->features & (0x1ULL << VHOST_F_LOG_ALL))) {
error_setg(&hdev->migration_blocker,
"Migration disabled: vhost lacks VHOST_F_LOG_ALL feature.");
migrate_add_blocker(hdev->migration_blocker);
@@ -874,7 +964,6 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
hdev->started = false;
hdev->memory_changed = false;
memory_listener_register(&hdev->memory_listener, &address_space_memory);
- hdev->force = force;
return 0;
fail_vq:
while (--i >= 0) {
@@ -902,17 +991,6 @@ void vhost_dev_cleanup(struct vhost_dev *hdev)
hdev->vhost_ops->vhost_backend_cleanup(hdev);
}
-bool vhost_dev_query(struct vhost_dev *hdev, VirtIODevice *vdev)
-{
- BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
- VirtioBusState *vbus = VIRTIO_BUS(qbus);
- VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus);
-
- return !k->query_guest_notifiers ||
- k->query_guest_notifiers(qbus->parent) ||
- hdev->force;
-}
-
/* Stop processing guest IO notifications in qemu.
* Start processing them in vhost in kernel.
*/
@@ -1003,12 +1081,12 @@ void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n,
assert(r >= 0);
}
-unsigned vhost_get_features(struct vhost_dev *hdev, const int *feature_bits,
- unsigned features)
+uint64_t vhost_get_features(struct vhost_dev *hdev, const int *feature_bits,
+ uint64_t features)
{
const int *bit = feature_bits;
while (*bit != VHOST_INVALID_FEATURE_BIT) {
- unsigned bit_mask = (1 << *bit);
+ uint64_t bit_mask = (1ULL << *bit);
if (!(hdev->features & bit_mask)) {
features &= ~bit_mask;
}
@@ -1018,11 +1096,11 @@ unsigned vhost_get_features(struct vhost_dev *hdev, const int *feature_bits,
}
void vhost_ack_features(struct vhost_dev *hdev, const int *feature_bits,
- unsigned features)
+ uint64_t features)
{
const int *bit = feature_bits;
while (*bit != VHOST_INVALID_FEATURE_BIT) {
- unsigned bit_mask = (1 << *bit);
+ uint64_t bit_mask = (1ULL << *bit);
if (features & bit_mask) {
hdev->acked_features |= bit_mask;
}
@@ -1060,10 +1138,10 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
uint64_t log_base;
hdev->log_size = vhost_get_log_size(hdev);
- hdev->log = hdev->log_size ?
- g_malloc0(hdev->log_size * sizeof *hdev->log) : NULL;
- log_base = (uintptr_t)hdev->log;
- r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_LOG_BASE, &log_base);
+ hdev->log = vhost_log_get(hdev->log_size);
+ log_base = (uintptr_t)hdev->log->log;
+ r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_LOG_BASE,
+ hdev->log_size ? &log_base : NULL);
if (r < 0) {
r = -errno;
goto fail_log;
@@ -1072,6 +1150,7 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
return 0;
fail_log:
+ vhost_log_put(hdev, false);
fail_vq:
while (--i >= 0) {
vhost_virtqueue_stop(hdev,
@@ -1098,10 +1177,9 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev)
hdev->vqs + i,
hdev->vq_index + i);
}
- vhost_log_sync_range(hdev, 0, ~0x0ull);
+ vhost_log_put(hdev, true);
hdev->started = false;
- g_free(hdev->log);
hdev->log = NULL;
hdev->log_size = 0;
}
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index 95b064344..c419b1714 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -70,7 +70,7 @@ static inline void reset_stats(VirtIOBalloon *dev)
static bool balloon_stats_supported(const VirtIOBalloon *s)
{
VirtIODevice *vdev = VIRTIO_DEVICE(s);
- return virtio_has_feature(vdev, VIRTIO_BALLOON_F_STATS_VQ);
+ return virtio_vdev_has_feature(vdev, VIRTIO_BALLOON_F_STATS_VQ);
}
static bool balloon_stats_enabled(const VirtIOBalloon *s)
@@ -310,9 +310,12 @@ static void virtio_balloon_set_config(VirtIODevice *vdev,
trace_virtio_balloon_set_config(dev->actual, oldactual);
}
-static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f)
+static uint64_t virtio_balloon_get_features(VirtIODevice *vdev, uint64_t f,
+ Error **errp)
{
- f |= (1 << VIRTIO_BALLOON_F_STATS_VQ);
+ VirtIOBalloon *dev = VIRTIO_BALLOON(vdev);
+ f |= dev->host_features;
+ virtio_add_feature(&f, VIRTIO_BALLOON_F_STATS_VQ);
return f;
}
@@ -383,7 +386,7 @@ static void virtio_balloon_device_realize(DeviceState *dev, Error **errp)
virtio_balloon_stat, s);
if (ret < 0) {
- error_setg(errp, "Adding balloon handler failed");
+ error_setg(errp, "Only one balloon device is supported");
virtio_cleanup(vdev);
return;
}
@@ -396,14 +399,6 @@ static void virtio_balloon_device_realize(DeviceState *dev, Error **errp)
register_savevm(dev, "virtio-balloon", -1, 1,
virtio_balloon_save, virtio_balloon_load, s);
-
- object_property_add(OBJECT(dev), "guest-stats", "guest statistics",
- balloon_stats_get_all, NULL, NULL, s, NULL);
-
- object_property_add(OBJECT(dev), "guest-stats-polling-interval", "int",
- balloon_stats_get_poll_interval,
- balloon_stats_set_poll_interval,
- NULL, s, NULL);
}
static void virtio_balloon_device_unrealize(DeviceState *dev, Error **errp)
@@ -417,7 +412,22 @@ static void virtio_balloon_device_unrealize(DeviceState *dev, Error **errp)
virtio_cleanup(vdev);
}
+static void virtio_balloon_instance_init(Object *obj)
+{
+ VirtIOBalloon *s = VIRTIO_BALLOON(obj);
+
+ object_property_add(obj, "guest-stats", "guest statistics",
+ balloon_stats_get_all, NULL, NULL, s, NULL);
+
+ object_property_add(obj, "guest-stats-polling-interval", "int",
+ balloon_stats_get_poll_interval,
+ balloon_stats_set_poll_interval,
+ NULL, s, NULL);
+}
+
static Property virtio_balloon_properties[] = {
+ DEFINE_PROP_BIT("deflate-on-oom", VirtIOBalloon, host_features,
+ VIRTIO_BALLOON_F_DEFLATE_ON_OOM, false),
DEFINE_PROP_END_OF_LIST(),
};
@@ -441,6 +451,7 @@ static const TypeInfo virtio_balloon_info = {
.name = TYPE_VIRTIO_BALLOON,
.parent = TYPE_VIRTIO_DEVICE,
.instance_size = sizeof(VirtIOBalloon),
+ .instance_init = virtio_balloon_instance_init,
.class_init = virtio_balloon_class_init,
};
diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c
index be886e75a..febda76b9 100644
--- a/hw/virtio/virtio-bus.c
+++ b/hw/virtio/virtio-bus.c
@@ -38,19 +38,24 @@ do { printf("virtio_bus: " fmt , ## __VA_ARGS__); } while (0)
#endif
/* A VirtIODevice is being plugged */
-int virtio_bus_device_plugged(VirtIODevice *vdev)
+void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp)
{
DeviceState *qdev = DEVICE(vdev);
BusState *qbus = BUS(qdev_get_parent_bus(qdev));
VirtioBusState *bus = VIRTIO_BUS(qbus);
VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus);
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
+
DPRINTF("%s: plug device.\n", qbus->name);
if (klass->device_plugged != NULL) {
- klass->device_plugged(qbus->parent);
+ klass->device_plugged(qbus->parent, errp);
}
- return 0;
+ /* Get the features of the plugged device. */
+ assert(vdc->get_features != NULL);
+ vdev->host_features = vdc->get_features(vdev, vdev->host_features,
+ errp);
}
/* Reset the virtio_bus */
@@ -96,19 +101,6 @@ size_t virtio_bus_get_vdev_config_len(VirtioBusState *bus)
return vdev->config_len;
}
-/* Get the features of the plugged device. */
-uint32_t virtio_bus_get_vdev_features(VirtioBusState *bus,
- uint32_t requested_features)
-{
- VirtIODevice *vdev = virtio_bus_get_device(bus);
- VirtioDeviceClass *k;
-
- assert(vdev != NULL);
- k = VIRTIO_DEVICE_GET_CLASS(vdev);
- assert(k->get_features != NULL);
- return k->get_features(vdev, requested_features);
-}
-
/* Get bad features of the plugged device. */
uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus)
{
diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c
index 10123f34b..18660b07b 100644
--- a/hw/virtio/virtio-mmio.c
+++ b/hw/virtio/virtio-mmio.c
@@ -22,7 +22,9 @@
#include "hw/sysbus.h"
#include "hw/virtio/virtio.h"
#include "qemu/host-utils.h"
+#include "sysemu/kvm.h"
#include "hw/virtio/virtio-bus.h"
+#include "qemu/error-report.h"
/* #define DEBUG_VIRTIO_MMIO */
@@ -80,15 +82,102 @@ typedef struct {
SysBusDevice parent_obj;
MemoryRegion iomem;
qemu_irq irq;
- uint32_t host_features;
/* Guest accessible state needing migration and reset */
uint32_t host_features_sel;
uint32_t guest_features_sel;
uint32_t guest_page_shift;
/* virtio-bus */
VirtioBusState bus;
+ bool ioeventfd_disabled;
+ bool ioeventfd_started;
} VirtIOMMIOProxy;
+static int virtio_mmio_set_host_notifier_internal(VirtIOMMIOProxy *proxy,
+ int n, bool assign,
+ bool set_handler)
+{
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ VirtQueue *vq = virtio_get_queue(vdev, n);
+ EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
+ int r = 0;
+
+ if (assign) {
+ r = event_notifier_init(notifier, 1);
+ if (r < 0) {
+ error_report("%s: unable to init event notifier: %d",
+ __func__, r);
+ return r;
+ }
+ virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler);
+ memory_region_add_eventfd(&proxy->iomem, VIRTIO_MMIO_QUEUENOTIFY, 4,
+ true, n, notifier);
+ } else {
+ memory_region_del_eventfd(&proxy->iomem, VIRTIO_MMIO_QUEUENOTIFY, 4,
+ true, n, notifier);
+ virtio_queue_set_host_notifier_fd_handler(vq, false, false);
+ event_notifier_cleanup(notifier);
+ }
+ return r;
+}
+
+static void virtio_mmio_start_ioeventfd(VirtIOMMIOProxy *proxy)
+{
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ int n, r;
+
+ if (!kvm_eventfds_enabled() ||
+ proxy->ioeventfd_disabled ||
+ proxy->ioeventfd_started) {
+ return;
+ }
+
+ for (n = 0; n < VIRTIO_QUEUE_MAX; n++) {
+ if (!virtio_queue_get_num(vdev, n)) {
+ continue;
+ }
+
+ r = virtio_mmio_set_host_notifier_internal(proxy, n, true, true);
+ if (r < 0) {
+ goto assign_error;
+ }
+ }
+ proxy->ioeventfd_started = true;
+ return;
+
+assign_error:
+ while (--n >= 0) {
+ if (!virtio_queue_get_num(vdev, n)) {
+ continue;
+ }
+
+ r = virtio_mmio_set_host_notifier_internal(proxy, n, false, false);
+ assert(r >= 0);
+ }
+ proxy->ioeventfd_started = false;
+ error_report("%s: failed. Fallback to a userspace (slower).", __func__);
+}
+
+static void virtio_mmio_stop_ioeventfd(VirtIOMMIOProxy *proxy)
+{
+ int r;
+ int n;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+
+ if (!proxy->ioeventfd_started) {
+ return;
+ }
+
+ for (n = 0; n < VIRTIO_QUEUE_MAX; n++) {
+ if (!virtio_queue_get_num(vdev, n)) {
+ continue;
+ }
+
+ r = virtio_mmio_set_host_notifier_internal(proxy, n, false, false);
+ assert(r >= 0);
+ }
+ proxy->ioeventfd_started = false;
+}
+
static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size)
{
VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque;
@@ -147,7 +236,7 @@ static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size)
if (proxy->host_features_sel) {
return 0;
}
- return proxy->host_features;
+ return vdev->host_features;
case VIRTIO_MMIO_QUEUENUMMAX:
if (!virtio_queue_get_num(vdev, vdev->queue_sel)) {
return 0;
@@ -237,15 +326,18 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value,
proxy->guest_page_shift);
break;
case VIRTIO_MMIO_QUEUESEL:
- if (value < VIRTIO_PCI_QUEUE_MAX) {
+ if (value < VIRTIO_QUEUE_MAX) {
vdev->queue_sel = value;
}
break;
case VIRTIO_MMIO_QUEUENUM:
DPRINTF("mmio_queue write %d max %d\n", (int)value, VIRTQUEUE_MAX_SIZE);
virtio_queue_set_num(vdev, vdev->queue_sel, value);
+ /* Note: only call this function for legacy devices */
+ virtio_queue_update_rings(vdev, vdev->queue_sel);
break;
case VIRTIO_MMIO_QUEUEALIGN:
+ /* Note: this is only valid for legacy devices */
virtio_queue_set_align(vdev, vdev->queue_sel, value);
break;
case VIRTIO_MMIO_QUEUEPFN:
@@ -257,7 +349,7 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value,
}
break;
case VIRTIO_MMIO_QUEUENOTIFY:
- if (value < VIRTIO_PCI_QUEUE_MAX) {
+ if (value < VIRTIO_QUEUE_MAX) {
virtio_queue_notify(vdev, value);
}
break;
@@ -266,7 +358,16 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value,
virtio_update_irq(vdev);
break;
case VIRTIO_MMIO_STATUS:
+ if (!(value & VIRTIO_CONFIG_S_DRIVER_OK)) {
+ virtio_mmio_stop_ioeventfd(proxy);
+ }
+
virtio_set_status(vdev, value & 0xff);
+
+ if (value & VIRTIO_CONFIG_S_DRIVER_OK) {
+ virtio_mmio_start_ioeventfd(proxy);
+ }
+
if (vdev->status == 0) {
virtio_reset(vdev);
}
@@ -306,13 +407,6 @@ static void virtio_mmio_update_irq(DeviceState *opaque, uint16_t vector)
qemu_set_irq(proxy->irq, level);
}
-static unsigned int virtio_mmio_get_features(DeviceState *opaque)
-{
- VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
-
- return proxy->host_features;
-}
-
static int virtio_mmio_load_config(DeviceState *opaque, QEMUFile *f)
{
VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
@@ -336,24 +430,94 @@ static void virtio_mmio_reset(DeviceState *d)
{
VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
+ virtio_mmio_stop_ioeventfd(proxy);
virtio_bus_reset(&proxy->bus);
proxy->host_features_sel = 0;
proxy->guest_features_sel = 0;
proxy->guest_page_shift = 0;
}
-/* virtio-mmio device */
+static int virtio_mmio_set_guest_notifier(DeviceState *d, int n, bool assign,
+ bool with_irqfd)
+{
+ VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
+ VirtQueue *vq = virtio_get_queue(vdev, n);
+ EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
+
+ if (assign) {
+ int r = event_notifier_init(notifier, 0);
+ if (r < 0) {
+ return r;
+ }
+ virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd);
+ } else {
+ virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd);
+ event_notifier_cleanup(notifier);
+ }
+
+ if (vdc->guest_notifier_mask) {
+ vdc->guest_notifier_mask(vdev, n, !assign);
+ }
-/* This is called by virtio-bus just after the device is plugged. */
-static void virtio_mmio_device_plugged(DeviceState *opaque)
+ return 0;
+}
+
+static int virtio_mmio_set_guest_notifiers(DeviceState *d, int nvqs,
+ bool assign)
+{
+ VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ /* TODO: need to check if kvm-arm supports irqfd */
+ bool with_irqfd = false;
+ int r, n;
+
+ nvqs = MIN(nvqs, VIRTIO_QUEUE_MAX);
+
+ for (n = 0; n < nvqs; n++) {
+ if (!virtio_queue_get_num(vdev, n)) {
+ break;
+ }
+
+ r = virtio_mmio_set_guest_notifier(d, n, assign, with_irqfd);
+ if (r < 0) {
+ goto assign_error;
+ }
+ }
+
+ return 0;
+
+assign_error:
+ /* We get here on assignment failure. Recover by undoing for VQs 0 .. n. */
+ assert(assign);
+ while (--n >= 0) {
+ virtio_mmio_set_guest_notifier(d, n, !assign, false);
+ }
+ return r;
+}
+
+static int virtio_mmio_set_host_notifier(DeviceState *opaque, int n,
+ bool assign)
{
VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
- virtio_add_feature(&proxy->host_features, VIRTIO_F_NOTIFY_ON_EMPTY);
- proxy->host_features = virtio_bus_get_vdev_features(&proxy->bus,
- proxy->host_features);
+ /* Stop using ioeventfd for virtqueue kick if the device starts using host
+ * notifiers. This makes it easy to avoid stepping on each others' toes.
+ */
+ proxy->ioeventfd_disabled = assign;
+ if (assign) {
+ virtio_mmio_stop_ioeventfd(proxy);
+ }
+ /* We don't need to start here: it's not needed because backend
+ * currently only stops on status change away from ok,
+ * reset, vmstop and such. If we do add code to start here,
+ * need to check vmstate, device state etc. */
+ return virtio_mmio_set_host_notifier_internal(proxy, n, assign, false);
}
+/* virtio-mmio device */
+
static void virtio_mmio_realizefn(DeviceState *d, Error **errp)
{
VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
@@ -367,16 +531,10 @@ static void virtio_mmio_realizefn(DeviceState *d, Error **errp)
sysbus_init_mmio(sbd, &proxy->iomem);
}
-static Property virtio_mmio_properties[] = {
- DEFINE_VIRTIO_COMMON_FEATURES(VirtIOMMIOProxy, host_features),
- DEFINE_PROP_END_OF_LIST(),
-};
-
static void virtio_mmio_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- dc->props = virtio_mmio_properties;
dc->realize = virtio_mmio_realizefn;
dc->reset = virtio_mmio_reset;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
@@ -399,8 +557,8 @@ static void virtio_mmio_bus_class_init(ObjectClass *klass, void *data)
k->notify = virtio_mmio_update_irq;
k->save_config = virtio_mmio_save_config;
k->load_config = virtio_mmio_load_config;
- k->get_features = virtio_mmio_get_features;
- k->device_plugged = virtio_mmio_device_plugged;
+ k->set_host_notifier = virtio_mmio_set_host_notifier;
+ k->set_guest_notifiers = virtio_mmio_set_guest_notifiers;
k->has_variable_vring_alignment = true;
bus_class->max_dev = 1;
}
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index c7c3f7249..c024161f5 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -24,6 +24,7 @@
#include "hw/virtio/virtio-serial.h"
#include "hw/virtio/virtio-scsi.h"
#include "hw/virtio/virtio-balloon.h"
+#include "hw/virtio/virtio-input.h"
#include "hw/pci/pci.h"
#include "qemu/error-report.h"
#include "hw/pci/msi.h"
@@ -38,6 +39,8 @@
#define VIRTIO_PCI_REGION_SIZE(dev) VIRTIO_PCI_CONFIG_OFF(msix_present(dev))
+#undef VIRTIO_PCI_CONFIG
+
/* The remaining space is defined by each driver as the per-driver
* configuration space */
#define VIRTIO_PCI_CONFIG_SIZE(dev) VIRTIO_PCI_CONFIG_OFF(msix_enabled(dev))
@@ -133,12 +136,21 @@ static int virtio_pci_load_queue(DeviceState *d, int n, QEMUFile *f)
return 0;
}
+#define QEMU_VIRTIO_PCI_QUEUE_MEM_MULT 0x1000
+
static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
int n, bool assign, bool set_handler)
{
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
VirtQueue *vq = virtio_get_queue(vdev, n);
EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
+ bool legacy = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_LEGACY);
+ bool modern = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN);
+ MemoryRegion *modern_mr = &proxy->notify.mr;
+ MemoryRegion *legacy_mr = &proxy->bar;
+ hwaddr modern_addr = QEMU_VIRTIO_PCI_QUEUE_MEM_MULT *
+ virtio_get_queue_index(vq);
+ hwaddr legacy_addr = VIRTIO_PCI_QUEUE_NOTIFY;
int r = 0;
if (assign) {
@@ -149,11 +161,23 @@ static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
return r;
}
virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler);
- memory_region_add_eventfd(&proxy->bar, VIRTIO_PCI_QUEUE_NOTIFY, 2,
- true, n, notifier);
+ if (modern) {
+ memory_region_add_eventfd(modern_mr, modern_addr, 2,
+ true, n, notifier);
+ }
+ if (legacy) {
+ memory_region_add_eventfd(legacy_mr, legacy_addr, 2,
+ true, n, notifier);
+ }
} else {
- memory_region_del_eventfd(&proxy->bar, VIRTIO_PCI_QUEUE_NOTIFY, 2,
- true, n, notifier);
+ if (modern) {
+ memory_region_del_eventfd(modern_mr, modern_addr, 2,
+ true, n, notifier);
+ }
+ if (legacy) {
+ memory_region_del_eventfd(legacy_mr, legacy_addr, 2,
+ true, n, notifier);
+ }
virtio_queue_set_host_notifier_fd_handler(vq, false, false);
event_notifier_cleanup(notifier);
}
@@ -171,7 +195,7 @@ static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy)
return;
}
- for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
+ for (n = 0; n < VIRTIO_QUEUE_MAX; n++) {
if (!virtio_queue_get_num(vdev, n)) {
continue;
}
@@ -207,7 +231,7 @@ static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy)
return;
}
- for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
+ for (n = 0; n < VIRTIO_QUEUE_MAX; n++) {
if (!virtio_queue_get_num(vdev, n)) {
continue;
}
@@ -243,11 +267,11 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
virtio_queue_set_addr(vdev, vdev->queue_sel, pa);
break;
case VIRTIO_PCI_QUEUE_SEL:
- if (val < VIRTIO_PCI_QUEUE_MAX)
+ if (val < VIRTIO_QUEUE_MAX)
vdev->queue_sel = val;
break;
case VIRTIO_PCI_QUEUE_NOTIFY:
- if (val < VIRTIO_PCI_QUEUE_MAX) {
+ if (val < VIRTIO_QUEUE_MAX) {
virtio_queue_notify(vdev, val);
}
break;
@@ -306,7 +330,7 @@ static uint32_t virtio_ioport_read(VirtIOPCIProxy *proxy, uint32_t addr)
switch (addr) {
case VIRTIO_PCI_HOST_FEATURES:
- ret = proxy->host_features;
+ ret = vdev->host_features;
break;
case VIRTIO_PCI_GUEST_FEATURES:
ret = vdev->guest_features;
@@ -419,11 +443,89 @@ static const MemoryRegionOps virtio_pci_config_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
+/* Below are generic functions to do memcpy from/to an address space,
+ * without byteswaps, with input validation.
+ *
+ * As regular address_space_* APIs all do some kind of byteswap at least for
+ * some host/target combinations, we are forced to explicitly convert to a
+ * known-endianness integer value.
+ * It doesn't really matter which endian format to go through, so the code
+ * below selects the endian that causes the least amount of work on the given
+ * host.
+ *
+ * Note: host pointer must be aligned.
+ */
+static
+void virtio_address_space_write(AddressSpace *as, hwaddr addr,
+ const uint8_t *buf, int len)
+{
+ uint32_t val;
+
+ /* address_space_* APIs assume an aligned address.
+ * As address is under guest control, handle illegal values.
+ */
+ addr &= ~(len - 1);
+
+ /* Make sure caller aligned buf properly */
+ assert(!(((uintptr_t)buf) & (len - 1)));
+
+ switch (len) {
+ case 1:
+ val = pci_get_byte(buf);
+ address_space_stb(as, addr, val, MEMTXATTRS_UNSPECIFIED, NULL);
+ break;
+ case 2:
+ val = pci_get_word(buf);
+ address_space_stw_le(as, addr, val, MEMTXATTRS_UNSPECIFIED, NULL);
+ break;
+ case 4:
+ val = pci_get_long(buf);
+ address_space_stl_le(as, addr, val, MEMTXATTRS_UNSPECIFIED, NULL);
+ break;
+ default:
+ /* As length is under guest control, handle illegal values. */
+ break;
+ }
+}
+
+static void
+virtio_address_space_read(AddressSpace *as, hwaddr addr, uint8_t *buf, int len)
+{
+ uint32_t val;
+
+ /* address_space_* APIs assume an aligned address.
+ * As address is under guest control, handle illegal values.
+ */
+ addr &= ~(len - 1);
+
+ /* Make sure caller aligned buf properly */
+ assert(!(((uintptr_t)buf) & (len - 1)));
+
+ switch (len) {
+ case 1:
+ val = address_space_ldub(as, addr, MEMTXATTRS_UNSPECIFIED, NULL);
+ pci_set_byte(buf, val);
+ break;
+ case 2:
+ val = address_space_lduw_le(as, addr, MEMTXATTRS_UNSPECIFIED, NULL);
+ pci_set_word(buf, val);
+ break;
+ case 4:
+ val = address_space_ldl_le(as, addr, MEMTXATTRS_UNSPECIFIED, NULL);
+ pci_set_long(buf, val);
+ break;
+ default:
+ /* As length is under guest control, handle illegal values. */
+ break;
+ }
+}
+
static void virtio_write_config(PCIDevice *pci_dev, uint32_t address,
uint32_t val, int len)
{
VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ struct virtio_pci_cfg_cap *cfg;
pci_default_write_config(pci_dev, address, val, len);
@@ -432,12 +534,51 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address,
virtio_pci_stop_ioeventfd(proxy);
virtio_set_status(vdev, vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK);
}
+
+ if (proxy->config_cap &&
+ ranges_overlap(address, len, proxy->config_cap + offsetof(struct virtio_pci_cfg_cap,
+ pci_cfg_data),
+ sizeof cfg->pci_cfg_data)) {
+ uint32_t off;
+ uint32_t len;
+
+ cfg = (void *)(proxy->pci_dev.config + proxy->config_cap);
+ off = le32_to_cpu(cfg->cap.offset);
+ len = le32_to_cpu(cfg->cap.length);
+
+ if (len == 1 || len == 2 || len == 4) {
+ assert(len <= sizeof cfg->pci_cfg_data);
+ virtio_address_space_write(&proxy->modern_as, off,
+ cfg->pci_cfg_data, len);
+ }
+ }
}
-static unsigned virtio_pci_get_features(DeviceState *d)
+static uint32_t virtio_read_config(PCIDevice *pci_dev,
+ uint32_t address, int len)
{
- VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
- return proxy->host_features;
+ VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+ struct virtio_pci_cfg_cap *cfg;
+
+ if (proxy->config_cap &&
+ ranges_overlap(address, len, proxy->config_cap + offsetof(struct virtio_pci_cfg_cap,
+ pci_cfg_data),
+ sizeof cfg->pci_cfg_data)) {
+ uint32_t off;
+ uint32_t len;
+
+ cfg = (void *)(proxy->pci_dev.config + proxy->config_cap);
+ off = le32_to_cpu(cfg->cap.offset);
+ len = le32_to_cpu(cfg->cap.length);
+
+ if (len == 1 || len == 2 || len == 4) {
+ assert(len <= sizeof cfg->pci_cfg_data);
+ virtio_address_space_read(&proxy->modern_as, off,
+ cfg->pci_cfg_data, len);
+ }
+ }
+
+ return pci_default_read_config(pci_dev, address, len);
}
static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy,
@@ -477,7 +618,7 @@ static int kvm_virtio_pci_irqfd_use(VirtIOPCIProxy *proxy,
VirtQueue *vq = virtio_get_queue(vdev, queue_no);
EventNotifier *n = virtio_queue_get_guest_notifier(vq);
int ret;
- ret = kvm_irqchip_add_irqfd_notifier(kvm_state, n, NULL, irqfd->virq);
+ ret = kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, irqfd->virq);
return ret;
}
@@ -491,7 +632,7 @@ static void kvm_virtio_pci_irqfd_release(VirtIOPCIProxy *proxy,
VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];
int ret;
- ret = kvm_irqchip_remove_irqfd_notifier(kvm_state, n, irqfd->virq);
+ ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n, irqfd->virq);
assert(ret == 0);
}
@@ -630,28 +771,35 @@ static int virtio_pci_vector_unmask(PCIDevice *dev, unsigned vector,
{
VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev);
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
- int ret, queue_no;
+ VirtQueue *vq = virtio_vector_first_queue(vdev, vector);
+ int ret, index, unmasked = 0;
- for (queue_no = 0; queue_no < proxy->nvqs_with_notifiers; queue_no++) {
- if (!virtio_queue_get_num(vdev, queue_no)) {
+ while (vq) {
+ index = virtio_get_queue_index(vq);
+ if (!virtio_queue_get_num(vdev, index)) {
break;
}
- if (virtio_queue_vector(vdev, queue_no) != vector) {
- continue;
- }
- ret = virtio_pci_vq_vector_unmask(proxy, queue_no, vector, msg);
- if (ret < 0) {
- goto undo;
+ if (index < proxy->nvqs_with_notifiers) {
+ ret = virtio_pci_vq_vector_unmask(proxy, index, vector, msg);
+ if (ret < 0) {
+ goto undo;
+ }
+ ++unmasked;
}
+ vq = virtio_vector_next_queue(vq);
}
+
return 0;
undo:
- while (--queue_no >= 0) {
- if (virtio_queue_vector(vdev, queue_no) != vector) {
- continue;
+ vq = virtio_vector_first_queue(vdev, vector);
+ while (vq && unmasked >= 0) {
+ index = virtio_get_queue_index(vq);
+ if (index < proxy->nvqs_with_notifiers) {
+ virtio_pci_vq_vector_mask(proxy, index, vector);
+ --unmasked;
}
- virtio_pci_vq_vector_mask(proxy, queue_no, vector);
+ vq = virtio_vector_next_queue(vq);
}
return ret;
}
@@ -660,16 +808,18 @@ static void virtio_pci_vector_mask(PCIDevice *dev, unsigned vector)
{
VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev);
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
- int queue_no;
+ VirtQueue *vq = virtio_vector_first_queue(vdev, vector);
+ int index;
- for (queue_no = 0; queue_no < proxy->nvqs_with_notifiers; queue_no++) {
- if (!virtio_queue_get_num(vdev, queue_no)) {
+ while (vq) {
+ index = virtio_get_queue_index(vq);
+ if (!virtio_queue_get_num(vdev, index)) {
break;
}
- if (virtio_queue_vector(vdev, queue_no) != vector) {
- continue;
+ if (index < proxy->nvqs_with_notifiers) {
+ virtio_pci_vq_vector_mask(proxy, index, vector);
}
- virtio_pci_vq_vector_mask(proxy, queue_no, vector);
+ vq = virtio_vector_next_queue(vq);
}
}
@@ -748,7 +898,7 @@ static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign)
bool with_irqfd = msix_enabled(&proxy->pci_dev) &&
kvm_msi_via_irqfd_enabled();
- nvqs = MIN(nvqs, VIRTIO_PCI_QUEUE_MAX);
+ nvqs = MIN(nvqs, VIRTIO_QUEUE_MAX);
/* When deassigning, pass a consistent nvqs value
* to avoid leaking notifiers.
@@ -908,74 +1058,548 @@ static const TypeInfo virtio_9p_pci_info = {
* virtio-pci: This is the PCIDevice which has a virtio-pci-bus.
*/
+static int virtio_pci_query_nvectors(DeviceState *d)
+{
+ VirtIOPCIProxy *proxy = VIRTIO_PCI(d);
+
+ return proxy->nvectors;
+}
+
+static int virtio_pci_add_mem_cap(VirtIOPCIProxy *proxy,
+ struct virtio_pci_cap *cap)
+{
+ PCIDevice *dev = &proxy->pci_dev;
+ int offset;
+
+ offset = pci_add_capability(dev, PCI_CAP_ID_VNDR, 0, cap->cap_len);
+ assert(offset > 0);
+
+ assert(cap->cap_len >= sizeof *cap);
+ memcpy(dev->config + offset + PCI_CAP_FLAGS, &cap->cap_len,
+ cap->cap_len - PCI_CAP_FLAGS);
+
+ return offset;
+}
+
+static uint64_t virtio_pci_common_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ VirtIOPCIProxy *proxy = opaque;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ uint32_t val = 0;
+ int i;
+
+ switch (addr) {
+ case VIRTIO_PCI_COMMON_DFSELECT:
+ val = proxy->dfselect;
+ break;
+ case VIRTIO_PCI_COMMON_DF:
+ if (proxy->dfselect <= 1) {
+ val = (vdev->host_features & ~VIRTIO_LEGACY_FEATURES) >>
+ (32 * proxy->dfselect);
+ }
+ break;
+ case VIRTIO_PCI_COMMON_GFSELECT:
+ val = proxy->gfselect;
+ break;
+ case VIRTIO_PCI_COMMON_GF:
+ if (proxy->gfselect < ARRAY_SIZE(proxy->guest_features)) {
+ val = proxy->guest_features[proxy->gfselect];
+ }
+ break;
+ case VIRTIO_PCI_COMMON_MSIX:
+ val = vdev->config_vector;
+ break;
+ case VIRTIO_PCI_COMMON_NUMQ:
+ for (i = 0; i < VIRTIO_QUEUE_MAX; ++i) {
+ if (virtio_queue_get_num(vdev, i)) {
+ val = i + 1;
+ }
+ }
+ break;
+ case VIRTIO_PCI_COMMON_STATUS:
+ val = vdev->status;
+ break;
+ case VIRTIO_PCI_COMMON_CFGGENERATION:
+ val = vdev->generation;
+ break;
+ case VIRTIO_PCI_COMMON_Q_SELECT:
+ val = vdev->queue_sel;
+ break;
+ case VIRTIO_PCI_COMMON_Q_SIZE:
+ val = virtio_queue_get_num(vdev, vdev->queue_sel);
+ break;
+ case VIRTIO_PCI_COMMON_Q_MSIX:
+ val = virtio_queue_vector(vdev, vdev->queue_sel);
+ break;
+ case VIRTIO_PCI_COMMON_Q_ENABLE:
+ val = proxy->vqs[vdev->queue_sel].enabled;
+ break;
+ case VIRTIO_PCI_COMMON_Q_NOFF:
+ /* Simply map queues in order */
+ val = vdev->queue_sel;
+ break;
+ case VIRTIO_PCI_COMMON_Q_DESCLO:
+ val = proxy->vqs[vdev->queue_sel].desc[0];
+ break;
+ case VIRTIO_PCI_COMMON_Q_DESCHI:
+ val = proxy->vqs[vdev->queue_sel].desc[1];
+ break;
+ case VIRTIO_PCI_COMMON_Q_AVAILLO:
+ val = proxy->vqs[vdev->queue_sel].avail[0];
+ break;
+ case VIRTIO_PCI_COMMON_Q_AVAILHI:
+ val = proxy->vqs[vdev->queue_sel].avail[1];
+ break;
+ case VIRTIO_PCI_COMMON_Q_USEDLO:
+ val = proxy->vqs[vdev->queue_sel].used[0];
+ break;
+ case VIRTIO_PCI_COMMON_Q_USEDHI:
+ val = proxy->vqs[vdev->queue_sel].used[1];
+ break;
+ default:
+ val = 0;
+ }
+
+ return val;
+}
+
+static void virtio_pci_common_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ VirtIOPCIProxy *proxy = opaque;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+
+ switch (addr) {
+ case VIRTIO_PCI_COMMON_DFSELECT:
+ proxy->dfselect = val;
+ break;
+ case VIRTIO_PCI_COMMON_GFSELECT:
+ proxy->gfselect = val;
+ break;
+ case VIRTIO_PCI_COMMON_GF:
+ if (proxy->gfselect < ARRAY_SIZE(proxy->guest_features)) {
+ proxy->guest_features[proxy->gfselect] = val;
+ virtio_set_features(vdev,
+ (((uint64_t)proxy->guest_features[1]) << 32) |
+ proxy->guest_features[0]);
+ }
+ break;
+ case VIRTIO_PCI_COMMON_MSIX:
+ msix_vector_unuse(&proxy->pci_dev, vdev->config_vector);
+ /* Make it possible for guest to discover an error took place. */
+ if (msix_vector_use(&proxy->pci_dev, val) < 0) {
+ val = VIRTIO_NO_VECTOR;
+ }
+ vdev->config_vector = val;
+ break;
+ case VIRTIO_PCI_COMMON_STATUS:
+ if (!(val & VIRTIO_CONFIG_S_DRIVER_OK)) {
+ virtio_pci_stop_ioeventfd(proxy);
+ }
+
+ virtio_set_status(vdev, val & 0xFF);
+
+ if (val & VIRTIO_CONFIG_S_DRIVER_OK) {
+ virtio_pci_start_ioeventfd(proxy);
+ }
+
+ if (vdev->status == 0) {
+ virtio_reset(vdev);
+ msix_unuse_all_vectors(&proxy->pci_dev);
+ }
+
+ break;
+ case VIRTIO_PCI_COMMON_Q_SELECT:
+ if (val < VIRTIO_QUEUE_MAX) {
+ vdev->queue_sel = val;
+ }
+ break;
+ case VIRTIO_PCI_COMMON_Q_SIZE:
+ proxy->vqs[vdev->queue_sel].num = val;
+ break;
+ case VIRTIO_PCI_COMMON_Q_MSIX:
+ msix_vector_unuse(&proxy->pci_dev,
+ virtio_queue_vector(vdev, vdev->queue_sel));
+ /* Make it possible for guest to discover an error took place. */
+ if (msix_vector_use(&proxy->pci_dev, val) < 0) {
+ val = VIRTIO_NO_VECTOR;
+ }
+ virtio_queue_set_vector(vdev, vdev->queue_sel, val);
+ break;
+ case VIRTIO_PCI_COMMON_Q_ENABLE:
+ /* TODO: need a way to put num back on reset. */
+ virtio_queue_set_num(vdev, vdev->queue_sel,
+ proxy->vqs[vdev->queue_sel].num);
+ virtio_queue_set_rings(vdev, vdev->queue_sel,
+ ((uint64_t)proxy->vqs[vdev->queue_sel].desc[1]) << 32 |
+ proxy->vqs[vdev->queue_sel].desc[0],
+ ((uint64_t)proxy->vqs[vdev->queue_sel].avail[1]) << 32 |
+ proxy->vqs[vdev->queue_sel].avail[0],
+ ((uint64_t)proxy->vqs[vdev->queue_sel].used[1]) << 32 |
+ proxy->vqs[vdev->queue_sel].used[0]);
+ break;
+ case VIRTIO_PCI_COMMON_Q_DESCLO:
+ proxy->vqs[vdev->queue_sel].desc[0] = val;
+ break;
+ case VIRTIO_PCI_COMMON_Q_DESCHI:
+ proxy->vqs[vdev->queue_sel].desc[1] = val;
+ break;
+ case VIRTIO_PCI_COMMON_Q_AVAILLO:
+ proxy->vqs[vdev->queue_sel].avail[0] = val;
+ break;
+ case VIRTIO_PCI_COMMON_Q_AVAILHI:
+ proxy->vqs[vdev->queue_sel].avail[1] = val;
+ break;
+ case VIRTIO_PCI_COMMON_Q_USEDLO:
+ proxy->vqs[vdev->queue_sel].used[0] = val;
+ break;
+ case VIRTIO_PCI_COMMON_Q_USEDHI:
+ proxy->vqs[vdev->queue_sel].used[1] = val;
+ break;
+ default:
+ break;
+ }
+}
+
+
+static uint64_t virtio_pci_notify_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ return 0;
+}
+
+static void virtio_pci_notify_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ VirtIODevice *vdev = opaque;
+ unsigned queue = addr / QEMU_VIRTIO_PCI_QUEUE_MEM_MULT;
+
+ if (queue < VIRTIO_QUEUE_MAX) {
+ virtio_queue_notify(vdev, queue);
+ }
+}
+
+static uint64_t virtio_pci_isr_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ VirtIOPCIProxy *proxy = opaque;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ uint64_t val = vdev->isr;
+
+ vdev->isr = 0;
+ pci_irq_deassert(&proxy->pci_dev);
+
+ return val;
+}
+
+static void virtio_pci_isr_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+}
+
+static uint64_t virtio_pci_device_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ VirtIODevice *vdev = opaque;
+ uint64_t val = 0;
+
+ switch (size) {
+ case 1:
+ val = virtio_config_modern_readb(vdev, addr);
+ break;
+ case 2:
+ val = virtio_config_modern_readw(vdev, addr);
+ break;
+ case 4:
+ val = virtio_config_modern_readl(vdev, addr);
+ break;
+ }
+ return val;
+}
+
+static void virtio_pci_device_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ VirtIODevice *vdev = opaque;
+ switch (size) {
+ case 1:
+ virtio_config_modern_writeb(vdev, addr, val);
+ break;
+ case 2:
+ virtio_config_modern_writew(vdev, addr, val);
+ break;
+ case 4:
+ virtio_config_modern_writel(vdev, addr, val);
+ break;
+ }
+}
+
+static void virtio_pci_modern_regions_init(VirtIOPCIProxy *proxy)
+{
+ static const MemoryRegionOps common_ops = {
+ .read = virtio_pci_common_read,
+ .write = virtio_pci_common_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ };
+ static const MemoryRegionOps isr_ops = {
+ .read = virtio_pci_isr_read,
+ .write = virtio_pci_isr_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ };
+ static const MemoryRegionOps device_ops = {
+ .read = virtio_pci_device_read,
+ .write = virtio_pci_device_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ };
+ static const MemoryRegionOps notify_ops = {
+ .read = virtio_pci_notify_read,
+ .write = virtio_pci_notify_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ };
+
+ memory_region_init_io(&proxy->common.mr, OBJECT(proxy),
+ &common_ops,
+ proxy,
+ "virtio-pci-common",
+ proxy->common.size);
+
+ memory_region_init_io(&proxy->isr.mr, OBJECT(proxy),
+ &isr_ops,
+ proxy,
+ "virtio-pci-isr",
+ proxy->isr.size);
+
+ memory_region_init_io(&proxy->device.mr, OBJECT(proxy),
+ &device_ops,
+ virtio_bus_get_device(&proxy->bus),
+ "virtio-pci-device",
+ proxy->device.size);
+
+ memory_region_init_io(&proxy->notify.mr, OBJECT(proxy),
+ &notify_ops,
+ virtio_bus_get_device(&proxy->bus),
+ "virtio-pci-notify",
+ proxy->notify.size);
+}
+
+static void virtio_pci_modern_region_map(VirtIOPCIProxy *proxy,
+ VirtIOPCIRegion *region,
+ struct virtio_pci_cap *cap)
+{
+ memory_region_add_subregion(&proxy->modern_bar,
+ region->offset,
+ &region->mr);
+
+ cap->cfg_type = region->type;
+ cap->bar = proxy->modern_mem_bar;
+ cap->offset = cpu_to_le32(region->offset);
+ cap->length = cpu_to_le32(region->size);
+ virtio_pci_add_mem_cap(proxy, cap);
+}
+
+static void virtio_pci_modern_region_unmap(VirtIOPCIProxy *proxy,
+ VirtIOPCIRegion *region)
+{
+ memory_region_del_subregion(&proxy->modern_bar,
+ &region->mr);
+}
+
/* This is called by virtio-bus just after the device is plugged. */
-static void virtio_pci_device_plugged(DeviceState *d)
+static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
{
VirtIOPCIProxy *proxy = VIRTIO_PCI(d);
VirtioBusState *bus = &proxy->bus;
+ bool legacy = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_LEGACY);
+ bool modern = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN);
uint8_t *config;
uint32_t size;
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
config = proxy->pci_dev.config;
if (proxy->class_code) {
pci_config_set_class(config, proxy->class_code);
}
- pci_set_word(config + PCI_SUBSYSTEM_VENDOR_ID,
- pci_get_word(config + PCI_VENDOR_ID));
- pci_set_word(config + PCI_SUBSYSTEM_ID, virtio_bus_get_vdev_id(bus));
+
+ if (legacy) {
+ /* legacy and transitional */
+ pci_set_word(config + PCI_SUBSYSTEM_VENDOR_ID,
+ pci_get_word(config + PCI_VENDOR_ID));
+ pci_set_word(config + PCI_SUBSYSTEM_ID, virtio_bus_get_vdev_id(bus));
+ } else {
+ /* pure virtio-1.0 */
+ pci_set_word(config + PCI_VENDOR_ID,
+ PCI_VENDOR_ID_REDHAT_QUMRANET);
+ pci_set_word(config + PCI_DEVICE_ID,
+ 0x1040 + virtio_bus_get_vdev_id(bus));
+ pci_config_set_revision(config, 1);
+ }
config[PCI_INTERRUPT_PIN] = 1;
+
+ if (modern) {
+ struct virtio_pci_cap cap = {
+ .cap_len = sizeof cap,
+ };
+ struct virtio_pci_notify_cap notify = {
+ .cap.cap_len = sizeof notify,
+ .notify_off_multiplier =
+ cpu_to_le32(QEMU_VIRTIO_PCI_QUEUE_MEM_MULT),
+ };
+ struct virtio_pci_cfg_cap cfg = {
+ .cap.cap_len = sizeof cfg,
+ .cap.cfg_type = VIRTIO_PCI_CAP_PCI_CFG,
+ };
+ struct virtio_pci_cfg_cap *cfg_mask;
+
+ /* TODO: add io access for speed */
+
+ virtio_add_feature(&vdev->host_features, VIRTIO_F_VERSION_1);
+ virtio_pci_modern_regions_init(proxy);
+ virtio_pci_modern_region_map(proxy, &proxy->common, &cap);
+ virtio_pci_modern_region_map(proxy, &proxy->isr, &cap);
+ virtio_pci_modern_region_map(proxy, &proxy->device, &cap);
+ virtio_pci_modern_region_map(proxy, &proxy->notify, &notify.cap);
+
+ pci_register_bar(&proxy->pci_dev, proxy->modern_mem_bar,
+ PCI_BASE_ADDRESS_SPACE_MEMORY |
+ PCI_BASE_ADDRESS_MEM_PREFETCH |
+ PCI_BASE_ADDRESS_MEM_TYPE_64,
+ &proxy->modern_bar);
+
+ proxy->config_cap = virtio_pci_add_mem_cap(proxy, &cfg.cap);
+ cfg_mask = (void *)(proxy->pci_dev.wmask + proxy->config_cap);
+ pci_set_byte(&cfg_mask->cap.bar, ~0x0);
+ pci_set_long((uint8_t *)&cfg_mask->cap.offset, ~0x0);
+ pci_set_long((uint8_t *)&cfg_mask->cap.length, ~0x0);
+ pci_set_long(cfg_mask->pci_cfg_data, ~0x0);
+ }
+
if (proxy->nvectors &&
- msix_init_exclusive_bar(&proxy->pci_dev, proxy->nvectors, 1)) {
+ msix_init_exclusive_bar(&proxy->pci_dev, proxy->nvectors,
+ proxy->msix_bar)) {
error_report("unable to init msix vectors to %" PRIu32,
proxy->nvectors);
proxy->nvectors = 0;
}
proxy->pci_dev.config_write = virtio_write_config;
+ proxy->pci_dev.config_read = virtio_read_config;
- size = VIRTIO_PCI_REGION_SIZE(&proxy->pci_dev)
- + virtio_bus_get_vdev_config_len(bus);
- if (size & (size - 1)) {
- size = 1 << qemu_fls(size);
- }
+ if (legacy) {
+ size = VIRTIO_PCI_REGION_SIZE(&proxy->pci_dev)
+ + virtio_bus_get_vdev_config_len(bus);
+ if (size & (size - 1)) {
+ size = 1 << qemu_fls(size);
+ }
- memory_region_init_io(&proxy->bar, OBJECT(proxy), &virtio_pci_config_ops,
- proxy, "virtio-pci", size);
- pci_register_bar(&proxy->pci_dev, 0, PCI_BASE_ADDRESS_SPACE_IO,
- &proxy->bar);
+ memory_region_init_io(&proxy->bar, OBJECT(proxy),
+ &virtio_pci_config_ops,
+ proxy, "virtio-pci", size);
+
+ pci_register_bar(&proxy->pci_dev, proxy->legacy_io_bar,
+ PCI_BASE_ADDRESS_SPACE_IO, &proxy->bar);
+ }
if (!kvm_has_many_ioeventfds()) {
proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD;
}
- virtio_add_feature(&proxy->host_features, VIRTIO_F_NOTIFY_ON_EMPTY);
- virtio_add_feature(&proxy->host_features, VIRTIO_F_BAD_FEATURE);
- proxy->host_features = virtio_bus_get_vdev_features(bus,
- proxy->host_features);
+ virtio_add_feature(&vdev->host_features, VIRTIO_F_BAD_FEATURE);
}
static void virtio_pci_device_unplugged(DeviceState *d)
{
VirtIOPCIProxy *proxy = VIRTIO_PCI(d);
+ bool modern = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN);
virtio_pci_stop_ioeventfd(proxy);
+
+ if (modern) {
+ virtio_pci_modern_region_unmap(proxy, &proxy->common);
+ virtio_pci_modern_region_unmap(proxy, &proxy->isr);
+ virtio_pci_modern_region_unmap(proxy, &proxy->device);
+ virtio_pci_modern_region_unmap(proxy, &proxy->notify);
+ }
}
static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
{
- VirtIOPCIProxy *dev = VIRTIO_PCI(pci_dev);
+ VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev);
VirtioPCIClass *k = VIRTIO_PCI_GET_CLASS(pci_dev);
- virtio_pci_bus_new(&dev->bus, sizeof(dev->bus), dev);
+ /*
+ * virtio pci bar layout used by default.
+ * subclasses can re-arrange things if needed.
+ *
+ * region 0 -- virtio legacy io bar
+ * region 1 -- msi-x bar
+ * region 4+5 -- virtio modern memory (64bit) bar
+ *
+ */
+ proxy->legacy_io_bar = 0;
+ proxy->msix_bar = 1;
+ proxy->modern_mem_bar = 4;
+
+ proxy->common.offset = 0x0;
+ proxy->common.size = 0x1000;
+ proxy->common.type = VIRTIO_PCI_CAP_COMMON_CFG;
+
+ proxy->isr.offset = 0x1000;
+ proxy->isr.size = 0x1000;
+ proxy->isr.type = VIRTIO_PCI_CAP_ISR_CFG;
+
+ proxy->device.offset = 0x2000;
+ proxy->device.size = 0x1000;
+ proxy->device.type = VIRTIO_PCI_CAP_DEVICE_CFG;
+
+ proxy->notify.offset = 0x3000;
+ proxy->notify.size =
+ QEMU_VIRTIO_PCI_QUEUE_MEM_MULT * VIRTIO_QUEUE_MAX;
+ proxy->notify.type = VIRTIO_PCI_CAP_NOTIFY_CFG;
+
+ /* subclasses can enforce modern, so do this unconditionally */
+ memory_region_init(&proxy->modern_bar, OBJECT(proxy), "virtio-pci",
+ 2 * QEMU_VIRTIO_PCI_QUEUE_MEM_MULT *
+ VIRTIO_QUEUE_MAX);
+
+ memory_region_init_alias(&proxy->modern_cfg,
+ OBJECT(proxy),
+ "virtio-pci-cfg",
+ &proxy->modern_bar,
+ 0,
+ memory_region_size(&proxy->modern_bar));
+
+ address_space_init(&proxy->modern_as, &proxy->modern_cfg, "virtio-pci-cfg-as");
+
+ virtio_pci_bus_new(&proxy->bus, sizeof(proxy->bus), proxy);
if (k->realize) {
- k->realize(dev, errp);
+ k->realize(proxy, errp);
}
}
static void virtio_pci_exit(PCIDevice *pci_dev)
{
+ VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev);
+
msix_uninit_exclusive_bar(pci_dev);
+ address_space_destroy(&proxy->modern_as);
}
static void virtio_pci_reset(DeviceState *qdev)
@@ -990,7 +1614,10 @@ static void virtio_pci_reset(DeviceState *qdev)
static Property virtio_pci_properties[] = {
DEFINE_PROP_BIT("virtio-pci-bus-master-bug-migration", VirtIOPCIProxy, flags,
VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT, false),
- DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
+ DEFINE_PROP_BIT("disable-legacy", VirtIOPCIProxy, flags,
+ VIRTIO_PCI_FLAG_DISABLE_LEGACY_BIT, false),
+ DEFINE_PROP_BIT("disable-modern", VirtIOPCIProxy, flags,
+ VIRTIO_PCI_FLAG_DISABLE_MODERN_BIT, true),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1078,7 +1705,6 @@ static Property virtio_scsi_pci_properties[] = {
VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
DEV_NVECTORS_UNSPECIFIED),
- DEFINE_VIRTIO_SCSI_FEATURES(VirtIOPCIProxy, host_features),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1199,32 +1825,6 @@ static const TypeInfo vhost_scsi_pci_info = {
/* virtio-balloon-pci */
-static void balloon_pci_stats_get_all(Object *obj, struct Visitor *v,
- void *opaque, const char *name,
- Error **errp)
-{
- VirtIOBalloonPCI *dev = opaque;
- object_property_get(OBJECT(&dev->vdev), v, "guest-stats", errp);
-}
-
-static void balloon_pci_stats_get_poll_interval(Object *obj, struct Visitor *v,
- void *opaque, const char *name,
- Error **errp)
-{
- VirtIOBalloonPCI *dev = opaque;
- object_property_get(OBJECT(&dev->vdev), v, "guest-stats-polling-interval",
- errp);
-}
-
-static void balloon_pci_stats_set_poll_interval(Object *obj, struct Visitor *v,
- void *opaque, const char *name,
- Error **errp)
-{
- VirtIOBalloonPCI *dev = opaque;
- object_property_set(OBJECT(&dev->vdev), v, "guest-stats-polling-interval",
- errp);
-}
-
static Property virtio_balloon_pci_properties[] = {
DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0),
DEFINE_PROP_END_OF_LIST(),
@@ -1261,16 +1861,14 @@ static void virtio_balloon_pci_class_init(ObjectClass *klass, void *data)
static void virtio_balloon_pci_instance_init(Object *obj)
{
VirtIOBalloonPCI *dev = VIRTIO_BALLOON_PCI(obj);
+
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
TYPE_VIRTIO_BALLOON);
- object_property_add(obj, "guest-stats", "guest statistics",
- balloon_pci_stats_get_all, NULL, NULL, dev,
- NULL);
-
- object_property_add(obj, "guest-stats-polling-interval", "int",
- balloon_pci_stats_get_poll_interval,
- balloon_pci_stats_set_poll_interval,
- NULL, dev, NULL);
+ object_property_add_alias(obj, "guest-stats", OBJECT(&dev->vdev),
+ "guest-stats", &error_abort);
+ object_property_add_alias(obj, "guest-stats-polling-interval",
+ OBJECT(&dev->vdev),
+ "guest-stats-polling-interval", &error_abort);
}
static const TypeInfo virtio_balloon_pci_info = {
@@ -1360,7 +1958,6 @@ static Property virtio_net_properties[] = {
DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, false),
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3),
- DEFINE_VIRTIO_NET_FEATURES(VirtIOPCIProxy, host_features),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1370,7 +1967,6 @@ static void virtio_net_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
VirtIONetPCI *dev = VIRTIO_NET_PCI(vpci_dev);
DeviceState *vdev = DEVICE(&dev->vdev);
- virtio_net_set_config_size(&dev->vdev, vpci_dev->host_features);
virtio_net_set_netclient_name(&dev->vdev, qdev->id,
object_get_typename(OBJECT(qdev)));
qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
@@ -1469,6 +2065,130 @@ static const TypeInfo virtio_rng_pci_info = {
.class_init = virtio_rng_pci_class_init,
};
+/* virtio-input-pci */
+
+static Property virtio_input_pci_properties[] = {
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_input_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ VirtIOInputPCI *vinput = VIRTIO_INPUT_PCI(vpci_dev);
+ DeviceState *vdev = DEVICE(&vinput->vdev);
+
+ qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+ /* force virtio-1.0 */
+ vpci_dev->flags &= ~VIRTIO_PCI_FLAG_DISABLE_MODERN;
+ vpci_dev->flags |= VIRTIO_PCI_FLAG_DISABLE_LEGACY;
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
+}
+
+static void virtio_input_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+ dc->props = virtio_input_pci_properties;
+ k->realize = virtio_input_pci_realize;
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
+
+ pcidev_k->class_id = PCI_CLASS_INPUT_OTHER;
+}
+
+static void virtio_input_hid_kbd_pci_class_init(ObjectClass *klass, void *data)
+{
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+ pcidev_k->class_id = PCI_CLASS_INPUT_KEYBOARD;
+}
+
+static void virtio_input_hid_mouse_pci_class_init(ObjectClass *klass,
+ void *data)
+{
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+ pcidev_k->class_id = PCI_CLASS_INPUT_MOUSE;
+}
+
+static void virtio_keyboard_initfn(Object *obj)
+{
+ VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VIRTIO_KEYBOARD);
+}
+
+static void virtio_mouse_initfn(Object *obj)
+{
+ VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VIRTIO_MOUSE);
+}
+
+static void virtio_tablet_initfn(Object *obj)
+{
+ VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VIRTIO_TABLET);
+}
+
+static void virtio_host_initfn(Object *obj)
+{
+ VirtIOInputHostPCI *dev = VIRTIO_INPUT_HOST_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VIRTIO_INPUT_HOST);
+}
+
+static const TypeInfo virtio_input_pci_info = {
+ .name = TYPE_VIRTIO_INPUT_PCI,
+ .parent = TYPE_VIRTIO_PCI,
+ .instance_size = sizeof(VirtIOInputPCI),
+ .class_init = virtio_input_pci_class_init,
+ .abstract = true,
+};
+
+static const TypeInfo virtio_input_hid_pci_info = {
+ .name = TYPE_VIRTIO_INPUT_HID_PCI,
+ .parent = TYPE_VIRTIO_INPUT_PCI,
+ .instance_size = sizeof(VirtIOInputHIDPCI),
+ .abstract = true,
+};
+
+static const TypeInfo virtio_keyboard_pci_info = {
+ .name = TYPE_VIRTIO_KEYBOARD_PCI,
+ .parent = TYPE_VIRTIO_INPUT_HID_PCI,
+ .class_init = virtio_input_hid_kbd_pci_class_init,
+ .instance_size = sizeof(VirtIOInputHIDPCI),
+ .instance_init = virtio_keyboard_initfn,
+};
+
+static const TypeInfo virtio_mouse_pci_info = {
+ .name = TYPE_VIRTIO_MOUSE_PCI,
+ .parent = TYPE_VIRTIO_INPUT_HID_PCI,
+ .class_init = virtio_input_hid_mouse_pci_class_init,
+ .instance_size = sizeof(VirtIOInputHIDPCI),
+ .instance_init = virtio_mouse_initfn,
+};
+
+static const TypeInfo virtio_tablet_pci_info = {
+ .name = TYPE_VIRTIO_TABLET_PCI,
+ .parent = TYPE_VIRTIO_INPUT_HID_PCI,
+ .instance_size = sizeof(VirtIOInputHIDPCI),
+ .instance_init = virtio_tablet_initfn,
+};
+
+static const TypeInfo virtio_host_pci_info = {
+ .name = TYPE_VIRTIO_INPUT_HOST_PCI,
+ .parent = TYPE_VIRTIO_INPUT_PCI,
+ .instance_size = sizeof(VirtIOInputHostPCI),
+ .instance_init = virtio_host_initfn,
+};
+
/* virtio-pci-bus */
static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size,
@@ -1491,13 +2211,13 @@ static void virtio_pci_bus_class_init(ObjectClass *klass, void *data)
k->load_config = virtio_pci_load_config;
k->save_queue = virtio_pci_save_queue;
k->load_queue = virtio_pci_load_queue;
- k->get_features = virtio_pci_get_features;
k->query_guest_notifiers = virtio_pci_query_guest_notifiers;
k->set_host_notifier = virtio_pci_set_host_notifier;
k->set_guest_notifiers = virtio_pci_set_guest_notifiers;
k->vmstate_change = virtio_pci_vmstate_change;
k->device_plugged = virtio_pci_device_plugged;
k->device_unplugged = virtio_pci_device_unplugged;
+ k->query_nvectors = virtio_pci_query_nvectors;
}
static const TypeInfo virtio_pci_bus_info = {
@@ -1510,6 +2230,12 @@ static const TypeInfo virtio_pci_bus_info = {
static void virtio_pci_register_types(void)
{
type_register_static(&virtio_rng_pci_info);
+ type_register_static(&virtio_input_pci_info);
+ type_register_static(&virtio_input_hid_pci_info);
+ type_register_static(&virtio_keyboard_pci_info);
+ type_register_static(&virtio_mouse_pci_info);
+ type_register_static(&virtio_tablet_pci_info);
+ type_register_static(&virtio_host_pci_info);
type_register_static(&virtio_pci_bus_info);
type_register_static(&virtio_pci_info);
#ifdef CONFIG_VIRTFS
diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h
index 3bac01699..b6c442f52 100644
--- a/hw/virtio/virtio-pci.h
+++ b/hw/virtio/virtio-pci.h
@@ -24,6 +24,8 @@
#include "hw/virtio/virtio-balloon.h"
#include "hw/virtio/virtio-bus.h"
#include "hw/virtio/virtio-9p.h"
+#include "hw/virtio/virtio-input.h"
+#include "hw/virtio/virtio-gpu.h"
#ifdef CONFIG_VIRTFS
#include "hw/9pfs/virtio-9p.h"
#endif
@@ -39,6 +41,10 @@ typedef struct VirtIOSerialPCI VirtIOSerialPCI;
typedef struct VirtIONetPCI VirtIONetPCI;
typedef struct VHostSCSIPCI VHostSCSIPCI;
typedef struct VirtIORngPCI VirtIORngPCI;
+typedef struct VirtIOInputPCI VirtIOInputPCI;
+typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI;
+typedef struct VirtIOInputHostPCI VirtIOInputHostPCI;
+typedef struct VirtIOGPUPCI VirtIOGPUPCI;
/* virtio-pci-bus */
@@ -63,6 +69,12 @@ typedef struct VirtioBusClass VirtioPCIBusClass;
#define VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT 1
#define VIRTIO_PCI_FLAG_USE_IOEVENTFD (1 << VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT)
+/* virtio version flags */
+#define VIRTIO_PCI_FLAG_DISABLE_LEGACY_BIT 2
+#define VIRTIO_PCI_FLAG_DISABLE_MODERN_BIT 3
+#define VIRTIO_PCI_FLAG_DISABLE_LEGACY (1 << VIRTIO_PCI_FLAG_DISABLE_LEGACY_BIT)
+#define VIRTIO_PCI_FLAG_DISABLE_MODERN (1 << VIRTIO_PCI_FLAG_DISABLE_MODERN_BIT)
+
typedef struct {
MSIMessage msg;
int virq;
@@ -85,13 +97,41 @@ typedef struct VirtioPCIClass {
void (*realize)(VirtIOPCIProxy *vpci_dev, Error **errp);
} VirtioPCIClass;
+typedef struct VirtIOPCIRegion {
+ MemoryRegion mr;
+ uint32_t offset;
+ uint32_t size;
+ uint32_t type;
+} VirtIOPCIRegion;
+
struct VirtIOPCIProxy {
PCIDevice pci_dev;
MemoryRegion bar;
+ VirtIOPCIRegion common;
+ VirtIOPCIRegion isr;
+ VirtIOPCIRegion device;
+ VirtIOPCIRegion notify;
+ MemoryRegion modern_bar;
+ MemoryRegion modern_cfg;
+ AddressSpace modern_as;
+ uint32_t legacy_io_bar;
+ uint32_t msix_bar;
+ uint32_t modern_mem_bar;
+ int config_cap;
uint32_t flags;
uint32_t class_code;
uint32_t nvectors;
- uint32_t host_features;
+ uint32_t dfselect;
+ uint32_t gfselect;
+ uint32_t guest_features[2];
+ struct {
+ uint16_t num;
+ bool enabled;
+ uint32_t desc[2];
+ uint32_t avail[2];
+ uint32_t used[2];
+ } vqs[VIRTIO_QUEUE_MAX];
+
bool ioeventfd_disabled;
bool ioeventfd_started;
VirtIOIRQFD *vector_irqfd;
@@ -203,6 +243,51 @@ struct VirtIORngPCI {
VirtIORNG vdev;
};
+/*
+ * virtio-input-pci: This extends VirtioPCIProxy.
+ */
+#define TYPE_VIRTIO_INPUT_PCI "virtio-input-pci"
+#define VIRTIO_INPUT_PCI(obj) \
+ OBJECT_CHECK(VirtIOInputPCI, (obj), TYPE_VIRTIO_INPUT_PCI)
+
+struct VirtIOInputPCI {
+ VirtIOPCIProxy parent_obj;
+ VirtIOInput vdev;
+};
+
+#define TYPE_VIRTIO_INPUT_HID_PCI "virtio-input-hid-pci"
+#define TYPE_VIRTIO_KEYBOARD_PCI "virtio-keyboard-pci"
+#define TYPE_VIRTIO_MOUSE_PCI "virtio-mouse-pci"
+#define TYPE_VIRTIO_TABLET_PCI "virtio-tablet-pci"
+#define VIRTIO_INPUT_HID_PCI(obj) \
+ OBJECT_CHECK(VirtIOInputHIDPCI, (obj), TYPE_VIRTIO_INPUT_HID_PCI)
+
+struct VirtIOInputHIDPCI {
+ VirtIOPCIProxy parent_obj;
+ VirtIOInputHID vdev;
+};
+
+#define TYPE_VIRTIO_INPUT_HOST_PCI "virtio-input-host-pci"
+#define VIRTIO_INPUT_HOST_PCI(obj) \
+ OBJECT_CHECK(VirtIOInputHostPCI, (obj), TYPE_VIRTIO_INPUT_HOST_PCI)
+
+struct VirtIOInputHostPCI {
+ VirtIOPCIProxy parent_obj;
+ VirtIOInputHost vdev;
+};
+
+/*
+ * virtio-gpu-pci: This extends VirtioPCIProxy.
+ */
+#define TYPE_VIRTIO_GPU_PCI "virtio-gpu-pci"
+#define VIRTIO_GPU_PCI(obj) \
+ OBJECT_CHECK(VirtIOGPUPCI, (obj), TYPE_VIRTIO_GPU_PCI)
+
+struct VirtIOGPUPCI {
+ VirtIOPCIProxy parent_obj;
+ VirtIOGPU vdev;
+};
+
/* Virtio ABI version, if we increment this, we break the guest driver. */
#define VIRTIO_PCI_ABI_VERSION 0
diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c
index 06e71782b..97d154191 100644
--- a/hw/virtio/virtio-rng.c
+++ b/hw/virtio/virtio-rng.c
@@ -11,7 +11,6 @@
#include "qemu/iov.h"
#include "hw/qdev.h"
-#include "qapi/qmp/qerror.h"
#include "hw/virtio/virtio.h"
#include "hw/virtio/virtio-rng.h"
#include "sysemu/rng.h"
@@ -78,6 +77,12 @@ static void virtio_rng_process(VirtIORNG *vrng)
return;
}
+ if (vrng->activate_timer) {
+ timer_mod(vrng->rate_limit_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + vrng->conf.period_ms);
+ vrng->activate_timer = false;
+ }
+
if (vrng->quota_remaining < 0) {
quota = 0;
} else {
@@ -99,7 +104,7 @@ static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
virtio_rng_process(vrng);
}
-static uint32_t get_features(VirtIODevice *vdev, uint32_t f)
+static uint64_t get_features(VirtIODevice *vdev, uint64_t f, Error **errp)
{
return f;
}
@@ -139,8 +144,7 @@ static void check_rate_limit(void *opaque)
vrng->quota_remaining = vrng->conf.max_bytes;
virtio_rng_process(vrng);
- timer_mod(vrng->rate_limit_timer,
- qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + vrng->conf.period_ms);
+ vrng->activate_timer = true;
}
static void virtio_rng_device_realize(DeviceState *dev, Error **errp)
@@ -196,13 +200,9 @@ static void virtio_rng_device_realize(DeviceState *dev, Error **errp)
vrng->vq = virtio_add_queue(vdev, 8, handle_input);
vrng->quota_remaining = vrng->conf.max_bytes;
-
vrng->rate_limit_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
check_rate_limit, vrng);
-
- timer_mod(vrng->rate_limit_timer,
- qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + vrng->conf.period_ms);
-
+ vrng->activate_timer = true;
register_savevm(dev, "virtio-rng", -1, 1, virtio_rng_save,
virtio_rng_load, vrng);
}
@@ -219,7 +219,13 @@ static void virtio_rng_device_unrealize(DeviceState *dev, Error **errp)
}
static Property virtio_rng_properties[] = {
- DEFINE_VIRTIO_RNG_PROPERTIES(VirtIORNG, conf),
+ /* Set a default rate limit of 2^47 bytes per minute or roughly 2TB/s. If
+ * you have an entropy source capable of generating more entropy than this
+ * and you can pass it through via virtio-rng, then hats off to you. Until
+ * then, this is unlimited for all practical purposes.
+ */
+ DEFINE_PROP_UINT64("max-bytes", VirtIORNG, conf.max_bytes, INT64_MAX),
+ DEFINE_PROP_UINT32("period", VirtIORNG, conf.period_ms, 1 << 16),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 17c1260c0..d24f77551 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -69,7 +69,6 @@ typedef struct VRing
struct VirtQueue
{
VRing vring;
- hwaddr pa;
uint16_t last_avail_idx;
/* Last used index value we have signalled on */
uint16_t signalled_used;
@@ -89,18 +88,22 @@ struct VirtQueue
VirtIODevice *vdev;
EventNotifier guest_notifier;
EventNotifier host_notifier;
+ QLIST_ENTRY(VirtQueue) node;
};
/* virt queue functions */
-static void virtqueue_init(VirtQueue *vq)
+void virtio_queue_update_rings(VirtIODevice *vdev, int n)
{
- hwaddr pa = vq->pa;
+ VRing *vring = &vdev->vq[n].vring;
- vq->vring.desc = pa;
- vq->vring.avail = pa + vq->vring.num * sizeof(VRingDesc);
- vq->vring.used = vring_align(vq->vring.avail +
- offsetof(VRingAvail, ring[vq->vring.num]),
- vq->vring.align);
+ if (!vring->desc) {
+ /* not yet setup -> nothing to do */
+ return;
+ }
+ vring->avail = vring->desc + vring->num * sizeof(VRingDesc);
+ vring->used = vring_align(vring->avail +
+ offsetof(VRingAvail, ring[vring->num]),
+ vring->align);
}
static inline uint64_t vring_desc_addr(VirtIODevice *vdev, hwaddr desc_pa,
@@ -217,7 +220,7 @@ static inline void vring_set_avail_event(VirtQueue *vq, uint16_t val)
void virtio_queue_set_notification(VirtQueue *vq, int enable)
{
vq->notification = enable;
- if (virtio_has_feature(vq->vdev, VIRTIO_RING_F_EVENT_IDX)) {
+ if (virtio_vdev_has_feature(vq->vdev, VIRTIO_RING_F_EVENT_IDX)) {
vring_set_avail_event(vq, vring_avail_idx(vq));
} else if (enable) {
vring_used_flags_unset_bit(vq, VRING_USED_F_NO_NOTIFY);
@@ -240,14 +243,12 @@ int virtio_queue_empty(VirtQueue *vq)
return vring_avail_idx(vq) == vq->last_avail_idx;
}
-void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
- unsigned int len, unsigned int idx)
+static void virtqueue_unmap_sg(VirtQueue *vq, const VirtQueueElement *elem,
+ unsigned int len)
{
unsigned int offset;
int i;
- trace_virtqueue_fill(vq, elem, len, idx);
-
offset = 0;
for (i = 0; i < elem->in_num; i++) {
size_t size = MIN(len - offset, elem->in_sg[i].iov_len);
@@ -263,6 +264,21 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
cpu_physical_memory_unmap(elem->out_sg[i].iov_base,
elem->out_sg[i].iov_len,
0, elem->out_sg[i].iov_len);
+}
+
+void virtqueue_discard(VirtQueue *vq, const VirtQueueElement *elem,
+ unsigned int len)
+{
+ vq->last_avail_idx--;
+ virtqueue_unmap_sg(vq, elem, len);
+}
+
+void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
+ unsigned int len, unsigned int idx)
+{
+ trace_virtqueue_fill(vq, elem, len, idx);
+
+ virtqueue_unmap_sg(vq, elem, len);
idx = (idx + vring_used_idx(vq)) % vq->vring.num;
@@ -468,7 +484,7 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
max = vq->vring.num;
i = head = virtqueue_get_head(vq, vq->last_avail_idx++);
- if (virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
+ if (virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
vring_set_avail_event(vq, vq->last_avail_idx);
}
@@ -541,15 +557,37 @@ void virtio_update_irq(VirtIODevice *vdev)
virtio_notify_vector(vdev, VIRTIO_NO_VECTOR);
}
-void virtio_set_status(VirtIODevice *vdev, uint8_t val)
+static int virtio_validate_features(VirtIODevice *vdev)
+{
+ VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
+
+ if (k->validate_features) {
+ return k->validate_features(vdev);
+ } else {
+ return 0;
+ }
+}
+
+int virtio_set_status(VirtIODevice *vdev, uint8_t val)
{
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
trace_virtio_set_status(vdev, val);
+ if (virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) {
+ if (!(vdev->status & VIRTIO_CONFIG_S_FEATURES_OK) &&
+ val & VIRTIO_CONFIG_S_FEATURES_OK) {
+ int ret = virtio_validate_features(vdev);
+
+ if (ret) {
+ return ret;
+ }
+ }
+ }
if (k->set_status) {
k->set_status(vdev, val);
}
vdev->status = val;
+ return 0;
}
bool target_words_bigendian(void);
@@ -599,13 +637,12 @@ void virtio_reset(void *opaque)
vdev->config_vector = VIRTIO_NO_VECTOR;
virtio_notify_vector(vdev, vdev->config_vector);
- for(i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) {
+ for(i = 0; i < VIRTIO_QUEUE_MAX; i++) {
vdev->vq[i].vring.desc = 0;
vdev->vq[i].vring.avail = 0;
vdev->vq[i].vring.used = 0;
vdev->vq[i].last_avail_idx = 0;
- vdev->vq[i].pa = 0;
- vdev->vq[i].vector = VIRTIO_NO_VECTOR;
+ virtio_queue_set_vector(vdev, i, VIRTIO_NO_VECTOR);
vdev->vq[i].signalled_used = 0;
vdev->vq[i].signalled_used_valid = false;
vdev->vq[i].notification = true;
@@ -705,15 +742,119 @@ void virtio_config_writel(VirtIODevice *vdev, uint32_t addr, uint32_t data)
}
}
+uint32_t virtio_config_modern_readb(VirtIODevice *vdev, uint32_t addr)
+{
+ VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
+ uint8_t val;
+
+ if (addr + sizeof(val) > vdev->config_len) {
+ return (uint32_t)-1;
+ }
+
+ k->get_config(vdev, vdev->config);
+
+ val = ldub_p(vdev->config + addr);
+ return val;
+}
+
+uint32_t virtio_config_modern_readw(VirtIODevice *vdev, uint32_t addr)
+{
+ VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
+ uint16_t val;
+
+ if (addr + sizeof(val) > vdev->config_len) {
+ return (uint32_t)-1;
+ }
+
+ k->get_config(vdev, vdev->config);
+
+ val = lduw_le_p(vdev->config + addr);
+ return val;
+}
+
+uint32_t virtio_config_modern_readl(VirtIODevice *vdev, uint32_t addr)
+{
+ VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
+ uint32_t val;
+
+ if (addr + sizeof(val) > vdev->config_len) {
+ return (uint32_t)-1;
+ }
+
+ k->get_config(vdev, vdev->config);
+
+ val = ldl_le_p(vdev->config + addr);
+ return val;
+}
+
+void virtio_config_modern_writeb(VirtIODevice *vdev,
+ uint32_t addr, uint32_t data)
+{
+ VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
+ uint8_t val = data;
+
+ if (addr + sizeof(val) > vdev->config_len) {
+ return;
+ }
+
+ stb_p(vdev->config + addr, val);
+
+ if (k->set_config) {
+ k->set_config(vdev, vdev->config);
+ }
+}
+
+void virtio_config_modern_writew(VirtIODevice *vdev,
+ uint32_t addr, uint32_t data)
+{
+ VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
+ uint16_t val = data;
+
+ if (addr + sizeof(val) > vdev->config_len) {
+ return;
+ }
+
+ stw_le_p(vdev->config + addr, val);
+
+ if (k->set_config) {
+ k->set_config(vdev, vdev->config);
+ }
+}
+
+void virtio_config_modern_writel(VirtIODevice *vdev,
+ uint32_t addr, uint32_t data)
+{
+ VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
+ uint32_t val = data;
+
+ if (addr + sizeof(val) > vdev->config_len) {
+ return;
+ }
+
+ stl_le_p(vdev->config + addr, val);
+
+ if (k->set_config) {
+ k->set_config(vdev, vdev->config);
+ }
+}
+
void virtio_queue_set_addr(VirtIODevice *vdev, int n, hwaddr addr)
{
- vdev->vq[n].pa = addr;
- virtqueue_init(&vdev->vq[n]);
+ vdev->vq[n].vring.desc = addr;
+ virtio_queue_update_rings(vdev, n);
}
hwaddr virtio_queue_get_addr(VirtIODevice *vdev, int n)
{
- return vdev->vq[n].pa;
+ return vdev->vq[n].vring.desc;
+}
+
+void virtio_queue_set_rings(VirtIODevice *vdev, int n, hwaddr desc,
+ hwaddr avail, hwaddr used)
+{
+ vdev->vq[n].vring.desc = desc;
+ vdev->vq[n].vring.avail = avail;
+ vdev->vq[n].vring.used = used;
}
void virtio_queue_set_num(VirtIODevice *vdev, int n, int num)
@@ -727,7 +868,16 @@ void virtio_queue_set_num(VirtIODevice *vdev, int n, int num)
return;
}
vdev->vq[n].vring.num = num;
- virtqueue_init(&vdev->vq[n]);
+}
+
+VirtQueue *virtio_vector_first_queue(VirtIODevice *vdev, uint16_t vector)
+{
+ return QLIST_FIRST(&vdev->vector_queues[vector]);
+}
+
+VirtQueue *virtio_vector_next_queue(VirtQueue *vq)
+{
+ return QLIST_NEXT(vq, node);
}
int virtio_queue_get_num(VirtIODevice *vdev, int n)
@@ -735,10 +885,23 @@ int virtio_queue_get_num(VirtIODevice *vdev, int n)
return vdev->vq[n].vring.num;
}
+int virtio_get_num_queues(VirtIODevice *vdev)
+{
+ int i;
+
+ for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
+ if (!virtio_queue_get_num(vdev, i)) {
+ break;
+ }
+ }
+
+ return i;
+}
+
int virtio_queue_get_id(VirtQueue *vq)
{
VirtIODevice *vdev = vq->vdev;
- assert(vq >= &vdev->vq[0] && vq < &vdev->vq[VIRTIO_PCI_QUEUE_MAX]);
+ assert(vq >= &vdev->vq[0] && vq < &vdev->vq[VIRTIO_QUEUE_MAX]);
return vq - &vdev->vq[0];
}
@@ -747,6 +910,11 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, int align)
BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
+ /* virtio-1 compliant devices cannot change the alignment */
+ if (virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) {
+ error_report("tried to modify queue alignment for virtio-1 device");
+ return;
+ }
/* Check that the transport told us it was going to do this
* (so a buggy transport will immediately assert rather than
* silently failing to migrate this state)
@@ -754,7 +922,7 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, int align)
assert(k->has_variable_vring_alignment);
vdev->vq[n].vring.align = align;
- virtqueue_init(&vdev->vq[n]);
+ virtio_queue_update_rings(vdev, n);
}
void virtio_queue_notify_vq(VirtQueue *vq)
@@ -774,14 +942,25 @@ void virtio_queue_notify(VirtIODevice *vdev, int n)
uint16_t virtio_queue_vector(VirtIODevice *vdev, int n)
{
- return n < VIRTIO_PCI_QUEUE_MAX ? vdev->vq[n].vector :
+ return n < VIRTIO_QUEUE_MAX ? vdev->vq[n].vector :
VIRTIO_NO_VECTOR;
}
void virtio_queue_set_vector(VirtIODevice *vdev, int n, uint16_t vector)
{
- if (n < VIRTIO_PCI_QUEUE_MAX)
+ VirtQueue *vq = &vdev->vq[n];
+
+ if (n < VIRTIO_QUEUE_MAX) {
+ if (vdev->vector_queues &&
+ vdev->vq[n].vector != VIRTIO_NO_VECTOR) {
+ QLIST_REMOVE(vq, node);
+ }
vdev->vq[n].vector = vector;
+ if (vdev->vector_queues &&
+ vector != VIRTIO_NO_VECTOR) {
+ QLIST_INSERT_HEAD(&vdev->vector_queues[vector], vq, node);
+ }
+ }
}
VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size,
@@ -789,12 +968,12 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size,
{
int i;
- for (i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) {
+ for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
if (vdev->vq[i].vring.num == 0)
break;
}
- if (i == VIRTIO_PCI_QUEUE_MAX || queue_size > VIRTQUEUE_MAX_SIZE)
+ if (i == VIRTIO_QUEUE_MAX || queue_size > VIRTQUEUE_MAX_SIZE)
abort();
vdev->vq[i].vring.num = queue_size;
@@ -806,7 +985,7 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size,
void virtio_del_queue(VirtIODevice *vdev, int n)
{
- if (n < 0 || n >= VIRTIO_PCI_QUEUE_MAX) {
+ if (n < 0 || n >= VIRTIO_QUEUE_MAX) {
abort();
}
@@ -827,12 +1006,12 @@ static bool vring_notify(VirtIODevice *vdev, VirtQueue *vq)
/* We need to expose used array entries before checking used event. */
smp_mb();
/* Always notify when queue is empty (when feature acknowledge) */
- if (virtio_has_feature(vdev, VIRTIO_F_NOTIFY_ON_EMPTY) &&
+ if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFY_ON_EMPTY) &&
!vq->inuse && vring_avail_idx(vq) == vq->last_avail_idx) {
return true;
}
- if (!virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
+ if (!virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
return !(vring_avail_flags(vq) & VRING_AVAIL_F_NO_INTERRUPT);
}
@@ -860,6 +1039,7 @@ void virtio_notify_config(VirtIODevice *vdev)
return;
vdev->isr |= 0x03;
+ vdev->generation++;
virtio_notify_vector(vdev, vdev->config_vector);
}
@@ -868,19 +1048,97 @@ static bool virtio_device_endian_needed(void *opaque)
VirtIODevice *vdev = opaque;
assert(vdev->device_endian != VIRTIO_DEVICE_ENDIAN_UNKNOWN);
- return vdev->device_endian != virtio_default_endian();
+ if (!virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) {
+ return vdev->device_endian != virtio_default_endian();
+ }
+ /* Devices conforming to VIRTIO 1.0 or later are always LE. */
+ return vdev->device_endian != VIRTIO_DEVICE_ENDIAN_LITTLE;
+}
+
+static bool virtio_64bit_features_needed(void *opaque)
+{
+ VirtIODevice *vdev = opaque;
+
+ return (vdev->host_features >> 32) != 0;
}
+static bool virtio_virtqueue_needed(void *opaque)
+{
+ VirtIODevice *vdev = opaque;
+
+ return virtio_host_has_feature(vdev, VIRTIO_F_VERSION_1);
+}
+
+static void put_virtqueue_state(QEMUFile *f, void *pv, size_t size)
+{
+ VirtIODevice *vdev = pv;
+ int i;
+
+ for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
+ qemu_put_be64(f, vdev->vq[i].vring.avail);
+ qemu_put_be64(f, vdev->vq[i].vring.used);
+ }
+}
+
+static int get_virtqueue_state(QEMUFile *f, void *pv, size_t size)
+{
+ VirtIODevice *vdev = pv;
+ int i;
+
+ for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
+ vdev->vq[i].vring.avail = qemu_get_be64(f);
+ vdev->vq[i].vring.used = qemu_get_be64(f);
+ }
+ return 0;
+}
+
+static VMStateInfo vmstate_info_virtqueue = {
+ .name = "virtqueue_state",
+ .get = get_virtqueue_state,
+ .put = put_virtqueue_state,
+};
+
+static const VMStateDescription vmstate_virtio_virtqueues = {
+ .name = "virtio/virtqueues",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = &virtio_virtqueue_needed,
+ .fields = (VMStateField[]) {
+ {
+ .name = "virtqueues",
+ .version_id = 0,
+ .field_exists = NULL,
+ .size = 0,
+ .info = &vmstate_info_virtqueue,
+ .flags = VMS_SINGLE,
+ .offset = 0,
+ },
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_virtio_device_endian = {
.name = "virtio/device_endian",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = &virtio_device_endian_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8(device_endian, VirtIODevice),
VMSTATE_END_OF_LIST()
}
};
+static const VMStateDescription vmstate_virtio_64bit_features = {
+ .name = "virtio/64bit_features",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = &virtio_64bit_features_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(guest_features, VirtIODevice),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_virtio = {
.name = "virtio",
.version_id = 1,
@@ -889,12 +1147,11 @@ static const VMStateDescription vmstate_virtio = {
.fields = (VMStateField[]) {
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_virtio_device_endian,
- .needed = &virtio_device_endian_needed
- },
- { 0 }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_virtio_device_endian,
+ &vmstate_virtio_64bit_features,
+ &vmstate_virtio_virtqueues,
+ NULL
}
};
@@ -903,6 +1160,7 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f)
BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
+ uint32_t guest_features_lo = (vdev->guest_features & 0xffffffff);
int i;
if (k->save_config) {
@@ -912,18 +1170,18 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f)
qemu_put_8s(f, &vdev->status);
qemu_put_8s(f, &vdev->isr);
qemu_put_be16s(f, &vdev->queue_sel);
- qemu_put_be32s(f, &vdev->guest_features);
+ qemu_put_be32s(f, &guest_features_lo);
qemu_put_be32(f, vdev->config_len);
qemu_put_buffer(f, vdev->config, vdev->config_len);
- for (i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) {
+ for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
if (vdev->vq[i].vring.num == 0)
break;
}
qemu_put_be32(f, i);
- for (i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) {
+ for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
if (vdev->vq[i].vring.num == 0)
break;
@@ -931,7 +1189,8 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f)
if (k->has_variable_vring_alignment) {
qemu_put_be32(f, vdev->vq[i].vring.align);
}
- qemu_put_be64(f, vdev->vq[i].pa);
+ /* XXX virtio-1 devices */
+ qemu_put_be64(f, vdev->vq[i].vring.desc);
qemu_put_be16s(f, &vdev->vq[i].last_avail_idx);
if (k->save_queue) {
k->save_queue(qbus->parent, i, f);
@@ -946,15 +1205,12 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f)
vmstate_save_state(f, &vmstate_virtio, vdev, NULL);
}
-int virtio_set_features(VirtIODevice *vdev, uint32_t val)
+static int virtio_set_features_nocheck(VirtIODevice *vdev, uint64_t val)
{
- BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
- VirtioBusClass *vbusk = VIRTIO_BUS_GET_CLASS(qbus);
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
- uint32_t supported_features = vbusk->get_features(qbus->parent);
- bool bad = (val & ~supported_features) != 0;
+ bool bad = (val & ~(vdev->host_features)) != 0;
- val &= supported_features;
+ val &= vdev->host_features;
if (k->set_features) {
k->set_features(vdev, val);
}
@@ -962,13 +1218,24 @@ int virtio_set_features(VirtIODevice *vdev, uint32_t val)
return bad ? -1 : 0;
}
+int virtio_set_features(VirtIODevice *vdev, uint64_t val)
+{
+ /*
+ * The driver must not attempt to set features after feature negotiation
+ * has finished.
+ */
+ if (vdev->status & VIRTIO_CONFIG_S_FEATURES_OK) {
+ return -EINVAL;
+ }
+ return virtio_set_features_nocheck(vdev, val);
+}
+
int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
{
int i, ret;
int32_t config_len;
uint32_t num;
uint32_t features;
- uint32_t supported_features;
BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
@@ -988,17 +1255,11 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
qemu_get_8s(f, &vdev->status);
qemu_get_8s(f, &vdev->isr);
qemu_get_be16s(f, &vdev->queue_sel);
- if (vdev->queue_sel >= VIRTIO_PCI_QUEUE_MAX) {
+ if (vdev->queue_sel >= VIRTIO_QUEUE_MAX) {
return -1;
}
qemu_get_be32s(f, &features);
- if (virtio_set_features(vdev, features) < 0) {
- supported_features = k->get_features(qbus->parent);
- error_report("Features 0x%x unsupported. Allowed features: 0x%x",
- features, supported_features);
- return -1;
- }
config_len = qemu_get_be32(f);
/*
@@ -1015,7 +1276,7 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
num = qemu_get_be32(f);
- if (num > VIRTIO_PCI_QUEUE_MAX) {
+ if (num > VIRTIO_QUEUE_MAX) {
error_report("Invalid number of PCI queues: 0x%x", num);
return -1;
}
@@ -1025,13 +1286,14 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
if (k->has_variable_vring_alignment) {
vdev->vq[i].vring.align = qemu_get_be32(f);
}
- vdev->vq[i].pa = qemu_get_be64(f);
+ vdev->vq[i].vring.desc = qemu_get_be64(f);
qemu_get_be16s(f, &vdev->vq[i].last_avail_idx);
vdev->vq[i].signalled_used_valid = false;
vdev->vq[i].notification = true;
- if (vdev->vq[i].pa) {
- virtqueue_init(&vdev->vq[i]);
+ if (vdev->vq[i].vring.desc) {
+ /* XXX virtio-1 devices */
+ virtio_queue_update_rings(vdev, i);
} else if (vdev->vq[i].last_avail_idx) {
error_report("VQ %d address 0x0 "
"inconsistent with Host index 0x%x",
@@ -1064,8 +1326,30 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
vdev->device_endian = virtio_default_endian();
}
+ if (virtio_64bit_features_needed(vdev)) {
+ /*
+ * Subsection load filled vdev->guest_features. Run them
+ * through virtio_set_features to sanity-check them against
+ * host_features.
+ */
+ uint64_t features64 = vdev->guest_features;
+ if (virtio_set_features_nocheck(vdev, features64) < 0) {
+ error_report("Features 0x%" PRIx64 " unsupported. "
+ "Allowed features: 0x%" PRIx64,
+ features64, vdev->host_features);
+ return -1;
+ }
+ } else {
+ if (virtio_set_features_nocheck(vdev, features) < 0) {
+ error_report("Features 0x%x unsupported. "
+ "Allowed features: 0x%" PRIx64,
+ features, vdev->host_features);
+ return -1;
+ }
+ }
+
for (i = 0; i < num; i++) {
- if (vdev->vq[i].pa) {
+ if (vdev->vq[i].vring.desc) {
uint16_t nheads;
nheads = vring_avail_idx(&vdev->vq[i]) - vdev->vq[i].last_avail_idx;
/* Check it isn't doing strange things with descriptor numbers. */
@@ -1088,6 +1372,7 @@ void virtio_cleanup(VirtIODevice *vdev)
qemu_del_vm_change_state_handler(vdev->vmstate);
g_free(vdev->config);
g_free(vdev->vq);
+ g_free(vdev->vector_queues);
}
static void virtio_vmstate_change(void *opaque, int running, RunState state)
@@ -1125,15 +1410,24 @@ void virtio_instance_init_common(Object *proxy_obj, void *data,
void virtio_init(VirtIODevice *vdev, const char *name,
uint16_t device_id, size_t config_size)
{
+ BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
int i;
+ int nvectors = k->query_nvectors ? k->query_nvectors(qbus->parent) : 0;
+
+ if (nvectors) {
+ vdev->vector_queues =
+ g_malloc0(sizeof(*vdev->vector_queues) * nvectors);
+ }
+
vdev->device_id = device_id;
vdev->status = 0;
vdev->isr = 0;
vdev->queue_sel = 0;
vdev->config_vector = VIRTIO_NO_VECTOR;
- vdev->vq = g_malloc0(sizeof(VirtQueue) * VIRTIO_PCI_QUEUE_MAX);
+ vdev->vq = g_malloc0(sizeof(VirtQueue) * VIRTIO_QUEUE_MAX);
vdev->vm_running = runstate_is_running();
- for (i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) {
+ for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
vdev->vq[i].vector = VIRTIO_NO_VECTOR;
vdev->vq[i].vdev = vdev;
vdev->vq[i].queue_index = i;
@@ -1296,7 +1590,12 @@ static void virtio_device_realize(DeviceState *dev, Error **errp)
return;
}
}
- virtio_bus_device_plugged(vdev);
+
+ virtio_bus_device_plugged(vdev, &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
}
static void virtio_device_unrealize(DeviceState *dev, Error **errp)
@@ -1319,6 +1618,11 @@ static void virtio_device_unrealize(DeviceState *dev, Error **errp)
vdev->bus_name = NULL;
}
+static Property virtio_properties[] = {
+ DEFINE_VIRTIO_COMMON_FEATURES(VirtIODevice, host_features),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void virtio_device_class_init(ObjectClass *klass, void *data)
{
/* Set the default value here. */
@@ -1327,6 +1631,7 @@ static void virtio_device_class_init(ObjectClass *klass, void *data)
dc->realize = virtio_device_realize;
dc->unrealize = virtio_device_unrealize;
dc->bus_type = TYPE_VIRTIO_BUS;
+ dc->props = virtio_properties;
}
static const TypeInfo virtio_device_info = {
diff --git a/hw/watchdog/Makefile.objs b/hw/watchdog/Makefile.objs
index 4b0374a55..72e3ffd93 100644
--- a/hw/watchdog/Makefile.objs
+++ b/hw/watchdog/Makefile.objs
@@ -1,3 +1,4 @@
common-obj-y += watchdog.o
common-obj-$(CONFIG_WDT_IB6300ESB) += wdt_i6300esb.o
common-obj-$(CONFIG_WDT_IB700) += wdt_ib700.o
+common-obj-$(CONFIG_WDT_DIAG288) += wdt_diag288.o
diff --git a/hw/watchdog/watchdog.c b/hw/watchdog/watchdog.c
index 54440c91c..8d4b0eeeb 100644
--- a/hw/watchdog/watchdog.c
+++ b/hw/watchdog/watchdog.c
@@ -27,6 +27,7 @@
#include "sysemu/sysemu.h"
#include "sysemu/watchdog.h"
#include "qapi-event.h"
+#include "hw/nmi.h"
/* Possible values for action parameter. */
#define WDT_RESET 1 /* Hard reset. */
@@ -35,6 +36,7 @@
#define WDT_PAUSE 4 /* Pause. */
#define WDT_DEBUG 5 /* Prints a message and continues running. */
#define WDT_NONE 6 /* Do nothing. */
+#define WDT_NMI 7 /* Inject nmi into the guest */
static int watchdog_action = WDT_RESET;
static QLIST_HEAD(watchdog_list, WatchdogTimerModel) watchdog_list;
@@ -95,6 +97,8 @@ int select_watchdog_action(const char *p)
watchdog_action = WDT_DEBUG;
else if (strcasecmp(p, "none") == 0)
watchdog_action = WDT_NONE;
+ else if (strcasecmp(p, "inject-nmi") == 0)
+ watchdog_action = WDT_NMI;
else
return -1;
@@ -138,5 +142,11 @@ void watchdog_perform_action(void)
case WDT_NONE:
qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_NONE, &error_abort);
break;
+
+ case WDT_NMI:
+ qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_INJECT_NMI,
+ &error_abort);
+ inject_nmi();
+ break;
}
}
diff --git a/hw/watchdog/wdt_diag288.c b/hw/watchdog/wdt_diag288.c
new file mode 100644
index 000000000..2a885a447
--- /dev/null
+++ b/hw/watchdog/wdt_diag288.c
@@ -0,0 +1,130 @@
+/*
+ * watchdog device diag288 support
+ *
+ * Copyright IBM, Corp. 2015
+ *
+ * Authors:
+ * Xu Wang <gesaint@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at your
+ * option) any later version. See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "sysemu/watchdog.h"
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#include "hw/watchdog/wdt_diag288.h"
+
+static WatchdogTimerModel model = {
+ .wdt_name = TYPE_WDT_DIAG288,
+ .wdt_description = "diag288 device for s390x platform",
+};
+
+static const VMStateDescription vmstate_diag288 = {
+ .name = "vmstate_diag288",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_TIMER_PTR(timer, DIAG288State),
+ VMSTATE_BOOL(enabled, DIAG288State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void wdt_diag288_reset(DeviceState *dev)
+{
+ DIAG288State *diag288 = DIAG288(dev);
+
+ diag288->enabled = false;
+ timer_del(diag288->timer);
+}
+
+static void diag288_reset(void *opaque)
+{
+ DeviceState *diag288 = opaque;
+
+ wdt_diag288_reset(diag288);
+}
+
+static void diag288_timer_expired(void *dev)
+{
+ qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n");
+ watchdog_perform_action();
+ wdt_diag288_reset(dev);
+}
+
+static int wdt_diag288_handle_timer(DIAG288State *diag288,
+ uint64_t func, uint64_t timeout)
+{
+ switch (func) {
+ case WDT_DIAG288_INIT:
+ diag288->enabled = true;
+ /* fall through */
+ case WDT_DIAG288_CHANGE:
+ if (!diag288->enabled) {
+ return -1;
+ }
+ timer_mod(diag288->timer,
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ timeout * get_ticks_per_sec());
+ break;
+ case WDT_DIAG288_CANCEL:
+ if (!diag288->enabled) {
+ return -1;
+ }
+ diag288->enabled = false;
+ timer_del(diag288->timer);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static void wdt_diag288_realize(DeviceState *dev, Error **errp)
+{
+ DIAG288State *diag288 = DIAG288(dev);
+
+ qemu_register_reset(diag288_reset, diag288);
+ diag288->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, diag288_timer_expired,
+ dev);
+}
+
+static void wdt_diag288_unrealize(DeviceState *dev, Error **errp)
+{
+ DIAG288State *diag288 = DIAG288(dev);
+
+ timer_del(diag288->timer);
+ timer_free(diag288->timer);
+}
+
+static void wdt_diag288_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ DIAG288Class *diag288 = DIAG288_CLASS(klass);
+
+ dc->realize = wdt_diag288_realize;
+ dc->unrealize = wdt_diag288_unrealize;
+ dc->reset = wdt_diag288_reset;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->vmsd = &vmstate_diag288;
+ diag288->handle_timer = wdt_diag288_handle_timer;
+}
+
+static const TypeInfo wdt_diag288_info = {
+ .class_init = wdt_diag288_class_init,
+ .parent = TYPE_DEVICE,
+ .name = TYPE_WDT_DIAG288,
+ .instance_size = sizeof(DIAG288State),
+ .class_size = sizeof(DIAG288Class),
+};
+
+static void wdt_diag288_register_types(void)
+{
+ watchdog_add_model(&model);
+ type_register_static(&wdt_diag288_info);
+}
+
+type_init(wdt_diag288_register_types)
diff --git a/hw/watchdog/wdt_i6300esb.c b/hw/watchdog/wdt_i6300esb.c
index 4ebdbb858..cfa2b1be1 100644
--- a/hw/watchdog/wdt_i6300esb.c
+++ b/hw/watchdog/wdt_i6300esb.c
@@ -103,6 +103,10 @@ struct I6300State {
typedef struct I6300State I6300State;
+#define TYPE_WATCHDOG_I6300ESB_DEVICE "i6300esb"
+#define WATCHDOG_I6300ESB_DEVICE(obj) \
+ OBJECT_CHECK(I6300State, (obj), TYPE_WATCHDOG_I6300ESB_DEVICE)
+
/* This function is called when the watchdog has either been enabled
* (hence it starts counting down) or has been keep-alived.
*/
@@ -150,7 +154,7 @@ static void i6300esb_disable_timer(I6300State *d)
static void i6300esb_reset(DeviceState *dev)
{
PCIDevice *pdev = PCI_DEVICE(dev);
- I6300State *d = DO_UPCAST(I6300State, dev, pdev);
+ I6300State *d = WATCHDOG_I6300ESB_DEVICE(pdev);
i6300esb_debug("I6300State = %p\n", d);
@@ -213,7 +217,7 @@ static void i6300esb_timer_expired(void *vp)
static void i6300esb_config_write(PCIDevice *dev, uint32_t addr,
uint32_t data, int len)
{
- I6300State *d = DO_UPCAST(I6300State, dev, dev);
+ I6300State *d = WATCHDOG_I6300ESB_DEVICE(dev);
int old;
i6300esb_debug("addr = %x, data = %x, len = %d\n", addr, data, len);
@@ -241,7 +245,7 @@ static void i6300esb_config_write(PCIDevice *dev, uint32_t addr,
static uint32_t i6300esb_config_read(PCIDevice *dev, uint32_t addr, int len)
{
- I6300State *d = DO_UPCAST(I6300State, dev, dev);
+ I6300State *d = WATCHDOG_I6300ESB_DEVICE(dev);
uint32_t data;
i6300esb_debug ("addr = %x, len = %d\n", addr, len);
@@ -416,7 +420,7 @@ static const VMStateDescription vmstate_i6300esb = {
static void i6300esb_realize(PCIDevice *dev, Error **errp)
{
- I6300State *d = DO_UPCAST(I6300State, dev, dev);
+ I6300State *d = WATCHDOG_I6300ESB_DEVICE(dev);
i6300esb_debug("I6300State = %p\n", d);
@@ -451,7 +455,7 @@ static void i6300esb_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo i6300esb_info = {
- .name = "i6300esb",
+ .name = TYPE_WATCHDOG_I6300ESB_DEVICE,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(I6300State),
.class_init = i6300esb_class_init,
diff --git a/hw/xen/xen_backend.c b/hw/xen/xen_backend.c
index b2cb22b99..2510e2e4f 100644
--- a/hw/xen/xen_backend.c
+++ b/hw/xen/xen_backend.c
@@ -714,9 +714,7 @@ int xen_be_init(void)
return -1;
}
- if (qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL) < 0) {
- goto err;
- }
+ qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL);
if (xen_xc == XC_HANDLER_INITIAL_VALUE) {
/* Check if xen_init() have been called */
diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c
index d095c081c..ed5fcaec0 100644
--- a/hw/xen/xen_pt.c
+++ b/hw/xen/xen_pt.c
@@ -125,7 +125,7 @@ int xen_pt_bar_offset_to_index(uint32_t offset)
static uint32_t xen_pt_pci_read_config(PCIDevice *d, uint32_t addr, int len)
{
- XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d);
+ XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
uint32_t val = 0;
XenPTRegGroup *reg_grp_entry = NULL;
XenPTReg *reg_entry = NULL;
@@ -230,15 +230,16 @@ exit:
static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr,
uint32_t val, int len)
{
- XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d);
+ XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
int index = 0;
XenPTRegGroup *reg_grp_entry = NULL;
int rc = 0;
- uint32_t read_val = 0;
+ uint32_t read_val = 0, wb_mask;
int emul_len = 0;
XenPTReg *reg_entry = NULL;
uint32_t find_addr = addr;
XenPTRegInfo *reg = NULL;
+ bool wp_flag = false;
if (xen_pt_pci_config_access_check(d, addr, len)) {
return;
@@ -248,10 +249,18 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr,
/* check unused BAR register */
index = xen_pt_bar_offset_to_index(addr);
- if ((index >= 0) && (val > 0 && val < XEN_PT_BAR_ALLF) &&
- (s->bases[index].bar_flag == XEN_PT_BAR_FLAG_UNUSED)) {
- XEN_PT_WARN(d, "Guest attempt to set address to unused Base Address "
- "Register. (addr: 0x%02x, len: %d)\n", addr, len);
+ if ((index >= 0) && (val != 0)) {
+ uint32_t chk = val;
+
+ if (index == PCI_ROM_SLOT)
+ chk |= (uint32_t)~PCI_ROM_ADDRESS_MASK;
+
+ if ((chk != XEN_PT_BAR_ALLF) &&
+ (s->bases[index].bar_flag == XEN_PT_BAR_FLAG_UNUSED)) {
+ XEN_PT_WARN(d, "Guest attempt to set address to unused "
+ "Base Address Register. (addr: 0x%02x, len: %d)\n",
+ addr, len);
+ }
}
/* find register group entry */
@@ -271,10 +280,17 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr,
if (rc < 0) {
XEN_PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc);
memset(&read_val, 0xff, len);
+ wb_mask = 0;
+ } else {
+ wb_mask = 0xFFFFFFFF >> ((4 - len) << 3);
}
/* pass directly to the real device for passthrough type register group */
if (reg_grp_entry == NULL) {
+ if (!s->permissive) {
+ wb_mask = 0;
+ wp_flag = true;
+ }
goto out;
}
@@ -295,9 +311,17 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr,
uint32_t real_offset = reg_grp_entry->base_offset + reg->offset;
uint32_t valid_mask = 0xFFFFFFFF >> ((4 - emul_len) << 3);
uint8_t *ptr_val = NULL;
+ uint32_t wp_mask = reg->emu_mask | reg->ro_mask;
valid_mask <<= (find_addr - real_offset) << 3;
ptr_val = (uint8_t *)&val + (real_offset & 3);
+ if (!s->permissive) {
+ wp_mask |= reg->res_mask;
+ }
+ if (wp_mask == (0xFFFFFFFF >> ((4 - reg->size) << 3))) {
+ wb_mask &= ~((wp_mask >> ((find_addr - real_offset) << 3))
+ << ((len - emul_len) << 3));
+ }
/* do emulation based on register size */
switch (reg->size) {
@@ -339,6 +363,16 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr,
} else {
/* nothing to do with passthrough type register,
* continue to find next byte */
+ if (!s->permissive) {
+ wb_mask &= ~(0xff << ((len - emul_len) << 3));
+ /* Unused BARs will make it here, but we don't want to issue
+ * warnings for writes to them (bogus writes get dealt with
+ * above).
+ */
+ if (index < 0) {
+ wp_flag = true;
+ }
+ }
emul_len--;
find_addr++;
}
@@ -350,10 +384,26 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr,
memory_region_transaction_commit();
out:
- if (!(reg && reg->no_wb)) {
+ if (wp_flag && !s->permissive_warned) {
+ s->permissive_warned = true;
+ xen_pt_log(d, "Write-back to unknown field 0x%02x (partially) inhibited (0x%0*x)\n",
+ addr, len * 2, wb_mask);
+ xen_pt_log(d, "If the device doesn't work, try enabling permissive mode\n");
+ xen_pt_log(d, "(unsafe) and if it helps report the problem to xen-devel\n");
+ }
+ for (index = 0; wb_mask; index += len) {
/* unknown regs are passed through */
- rc = xen_host_pci_set_block(&s->real_device, addr,
- (uint8_t *)&val, len);
+ while (!(wb_mask & 0xff)) {
+ index++;
+ wb_mask >>= 8;
+ }
+ len = 0;
+ do {
+ len++;
+ wb_mask >>= 8;
+ } while (wb_mask & 0xff);
+ rc = xen_host_pci_set_block(&s->real_device, addr + index,
+ (uint8_t *)&val + index, len);
if (rc < 0) {
XEN_PT_ERR(d, "pci_write_block failed. return value: %d.\n", rc);
@@ -565,8 +615,8 @@ static void xen_pt_region_update(XenPCIPassthroughState *s,
guest_port, machine_port, size,
op);
if (rc) {
- XEN_PT_ERR(d, "%s ioport mapping failed! (rc: %i)\n",
- adding ? "create new" : "remove old", rc);
+ XEN_PT_ERR(d, "%s ioport mapping failed! (err: %i)\n",
+ adding ? "create new" : "remove old", errno);
}
} else {
pcibus_t guest_addr = sec->offset_within_address_space;
@@ -579,8 +629,8 @@ static void xen_pt_region_update(XenPCIPassthroughState *s,
XEN_PFN(size + XC_PAGE_SIZE - 1),
op);
if (rc) {
- XEN_PT_ERR(d, "%s mem mapping failed! (rc: %i)\n",
- adding ? "create new" : "remove old", rc);
+ XEN_PT_ERR(d, "%s mem mapping failed! (err: %i)\n",
+ adding ? "create new" : "remove old", errno);
}
}
}
@@ -637,7 +687,7 @@ static const MemoryListener xen_pt_io_listener = {
static int xen_pt_initfn(PCIDevice *d)
{
- XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d);
+ XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
int rc = 0;
uint8_t machine_irq = 0;
uint16_t cmd = 0;
@@ -694,14 +744,11 @@ static int xen_pt_initfn(PCIDevice *d)
rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq);
if (rc < 0) {
- XEN_PT_ERR(d, "Mapping machine irq %u to pirq %i failed, (rc: %d)\n",
- machine_irq, pirq, rc);
+ XEN_PT_ERR(d, "Mapping machine irq %u to pirq %i failed, (err: %d)\n",
+ machine_irq, pirq, errno);
/* Disable PCI intx assertion (turn on bit10 of devctl) */
- xen_host_pci_set_word(&s->real_device,
- PCI_COMMAND,
- pci_get_word(s->dev.config + PCI_COMMAND)
- | PCI_COMMAND_INTX_DISABLE);
+ cmd |= PCI_COMMAND_INTX_DISABLE;
machine_irq = 0;
s->machine_irq = 0;
} else {
@@ -719,19 +766,17 @@ static int xen_pt_initfn(PCIDevice *d)
PCI_SLOT(d->devfn),
e_intx);
if (rc < 0) {
- XEN_PT_ERR(d, "Binding of interrupt %i failed! (rc: %d)\n",
- e_intx, rc);
+ XEN_PT_ERR(d, "Binding of interrupt %i failed! (err: %d)\n",
+ e_intx, errno);
/* Disable PCI intx assertion (turn on bit10 of devctl) */
- xen_host_pci_set_word(&s->real_device, PCI_COMMAND,
- *(uint16_t *)(&s->dev.config[PCI_COMMAND])
- | PCI_COMMAND_INTX_DISABLE);
+ cmd |= PCI_COMMAND_INTX_DISABLE;
xen_pt_mapped_machine_irq[machine_irq]--;
if (xen_pt_mapped_machine_irq[machine_irq] == 0) {
if (xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq)) {
XEN_PT_ERR(d, "Unmapping of machine interrupt %i failed!"
- " (rc: %d)\n", machine_irq, rc);
+ " (err: %d)\n", machine_irq, errno);
}
}
s->machine_irq = 0;
@@ -755,7 +800,7 @@ out:
static void xen_pt_unregister_device(PCIDevice *d)
{
- XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d);
+ XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
uint8_t machine_irq = s->machine_irq;
uint8_t intx = xen_pt_pci_intx(s);
int rc;
@@ -769,9 +814,9 @@ static void xen_pt_unregister_device(PCIDevice *d)
0 /* isa_irq */);
if (rc < 0) {
XEN_PT_ERR(d, "unbinding of interrupt INT%c failed."
- " (machine irq: %i, rc: %d)"
+ " (machine irq: %i, err: %d)"
" But bravely continuing on..\n",
- 'a' + intx, machine_irq, rc);
+ 'a' + intx, machine_irq, errno);
}
}
@@ -789,9 +834,9 @@ static void xen_pt_unregister_device(PCIDevice *d)
rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq);
if (rc < 0) {
- XEN_PT_ERR(d, "unmapping of interrupt %i failed. (rc: %d)"
+ XEN_PT_ERR(d, "unmapping of interrupt %i failed. (err: %d)"
" But bravely continuing on..\n",
- machine_irq, rc);
+ machine_irq, errno);
}
}
}
@@ -807,6 +852,7 @@ static void xen_pt_unregister_device(PCIDevice *d)
static Property xen_pci_passthrough_properties[] = {
DEFINE_PROP_PCI_HOST_DEVADDR("hostaddr", XenPCIPassthroughState, hostaddr),
+ DEFINE_PROP_BOOL("permissive", XenPCIPassthroughState, permissive, false),
DEFINE_PROP_END_OF_LIST(),
};
@@ -825,7 +871,7 @@ static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data)
};
static const TypeInfo xen_pci_passthrough_info = {
- .name = "xen-pci-passthrough",
+ .name = TYPE_XEN_PT_DEVICE,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(XenPCIPassthroughState),
.class_init = xen_pci_passthrough_class_init,
diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h
index 942dc60cc..393f36ccb 100644
--- a/hw/xen/xen_pt.h
+++ b/hw/xen/xen_pt.h
@@ -31,11 +31,15 @@ void xen_pt_log(const PCIDevice *d, const char *f, ...) GCC_FMT_ATTR(2, 3);
/* Helper */
#define XEN_PFN(x) ((x) >> XC_PAGE_SHIFT)
-typedef struct XenPTRegInfo XenPTRegInfo;
+typedef const struct XenPTRegInfo XenPTRegInfo;
typedef struct XenPTReg XenPTReg;
typedef struct XenPCIPassthroughState XenPCIPassthroughState;
+#define TYPE_XEN_PT_DEVICE "xen-pci-passthrough"
+#define XEN_PT_DEVICE(obj) \
+ OBJECT_CHECK(XenPCIPassthroughState, (obj), TYPE_XEN_PT_DEVICE)
+
/* function type for config reg */
typedef int (*xen_pt_conf_reg_init)
(XenPCIPassthroughState *, XenPTRegInfo *, uint32_t real_offset,
@@ -101,12 +105,12 @@ struct XenPTRegInfo {
uint32_t offset;
uint32_t size;
uint32_t init_val;
+ /* reg reserved field mask (ON:reserved, OFF:defined) */
+ uint32_t res_mask;
/* reg read only field mask (ON:RO/ROS, OFF:other) */
uint32_t ro_mask;
/* reg emulate field mask (ON:emu, OFF:passthrough) */
uint32_t emu_mask;
- /* no write back allowed */
- uint32_t no_wb;
xen_pt_conf_reg_init init;
/* read/write function pointer
* for double_word/word/byte size */
@@ -133,11 +137,11 @@ struct XenPTReg {
uint32_t data; /* emulated value */
};
-typedef struct XenPTRegGroupInfo XenPTRegGroupInfo;
+typedef const struct XenPTRegGroupInfo XenPTRegGroupInfo;
/* emul reg group size initialize method */
typedef int (*xen_pt_reg_size_init_fn)
- (XenPCIPassthroughState *, const XenPTRegGroupInfo *,
+ (XenPCIPassthroughState *, XenPTRegGroupInfo *,
uint32_t base_offset, uint8_t *size);
/* emulated register group information */
@@ -152,7 +156,7 @@ struct XenPTRegGroupInfo {
/* emul register group management table */
typedef struct XenPTRegGroup {
QLIST_ENTRY(XenPTRegGroup) entries;
- const XenPTRegGroupInfo *reg_grp;
+ XenPTRegGroupInfo *reg_grp;
uint32_t base_offset;
uint8_t size;
QLIST_HEAD(, XenPTReg) reg_tbl_list;
@@ -177,6 +181,7 @@ typedef struct XenPTMSIXEntry {
uint32_t data;
uint32_t vector_ctrl;
bool updated; /* indicate whether MSI ADDR or DATA is updated */
+ bool warned; /* avoid issuing (bogus) warning more than once */
} XenPTMSIXEntry;
typedef struct XenPTMSIX {
uint32_t ctrl_offset;
@@ -196,6 +201,8 @@ struct XenPCIPassthroughState {
PCIHostDeviceAddress hostaddr;
bool is_virtfn;
+ bool permissive;
+ bool permissive_warned;
XenHostPCIDevice real_device;
XenPTRegion bases[PCI_NUM_REGIONS]; /* Access regions */
QLIST_HEAD(, XenPTRegGroup) reg_grps;
diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c
index 95a51dbbb..dd37be38a 100644
--- a/hw/xen/xen_pt_config_init.c
+++ b/hw/xen/xen_pt_config_init.c
@@ -95,6 +95,17 @@ XenPTReg *xen_pt_find_reg(XenPTRegGroup *reg_grp, uint32_t address)
return NULL;
}
+static uint32_t get_throughable_mask(const XenPCIPassthroughState *s,
+ XenPTRegInfo *reg, uint32_t valid_mask)
+{
+ uint32_t throughable_mask = ~(reg->emu_mask | reg->ro_mask);
+
+ if (!s->permissive) {
+ throughable_mask &= ~reg->res_mask;
+ }
+
+ return throughable_mask & valid_mask;
+}
/****************
* general register functions
@@ -157,14 +168,13 @@ static int xen_pt_byte_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint8_t writable_mask = 0;
- uint8_t throughable_mask = 0;
+ uint8_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
/* modify emulate register */
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
/* create value for writing to I/O device register */
- throughable_mask = ~reg->emu_mask & valid_mask;
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
return 0;
@@ -175,14 +185,13 @@ static int xen_pt_word_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint16_t writable_mask = 0;
- uint16_t throughable_mask = 0;
+ uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
/* modify emulate register */
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
/* create value for writing to I/O device register */
- throughable_mask = ~reg->emu_mask & valid_mask;
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
return 0;
@@ -193,14 +202,13 @@ static int xen_pt_long_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint32_t writable_mask = 0;
- uint32_t throughable_mask = 0;
+ uint32_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
/* modify emulate register */
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
/* create value for writing to I/O device register */
- throughable_mask = ~reg->emu_mask & valid_mask;
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
return 0;
@@ -292,15 +300,13 @@ static int xen_pt_cmd_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint16_t writable_mask = 0;
- uint16_t throughable_mask = 0;
+ uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
/* modify emulate register */
writable_mask = ~reg->ro_mask & valid_mask;
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
/* create value for writing to I/O device register */
- throughable_mask = ~reg->emu_mask & valid_mask;
-
if (*val & PCI_COMMAND_INTX_DISABLE) {
throughable_mask |= PCI_COMMAND_INTX_DISABLE;
} else {
@@ -454,7 +460,6 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
PCIDevice *d = &s->dev;
const PCIIORegion *r;
uint32_t writable_mask = 0;
- uint32_t throughable_mask = 0;
uint32_t bar_emu_mask = 0;
uint32_t bar_ro_mask = 0;
uint32_t r_size = 0;
@@ -511,8 +516,7 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
}
/* create value for writing to I/O device register */
- throughable_mask = ~bar_emu_mask & valid_mask;
- *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
+ *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0);
return 0;
}
@@ -526,9 +530,8 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s,
XenPTRegion *base = NULL;
PCIDevice *d = (PCIDevice *)&s->dev;
uint32_t writable_mask = 0;
- uint32_t throughable_mask = 0;
+ uint32_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
pcibus_t r_size = 0;
- uint32_t bar_emu_mask = 0;
uint32_t bar_ro_mask = 0;
r_size = d->io_regions[PCI_ROM_SLOT].size;
@@ -537,7 +540,6 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s,
r_size = xen_pt_get_emul_size(base->bar_flag, r_size);
/* set emulate mask and read-only mask */
- bar_emu_mask = reg->emu_mask;
bar_ro_mask = (reg->ro_mask | (r_size - 1)) & ~PCI_ROM_ADDRESS_ENABLE;
/* modify emulate register */
@@ -545,7 +547,6 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s,
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
/* create value for writing to I/O device register */
- throughable_mask = ~bar_emu_mask & valid_mask;
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
return 0;
@@ -580,7 +581,7 @@ static XenPTRegInfo xen_pt_emu_reg_header0[] = {
.offset = PCI_COMMAND,
.size = 2,
.init_val = 0x0000,
- .ro_mask = 0xF880,
+ .res_mask = 0xF880,
.emu_mask = 0x0743,
.init = xen_pt_common_reg_init,
.u.w.read = xen_pt_word_reg_read,
@@ -605,7 +606,8 @@ static XenPTRegInfo xen_pt_emu_reg_header0[] = {
.offset = PCI_STATUS,
.size = 2,
.init_val = 0x0000,
- .ro_mask = 0x06FF,
+ .res_mask = 0x0007,
+ .ro_mask = 0x06F8,
.emu_mask = 0x0010,
.init = xen_pt_status_reg_init,
.u.w.read = xen_pt_word_reg_read,
@@ -726,8 +728,8 @@ static XenPTRegInfo xen_pt_emu_reg_header0[] = {
.offset = PCI_ROM_ADDRESS,
.size = 4,
.init_val = 0x00000000,
- .ro_mask = 0x000007FE,
- .emu_mask = 0xFFFFF800,
+ .ro_mask = ~PCI_ROM_ADDRESS_MASK & ~PCI_ROM_ADDRESS_ENABLE,
+ .emu_mask = (uint32_t)PCI_ROM_ADDRESS_MASK,
.init = xen_pt_bar_reg_init,
.u.dw.read = xen_pt_long_reg_read,
.u.dw.write = xen_pt_exp_rom_bar_reg_write,
@@ -755,6 +757,15 @@ static XenPTRegInfo xen_pt_emu_reg_vpd[] = {
.u.b.write = xen_pt_byte_reg_write,
},
{
+ .offset = PCI_VPD_ADDR,
+ .size = 2,
+ .ro_mask = 0x0003,
+ .emu_mask = 0x0003,
+ .init = xen_pt_common_reg_init,
+ .u.w.read = xen_pt_word_reg_read,
+ .u.w.write = xen_pt_word_reg_write,
+ },
+ {
.size = 0,
},
};
@@ -873,7 +884,7 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[] = {
.offset = PCI_EXP_DEVCAP,
.size = 4,
.init_val = 0x00000000,
- .ro_mask = 0x1FFCFFFF,
+ .ro_mask = 0xFFFFFFFF,
.emu_mask = 0x10000000,
.init = xen_pt_common_reg_init,
.u.dw.read = xen_pt_long_reg_read,
@@ -890,6 +901,16 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[] = {
.u.w.read = xen_pt_word_reg_read,
.u.w.write = xen_pt_word_reg_write,
},
+ /* Device Status reg */
+ {
+ .offset = PCI_EXP_DEVSTA,
+ .size = 2,
+ .res_mask = 0xFFC0,
+ .ro_mask = 0x0030,
+ .init = xen_pt_common_reg_init,
+ .u.w.read = xen_pt_word_reg_read,
+ .u.w.write = xen_pt_word_reg_write,
+ },
/* Link Control reg */
{
.offset = PCI_EXP_LNKCTL,
@@ -901,6 +922,15 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[] = {
.u.w.read = xen_pt_word_reg_read,
.u.w.write = xen_pt_word_reg_write,
},
+ /* Link Status reg */
+ {
+ .offset = PCI_EXP_LNKSTA,
+ .size = 2,
+ .ro_mask = 0x3FFF,
+ .init = xen_pt_common_reg_init,
+ .u.w.read = xen_pt_word_reg_read,
+ .u.w.write = xen_pt_word_reg_write,
+ },
/* Device Control 2 reg */
{
.offset = 0x28,
@@ -933,39 +963,22 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[] = {
* Power Management Capability
*/
-/* read Power Management Control/Status register */
-static int xen_pt_pmcsr_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
- uint16_t *value, uint16_t valid_mask)
-{
- XenPTRegInfo *reg = cfg_entry->reg;
- uint16_t valid_emu_mask = reg->emu_mask;
-
- valid_emu_mask |= PCI_PM_CTRL_STATE_MASK | PCI_PM_CTRL_NO_SOFT_RESET;
-
- valid_emu_mask = valid_emu_mask & valid_mask;
- *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask);
-
- return 0;
-}
/* write Power Management Control/Status register */
static int xen_pt_pmcsr_reg_write(XenPCIPassthroughState *s,
XenPTReg *cfg_entry, uint16_t *val,
uint16_t dev_value, uint16_t valid_mask)
{
XenPTRegInfo *reg = cfg_entry->reg;
- uint16_t emu_mask = reg->emu_mask;
uint16_t writable_mask = 0;
- uint16_t throughable_mask = 0;
-
- emu_mask |= PCI_PM_CTRL_STATE_MASK | PCI_PM_CTRL_NO_SOFT_RESET;
+ uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
/* modify emulate register */
- writable_mask = emu_mask & ~reg->ro_mask & valid_mask;
+ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
/* create value for writing to I/O device register */
- throughable_mask = ~emu_mask & valid_mask;
- *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
+ *val = XEN_PT_MERGE_VALUE(*val, dev_value & ~PCI_PM_CTRL_PME_STATUS,
+ throughable_mask);
return 0;
}
@@ -999,10 +1012,11 @@ static XenPTRegInfo xen_pt_emu_reg_pm[] = {
.offset = PCI_PM_CTRL,
.size = 2,
.init_val = 0x0008,
- .ro_mask = 0xE1FC,
- .emu_mask = 0x8100,
+ .res_mask = 0x00F0,
+ .ro_mask = 0xE10C,
+ .emu_mask = 0x810B,
.init = xen_pt_common_reg_init,
- .u.w.read = xen_pt_pmcsr_reg_read,
+ .u.w.read = xen_pt_word_reg_read,
.u.w.write = xen_pt_pmcsr_reg_write,
},
{
@@ -1016,13 +1030,9 @@ static XenPTRegInfo xen_pt_emu_reg_pm[] = {
*/
/* Helper */
-static bool xen_pt_msgdata_check_type(uint32_t offset, uint16_t flags)
-{
- /* check the offset whether matches the type or not */
- bool is_32 = (offset == PCI_MSI_DATA_32) && !(flags & PCI_MSI_FLAGS_64BIT);
- bool is_64 = (offset == PCI_MSI_DATA_64) && (flags & PCI_MSI_FLAGS_64BIT);
- return is_32 || is_64;
-}
+#define xen_pt_msi_check_type(offset, flags, what) \
+ ((offset) == ((flags) & PCI_MSI_FLAGS_64BIT ? \
+ PCI_MSI_##what##_64 : PCI_MSI_##what##_32))
/* Message Control register */
static int xen_pt_msgctrl_reg_init(XenPCIPassthroughState *s,
@@ -1056,8 +1066,7 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s,
XenPTRegInfo *reg = cfg_entry->reg;
XenPTMSI *msi = s->msi;
uint16_t writable_mask = 0;
- uint16_t throughable_mask = 0;
- uint16_t raw_val;
+ uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
/* Currently no support for multi-vector */
if (*val & PCI_MSI_FLAGS_QSIZE) {
@@ -1070,12 +1079,10 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s,
msi->flags |= cfg_entry->data & ~PCI_MSI_FLAGS_ENABLE;
/* create value for writing to I/O device register */
- raw_val = *val;
- throughable_mask = ~reg->emu_mask & valid_mask;
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
/* update MSI */
- if (raw_val & PCI_MSI_FLAGS_ENABLE) {
+ if (*val & PCI_MSI_FLAGS_ENABLE) {
/* setup MSI pirq for the first time */
if (!msi->initialized) {
/* Init physical one */
@@ -1103,10 +1110,6 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s,
xen_pt_msi_disable(s);
}
- /* pass through MSI_ENABLE bit */
- *val &= ~PCI_MSI_FLAGS_ENABLE;
- *val |= raw_val & PCI_MSI_FLAGS_ENABLE;
-
return 0;
}
@@ -1134,7 +1137,45 @@ static int xen_pt_msgdata_reg_init(XenPCIPassthroughState *s,
uint32_t offset = reg->offset;
/* check the offset whether matches the type or not */
- if (xen_pt_msgdata_check_type(offset, flags)) {
+ if (xen_pt_msi_check_type(offset, flags, DATA)) {
+ *data = reg->init_val;
+ } else {
+ *data = XEN_PT_INVALID_REG;
+ }
+ return 0;
+}
+
+/* this function will be called twice (for 32 bit and 64 bit type) */
+/* initialize Mask register */
+static int xen_pt_mask_reg_init(XenPCIPassthroughState *s,
+ XenPTRegInfo *reg, uint32_t real_offset,
+ uint32_t *data)
+{
+ uint32_t flags = s->msi->flags;
+
+ /* check the offset whether matches the type or not */
+ if (!(flags & PCI_MSI_FLAGS_MASKBIT)) {
+ *data = XEN_PT_INVALID_REG;
+ } else if (xen_pt_msi_check_type(reg->offset, flags, MASK)) {
+ *data = reg->init_val;
+ } else {
+ *data = XEN_PT_INVALID_REG;
+ }
+ return 0;
+}
+
+/* this function will be called twice (for 32 bit and 64 bit type) */
+/* initialize Pending register */
+static int xen_pt_pending_reg_init(XenPCIPassthroughState *s,
+ XenPTRegInfo *reg, uint32_t real_offset,
+ uint32_t *data)
+{
+ uint32_t flags = s->msi->flags;
+
+ /* check the offset whether matches the type or not */
+ if (!(flags & PCI_MSI_FLAGS_MASKBIT)) {
+ *data = XEN_PT_INVALID_REG;
+ } else if (xen_pt_msi_check_type(reg->offset, flags, PENDING)) {
*data = reg->init_val;
} else {
*data = XEN_PT_INVALID_REG;
@@ -1149,7 +1190,6 @@ static int xen_pt_msgaddr32_reg_write(XenPCIPassthroughState *s,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint32_t writable_mask = 0;
- uint32_t throughable_mask = 0;
uint32_t old_addr = cfg_entry->data;
/* modify emulate register */
@@ -1158,8 +1198,7 @@ static int xen_pt_msgaddr32_reg_write(XenPCIPassthroughState *s,
s->msi->addr_lo = cfg_entry->data;
/* create value for writing to I/O device register */
- throughable_mask = ~reg->emu_mask & valid_mask;
- *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
+ *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0);
/* update MSI */
if (cfg_entry->data != old_addr) {
@@ -1177,7 +1216,6 @@ static int xen_pt_msgaddr64_reg_write(XenPCIPassthroughState *s,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint32_t writable_mask = 0;
- uint32_t throughable_mask = 0;
uint32_t old_addr = cfg_entry->data;
/* check whether the type is 64 bit or not */
@@ -1194,8 +1232,7 @@ static int xen_pt_msgaddr64_reg_write(XenPCIPassthroughState *s,
s->msi->addr_hi = cfg_entry->data;
/* create value for writing to I/O device register */
- throughable_mask = ~reg->emu_mask & valid_mask;
- *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
+ *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0);
/* update MSI */
if (cfg_entry->data != old_addr) {
@@ -1217,12 +1254,11 @@ static int xen_pt_msgdata_reg_write(XenPCIPassthroughState *s,
XenPTRegInfo *reg = cfg_entry->reg;
XenPTMSI *msi = s->msi;
uint16_t writable_mask = 0;
- uint16_t throughable_mask = 0;
uint16_t old_data = cfg_entry->data;
uint32_t offset = reg->offset;
/* check the offset whether matches the type or not */
- if (!xen_pt_msgdata_check_type(offset, msi->flags)) {
+ if (!xen_pt_msi_check_type(offset, msi->flags, DATA)) {
/* exit I/O emulator */
XEN_PT_ERR(&s->dev, "the offset does not match the 32/64 bit type!\n");
return -1;
@@ -1235,8 +1271,7 @@ static int xen_pt_msgdata_reg_write(XenPCIPassthroughState *s,
msi->data = cfg_entry->data;
/* create value for writing to I/O device register */
- throughable_mask = ~reg->emu_mask & valid_mask;
- *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
+ *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0);
/* update MSI */
if (cfg_entry->data != old_data) {
@@ -1266,8 +1301,9 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] = {
.offset = PCI_MSI_FLAGS,
.size = 2,
.init_val = 0x0000,
- .ro_mask = 0xFF8E,
- .emu_mask = 0x007F,
+ .res_mask = 0xFE00,
+ .ro_mask = 0x018E,
+ .emu_mask = 0x017E,
.init = xen_pt_msgctrl_reg_init,
.u.w.read = xen_pt_word_reg_read,
.u.w.write = xen_pt_msgctrl_reg_write,
@@ -1279,7 +1315,6 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] = {
.init_val = 0x00000000,
.ro_mask = 0x00000003,
.emu_mask = 0xFFFFFFFF,
- .no_wb = 1,
.init = xen_pt_common_reg_init,
.u.dw.read = xen_pt_long_reg_read,
.u.dw.write = xen_pt_msgaddr32_reg_write,
@@ -1291,7 +1326,6 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] = {
.init_val = 0x00000000,
.ro_mask = 0x00000000,
.emu_mask = 0xFFFFFFFF,
- .no_wb = 1,
.init = xen_pt_msgaddr64_reg_init,
.u.dw.read = xen_pt_long_reg_read,
.u.dw.write = xen_pt_msgaddr64_reg_write,
@@ -1303,7 +1337,6 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] = {
.init_val = 0x0000,
.ro_mask = 0x0000,
.emu_mask = 0xFFFF,
- .no_wb = 1,
.init = xen_pt_msgdata_reg_init,
.u.w.read = xen_pt_word_reg_read,
.u.w.write = xen_pt_msgdata_reg_write,
@@ -1315,11 +1348,54 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] = {
.init_val = 0x0000,
.ro_mask = 0x0000,
.emu_mask = 0xFFFF,
- .no_wb = 1,
.init = xen_pt_msgdata_reg_init,
.u.w.read = xen_pt_word_reg_read,
.u.w.write = xen_pt_msgdata_reg_write,
},
+ /* Mask reg (if PCI_MSI_FLAGS_MASKBIT set, for 32-bit devices) */
+ {
+ .offset = PCI_MSI_MASK_32,
+ .size = 4,
+ .init_val = 0x00000000,
+ .ro_mask = 0xFFFFFFFF,
+ .emu_mask = 0xFFFFFFFF,
+ .init = xen_pt_mask_reg_init,
+ .u.dw.read = xen_pt_long_reg_read,
+ .u.dw.write = xen_pt_long_reg_write,
+ },
+ /* Mask reg (if PCI_MSI_FLAGS_MASKBIT set, for 64-bit devices) */
+ {
+ .offset = PCI_MSI_MASK_64,
+ .size = 4,
+ .init_val = 0x00000000,
+ .ro_mask = 0xFFFFFFFF,
+ .emu_mask = 0xFFFFFFFF,
+ .init = xen_pt_mask_reg_init,
+ .u.dw.read = xen_pt_long_reg_read,
+ .u.dw.write = xen_pt_long_reg_write,
+ },
+ /* Pending reg (if PCI_MSI_FLAGS_MASKBIT set, for 32-bit devices) */
+ {
+ .offset = PCI_MSI_MASK_32 + 4,
+ .size = 4,
+ .init_val = 0x00000000,
+ .ro_mask = 0xFFFFFFFF,
+ .emu_mask = 0x00000000,
+ .init = xen_pt_pending_reg_init,
+ .u.dw.read = xen_pt_long_reg_read,
+ .u.dw.write = xen_pt_long_reg_write,
+ },
+ /* Pending reg (if PCI_MSI_FLAGS_MASKBIT set, for 64-bit devices) */
+ {
+ .offset = PCI_MSI_MASK_64 + 4,
+ .size = 4,
+ .init_val = 0x00000000,
+ .ro_mask = 0xFFFFFFFF,
+ .emu_mask = 0x00000000,
+ .init = xen_pt_pending_reg_init,
+ .u.dw.read = xen_pt_long_reg_read,
+ .u.dw.write = xen_pt_long_reg_write,
+ },
{
.size = 0,
},
@@ -1358,7 +1434,7 @@ static int xen_pt_msixctrl_reg_write(XenPCIPassthroughState *s,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint16_t writable_mask = 0;
- uint16_t throughable_mask = 0;
+ uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
int debug_msix_enabled_old;
/* modify emulate register */
@@ -1366,7 +1442,6 @@ static int xen_pt_msixctrl_reg_write(XenPCIPassthroughState *s,
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
/* create value for writing to I/O device register */
- throughable_mask = ~reg->emu_mask & valid_mask;
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
/* update MSI-X */
@@ -1405,7 +1480,8 @@ static XenPTRegInfo xen_pt_emu_reg_msix[] = {
.offset = PCI_MSI_FLAGS,
.size = 2,
.init_val = 0x0000,
- .ro_mask = 0x3FFF,
+ .res_mask = 0x3800,
+ .ro_mask = 0x07FF,
.emu_mask = 0x0000,
.init = xen_pt_msixctrl_reg_init,
.u.w.read = xen_pt_word_reg_read,
diff --git a/hw/xen/xen_pt_msi.c b/hw/xen/xen_pt_msi.c
index 9ed9321c9..263e0514a 100644
--- a/hw/xen/xen_pt_msi.c
+++ b/hw/xen/xen_pt_msi.c
@@ -132,8 +132,8 @@ static int msi_msix_setup(XenPCIPassthroughState *s,
msix_entry, table_base);
if (rc) {
XEN_PT_ERR(&s->dev,
- "Mapping of MSI%s (rc: %i, vec: %#x, entry %#x)\n",
- is_msix ? "-X" : "", rc, gvec, msix_entry);
+ "Mapping of MSI%s (err: %i, vec: %#x, entry %#x)\n",
+ is_msix ? "-X" : "", errno, gvec, msix_entry);
return rc;
}
}
@@ -166,12 +166,12 @@ static int msi_msix_update(XenPCIPassthroughState *s,
pirq, gflags, table_addr);
if (rc) {
- XEN_PT_ERR(d, "Updating of MSI%s failed. (rc: %d)\n",
- is_msix ? "-X" : "", rc);
+ XEN_PT_ERR(d, "Updating of MSI%s failed. (err: %d)\n",
+ is_msix ? "-X" : "", errno);
if (xc_physdev_unmap_pirq(xen_xc, xen_domid, *old_pirq)) {
- XEN_PT_ERR(d, "Unmapping of MSI%s pirq %d failed.\n",
- is_msix ? "-X" : "", *old_pirq);
+ XEN_PT_ERR(d, "Unmapping of MSI%s pirq %d failed. (err: %d)\n",
+ is_msix ? "-X" : "", *old_pirq, errno);
}
*old_pirq = XEN_PT_UNASSIGNED_PIRQ;
}
@@ -199,8 +199,8 @@ static int msi_msix_disable(XenPCIPassthroughState *s,
is_msix ? "-X" : "", pirq, gvec);
rc = xc_domain_unbind_msi_irq(xen_xc, xen_domid, gvec, pirq, gflags);
if (rc) {
- XEN_PT_ERR(d, "Unbinding of MSI%s failed. (pirq: %d, gvec: %#x)\n",
- is_msix ? "-X" : "", pirq, gvec);
+ XEN_PT_ERR(d, "Unbinding of MSI%s failed. (err: %d, pirq: %d, gvec: %#x)\n",
+ is_msix ? "-X" : "", errno, pirq, gvec);
return rc;
}
}
@@ -208,8 +208,8 @@ static int msi_msix_disable(XenPCIPassthroughState *s,
XEN_PT_LOG(d, "Unmap MSI%s pirq %d\n", is_msix ? "-X" : "", pirq);
rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, pirq);
if (rc) {
- XEN_PT_ERR(d, "Unmapping of MSI%s pirq %d failed. (rc: %i)\n",
- is_msix ? "-X" : "", pirq, rc);
+ XEN_PT_ERR(d, "Unmapping of MSI%s pirq %d failed. (err: %i)\n",
+ is_msix ? "-X" : "", pirq, errno);
return rc;
}
@@ -385,8 +385,8 @@ int xen_pt_msix_update_remap(XenPCIPassthroughState *s, int bar_index)
ret = xc_domain_unbind_pt_irq(xen_xc, xen_domid, entry->pirq,
PT_IRQ_TYPE_MSI, 0, 0, 0, 0);
if (ret) {
- XEN_PT_ERR(&s->dev, "unbind MSI-X entry %d failed\n",
- entry->pirq);
+ XEN_PT_ERR(&s->dev, "unbind MSI-X entry %d failed (err: %d)\n",
+ entry->pirq, errno);
}
entry->updated = true;
}
@@ -434,11 +434,10 @@ static void pci_msix_write(void *opaque, hwaddr addr,
XenPCIPassthroughState *s = opaque;
XenPTMSIX *msix = s->msix;
XenPTMSIXEntry *entry;
- int entry_nr, offset;
+ unsigned int entry_nr, offset;
entry_nr = addr / PCI_MSIX_ENTRY_SIZE;
- if (entry_nr < 0 || entry_nr >= msix->total_entries) {
- XEN_PT_ERR(&s->dev, "asked MSI-X entry '%i' invalid!\n", entry_nr);
+ if (entry_nr >= msix->total_entries) {
return;
}
entry = &msix->msix_entry[entry_nr];
@@ -460,8 +459,11 @@ static void pci_msix_write(void *opaque, hwaddr addr,
+ PCI_MSIX_ENTRY_VECTOR_CTRL;
if (msix->enabled && !(*vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT)) {
- XEN_PT_ERR(&s->dev, "Can't update msix entry %d since MSI-X is"
- " already enabled.\n", entry_nr);
+ if (!entry->warned) {
+ entry->warned = true;
+ XEN_PT_ERR(&s->dev, "Can't update msix entry %d since MSI-X is"
+ " already enabled.\n", entry_nr);
+ }
return;
}