summaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/9pfs/codir.c2
-rw-r--r--hw/9pfs/cofile.c2
-rw-r--r--hw/9pfs/cofs.c2
-rw-r--r--hw/9pfs/coxattr.c2
-rw-r--r--hw/9pfs/virtio-9p-coth.c71
-rw-r--r--hw/9pfs/virtio-9p-coth.h12
-rw-r--r--hw/9pfs/virtio-9p-device.c27
-rw-r--r--hw/9pfs/virtio-9p.h2
-rw-r--r--hw/Makefile.objs1
-rw-r--r--hw/acpi/aml-build.c2
-rw-r--r--hw/acpi/core.c8
-rw-r--r--hw/acpi/memory_hotplug.c9
-rw-r--r--hw/alpha/dp264.c17
-rw-r--r--hw/arm/Makefile.objs4
-rw-r--r--hw/arm/allwinner-a10.c11
-rw-r--r--hw/arm/armv7m.c13
-rw-r--r--hw/arm/boot.c80
-rw-r--r--hw/arm/collie.c13
-rw-r--r--hw/arm/cubieboard.c14
-rw-r--r--hw/arm/digic_boards.c13
-rw-r--r--hw/arm/exynos4210.c8
-rw-r--r--hw/arm/exynos4_boards.c56
-rw-r--r--hw/arm/fsl-imx25.c308
-rw-r--r--hw/arm/fsl-imx31.c282
-rw-r--r--hw/arm/gumstix.c38
-rw-r--r--hw/arm/highbank.c139
-rw-r--r--hw/arm/imx25_pdk.c154
-rw-r--r--hw/arm/integratorcp.c15
-rw-r--r--hw/arm/kzm.c217
-rw-r--r--hw/arm/mainstone.c15
-rw-r--r--hw/arm/musicpal.c15
-rw-r--r--hw/arm/netduino2.c13
-rw-r--r--hw/arm/nseries.c44
-rw-r--r--hw/arm/omap1.c32
-rw-r--r--hw/arm/omap2.c17
-rw-r--r--hw/arm/omap_sx1.c42
-rw-r--r--hw/arm/palm.c15
-rw-r--r--hw/arm/pxa2xx.c19
-rw-r--r--hw/arm/realview.c90
-rw-r--r--hw/arm/spitz.c81
-rw-r--r--hw/arm/stellaris.c85
-rw-r--r--hw/arm/stm32f205_soc.c19
-rw-r--r--hw/arm/strongarm.c2
-rw-r--r--hw/arm/tosa.c15
-rw-r--r--hw/arm/versatilepb.c42
-rw-r--r--hw/arm/vexpress.c17
-rw-r--r--hw/arm/virt-acpi-build.c64
-rw-r--r--hw/arm/virt.c328
-rw-r--r--hw/arm/xilinx_zynq.c69
-rw-r--r--hw/arm/xlnx-ep108.c15
-rw-r--r--hw/arm/xlnx-zynqmp.c85
-rw-r--r--hw/arm/z2.c13
-rw-r--r--hw/audio/adlib.c9
-rw-r--r--hw/audio/es1370.c17
-rw-r--r--hw/audio/fmopl.c2
-rw-r--r--hw/audio/gus.c9
-rw-r--r--hw/audio/sb16.c15
-rw-r--r--hw/block/dataplane/virtio-blk.c20
-rw-r--r--hw/block/fdc.c22
-rw-r--r--hw/block/nand.c6
-rw-r--r--hw/block/nvme.c13
-rw-r--r--hw/block/onenand.c2
-rw-r--r--hw/block/virtio-blk.c23
-rw-r--r--hw/block/xen_blkif.h12
-rw-r--r--hw/block/xen_disk.c40
-rw-r--r--hw/bt/hci.c20
-rw-r--r--hw/bt/sdp.c31
-rw-r--r--hw/char/cadence_uart.c3
-rw-r--r--hw/char/escc.c13
-rw-r--r--hw/char/etraxfs_ser.c4
-rw-r--r--hw/char/exynos4210_uart.c6
-rw-r--r--hw/char/imx_serial.c234
-rw-r--r--hw/char/mcf_uart.c2
-rw-r--r--hw/char/omap_uart.c3
-rw-r--r--hw/char/virtio-serial-bus.c5
-rw-r--r--hw/core/loader.c5
-rw-r--r--hw/core/machine.c58
-rw-r--r--hw/core/null-machine.c16
-rw-r--r--hw/core/ptimer.c3
-rw-r--r--hw/core/qdev-properties.c4
-rw-r--r--hw/core/qdev.c28
-rw-r--r--hw/cpu/Makefile.objs1
-rw-r--r--hw/cpu/a15mpcore.c42
-rw-r--r--hw/cpu/a9mpcore.c11
-rw-r--r--hw/cpu/icc_bus.c118
-rw-r--r--hw/cris/axis_dev88.c19
-rw-r--r--hw/cris/boot.c2
-rw-r--r--hw/display/Makefile.objs6
-rw-r--r--hw/display/cg3.c4
-rw-r--r--hw/display/exynos4210_fimd.c4
-rw-r--r--hw/display/milkymist-tmu2.c4
-rw-r--r--hw/display/omap_dss.c3
-rw-r--r--hw/display/omap_lcdc.c3
-rw-r--r--hw/display/qxl-render.c2
-rw-r--r--hw/display/qxl.c8
-rw-r--r--hw/display/sm501.c2
-rw-r--r--hw/display/tc6393xb.c2
-rw-r--r--hw/display/tcx.c30
-rw-r--r--hw/display/vga.c124
-rw-r--r--hw/display/virtio-gpu-3d.c598
-rw-r--r--hw/display/virtio-gpu-pci.c4
-rw-r--r--hw/display/virtio-gpu.c151
-rw-r--r--hw/display/vmware_vga.c13
-rw-r--r--hw/display/xenfb.c10
-rw-r--r--hw/dma/i82374.c5
-rw-r--r--hw/dma/i8257.c31
-rw-r--r--hw/dma/omap_dma.c6
-rw-r--r--hw/dma/pxa2xx_dma.c3
-rw-r--r--hw/dma/xilinx_axidma.c2
-rw-r--r--hw/gpio/Makefile.objs1
-rw-r--r--hw/gpio/imx_gpio.c341
-rw-r--r--hw/gpio/omap_gpio.c4
-rw-r--r--hw/gpio/zaurus.c5
-rw-r--r--hw/i2c/Makefile.objs1
-rw-r--r--hw/i2c/imx_i2c.c335
-rw-r--r--hw/i386/Makefile.objs11
-rw-r--r--hw/i386/acpi-build.c1
-rw-r--r--hw/i386/acpi-dsdt.dsl1
-rw-r--r--hw/i386/intel_iommu.c89
-rw-r--r--hw/i386/kvm/clock.c18
-rw-r--r--hw/i386/kvm/ioapic.c10
-rw-r--r--hw/i386/kvm/pci-assign.c94
-rw-r--r--hw/i386/kvmvapic.c15
-rw-r--r--hw/i386/multiboot.c2
-rw-r--r--hw/i386/pc.c260
-rw-r--r--hw/i386/pc_piix.c280
-rw-r--r--hw/i386/pc_q35.c123
-rw-r--r--hw/i386/pc_sysfw.c4
-rw-r--r--hw/i386/pci-assign-load-rom.c87
-rw-r--r--hw/i386/q35-acpi-dsdt.dsl1
-rw-r--r--hw/i386/xen/xen_platform.c15
-rw-r--r--hw/ide/ahci.c268
-rw-r--r--hw/ide/ahci.h35
-rw-r--r--hw/ide/atapi.c194
-rw-r--r--hw/ide/cmd646.c1
-rw-r--r--hw/ide/core.c67
-rw-r--r--hw/ide/ich.c10
-rw-r--r--hw/ide/internal.h17
-rw-r--r--hw/ide/macio.c13
-rw-r--r--hw/ide/pci.c26
-rw-r--r--hw/input/Makefile.objs2
-rw-r--r--hw/input/adb.c2
-rw-r--r--hw/input/hid.c32
-rw-r--r--hw/input/milkymist-softusb.c4
-rw-r--r--hw/input/ps2.c24
-rw-r--r--hw/input/stellaris_input.c6
-rw-r--r--hw/input/tsc210x.c8
-rw-r--r--hw/input/virtio-input-hid.c27
-rw-r--r--hw/intc/Makefile.objs2
-rw-r--r--hw/intc/apic.c22
-rw-r--r--hw/intc/apic_common.c11
-rw-r--r--hw/intc/arm_gic.c317
-rw-r--r--hw/intc/arm_gic_common.c100
-rw-r--r--hw/intc/arm_gic_kvm.c146
-rw-r--r--hw/intc/arm_gicv3_common.c140
-rw-r--r--hw/intc/arm_gicv3_kvm.c149
-rw-r--r--hw/intc/armv7m_nvic.c22
-rw-r--r--hw/intc/imx_avic.c88
-rw-r--r--hw/intc/ioapic.c20
-rw-r--r--hw/intc/ioapic_common.c55
-rw-r--r--hw/intc/openpic.c1
-rw-r--r--hw/intc/openpic_kvm.c1
-rw-r--r--hw/intc/s390_flic_kvm.c2
-rw-r--r--hw/intc/vgic_common.h35
-rw-r--r--hw/intc/xics.c4
-rw-r--r--hw/isa/i82378.c3
-rw-r--r--hw/isa/lpc_ich9.c2
-rw-r--r--hw/lm32/lm32_boards.c46
-rw-r--r--hw/lm32/milkymist-hw.h3
-rw-r--r--hw/lm32/milkymist.c17
-rw-r--r--hw/m68k/an5206.c17
-rw-r--r--hw/m68k/dummy_m68k.c15
-rw-r--r--hw/m68k/mcf5208.c19
-rw-r--r--hw/mem/pc-dimm.c18
-rw-r--r--hw/microblaze/boot.c4
-rw-r--r--hw/microblaze/petalogix_ml605_mmu.c19
-rw-r--r--hw/microblaze/petalogix_s3adsp1800_mmu.c19
-rw-r--r--hw/mips/cputimer.c33
-rw-r--r--hw/mips/gt64xxx_pci.c3
-rw-r--r--hw/mips/mips_fulong2e.c30
-rw-r--r--hw/mips/mips_jazz.c59
-rw-r--r--hw/mips/mips_malta.c41
-rw-r--r--hw/mips/mips_mipssim.c17
-rw-r--r--hw/mips/mips_r4k.c21
-rw-r--r--hw/misc/Makefile.objs1
-rw-r--r--hw/misc/imx_ccm.c105
-rw-r--r--hw/misc/ivshmem.c837
-rw-r--r--hw/misc/macio/cuda.c247
-rw-r--r--hw/misc/macio/mac_dbdma.c2
-rw-r--r--hw/misc/macio/macio.c1
-rw-r--r--hw/misc/omap_clk.c2
-rw-r--r--hw/misc/omap_gpmc.c3
-rw-r--r--hw/misc/omap_sdrc.c3
-rw-r--r--hw/misc/pvpanic.c3
-rw-r--r--hw/misc/zynq-xadc.c302
-rw-r--r--hw/moxie/moxiesim.c23
-rw-r--r--hw/net/Makefile.objs1
-rw-r--r--hw/net/cadence_gem.c8
-rw-r--r--hw/net/e1000.c495
-rw-r--r--hw/net/e1000_regs.h8
-rw-r--r--hw/net/eepro100.c16
-rw-r--r--hw/net/fsl_etsec/etsec.c2
-rw-r--r--hw/net/fsl_etsec/rings.c4
-rw-r--r--hw/net/imx_fec.c709
-rw-r--r--hw/net/lan9118.c20
-rw-r--r--hw/net/milkymist-minimac2.c2
-rw-r--r--hw/net/ne2000-isa.c1
-rw-r--r--hw/net/ne2000.c24
-rw-r--r--hw/net/ne2000.h1
-rw-r--r--hw/net/pcnet.c17
-rw-r--r--hw/net/rocker/rocker.c20
-rw-r--r--hw/net/rocker/rocker_desc.c12
-rw-r--r--hw/net/rocker/rocker_fp.c2
-rw-r--r--hw/net/rocker/rocker_of_dpa.c21
-rw-r--r--hw/net/rtl8139.c125
-rw-r--r--hw/net/smc91c111.c33
-rw-r--r--hw/net/vhost_net.c108
-rw-r--r--hw/net/virtio-net.c16
-rw-r--r--hw/net/vmxnet3.c52
-rw-r--r--hw/net/vmxnet3.h6
-rw-r--r--hw/net/vmxnet_tx_pkt.c19
-rw-r--r--hw/net/xen_nic.c1
-rw-r--r--hw/nvram/fw_cfg.c262
-rw-r--r--hw/nvram/mac_nvram.c1
-rw-r--r--hw/openrisc/cputimer.c7
-rw-r--r--hw/openrisc/openrisc_sim.c21
-rw-r--r--hw/pci-host/bonito.c16
-rw-r--r--hw/pci-host/grackle.c2
-rw-r--r--hw/pci-host/piix.c109
-rw-r--r--hw/pci-host/ppce500.c4
-rw-r--r--hw/pci-host/prep.c6
-rw-r--r--hw/pci-host/q35.c27
-rw-r--r--hw/pci-host/uninorth.c8
-rw-r--r--hw/pci/msi.c2
-rw-r--r--hw/pci/msix.c10
-rw-r--r--hw/pci/pci.c75
-rw-r--r--hw/pci/pci_host.c15
-rw-r--r--hw/pci/pcie.c51
-rw-r--r--hw/pci/pcie_aer.c6
-rw-r--r--hw/pci/shpc.c1
-rw-r--r--hw/ppc/Makefile.objs2
-rw-r--r--hw/ppc/e500.c6
-rw-r--r--hw/ppc/e500plat.c17
-rw-r--r--hw/ppc/mac.h3
-rw-r--r--hw/ppc/mac_newworld.c12
-rw-r--r--hw/ppc/mac_oldworld.c30
-rw-r--r--hw/ppc/mpc8544ds.c15
-rw-r--r--hw/ppc/ppc.c4
-rw-r--r--hw/ppc/ppc405_boards.c97
-rw-r--r--hw/ppc/ppc405_uc.c2
-rw-r--r--hw/ppc/ppc440_bamboo.c18
-rw-r--r--hw/ppc/prep.c60
-rw-r--r--hw/ppc/spapr.c604
-rw-r--r--hw/ppc/spapr_drc.c113
-rw-r--r--hw/ppc/spapr_events.c51
-rw-r--r--hw/ppc/spapr_hcall.c54
-rw-r--r--hw/ppc/spapr_iommu.c38
-rw-r--r--hw/ppc/spapr_pci.c60
-rw-r--r--hw/ppc/spapr_pci_vfio.c2
-rw-r--r--hw/ppc/spapr_rng.c186
-rw-r--r--hw/ppc/spapr_rtas.c59
-rw-r--r--hw/ppc/virtex_ml507.c15
-rw-r--r--hw/s390x/Makefile.objs2
-rw-r--r--hw/s390x/css.c15
-rw-r--r--hw/s390x/event-facility.c53
-rw-r--r--hw/s390x/ipl.c125
-rw-r--r--hw/s390x/ipl.h30
-rw-r--r--hw/s390x/s390-pci-bus.c34
-rw-r--r--hw/s390x/s390-pci-bus.h2
-rw-r--r--hw/s390x/s390-pci-inst.c7
-rw-r--r--hw/s390x/s390-skeys-kvm.c75
-rw-r--r--hw/s390x/s390-skeys.c415
-rw-r--r--hw/s390x/s390-virtio-bus.c5
-rw-r--r--hw/s390x/s390-virtio-ccw.c179
-rw-r--r--hw/s390x/s390-virtio.c73
-rw-r--r--hw/s390x/s390-virtio.h4
-rw-r--r--hw/s390x/sclp.c270
-rw-r--r--hw/s390x/sclpcpu.c28
-rw-r--r--hw/s390x/sclpquiesce.c4
-rw-r--r--hw/s390x/virtio-ccw.c76
-rw-r--r--hw/s390x/virtio-ccw.h6
-rw-r--r--hw/scsi/megasas.c6
-rw-r--r--hw/scsi/scsi-bus.c9
-rw-r--r--hw/scsi/scsi-disk.c135
-rw-r--r--hw/scsi/scsi-generic.c80
-rw-r--r--hw/scsi/spapr_vscsi.c1
-rw-r--r--hw/scsi/vhost-scsi.c11
-rw-r--r--hw/scsi/virtio-scsi-dataplane.c32
-rw-r--r--hw/scsi/virtio-scsi.c25
-rw-r--r--hw/sd/milkymist-memcard.c2
-rw-r--r--hw/sd/omap_mmc.c8
-rw-r--r--hw/sd/pl181.c4
-rw-r--r--hw/sd/pxa2xx_mmci.c2
-rw-r--r--hw/sd/sd.c10
-rw-r--r--hw/sd/sdhci-internal.h (renamed from hw/sd/sdhci.h)71
-rw-r--r--hw/sd/sdhci.c99
-rw-r--r--hw/sd/ssi-sd.c2
-rw-r--r--hw/sh4/r2d.c21
-rw-r--r--hw/sh4/shix.c21
-rw-r--r--hw/smbios/Makefile.objs1
-rw-r--r--hw/smbios/smbios.c (renamed from hw/i386/smbios.c)103
-rw-r--r--hw/sparc/leon3.c17
-rw-r--r--hw/sparc/sun4m.c222
-rw-r--r--hw/sparc64/sun4u.c82
-rw-r--r--hw/ssi/omap_spi.c3
-rw-r--r--hw/timer/hpet.c10
-rw-r--r--hw/timer/imx_epit.c121
-rw-r--r--hw/timer/imx_gpt.c150
-rw-r--r--hw/timer/m48t59.c4
-rw-r--r--hw/timer/omap_gptimer.c3
-rw-r--r--hw/tpm/tpm_passthrough.c2
-rw-r--r--hw/tpm/tpm_tis.c2
-rw-r--r--hw/tricore/tricore_testboard.c35
-rw-r--r--hw/unicore32/puv3.c17
-rw-r--r--hw/usb/Makefile.objs5
-rw-r--r--hw/usb/bus.c9
-rw-r--r--hw/usb/ccid-card-emulated.c4
-rw-r--r--hw/usb/ccid-card-passthru.c2
-rw-r--r--hw/usb/core.c18
-rw-r--r--hw/usb/dev-audio.c2
-rw-r--r--hw/usb/dev-mtp.c3
-rw-r--r--hw/usb/dev-network.c12
-rw-r--r--hw/usb/dev-storage.c30
-rw-r--r--hw/usb/hcd-ehci-pci.c6
-rw-r--r--hw/usb/hcd-ehci.c19
-rw-r--r--hw/usb/hcd-xhci.c6
-rw-r--r--hw/usb/host-libusb.c5
-rw-r--r--hw/usb/redirect.c8
-rw-r--r--hw/vfio/Makefile.objs2
-rw-r--r--hw/vfio/common.c144
-rw-r--r--hw/vfio/pci-quirks.c1204
-rw-r--r--hw/vfio/pci.c1476
-rw-r--r--hw/vfio/pci.h159
-rw-r--r--hw/vfio/platform.c122
-rw-r--r--hw/virtio/dataplane/vring.c96
-rw-r--r--hw/virtio/vhost-backend.c146
-rw-r--r--hw/virtio/vhost-user.c620
-rw-r--r--hw/virtio/vhost.c175
-rw-r--r--hw/virtio/virtio-balloon.c4
-rw-r--r--hw/virtio/virtio-bus.c3
-rw-r--r--hw/virtio/virtio-pci.c370
-rw-r--r--hw/virtio/virtio-pci.h46
-rw-r--r--hw/virtio/virtio.c176
-rw-r--r--hw/watchdog/wdt_i6300esb.c11
-rw-r--r--hw/xen/Makefile.objs1
-rw-r--r--hw/xen/xen-host-pci-device.c12
-rw-r--r--hw/xen/xen-host-pci-device.h2
-rw-r--r--hw/xen/xen_pt.c200
-rw-r--r--hw/xen/xen_pt.h31
-rw-r--r--hw/xen/xen_pt_config_init.c287
-rw-r--r--hw/xen/xen_pt_graphics.c272
-rw-r--r--hw/xen/xen_pt_msi.c33
-rw-r--r--hw/xenpv/xen_domainbuild.c2
-rw-r--r--hw/xenpv/xen_machine_pv.c17
-rw-r--r--hw/xtensa/sim.c25
-rw-r--r--hw/xtensa/xtfpga.c132
356 files changed, 15718 insertions, 6643 deletions
diff --git a/hw/9pfs/codir.c b/hw/9pfs/codir.c
index 65ad3298b..ec9cc7fb2 100644
--- a/hw/9pfs/codir.c
+++ b/hw/9pfs/codir.c
@@ -14,7 +14,7 @@
#include "fsdev/qemu-fsdev.h"
#include "qemu/thread.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
#include "virtio-9p-coth.h"
int v9fs_co_readdir_r(V9fsPDU *pdu, V9fsFidState *fidp, struct dirent *dent,
diff --git a/hw/9pfs/cofile.c b/hw/9pfs/cofile.c
index 2efebf357..7cb55ee93 100644
--- a/hw/9pfs/cofile.c
+++ b/hw/9pfs/cofile.c
@@ -14,7 +14,7 @@
#include "fsdev/qemu-fsdev.h"
#include "qemu/thread.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
#include "virtio-9p-coth.h"
int v9fs_co_st_gen(V9fsPDU *pdu, V9fsPath *path, mode_t st_mode,
diff --git a/hw/9pfs/cofs.c b/hw/9pfs/cofs.c
index 42ee614e2..e1953a9aa 100644
--- a/hw/9pfs/cofs.c
+++ b/hw/9pfs/cofs.c
@@ -14,7 +14,7 @@
#include "fsdev/qemu-fsdev.h"
#include "qemu/thread.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
#include "virtio-9p-coth.h"
static ssize_t __readlink(V9fsState *s, V9fsPath *path, V9fsString *buf)
diff --git a/hw/9pfs/coxattr.c b/hw/9pfs/coxattr.c
index 18ee08df0..55c0d231c 100644
--- a/hw/9pfs/coxattr.c
+++ b/hw/9pfs/coxattr.c
@@ -14,7 +14,7 @@
#include "fsdev/qemu-fsdev.h"
#include "qemu/thread.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
#include "virtio-9p-coth.h"
int v9fs_co_llistxattr(V9fsPDU *pdu, V9fsPath *path, void *value, size_t size)
diff --git a/hw/9pfs/virtio-9p-coth.c b/hw/9pfs/virtio-9p-coth.c
index 8185c533c..ab9425c60 100644
--- a/hw/9pfs/virtio-9p-coth.c
+++ b/hw/9pfs/virtio-9p-coth.c
@@ -12,71 +12,30 @@
*
*/
-#include "fsdev/qemu-fsdev.h"
-#include "qemu/thread.h"
-#include "qemu/event_notifier.h"
-#include "block/coroutine.h"
+#include "qemu-common.h"
+#include "block/thread-pool.h"
+#include "qemu/coroutine.h"
+#include "qemu/main-loop.h"
#include "virtio-9p-coth.h"
-/* v9fs glib thread pool */
-static V9fsThPool v9fs_pool;
-
-void co_run_in_worker_bh(void *opaque)
+/* Called from QEMU I/O thread. */
+static void coroutine_enter_cb(void *opaque, int ret)
{
Coroutine *co = opaque;
- g_thread_pool_push(v9fs_pool.pool, co, NULL);
-}
-
-static void v9fs_qemu_process_req_done(EventNotifier *e)
-{
- Coroutine *co;
-
- event_notifier_test_and_clear(e);
-
- while ((co = g_async_queue_try_pop(v9fs_pool.completed)) != NULL) {
- qemu_coroutine_enter(co, NULL);
- }
+ qemu_coroutine_enter(co, NULL);
}
-static void v9fs_thread_routine(gpointer data, gpointer user_data)
+/* Called from worker thread. */
+static int coroutine_enter_func(void *arg)
{
- Coroutine *co = data;
-
+ Coroutine *co = arg;
qemu_coroutine_enter(co, NULL);
-
- g_async_queue_push(v9fs_pool.completed, co);
-
- event_notifier_set(&v9fs_pool.e);
+ return 0;
}
-int v9fs_init_worker_threads(void)
+void co_run_in_worker_bh(void *opaque)
{
- int ret = 0;
- V9fsThPool *p = &v9fs_pool;
- sigset_t set, oldset;
-
- sigfillset(&set);
- /* Leave signal handling to the iothread. */
- pthread_sigmask(SIG_SETMASK, &set, &oldset);
-
- p->pool = g_thread_pool_new(v9fs_thread_routine, p, -1, FALSE, NULL);
- if (!p->pool) {
- ret = -1;
- goto err_out;
- }
- p->completed = g_async_queue_new();
- if (!p->completed) {
- /*
- * We are going to terminate.
- * So don't worry about cleanup
- */
- ret = -1;
- goto err_out;
- }
- event_notifier_init(&p->e, 0);
-
- event_notifier_set_handler(&p->e, v9fs_qemu_process_req_done);
-err_out:
- pthread_sigmask(SIG_SETMASK, &oldset, NULL);
- return ret;
+ Coroutine *co = opaque;
+ thread_pool_submit_aio(aio_get_thread_pool(qemu_get_aio_context()),
+ coroutine_enter_func, co, coroutine_enter_cb, co);
}
diff --git a/hw/9pfs/virtio-9p-coth.h b/hw/9pfs/virtio-9p-coth.h
index 4f51b250d..4ac1aaf90 100644
--- a/hw/9pfs/virtio-9p-coth.h
+++ b/hw/9pfs/virtio-9p-coth.h
@@ -16,16 +16,8 @@
#define _QEMU_VIRTIO_9P_COTH_H
#include "qemu/thread.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
#include "virtio-9p.h"
-#include <glib.h>
-
-typedef struct V9fsThPool {
- EventNotifier e;
-
- GThreadPool *pool;
- GAsyncQueue *completed;
-} V9fsThPool;
/*
* we want to use bottom half because we want to make sure the below
@@ -45,7 +37,7 @@ typedef struct V9fsThPool {
qemu_bh_schedule(co_bh); \
/* \
* yield in qemu thread and re-enter back \
- * in glib worker thread \
+ * in worker thread \
*/ \
qemu_coroutine_yield(); \
qemu_bh_delete(co_bh); \
diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c
index 93a407c45..b42d3b30a 100644
--- a/hw/9pfs/virtio-9p-device.c
+++ b/hw/9pfs/virtio-9p-device.c
@@ -43,6 +43,16 @@ static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config)
g_free(cfg);
}
+static void virtio_9p_save(QEMUFile *f, void *opaque)
+{
+ virtio_save(VIRTIO_DEVICE(opaque), f);
+}
+
+static int virtio_9p_load(QEMUFile *f, void *opaque, int version_id)
+{
+ return virtio_load(VIRTIO_DEVICE(opaque), f, version_id);
+}
+
static void virtio_9p_device_realize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
@@ -106,10 +116,6 @@ static void virtio_9p_device_realize(DeviceState *dev, Error **errp)
" and export path:%s", s->fsconf.fsdev_id, s->ctx.fs_root);
goto out;
}
- if (v9fs_init_worker_threads() < 0) {
- error_setg(errp, "worker thread initialization failed");
- goto out;
- }
/*
* Check details of export path, We need to use fs driver
@@ -130,6 +136,7 @@ static void virtio_9p_device_realize(DeviceState *dev, Error **errp)
}
v9fs_path_free(&path);
+ register_savevm(dev, "virtio-9p", -1, 1, virtio_9p_save, virtio_9p_load, s);
return;
out:
g_free(s->ctx.fs_root);
@@ -138,6 +145,17 @@ out:
v9fs_path_free(&path);
}
+static void virtio_9p_device_unrealize(DeviceState *dev, Error **errp)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ V9fsState *s = VIRTIO_9P(dev);
+
+ virtio_cleanup(vdev);
+ unregister_savevm(dev, "virtio-9p", s);
+ g_free(s->ctx.fs_root);
+ g_free(s->tag);
+}
+
/* virtio-9p device */
static Property virtio_9p_properties[] = {
@@ -154,6 +172,7 @@ static void virtio_9p_class_init(ObjectClass *klass, void *data)
dc->props = virtio_9p_properties;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
vdc->realize = virtio_9p_device_realize;
+ vdc->unrealize = virtio_9p_device_unrealize;
vdc->get_features = virtio_9p_get_features;
vdc->get_config = virtio_9p_get_config;
}
diff --git a/hw/9pfs/virtio-9p.h b/hw/9pfs/virtio-9p.h
index 2e7d48857..d7a4dc1e9 100644
--- a/hw/9pfs/virtio-9p.h
+++ b/hw/9pfs/virtio-9p.h
@@ -13,7 +13,7 @@
#include "fsdev/file-op-9p.h"
#include "fsdev/virtio-9p-marshal.h"
#include "qemu/thread.h"
-#include "block/coroutine.h"
+#include "qemu/coroutine.h"
enum {
P9_TLERROR = 6,
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 73afa41b3..7e7c24110 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -31,6 +31,7 @@ devices-dirs-$(CONFIG_VIRTIO) += virtio/
devices-dirs-$(CONFIG_SOFTMMU) += watchdog/
devices-dirs-$(CONFIG_SOFTMMU) += xen/
devices-dirs-$(CONFIG_MEM_HOTPLUG) += mem/
+devices-dirs-$(CONFIG_SMBIOS) += smbios/
devices-dirs-y += core/
common-obj-y += $(devices-dirs-y)
obj-y += $(devices-dirs-y)
diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c
index 0d4b3247b..a00a0abe8 100644
--- a/hw/acpi/aml-build.c
+++ b/hw/acpi/aml-build.c
@@ -1163,9 +1163,7 @@ void *acpi_data_push(GArray *table_data, unsigned size)
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;
}
diff --git a/hw/acpi/core.c b/hw/acpi/core.c
index fe6215af4..21e113d71 100644
--- a/hw/acpi/core.c
+++ b/hw/acpi/core.c
@@ -625,8 +625,12 @@ void acpi_pm1_cnt_reset(ACPIREGS *ar)
void acpi_gpe_init(ACPIREGS *ar, uint8_t len)
{
ar->gpe.len = len;
- ar->gpe.sts = g_malloc0(len / 2);
- ar->gpe.en = g_malloc0(len / 2);
+ /* Only first len / 2 bytes are ever used,
+ * but the caller in ich9.c migrates full len bytes.
+ * TODO: fix ich9.c and drop the extra allocation.
+ */
+ ar->gpe.sts = g_malloc0(len);
+ ar->gpe.en = g_malloc0(len);
}
void acpi_gpe_reset(ACPIREGS *ar)
diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c
index 2ff0d5ce1..e4b9a01f8 100644
--- a/hw/acpi/memory_hotplug.c
+++ b/hw/acpi/memory_hotplug.c
@@ -155,6 +155,7 @@ static void acpi_memory_hotplug_write(void *opaque, hwaddr addr, uint64_t data,
qapi_event_send_mem_unplug_error(dev->id,
error_get_pretty(local_err),
&error_abort);
+ error_free(local_err);
break;
}
trace_mhp_acpi_pc_dimm_deleted(mem_st->selector);
@@ -238,10 +239,12 @@ void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st,
mdev->dimm = dev;
mdev->is_enabled = true;
- mdev->is_inserting = true;
+ if (dev->hotplugged) {
+ mdev->is_inserting = true;
- /* do ACPI magic */
- acpi_send_gpe_event(ar, irq, ACPI_MEMORY_HOTPLUG_STATUS);
+ /* do ACPI magic */
+ acpi_send_gpe_event(ar, irq, ACPI_MEMORY_HOTPLUG_STATUS);
+ }
return;
}
diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c
index f86e7bb83..27bdaa1ad 100644
--- a/hw/alpha/dp264.c
+++ b/hw/alpha/dp264.c
@@ -168,17 +168,12 @@ static void clipper_init(MachineState *machine)
}
}
-static QEMUMachine clipper_machine = {
- .name = "clipper",
- .desc = "Alpha DP264/CLIPPER",
- .init = clipper_init,
- .max_cpus = 4,
- .is_default = 1,
-};
-
-static void clipper_machine_init(void)
+static void clipper_machine_init(MachineClass *mc)
{
- qemu_register_machine(&clipper_machine);
+ mc->desc = "Alpha DP264/CLIPPER";
+ mc->init = clipper_init;
+ mc->max_cpus = 4;
+ mc->is_default = 1;
}
-machine_init(clipper_machine_init);
+DEFINE_MACHINE("clipper", clipper_machine_init)
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index cf346c1d0..2195b60fa 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -1,6 +1,6 @@
obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o
obj-$(CONFIG_DIGIC) += digic_boards.o
-obj-y += integratorcp.o kzm.o mainstone.o musicpal.o nseries.o
+obj-y += integratorcp.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
@@ -13,3 +13,5 @@ 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
+obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
+obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c
index 43dc0a12d..b0ca81cea 100644
--- a/hw/arm/allwinner-a10.c
+++ b/hw/arm/allwinner-a10.c
@@ -39,6 +39,9 @@ static void aw_a10_init(Object *obj)
qemu_check_nic_model(&nd_table[0], TYPE_AW_EMAC);
qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]);
}
+
+ object_initialize(&s->sata, sizeof(s->sata), TYPE_ALLWINNER_AHCI);
+ qdev_set_parent_bus(DEVICE(&s->sata), sysbus_get_default());
}
static void aw_a10_realize(DeviceState *dev, Error **errp)
@@ -93,6 +96,14 @@ static void aw_a10_realize(DeviceState *dev, Error **errp)
sysbus_mmio_map(sysbusdev, 0, AW_A10_EMAC_BASE);
sysbus_connect_irq(sysbusdev, 0, s->irq[55]);
+ object_property_set_bool(OBJECT(&s->sata), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->sata), 0, AW_A10_SATA_BASE);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->sata), 0, s->irq[56]);
+
/* FIXME use a qdev chardev prop instead of serial_hds[] */
serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, s->irq[1],
115200, serial_hds[0], DEVICE_NATIVE_ENDIAN);
diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c
index c6eab6de3..a80d2ad29 100644
--- a/hw/arm/armv7m.c
+++ b/hw/arm/armv7m.c
@@ -166,17 +166,15 @@ static void armv7m_reset(void *opaque)
mem_size is in bytes.
Returns the NVIC array. */
-qemu_irq *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
+DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
const char *kernel_filename, const char *cpu_model)
{
ARMCPU *cpu;
CPUARMState *env;
DeviceState *nvic;
- qemu_irq *pic = g_new(qemu_irq, num_irq);
int image_size;
uint64_t entry;
uint64_t lowaddr;
- int i;
int big_endian;
MemoryRegion *hack = g_new(MemoryRegion, 1);
@@ -198,9 +196,6 @@ qemu_irq *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
qdev_init_nofail(nvic);
sysbus_connect_irq(SYS_BUS_DEVICE(nvic), 0,
qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ));
- for (i = 0; i < num_irq; i++) {
- pic[i] = qdev_get_gpio_in(nvic, i);
- }
#ifdef TARGET_WORDS_BIGENDIAN
big_endian = 1;
@@ -215,7 +210,7 @@ qemu_irq *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
if (kernel_filename) {
image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr,
- NULL, big_endian, ELF_MACHINE, 1);
+ NULL, big_endian, EM_ARM, 1);
if (image_size < 0) {
image_size = load_image_targphys(kernel_filename, 0, mem_size);
lowaddr = 0;
@@ -229,12 +224,12 @@ qemu_irq *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
/* Hack to map an additional page of ram at the top of the address
space. This stops qemu complaining about executing code outside RAM
when returning from an exception. */
- memory_region_init_ram(hack, NULL, "armv7m.hack", 0x1000, &error_abort);
+ memory_region_init_ram(hack, NULL, "armv7m.hack", 0x1000, &error_fatal);
vmstate_register_ram_global(hack);
memory_region_add_subregion(system_memory, 0xfffff000, hack);
qemu_register_reset(armv7m_reset, cpu);
- return pic;
+ return nvic;
}
static Property bitband_properties[] = {
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index 5b969cda1..75f69bfe0 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -10,6 +10,8 @@
#include "config.h"
#include "hw/hw.h"
#include "hw/arm/arm.h"
+#include "hw/arm/linux-boot-if.h"
+#include "sysemu/kvm.h"
#include "sysemu/sysemu.h"
#include "hw/boards.h"
#include "hw/loader.h"
@@ -27,14 +29,15 @@
#define KERNEL64_LOAD_ADDR 0x00080000
typedef enum {
- FIXUP_NONE = 0, /* do nothing */
- FIXUP_TERMINATOR, /* end of insns */
- FIXUP_BOARDID, /* overwrite with board ID number */
- FIXUP_ARGPTR, /* overwrite with pointer to kernel args */
- FIXUP_ENTRYPOINT, /* overwrite with kernel entry point */
- FIXUP_GIC_CPU_IF, /* overwrite with GIC CPU interface address */
- FIXUP_BOOTREG, /* overwrite with boot register address */
- FIXUP_DSB, /* overwrite with correct DSB insn for cpu */
+ FIXUP_NONE = 0, /* do nothing */
+ FIXUP_TERMINATOR, /* end of insns */
+ FIXUP_BOARDID, /* overwrite with board ID number */
+ FIXUP_BOARD_SETUP, /* overwrite with board specific setup code address */
+ FIXUP_ARGPTR, /* overwrite with pointer to kernel args */
+ FIXUP_ENTRYPOINT, /* overwrite with kernel entry point */
+ FIXUP_GIC_CPU_IF, /* overwrite with GIC CPU interface address */
+ FIXUP_BOOTREG, /* overwrite with boot register address */
+ FIXUP_DSB, /* overwrite with correct DSB insn for cpu */
FIXUP_MAX,
} FixupType;
@@ -57,8 +60,17 @@ static const ARMInsnFixup bootloader_aarch64[] = {
{ 0, FIXUP_TERMINATOR }
};
-/* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */
+/* A very small bootloader: call the board-setup code (if needed),
+ * set r0-r2, then jump to the kernel.
+ * If we're not calling boot setup code then we don't copy across
+ * the first BOOTLOADER_NO_BOARD_SETUP_OFFSET insns in this array.
+ */
+
static const ARMInsnFixup bootloader[] = {
+ { 0xe28fe008 }, /* add lr, pc, #8 */
+ { 0xe51ff004 }, /* ldr pc, [pc, #-4] */
+ { 0, FIXUP_BOARD_SETUP },
+#define BOOTLOADER_NO_BOARD_SETUP_OFFSET 3
{ 0xe3a00000 }, /* mov r0, #0 */
{ 0xe59f1004 }, /* ldr r1, [pc, #4] */
{ 0xe59f2004 }, /* ldr r2, [pc, #4] */
@@ -130,6 +142,7 @@ static void write_bootloader(const char *name, hwaddr addr,
case FIXUP_NONE:
break;
case FIXUP_BOARDID:
+ case FIXUP_BOARD_SETUP:
case FIXUP_ARGPTR:
case FIXUP_ENTRYPOINT:
case FIXUP_GIC_CPU_IF:
@@ -483,7 +496,8 @@ static void do_cpu_reset(void *opaque)
}
/* Set to non-secure if not a secure boot */
- if (!info->secure_boot) {
+ if (!info->secure_boot &&
+ (cs != first_cpu || !info->secure_board_setup)) {
/* Linux expects non-secure state */
env->cp15.scr_el3 |= SCR_NS;
}
@@ -555,6 +569,20 @@ static void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key,
fw_cfg_add_bytes(fw_cfg, data_key, data, size);
}
+static int do_arm_linux_init(Object *obj, void *opaque)
+{
+ if (object_dynamic_cast(obj, TYPE_ARM_LINUX_BOOT_IF)) {
+ ARMLinuxBootIf *albif = ARM_LINUX_BOOT_IF(obj);
+ ARMLinuxBootIfClass *albifc = ARM_LINUX_BOOT_IF_GET_CLASS(obj);
+ struct arm_boot_info *info = opaque;
+
+ if (albifc->arm_linux_init) {
+ albifc->arm_linux_init(albif, info->secure_boot);
+ }
+ }
+ return 0;
+}
+
static void arm_load_kernel_notify(Notifier *notifier, void *data)
{
CPUState *cs;
@@ -572,6 +600,12 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
struct arm_boot_info *info =
container_of(n, struct arm_boot_info, load_kernel_notifier);
+ /* The board code is not supposed to set secure_board_setup unless
+ * running its code in secure mode is actually possible, and KVM
+ * doesn't support secure.
+ */
+ assert(!(info->secure_board_setup && kvm_enabled()));
+
/* Load the kernel. */
if (!info->kernel_filename || info->firmware_loaded) {
@@ -625,6 +659,9 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
elf_machine = EM_AARCH64;
} else {
primary_loader = bootloader;
+ if (!info->write_board_setup) {
+ primary_loader += BOOTLOADER_NO_BOARD_SETUP_OFFSET;
+ }
kernel_load_offset = KERNEL_LOAD_ADDR;
elf_machine = EM_ARM;
}
@@ -730,6 +767,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
info->initrd_size = initrd_size;
fixupcontext[FIXUP_BOARDID] = info->board_id;
+ fixupcontext[FIXUP_BOARD_SETUP] = info->board_setup_addr;
/* for device tree boot, we pass the DTB directly in r2. Otherwise
* we point to the kernel args.
@@ -778,6 +816,15 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
if (info->nb_cpus > 1) {
info->write_secondary_boot(cpu, info);
}
+ if (info->write_board_setup) {
+ info->write_board_setup(cpu, info);
+ }
+
+ /* Notify devices which need to fake up firmware initialization
+ * that we're doing a direct kernel boot.
+ */
+ object_child_foreach_recursive(object_get_root(),
+ do_arm_linux_init, info);
}
info->is_linux = is_linux;
@@ -803,3 +850,16 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
qemu_register_reset(do_cpu_reset, ARM_CPU(cs));
}
}
+
+static const TypeInfo arm_linux_boot_if_info = {
+ .name = TYPE_ARM_LINUX_BOOT_IF,
+ .parent = TYPE_INTERFACE,
+ .class_size = sizeof(ARMLinuxBootIfClass),
+};
+
+static void arm_linux_boot_register_types(void)
+{
+ type_register_static(&arm_linux_boot_if_info);
+}
+
+type_init(arm_linux_boot_register_types)
diff --git a/hw/arm/collie.c b/hw/arm/collie.c
index 6c9b82fc5..9991c0c4a 100644
--- a/hw/arm/collie.c
+++ b/hw/arm/collie.c
@@ -58,15 +58,10 @@ static void collie_init(MachineState *machine)
arm_load_kernel(s->cpu, &collie_binfo);
}
-static QEMUMachine collie_machine = {
- .name = "collie",
- .desc = "Collie PDA (SA-1110)",
- .init = collie_init,
-};
-
-static void collie_machine_init(void)
+static void collie_machine_init(MachineClass *mc)
{
- qemu_register_machine(&collie_machine);
+ mc->desc = "Sharp SL-5500 (Collie) PDA (SA-1110)";
+ mc->init = collie_init;
}
-machine_init(collie_machine_init)
+DEFINE_MACHINE("collie", collie_machine_init)
diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c
index 1582250eb..bf068cd3c 100644
--- a/hw/arm/cubieboard.c
+++ b/hw/arm/cubieboard.c
@@ -74,16 +74,10 @@ static void cubieboard_init(MachineState *machine)
arm_load_kernel(&s->a10->cpu, &cubieboard_binfo);
}
-static QEMUMachine cubieboard_machine = {
- .name = "cubieboard",
- .desc = "cubietech cubieboard",
- .init = cubieboard_init,
-};
-
-
-static void cubieboard_machine_init(void)
+static void cubieboard_machine_init(MachineClass *mc)
{
- qemu_register_machine(&cubieboard_machine);
+ mc->desc = "cubietech cubieboard";
+ mc->init = cubieboard_init;
}
-machine_init(cubieboard_machine_init)
+DEFINE_MACHINE("cubieboard", cubieboard_machine_init)
diff --git a/hw/arm/digic_boards.c b/hw/arm/digic_boards.c
index f8ba9e595..710045adc 100644
--- a/hw/arm/digic_boards.c
+++ b/hw/arm/digic_boards.c
@@ -148,15 +148,10 @@ static void canon_a1100_init(MachineState *machine)
digic4_board_init(&digic4_board_canon_a1100);
}
-static QEMUMachine canon_a1100 = {
- .name = "canon-a1100",
- .desc = "Canon PowerShot A1100 IS",
- .init = &canon_a1100_init,
-};
-
-static void digic_register_machines(void)
+static void canon_a1100_machine_init(MachineClass *mc)
{
- qemu_register_machine(&canon_a1100);
+ mc->desc = "Canon PowerShot A1100 IS";
+ mc->init = &canon_a1100_init;
}
-machine_init(digic_register_machines)
+DEFINE_MACHINE("canon-a1100", canon_a1100_machine_init)
diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c
index c55fab813..d934980ef 100644
--- a/hw/arm/exynos4210.c
+++ b/hw/arm/exynos4210.c
@@ -259,7 +259,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
/* Internal ROM */
memory_region_init_ram(&s->irom_mem, NULL, "exynos4210.irom",
- EXYNOS4210_IROM_SIZE, &error_abort);
+ EXYNOS4210_IROM_SIZE, &error_fatal);
vmstate_register_ram_global(&s->irom_mem);
memory_region_set_readonly(&s->irom_mem, true);
memory_region_add_subregion(system_mem, EXYNOS4210_IROM_BASE_ADDR,
@@ -275,7 +275,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
/* Internal RAM */
memory_region_init_ram(&s->iram_mem, NULL, "exynos4210.iram",
- EXYNOS4210_IRAM_SIZE, &error_abort);
+ EXYNOS4210_IRAM_SIZE, &error_fatal);
vmstate_register_ram_global(&s->iram_mem);
memory_region_add_subregion(system_mem, EXYNOS4210_IRAM_BASE_ADDR,
&s->iram_mem);
@@ -284,14 +284,14 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
mem_size = ram_size;
if (mem_size > EXYNOS4210_DRAM_MAX_SIZE) {
memory_region_init_ram(&s->dram1_mem, NULL, "exynos4210.dram1",
- mem_size - EXYNOS4210_DRAM_MAX_SIZE, &error_abort);
+ mem_size - EXYNOS4210_DRAM_MAX_SIZE, &error_fatal);
vmstate_register_ram_global(&s->dram1_mem);
memory_region_add_subregion(system_mem, EXYNOS4210_DRAM1_BASE_ADDR,
&s->dram1_mem);
mem_size = EXYNOS4210_DRAM_MAX_SIZE;
}
memory_region_init_ram(&s->dram0_mem, NULL, "exynos4210.dram0", mem_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&s->dram0_mem);
memory_region_add_subregion(system_mem, EXYNOS4210_DRAM0_BASE_ADDR,
&s->dram0_mem);
diff --git a/hw/arm/exynos4_boards.c b/hw/arm/exynos4_boards.c
index d644db1ef..da82b27ba 100644
--- a/hw/arm/exynos4_boards.c
+++ b/hw/arm/exynos4_boards.c
@@ -74,8 +74,6 @@ static struct arm_boot_info exynos4_board_binfo = {
.write_secondary_boot = exynos4210_write_secondary,
};
-static QEMUMachine exynos4_machines[EXYNOS4_NUM_OF_BOARDS];
-
static void lan9215_init(uint32_t base, qemu_irq irq)
{
DeviceState *dev;
@@ -97,11 +95,12 @@ static void lan9215_init(uint32_t base, qemu_irq irq)
static Exynos4210State *exynos4_boards_init_common(MachineState *machine,
Exynos4BoardType board_type)
{
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
+
if (smp_cpus != EXYNOS4210_NCPUS && !qtest_enabled()) {
fprintf(stderr, "%s board supports only %d CPU cores. Ignoring smp_cpus"
" value.\n",
- exynos4_machines[board_type].name,
- exynos4_machines[board_type].max_cpus);
+ mc->name, EXYNOS4210_NCPUS);
}
exynos4_board_binfo.ram_size = exynos4_board_ram_size[board_type];
@@ -145,25 +144,40 @@ static void smdkc210_init(MachineState *machine)
arm_load_kernel(ARM_CPU(first_cpu), &exynos4_board_binfo);
}
-static QEMUMachine exynos4_machines[EXYNOS4_NUM_OF_BOARDS] = {
- [EXYNOS4_BOARD_NURI] = {
- .name = "nuri",
- .desc = "Samsung NURI board (Exynos4210)",
- .init = nuri_init,
- .max_cpus = EXYNOS4210_NCPUS,
- },
- [EXYNOS4_BOARD_SMDKC210] = {
- .name = "smdkc210",
- .desc = "Samsung SMDKC210 board (Exynos4210)",
- .init = smdkc210_init,
- .max_cpus = EXYNOS4210_NCPUS,
- },
+static void nuri_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Samsung NURI board (Exynos4210)";
+ mc->init = nuri_init;
+ mc->max_cpus = EXYNOS4210_NCPUS;
+}
+
+static const TypeInfo nuri_type = {
+ .name = MACHINE_TYPE_NAME("nuri"),
+ .parent = TYPE_MACHINE,
+ .class_init = nuri_class_init,
+};
+
+static void smdkc210_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Samsung SMDKC210 board (Exynos4210)";
+ mc->init = smdkc210_init;
+ mc->max_cpus = EXYNOS4210_NCPUS;
+}
+
+static const TypeInfo smdkc210_type = {
+ .name = MACHINE_TYPE_NAME("smdkc210"),
+ .parent = TYPE_MACHINE,
+ .class_init = smdkc210_class_init,
};
-static void exynos4_machine_init(void)
+static void exynos4_machines_init(void)
{
- qemu_register_machine(&exynos4_machines[EXYNOS4_BOARD_NURI]);
- qemu_register_machine(&exynos4_machines[EXYNOS4_BOARD_SMDKC210]);
+ type_register_static(&nuri_type);
+ type_register_static(&smdkc210_type);
}
-machine_init(exynos4_machine_init);
+machine_init(exynos4_machines_init)
diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c
new file mode 100644
index 000000000..e1cadac99
--- /dev/null
+++ b/hw/arm/fsl-imx25.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2013 Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * i.MX25 SOC emulation.
+ *
+ * Based on hw/arm/xlnx-zynqmp.c
+ *
+ * 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.
+ *
+ * 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/fsl-imx25.h"
+#include "sysemu/sysemu.h"
+#include "exec/address-spaces.h"
+#include "hw/boards.h"
+#include "sysemu/char.h"
+
+static void fsl_imx25_init(Object *obj)
+{
+ FslIMX25State *s = FSL_IMX25(obj);
+ int i;
+
+ object_initialize(&s->cpu, sizeof(s->cpu), "arm926-" TYPE_ARM_CPU);
+
+ object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC);
+ qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default());
+
+ object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX_CCM);
+ qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default());
+
+ for (i = 0; i < FSL_IMX25_NUM_UARTS; i++) {
+ object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL);
+ qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default());
+ }
+
+ for (i = 0; i < FSL_IMX25_NUM_GPTS; i++) {
+ object_initialize(&s->gpt[i], sizeof(s->gpt[i]), TYPE_IMX_GPT);
+ qdev_set_parent_bus(DEVICE(&s->gpt[i]), sysbus_get_default());
+ }
+
+ for (i = 0; i < FSL_IMX25_NUM_EPITS; i++) {
+ object_initialize(&s->epit[i], sizeof(s->epit[i]), TYPE_IMX_EPIT);
+ qdev_set_parent_bus(DEVICE(&s->epit[i]), sysbus_get_default());
+ }
+
+ object_initialize(&s->fec, sizeof(s->fec), TYPE_IMX_FEC);
+ qdev_set_parent_bus(DEVICE(&s->fec), sysbus_get_default());
+
+ for (i = 0; i < FSL_IMX25_NUM_I2CS; i++) {
+ object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C);
+ qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default());
+ }
+
+ for (i = 0; i < FSL_IMX25_NUM_GPIOS; i++) {
+ object_initialize(&s->gpio[i], sizeof(s->gpio[i]), TYPE_IMX_GPIO);
+ qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus_get_default());
+ }
+}
+
+static void fsl_imx25_realize(DeviceState *dev, Error **errp)
+{
+ FslIMX25State *s = FSL_IMX25(dev);
+ uint8_t i;
+ Error *err = NULL;
+
+ object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ object_property_set_bool(OBJECT(&s->avic), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->avic), 0, FSL_IMX25_AVIC_ADDR);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->avic), 0,
+ qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->avic), 1,
+ qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ));
+
+ object_property_set_bool(OBJECT(&s->ccm), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, FSL_IMX25_CCM_ADDR);
+
+ /* Initialize all UARTs */
+ for (i = 0; i < FSL_IMX25_NUM_UARTS; i++) {
+ static const struct {
+ hwaddr addr;
+ unsigned int irq;
+ } serial_table[FSL_IMX25_NUM_UARTS] = {
+ { FSL_IMX25_UART1_ADDR, FSL_IMX25_UART1_IRQ },
+ { FSL_IMX25_UART2_ADDR, FSL_IMX25_UART2_IRQ },
+ { FSL_IMX25_UART3_ADDR, FSL_IMX25_UART3_IRQ },
+ { FSL_IMX25_UART4_ADDR, FSL_IMX25_UART4_IRQ },
+ { FSL_IMX25_UART5_ADDR, FSL_IMX25_UART5_IRQ }
+ };
+
+ if (i < MAX_SERIAL_PORTS) {
+ CharDriverState *chr;
+
+ chr = serial_hds[i];
+
+ if (!chr) {
+ char label[20];
+ snprintf(label, sizeof(label), "imx31.uart%d", i);
+ chr = qemu_chr_new(label, "null", NULL);
+ }
+
+ qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr);
+ }
+
+ 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, serial_table[i].addr);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0,
+ qdev_get_gpio_in(DEVICE(&s->avic),
+ serial_table[i].irq));
+ }
+
+ /* Initialize all GPT timers */
+ for (i = 0; i < FSL_IMX25_NUM_GPTS; i++) {
+ static const struct {
+ hwaddr addr;
+ unsigned int irq;
+ } gpt_table[FSL_IMX25_NUM_GPTS] = {
+ { FSL_IMX25_GPT1_ADDR, FSL_IMX25_GPT1_IRQ },
+ { FSL_IMX25_GPT2_ADDR, FSL_IMX25_GPT2_IRQ },
+ { FSL_IMX25_GPT3_ADDR, FSL_IMX25_GPT3_IRQ },
+ { FSL_IMX25_GPT4_ADDR, FSL_IMX25_GPT4_IRQ }
+ };
+
+ s->gpt[i].ccm = DEVICE(&s->ccm);
+
+ object_property_set_bool(OBJECT(&s->gpt[i]), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt[i]), 0, gpt_table[i].addr);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt[i]), 0,
+ qdev_get_gpio_in(DEVICE(&s->avic),
+ gpt_table[i].irq));
+ }
+
+ /* Initialize all EPIT timers */
+ for (i = 0; i < FSL_IMX25_NUM_EPITS; i++) {
+ static const struct {
+ hwaddr addr;
+ unsigned int irq;
+ } epit_table[FSL_IMX25_NUM_EPITS] = {
+ { FSL_IMX25_EPIT1_ADDR, FSL_IMX25_EPIT1_IRQ },
+ { FSL_IMX25_EPIT2_ADDR, FSL_IMX25_EPIT2_IRQ }
+ };
+
+ s->epit[i].ccm = DEVICE(&s->ccm);
+
+ object_property_set_bool(OBJECT(&s->epit[i]), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->epit[i]), 0, epit_table[i].addr);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->epit[i]), 0,
+ qdev_get_gpio_in(DEVICE(&s->avic),
+ epit_table[i].irq));
+ }
+
+ qdev_set_nic_properties(DEVICE(&s->fec), &nd_table[0]);
+ object_property_set_bool(OBJECT(&s->fec), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->fec), 0, FSL_IMX25_FEC_ADDR);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->fec), 0,
+ qdev_get_gpio_in(DEVICE(&s->avic), FSL_IMX25_FEC_IRQ));
+
+
+ /* Initialize all I2C */
+ for (i = 0; i < FSL_IMX25_NUM_I2CS; i++) {
+ static const struct {
+ hwaddr addr;
+ unsigned int irq;
+ } i2c_table[FSL_IMX25_NUM_I2CS] = {
+ { FSL_IMX25_I2C1_ADDR, FSL_IMX25_I2C1_IRQ },
+ { FSL_IMX25_I2C2_ADDR, FSL_IMX25_I2C2_IRQ },
+ { FSL_IMX25_I2C3_ADDR, FSL_IMX25_I2C3_IRQ }
+ };
+
+ object_property_set_bool(OBJECT(&s->i2c[i]), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, i2c_table[i].addr);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0,
+ qdev_get_gpio_in(DEVICE(&s->avic),
+ i2c_table[i].irq));
+ }
+
+ /* Initialize all GPIOs */
+ for (i = 0; i < FSL_IMX25_NUM_GPIOS; i++) {
+ static const struct {
+ hwaddr addr;
+ unsigned int irq;
+ } gpio_table[FSL_IMX25_NUM_GPIOS] = {
+ { FSL_IMX25_GPIO1_ADDR, FSL_IMX25_GPIO1_IRQ },
+ { FSL_IMX25_GPIO2_ADDR, FSL_IMX25_GPIO2_IRQ },
+ { FSL_IMX25_GPIO3_ADDR, FSL_IMX25_GPIO3_IRQ },
+ { FSL_IMX25_GPIO4_ADDR, FSL_IMX25_GPIO4_IRQ }
+ };
+
+ object_property_set_bool(OBJECT(&s->gpio[i]), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr);
+ /* Connect GPIO IRQ to PIC */
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0,
+ qdev_get_gpio_in(DEVICE(&s->avic),
+ gpio_table[i].irq));
+ }
+
+ /* initialize 2 x 16 KB ROM */
+ memory_region_init_rom_device(&s->rom[0], NULL, NULL, NULL,
+ "imx25.rom0", FSL_IMX25_ROM0_SIZE, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ memory_region_add_subregion(get_system_memory(), FSL_IMX25_ROM0_ADDR,
+ &s->rom[0]);
+ memory_region_init_rom_device(&s->rom[1], NULL, NULL, NULL,
+ "imx25.rom1", FSL_IMX25_ROM1_SIZE, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ memory_region_add_subregion(get_system_memory(), FSL_IMX25_ROM1_ADDR,
+ &s->rom[1]);
+
+ /* initialize internal RAM (128 KB) */
+ memory_region_init_ram(&s->iram, NULL, "imx25.iram", FSL_IMX25_IRAM_SIZE,
+ &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ memory_region_add_subregion(get_system_memory(), FSL_IMX25_IRAM_ADDR,
+ &s->iram);
+ vmstate_register_ram_global(&s->iram);
+
+ /* internal RAM (128 KB) is aliased over 128 MB - 128 KB */
+ memory_region_init_alias(&s->iram_alias, NULL, "imx25.iram_alias",
+ &s->iram, 0, FSL_IMX25_IRAM_ALIAS_SIZE);
+ memory_region_add_subregion(get_system_memory(), FSL_IMX25_IRAM_ALIAS_ADDR,
+ &s->iram_alias);
+}
+
+static void fsl_imx25_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = fsl_imx25_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 fsl_imx25_type_info = {
+ .name = TYPE_FSL_IMX25,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(FslIMX25State),
+ .instance_init = fsl_imx25_init,
+ .class_init = fsl_imx25_class_init,
+};
+
+static void fsl_imx25_register_types(void)
+{
+ type_register_static(&fsl_imx25_type_info);
+}
+
+type_init(fsl_imx25_register_types)
diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c
new file mode 100644
index 000000000..53d447325
--- /dev/null
+++ b/hw/arm/fsl-imx31.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2013 Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * i.MX31 SOC emulation.
+ *
+ * Based on hw/arm/fsl-imx31.c
+ *
+ * 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/arm/fsl-imx31.h"
+#include "sysemu/sysemu.h"
+#include "exec/address-spaces.h"
+#include "hw/boards.h"
+#include "sysemu/char.h"
+
+static void fsl_imx31_init(Object *obj)
+{
+ FslIMX31State *s = FSL_IMX31(obj);
+ int i;
+
+ object_initialize(&s->cpu, sizeof(s->cpu), "arm1136-" TYPE_ARM_CPU);
+
+ object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC);
+ qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default());
+
+ object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX_CCM);
+ qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default());
+
+ for (i = 0; i < FSL_IMX31_NUM_UARTS; i++) {
+ object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL);
+ qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default());
+ }
+
+ object_initialize(&s->gpt, sizeof(s->gpt), TYPE_IMX_GPT);
+ qdev_set_parent_bus(DEVICE(&s->gpt), sysbus_get_default());
+
+ for (i = 0; i < FSL_IMX31_NUM_EPITS; i++) {
+ object_initialize(&s->epit[i], sizeof(s->epit[i]), TYPE_IMX_EPIT);
+ qdev_set_parent_bus(DEVICE(&s->epit[i]), sysbus_get_default());
+ }
+
+ for (i = 0; i < FSL_IMX31_NUM_I2CS; i++) {
+ object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C);
+ qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default());
+ }
+
+ for (i = 0; i < FSL_IMX31_NUM_GPIOS; i++) {
+ object_initialize(&s->gpio[i], sizeof(s->gpio[i]), TYPE_IMX_GPIO);
+ qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus_get_default());
+ }
+}
+
+static void fsl_imx31_realize(DeviceState *dev, Error **errp)
+{
+ FslIMX31State *s = FSL_IMX31(dev);
+ uint16_t i;
+ Error *err = NULL;
+
+ object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ object_property_set_bool(OBJECT(&s->avic), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->avic), 0, FSL_IMX31_AVIC_ADDR);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->avic), 0,
+ qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->avic), 1,
+ qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_FIQ));
+
+ object_property_set_bool(OBJECT(&s->ccm), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, FSL_IMX31_CCM_ADDR);
+
+ /* Initialize all UARTS */
+ for (i = 0; i < FSL_IMX31_NUM_UARTS; i++) {
+ static const struct {
+ hwaddr addr;
+ unsigned int irq;
+ } serial_table[FSL_IMX31_NUM_UARTS] = {
+ { FSL_IMX31_UART1_ADDR, FSL_IMX31_UART1_IRQ },
+ { FSL_IMX31_UART2_ADDR, FSL_IMX31_UART2_IRQ },
+ };
+
+ if (i < MAX_SERIAL_PORTS) {
+ CharDriverState *chr;
+
+ chr = serial_hds[i];
+
+ if (!chr) {
+ char label[20];
+ snprintf(label, sizeof(label), "imx31.uart%d", i);
+ chr = qemu_chr_new(label, "null", NULL);
+ }
+
+ qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr);
+ }
+
+ 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, serial_table[i].addr);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0,
+ qdev_get_gpio_in(DEVICE(&s->avic),
+ serial_table[i].irq));
+ }
+
+ s->gpt.ccm = DEVICE(&s->ccm);
+
+ object_property_set_bool(OBJECT(&s->gpt), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt), 0, FSL_IMX31_GPT_ADDR);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt), 0,
+ qdev_get_gpio_in(DEVICE(&s->avic), FSL_IMX31_GPT_IRQ));
+
+ /* Initialize all EPIT timers */
+ for (i = 0; i < FSL_IMX31_NUM_EPITS; i++) {
+ static const struct {
+ hwaddr addr;
+ unsigned int irq;
+ } epit_table[FSL_IMX31_NUM_EPITS] = {
+ { FSL_IMX31_EPIT1_ADDR, FSL_IMX31_EPIT1_IRQ },
+ { FSL_IMX31_EPIT2_ADDR, FSL_IMX31_EPIT2_IRQ },
+ };
+
+ s->epit[i].ccm = DEVICE(&s->ccm);
+
+ object_property_set_bool(OBJECT(&s->epit[i]), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->epit[i]), 0, epit_table[i].addr);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->epit[i]), 0,
+ qdev_get_gpio_in(DEVICE(&s->avic),
+ epit_table[i].irq));
+ }
+
+ /* Initialize all I2C */
+ for (i = 0; i < FSL_IMX31_NUM_I2CS; i++) {
+ static const struct {
+ hwaddr addr;
+ unsigned int irq;
+ } i2c_table[FSL_IMX31_NUM_I2CS] = {
+ { FSL_IMX31_I2C1_ADDR, FSL_IMX31_I2C1_IRQ },
+ { FSL_IMX31_I2C2_ADDR, FSL_IMX31_I2C2_IRQ },
+ { FSL_IMX31_I2C3_ADDR, FSL_IMX31_I2C3_IRQ }
+ };
+
+ /* Initialize the I2C */
+ object_property_set_bool(OBJECT(&s->i2c[i]), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ /* Map I2C memory */
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, i2c_table[i].addr);
+ /* Connect I2C IRQ to PIC */
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0,
+ qdev_get_gpio_in(DEVICE(&s->avic),
+ i2c_table[i].irq));
+ }
+
+ /* Initialize all GPIOs */
+ for (i = 0; i < FSL_IMX31_NUM_GPIOS; i++) {
+ static const struct {
+ hwaddr addr;
+ unsigned int irq;
+ } gpio_table[FSL_IMX31_NUM_GPIOS] = {
+ { FSL_IMX31_GPIO1_ADDR, FSL_IMX31_GPIO1_IRQ },
+ { FSL_IMX31_GPIO2_ADDR, FSL_IMX31_GPIO2_IRQ },
+ { FSL_IMX31_GPIO3_ADDR, FSL_IMX31_GPIO3_IRQ }
+ };
+
+ object_property_set_bool(OBJECT(&s->gpio[i]), false, "has-edge-sel",
+ &error_abort);
+ object_property_set_bool(OBJECT(&s->gpio[i]), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr);
+ /* Connect GPIO IRQ to PIC */
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0,
+ qdev_get_gpio_in(DEVICE(&s->avic),
+ gpio_table[i].irq));
+ }
+
+ /* On a real system, the first 16k is a `secure boot rom' */
+ memory_region_init_rom_device(&s->secure_rom, NULL, NULL, NULL,
+ "imx31.secure_rom",
+ FSL_IMX31_SECURE_ROM_SIZE, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ memory_region_add_subregion(get_system_memory(), FSL_IMX31_SECURE_ROM_ADDR,
+ &s->secure_rom);
+
+ /* There is also a 16k ROM */
+ memory_region_init_rom_device(&s->rom, NULL, NULL, NULL, "imx31.rom",
+ FSL_IMX31_ROM_SIZE, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ memory_region_add_subregion(get_system_memory(), FSL_IMX31_ROM_ADDR,
+ &s->rom);
+
+ /* initialize internal RAM (16 KB) */
+ memory_region_init_ram(&s->iram, NULL, "imx31.iram", FSL_IMX31_IRAM_SIZE,
+ &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ memory_region_add_subregion(get_system_memory(), FSL_IMX31_IRAM_ADDR,
+ &s->iram);
+ vmstate_register_ram_global(&s->iram);
+
+ /* internal RAM (16 KB) is aliased over 256 MB - 16 KB */
+ memory_region_init_alias(&s->iram_alias, NULL, "imx31.iram_alias",
+ &s->iram, 0, FSL_IMX31_IRAM_ALIAS_SIZE);
+ memory_region_add_subregion(get_system_memory(), FSL_IMX31_IRAM_ALIAS_ADDR,
+ &s->iram_alias);
+}
+
+static void fsl_imx31_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = fsl_imx31_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 fsl_imx31_type_info = {
+ .name = TYPE_FSL_IMX31,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(FslIMX31State),
+ .instance_init = fsl_imx31_init,
+ .class_init = fsl_imx31_class_init,
+};
+
+static void fsl_imx31_register_types(void)
+{
+ type_register_static(&fsl_imx31_type_info);
+}
+
+type_init(fsl_imx31_register_types)
diff --git a/hw/arm/gumstix.c b/hw/arm/gumstix.c
index 8103278b1..32ad041b2 100644
--- a/hw/arm/gumstix.c
+++ b/hw/arm/gumstix.c
@@ -121,22 +121,38 @@ static void verdex_init(MachineState *machine)
qdev_get_gpio_in(cpu->gpio, 99));
}
-static QEMUMachine connex_machine = {
- .name = "connex",
- .desc = "Gumstix Connex (PXA255)",
- .init = connex_init,
+static void connex_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Gumstix Connex (PXA255)";
+ mc->init = connex_init;
+}
+
+static const TypeInfo connex_type = {
+ .name = MACHINE_TYPE_NAME("connex"),
+ .parent = TYPE_MACHINE,
+ .class_init = connex_class_init,
};
-static QEMUMachine verdex_machine = {
- .name = "verdex",
- .desc = "Gumstix Verdex (PXA270)",
- .init = verdex_init,
+static void verdex_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Gumstix Verdex (PXA270)";
+ mc->init = verdex_init;
+}
+
+static const TypeInfo verdex_type = {
+ .name = MACHINE_TYPE_NAME("verdex"),
+ .parent = TYPE_MACHINE,
+ .class_init = verdex_class_init,
};
static void gumstix_machine_init(void)
{
- qemu_register_machine(&connex_machine);
- qemu_register_machine(&verdex_machine);
+ type_register_static(&connex_type);
+ type_register_static(&verdex_type);
}
-machine_init(gumstix_machine_init);
+machine_init(gumstix_machine_init)
diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c
index f8353a787..85ae69efd 100644
--- a/hw/arm/highbank.c
+++ b/hw/arm/highbank.c
@@ -22,6 +22,7 @@
#include "hw/devices.h"
#include "hw/loader.h"
#include "net/net.h"
+#include "sysemu/kvm.h"
#include "sysemu/sysemu.h"
#include "hw/boards.h"
#include "sysemu/block-backend.h"
@@ -32,10 +33,52 @@
#define SMP_BOOT_REG 0x40
#define MPCORE_PERIPHBASE 0xfff10000
+#define MVBAR_ADDR 0x200
+
#define NIRQ_GIC 160
/* Board init. */
+/* MVBAR_ADDR is limited by precision of movw */
+
+QEMU_BUILD_BUG_ON(MVBAR_ADDR >= (1 << 16));
+
+#define ARMV7_IMM16(x) (extract32((x), 0, 12) | \
+ extract32((x), 12, 4) << 16)
+
+static void hb_write_board_setup(ARMCPU *cpu,
+ const struct arm_boot_info *info)
+{
+ int n;
+ uint32_t board_setup_blob[] = {
+ /* MVBAR_ADDR */
+ /* Default unimplemented and unused vectors to spin. Makes it
+ * easier to debug (as opposed to the CPU running away).
+ */
+ 0xeafffffe, /* notused1: b notused */
+ 0xeafffffe, /* notused2: b notused */
+ 0xe1b0f00e, /* smc: movs pc, lr - exception return */
+ 0xeafffffe, /* prefetch_abort: b prefetch_abort */
+ 0xeafffffe, /* data_abort: b data_abort */
+ 0xeafffffe, /* notused3: b notused3 */
+ 0xeafffffe, /* irq: b irq */
+ 0xeafffffe, /* fiq: b fiq */
+#define BOARD_SETUP_ADDR (MVBAR_ADDR + 8 * sizeof(uint32_t))
+ 0xe3000000 + ARMV7_IMM16(MVBAR_ADDR), /* movw r0, MVBAR_ADDR */
+ 0xee0c0f30, /* mcr p15, 0, r0, c12, c0, 1 - set MVBAR */
+ 0xee110f11, /* mrc p15, 0, r0, c1 , c1, 0 - get SCR */
+ 0xe3810001, /* orr r0, #1 - set NS */
+ 0xee010f11, /* mcr p15, 0, r0, c1 , c1, 0 - set SCR */
+ 0xe1600070, /* smc - go to monitor mode to flush NS change */
+ 0xe12fff1e, /* bx lr - return to caller */
+ };
+ for (n = 0; n < ARRAY_SIZE(board_setup_blob); n++) {
+ board_setup_blob[n] = tswap32(board_setup_blob[n]);
+ }
+ rom_add_blob_fixed("board-setup", board_setup_blob,
+ sizeof(board_setup_blob), MVBAR_ADDR);
+}
+
static void hb_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info)
{
int n;
@@ -223,15 +266,13 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
MemoryRegion *sysmem;
char *sysboot_filename;
- if (!cpu_model) {
- switch (machine_id) {
- case CALXEDA_HIGHBANK:
- cpu_model = "cortex-a9";
- break;
- case CALXEDA_MIDWAY:
- cpu_model = "cortex-a15";
- break;
- }
+ switch (machine_id) {
+ case CALXEDA_HIGHBANK:
+ cpu_model = "cortex-a9";
+ break;
+ case CALXEDA_MIDWAY:
+ cpu_model = "cortex-a15";
+ break;
}
for (n = 0; n < smp_cpus; n++) {
@@ -240,24 +281,16 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
ARMCPU *cpu;
Error *err = NULL;
- if (!oc) {
- error_report("Unable to find CPU definition");
- exit(1);
- }
-
cpuobj = object_new(object_class_get_name(oc));
cpu = ARM_CPU(cpuobj);
- /* By default A9 and A15 CPUs have EL3 enabled. This board does not
- * currently support EL3 so the CPU EL3 property is disabled before
- * realization.
- */
- if (object_property_find(cpuobj, "has_el3", NULL)) {
- object_property_set_bool(cpuobj, false, "has_el3", &err);
- if (err) {
- error_report_err(err);
- exit(1);
- }
+ object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_SMC,
+ "psci-conduit", &error_abort);
+
+ if (n) {
+ /* Secondary CPUs start in PSCI powered-down state */
+ object_property_set_bool(cpuobj, true,
+ "start-powered-off", &error_abort);
}
if (object_property_find(cpuobj, "reset-cbar", NULL)) {
@@ -281,7 +314,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
sysram = g_new(MemoryRegion, 1);
memory_region_init_ram(sysram, NULL, "highbank.sysram", 0x8000,
- &error_abort);
+ &error_fatal);
memory_region_add_subregion(sysmem, 0xfff88000, sysram);
if (bios_name != NULL) {
sysboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
@@ -378,6 +411,16 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id)
highbank_binfo.loader_start = 0;
highbank_binfo.write_secondary_boot = hb_write_secondary;
highbank_binfo.secondary_cpu_reset_hook = hb_reset_secondary;
+ if (!kvm_enabled()) {
+ highbank_binfo.board_setup_addr = BOARD_SETUP_ADDR;
+ highbank_binfo.write_board_setup = hb_write_board_setup;
+ highbank_binfo.secure_board_setup = true;
+ } else {
+ error_report("WARNING: cannot load built-in Monitor support "
+ "if KVM is enabled. Some guests (such as Linux) "
+ "may not boot.");
+ }
+
arm_load_kernel(ARM_CPU(first_cpu), &highbank_binfo);
}
@@ -391,26 +434,42 @@ static void midway_init(MachineState *machine)
calxeda_init(machine, CALXEDA_MIDWAY);
}
-static QEMUMachine highbank_machine = {
- .name = "highbank",
- .desc = "Calxeda Highbank (ECX-1000)",
- .init = highbank_init,
- .block_default_type = IF_SCSI,
- .max_cpus = 4,
+static void highbank_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Calxeda Highbank (ECX-1000)";
+ mc->init = highbank_init;
+ mc->block_default_type = IF_SCSI;
+ mc->max_cpus = 4;
+}
+
+static const TypeInfo highbank_type = {
+ .name = MACHINE_TYPE_NAME("highbank"),
+ .parent = TYPE_MACHINE,
+ .class_init = highbank_class_init,
};
-static QEMUMachine midway_machine = {
- .name = "midway",
- .desc = "Calxeda Midway (ECX-2000)",
- .init = midway_init,
- .block_default_type = IF_SCSI,
- .max_cpus = 4,
+static void midway_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Calxeda Midway (ECX-2000)";
+ mc->init = midway_init;
+ mc->block_default_type = IF_SCSI;
+ mc->max_cpus = 4;
+}
+
+static const TypeInfo midway_type = {
+ .name = MACHINE_TYPE_NAME("midway"),
+ .parent = TYPE_MACHINE,
+ .class_init = midway_class_init,
};
static void calxeda_machines_init(void)
{
- qemu_register_machine(&highbank_machine);
- qemu_register_machine(&midway_machine);
+ type_register_static(&highbank_type);
+ type_register_static(&midway_type);
}
-machine_init(calxeda_machines_init);
+machine_init(calxeda_machines_init)
diff --git a/hw/arm/imx25_pdk.c b/hw/arm/imx25_pdk.c
new file mode 100644
index 000000000..59a4c1127
--- /dev/null
+++ b/hw/arm/imx25_pdk.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2013 Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * PDK Board System emulation.
+ *
+ * Based on hw/arm/kzm.c
+ *
+ * Copyright (c) 2008 OKL and 2011 NICTA
+ * Written by Hans at OK-Labs
+ * Updated by Peter Chubb.
+ *
+ * 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/arm/fsl-imx25.h"
+#include "hw/boards.h"
+#include "qemu/error-report.h"
+#include "exec/address-spaces.h"
+#include "sysemu/qtest.h"
+#include "hw/i2c/i2c.h"
+
+/* Memory map for PDK Emulation Baseboard:
+ * 0x00000000-0x7fffffff See i.MX25 SOC fr support
+ * 0x80000000-0x87ffffff RAM + Alias EMULATED
+ * 0x90000000-0x9fffffff RAM + Alias EMULATED
+ * 0xa0000000-0xa7ffffff Flash IGNORED
+ * 0xa8000000-0xafffffff Flash IGNORED
+ * 0xb0000000-0xb1ffffff SRAM IGNORED
+ * 0xb2000000-0xb3ffffff SRAM IGNORED
+ * 0xb4000000-0xb5ffffff CS4 IGNORED
+ * 0xb6000000-0xb8000fff Reserved IGNORED
+ * 0xb8001000-0xb8001fff SDRAM CTRL reg IGNORED
+ * 0xb8002000-0xb8002fff WEIM CTRL reg IGNORED
+ * 0xb8003000-0xb8003fff M3IF CTRL reg IGNORED
+ * 0xb8004000-0xb8004fff EMI CTRL reg IGNORED
+ * 0xb8005000-0xbaffffff Reserved IGNORED
+ * 0xbb000000-0xbb000fff NAND flash area buf IGNORED
+ * 0xbb001000-0xbb0011ff NAND flash reserved IGNORED
+ * 0xbb001200-0xbb001dff Reserved IGNORED
+ * 0xbb001e00-0xbb001fff NAN flash CTRL reg IGNORED
+ * 0xbb012000-0xbfffffff Reserved IGNORED
+ * 0xc0000000-0xffffffff Reserved IGNORED
+ */
+
+typedef struct IMX25PDK {
+ FslIMX25State soc;
+ MemoryRegion ram;
+ MemoryRegion ram_alias;
+} IMX25PDK;
+
+static struct arm_boot_info imx25_pdk_binfo;
+
+static void imx25_pdk_init(MachineState *machine)
+{
+ IMX25PDK *s = g_new0(IMX25PDK, 1);
+ Error *err = NULL;
+ unsigned int ram_size;
+ unsigned int alias_offset;
+ int i;
+
+ object_initialize(&s->soc, sizeof(s->soc), TYPE_FSL_IMX25);
+ object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
+ &error_abort);
+
+ object_property_set_bool(OBJECT(&s->soc), true, "realized", &err);
+ if (err != NULL) {
+ error_report("%s", error_get_pretty(err));
+ exit(1);
+ }
+
+ /* We need to initialize our memory */
+ if (machine->ram_size > (FSL_IMX25_SDRAM0_SIZE + FSL_IMX25_SDRAM1_SIZE)) {
+ error_report("WARNING: RAM size " RAM_ADDR_FMT " above max supported, "
+ "reduced to %x", machine->ram_size,
+ FSL_IMX25_SDRAM0_SIZE + FSL_IMX25_SDRAM1_SIZE);
+ machine->ram_size = FSL_IMX25_SDRAM0_SIZE + FSL_IMX25_SDRAM1_SIZE;
+ }
+
+ memory_region_allocate_system_memory(&s->ram, NULL, "imx25.ram",
+ machine->ram_size);
+ memory_region_add_subregion(get_system_memory(), FSL_IMX25_SDRAM0_ADDR,
+ &s->ram);
+
+ /* initialize the alias memory if any */
+ for (i = 0, ram_size = machine->ram_size, alias_offset = 0;
+ (i < 2) && ram_size; i++) {
+ unsigned int size;
+ static const struct {
+ hwaddr addr;
+ unsigned int size;
+ } ram[2] = {
+ { FSL_IMX25_SDRAM0_ADDR, FSL_IMX25_SDRAM0_SIZE },
+ { FSL_IMX25_SDRAM1_ADDR, FSL_IMX25_SDRAM1_SIZE },
+ };
+
+ size = MIN(ram_size, ram[i].size);
+
+ ram_size -= size;
+
+ if (size < ram[i].size) {
+ memory_region_init_alias(&s->ram_alias, NULL, "ram.alias",
+ &s->ram, alias_offset, ram[i].size - size);
+ memory_region_add_subregion(get_system_memory(),
+ ram[i].addr + size, &s->ram_alias);
+ }
+
+ alias_offset += ram[i].size;
+ }
+
+ imx25_pdk_binfo.ram_size = machine->ram_size;
+ imx25_pdk_binfo.kernel_filename = machine->kernel_filename;
+ imx25_pdk_binfo.kernel_cmdline = machine->kernel_cmdline;
+ imx25_pdk_binfo.initrd_filename = machine->initrd_filename;
+ imx25_pdk_binfo.loader_start = FSL_IMX25_SDRAM0_ADDR;
+ imx25_pdk_binfo.board_id = 1771,
+ imx25_pdk_binfo.nb_cpus = 1;
+
+ /*
+ * We test explicitly for qtest here as it is not done (yet?) in
+ * arm_load_kernel(). Without this the "make check" command would
+ * fail.
+ */
+ if (!qtest_enabled()) {
+ arm_load_kernel(&s->soc.cpu, &imx25_pdk_binfo);
+ } else {
+ /*
+ * This I2C device doesn't exist on the real board.
+ * We add it here (only on qtest usage) to be able to do a bit
+ * of simple qtest. See "make check" for details.
+ */
+ i2c_create_slave((I2CBus *)qdev_get_child_bus(DEVICE(&s->soc.i2c[0]),
+ "i2c"),
+ "ds1338", 0x68);
+ }
+}
+
+static void imx25_pdk_machine_init(MachineClass *mc)
+{
+ mc->desc = "ARM i.MX25 PDK board (ARM926)";
+ mc->init = imx25_pdk_init;
+}
+
+DEFINE_MACHINE("imx25-pdk", imx25_pdk_machine_init)
diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c
index 0fbbf997e..421bde9a1 100644
--- a/hw/arm/integratorcp.c
+++ b/hw/arm/integratorcp.c
@@ -266,7 +266,7 @@ static int integratorcm_init(SysBusDevice *dev)
s->cm_refcnt_offset = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 24,
1000);
memory_region_init_ram(&s->flash, OBJECT(s), "integrator.flash", 0x100000,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&s->flash);
memory_region_init_io(&s->iomem, OBJECT(s), &integratorcm_ops, s,
@@ -619,18 +619,13 @@ static void integratorcp_init(MachineState *machine)
arm_load_kernel(cpu, &integrator_binfo);
}
-static QEMUMachine integratorcp_machine = {
- .name = "integratorcp",
- .desc = "ARM Integrator/CP (ARM926EJ-S)",
- .init = integratorcp_init,
-};
-
-static void integratorcp_machine_init(void)
+static void integratorcp_machine_init(MachineClass *mc)
{
- qemu_register_machine(&integratorcp_machine);
+ mc->desc = "ARM Integrator/CP (ARM926EJ-S)";
+ mc->init = integratorcp_init;
}
-machine_init(integratorcp_machine_init);
+DEFINE_MACHINE("integratorcp", integratorcp_machine_init)
static Property core_properties[] = {
DEFINE_PROP_UINT32("memsz", IntegratorCMState, memsz, 0),
diff --git a/hw/arm/kzm.c b/hw/arm/kzm.c
index 5be0369a5..eff6f4681 100644
--- a/hw/arm/kzm.c
+++ b/hw/arm/kzm.c
@@ -13,141 +13,136 @@
* i.MX31 SoC
*/
-#include "hw/sysbus.h"
+#include "hw/arm/fsl-imx31.h"
+#include "hw/boards.h"
+#include "qemu/error-report.h"
#include "exec/address-spaces.h"
-#include "hw/hw.h"
-#include "hw/arm/arm.h"
-#include "hw/devices.h"
#include "net/net.h"
-#include "sysemu/sysemu.h"
-#include "hw/boards.h"
+#include "hw/devices.h"
#include "hw/char/serial.h"
-#include "hw/arm/imx.h"
-
- /* Memory map for Kzm Emulation Baseboard:
- * 0x00000000-0x00003fff 16k secure ROM IGNORED
- * 0x00004000-0x00407fff Reserved IGNORED
- * 0x00404000-0x00407fff ROM IGNORED
- * 0x00408000-0x0fffffff Reserved IGNORED
- * 0x10000000-0x1fffbfff RAM aliasing IGNORED
- * 0x1fffc000-0x1fffffff RAM EMULATED
- * 0x20000000-0x2fffffff Reserved IGNORED
- * 0x30000000-0x7fffffff I.MX31 Internal Register Space
- * 0x43f00000 IO_AREA0
- * 0x43f90000 UART1 EMULATED
- * 0x43f94000 UART2 EMULATED
- * 0x68000000 AVIC EMULATED
- * 0x53f80000 CCM EMULATED
- * 0x53f94000 PIT 1 EMULATED
- * 0x53f98000 PIT 2 EMULATED
- * 0x53f90000 GPT EMULATED
- * 0x80000000-0x87ffffff RAM EMULATED
- * 0x88000000-0x8fffffff RAM Aliasing EMULATED
- * 0xa0000000-0xafffffff NAND Flash IGNORED
- * 0xb0000000-0xb3ffffff Unavailable IGNORED
- * 0xb4000000-0xb4000fff 8-bit free space IGNORED
- * 0xb4001000-0xb400100f Board control IGNORED
- * 0xb4001003 DIP switch
- * 0xb4001010-0xb400101f 7-segment LED IGNORED
- * 0xb4001020-0xb400102f LED IGNORED
- * 0xb4001030-0xb400103f LED IGNORED
- * 0xb4001040-0xb400104f FPGA, UART EMULATED
- * 0xb4001050-0xb400105f FPGA, UART EMULATED
- * 0xb4001060-0xb40fffff FPGA IGNORED
- * 0xb6000000-0xb61fffff LAN controller EMULATED
- * 0xb6200000-0xb62fffff FPGA NAND Controller IGNORED
- * 0xb6300000-0xb7ffffff Free IGNORED
- * 0xb8000000-0xb8004fff Memory control registers IGNORED
- * 0xc0000000-0xc3ffffff PCMCIA/CF IGNORED
- * 0xc4000000-0xffffffff Reserved IGNORED
- */
-
-#define KZM_RAMADDRESS (0x80000000)
-#define KZM_FPGA (0xb4001040)
+#include "sysemu/qtest.h"
+
+/* Memory map for Kzm Emulation Baseboard:
+ * 0x00000000-0x7fffffff See i.MX31 SOC for support
+ * 0x80000000-0x8fffffff RAM EMULATED
+ * 0x90000000-0x9fffffff RAM EMULATED
+ * 0xa0000000-0xafffffff Flash IGNORED
+ * 0xb0000000-0xb3ffffff Unavailable IGNORED
+ * 0xb4000000-0xb4000fff 8-bit free space IGNORED
+ * 0xb4001000-0xb400100f Board control IGNORED
+ * 0xb4001003 DIP switch
+ * 0xb4001010-0xb400101f 7-segment LED IGNORED
+ * 0xb4001020-0xb400102f LED IGNORED
+ * 0xb4001030-0xb400103f LED IGNORED
+ * 0xb4001040-0xb400104f FPGA, UART EMULATED
+ * 0xb4001050-0xb400105f FPGA, UART EMULATED
+ * 0xb4001060-0xb40fffff FPGA IGNORED
+ * 0xb6000000-0xb61fffff LAN controller EMULATED
+ * 0xb6200000-0xb62fffff FPGA NAND Controller IGNORED
+ * 0xb6300000-0xb7ffffff Free IGNORED
+ * 0xb8000000-0xb8004fff Memory control registers IGNORED
+ * 0xc0000000-0xc3ffffff PCMCIA/CF IGNORED
+ * 0xc4000000-0xffffffff Reserved IGNORED
+ */
+
+typedef struct IMX31KZM {
+ FslIMX31State soc;
+ MemoryRegion ram;
+ MemoryRegion ram_alias;
+} IMX31KZM;
+
+#define KZM_RAM_ADDR (FSL_IMX31_SDRAM0_ADDR)
+#define KZM_FPGA_ADDR (FSL_IMX31_CS4_ADDR + 0x1040)
+#define KZM_LAN9118_ADDR (FSL_IMX31_CS5_ADDR)
static struct arm_boot_info kzm_binfo = {
- .loader_start = KZM_RAMADDRESS,
+ .loader_start = KZM_RAM_ADDR,
.board_id = 1722,
};
static void kzm_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;
- ARMCPU *cpu;
- MemoryRegion *address_space_mem = get_system_memory();
- MemoryRegion *ram = g_new(MemoryRegion, 1);
- MemoryRegion *sram = g_new(MemoryRegion, 1);
- MemoryRegion *ram_alias = g_new(MemoryRegion, 1);
- DeviceState *dev;
- DeviceState *ccm;
-
- if (!cpu_model) {
- cpu_model = "arm1136";
- }
-
- cpu = cpu_arm_init(cpu_model);
- if (!cpu) {
- fprintf(stderr, "Unable to find CPU definition\n");
+ IMX31KZM *s = g_new0(IMX31KZM, 1);
+ Error *err = NULL;
+ unsigned int ram_size;
+ unsigned int alias_offset;
+ unsigned int i;
+
+ object_initialize(&s->soc, sizeof(s->soc), TYPE_FSL_IMX31);
+ object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
+ &error_abort);
+
+ object_property_set_bool(OBJECT(&s->soc), true, "realized", &err);
+ if (err != NULL) {
+ error_report("%s", error_get_pretty(err));
exit(1);
}
- /* On a real system, the first 16k is a `secure boot rom' */
-
- memory_region_allocate_system_memory(ram, NULL, "kzm.ram", ram_size);
- memory_region_add_subregion(address_space_mem, KZM_RAMADDRESS, ram);
-
- memory_region_init_alias(ram_alias, NULL, "ram.alias", ram, 0, ram_size);
- memory_region_add_subregion(address_space_mem, 0x88000000, ram_alias);
-
- memory_region_init_ram(sram, NULL, "kzm.sram", 0x4000, &error_abort);
- memory_region_add_subregion(address_space_mem, 0x1FFFC000, sram);
-
- dev = sysbus_create_varargs("imx_avic", 0x68000000,
- qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ),
- qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_FIQ),
- NULL);
-
- imx_serial_create(0, 0x43f90000, qdev_get_gpio_in(dev, 45));
- imx_serial_create(1, 0x43f94000, qdev_get_gpio_in(dev, 32));
-
- ccm = sysbus_create_simple("imx_ccm", 0x53f80000, NULL);
+ /* Check the amount of memory is compatible with the SOC */
+ if (machine->ram_size > (FSL_IMX31_SDRAM0_SIZE + FSL_IMX31_SDRAM1_SIZE)) {
+ error_report("WARNING: RAM size " RAM_ADDR_FMT " above max supported, "
+ "reduced to %x", machine->ram_size,
+ FSL_IMX31_SDRAM0_SIZE + FSL_IMX31_SDRAM1_SIZE);
+ machine->ram_size = FSL_IMX31_SDRAM0_SIZE + FSL_IMX31_SDRAM1_SIZE;
+ }
- imx_timerp_create(0x53f94000, qdev_get_gpio_in(dev, 28), ccm);
- imx_timerp_create(0x53f98000, qdev_get_gpio_in(dev, 27), ccm);
- imx_timerg_create(0x53f90000, qdev_get_gpio_in(dev, 29), ccm);
+ memory_region_allocate_system_memory(&s->ram, NULL, "kzm.ram",
+ machine->ram_size);
+ memory_region_add_subregion(get_system_memory(), FSL_IMX31_SDRAM0_ADDR,
+ &s->ram);
+
+ /* initialize the alias memory if any */
+ for (i = 0, ram_size = machine->ram_size, alias_offset = 0;
+ (i < 2) && ram_size; i++) {
+ unsigned int size;
+ static const struct {
+ hwaddr addr;
+ unsigned int size;
+ } ram[2] = {
+ { FSL_IMX31_SDRAM0_ADDR, FSL_IMX31_SDRAM0_SIZE },
+ { FSL_IMX31_SDRAM1_ADDR, FSL_IMX31_SDRAM1_SIZE },
+ };
+
+ size = MIN(ram_size, ram[i].size);
+
+ ram_size -= size;
+
+ if (size < ram[i].size) {
+ memory_region_init_alias(&s->ram_alias, NULL, "ram.alias",
+ &s->ram, alias_offset, ram[i].size - size);
+ memory_region_add_subregion(get_system_memory(),
+ ram[i].addr + size, &s->ram_alias);
+ }
+
+ alias_offset += ram[i].size;
+ }
if (nd_table[0].used) {
- lan9118_init(&nd_table[0], 0xb6000000, qdev_get_gpio_in(dev, 52));
+ lan9118_init(&nd_table[0], KZM_LAN9118_ADDR,
+ qdev_get_gpio_in(DEVICE(&s->soc.avic), 52));
}
if (serial_hds[2]) { /* touchscreen */
- serial_mm_init(address_space_mem, KZM_FPGA+0x10, 0,
- qdev_get_gpio_in(dev, 52),
- 14745600, serial_hds[2],
- DEVICE_NATIVE_ENDIAN);
+ serial_mm_init(get_system_memory(), KZM_FPGA_ADDR+0x10, 0,
+ qdev_get_gpio_in(DEVICE(&s->soc.avic), 52),
+ 14745600, serial_hds[2], DEVICE_NATIVE_ENDIAN);
}
- kzm_binfo.ram_size = ram_size;
- kzm_binfo.kernel_filename = kernel_filename;
- kzm_binfo.kernel_cmdline = kernel_cmdline;
- kzm_binfo.initrd_filename = initrd_filename;
+ kzm_binfo.ram_size = machine->ram_size;
+ kzm_binfo.kernel_filename = machine->kernel_filename;
+ kzm_binfo.kernel_cmdline = machine->kernel_cmdline;
+ kzm_binfo.initrd_filename = machine->initrd_filename;
kzm_binfo.nb_cpus = 1;
- arm_load_kernel(cpu, &kzm_binfo);
-}
-static QEMUMachine kzm_machine = {
- .name = "kzm",
- .desc = "ARM KZM Emulation Baseboard (ARM1136)",
- .init = kzm_init,
-};
+ if (!qtest_enabled()) {
+ arm_load_kernel(&s->soc.cpu, &kzm_binfo);
+ }
+}
-static void kzm_machine_init(void)
+static void kzm_machine_init(MachineClass *mc)
{
- qemu_register_machine(&kzm_machine);
+ mc->desc = "ARM KZM Emulation Baseboard (ARM1136)";
+ mc->init = kzm_init;
}
-machine_init(kzm_machine_init)
+DEFINE_MACHINE("kzm", kzm_machine_init)
diff --git a/hw/arm/mainstone.c b/hw/arm/mainstone.c
index 0da02a67e..e434cb6ab 100644
--- a/hw/arm/mainstone.c
+++ b/hw/arm/mainstone.c
@@ -124,7 +124,7 @@ static void mainstone_common_init(MemoryRegion *address_space_mem,
/* Setup CPU & memory */
mpu = pxa270_init(address_space_mem, mainstone_binfo.ram_size, cpu_model);
memory_region_init_ram(rom, NULL, "mainstone.rom", MAINSTONE_ROM,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(rom);
memory_region_set_readonly(rom, true);
memory_region_add_subregion(address_space_mem, 0, rom);
@@ -188,15 +188,10 @@ static void mainstone_init(MachineState *machine)
mainstone_common_init(get_system_memory(), machine, mainstone, 0x196);
}
-static QEMUMachine mainstone2_machine = {
- .name = "mainstone",
- .desc = "Mainstone II (PXA27x)",
- .init = mainstone_init,
-};
-
-static void mainstone_machine_init(void)
+static void mainstone2_machine_init(MachineClass *mc)
{
- qemu_register_machine(&mainstone2_machine);
+ mc->desc = "Mainstone II (PXA27x)";
+ mc->init = mainstone_init;
}
-machine_init(mainstone_machine_init);
+DEFINE_MACHINE("mainstone", mainstone2_machine_init)
diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c
index 42f66b33e..b534bb904 100644
--- a/hw/arm/musicpal.c
+++ b/hw/arm/musicpal.c
@@ -1599,7 +1599,7 @@ static void musicpal_init(MachineState *machine)
memory_region_add_subregion(address_space_mem, 0, ram);
memory_region_init_ram(sram, NULL, "musicpal.sram", MP_SRAM_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(sram);
memory_region_add_subregion(address_space_mem, MP_SRAM_BASE, sram);
@@ -1709,18 +1709,13 @@ static void musicpal_init(MachineState *machine)
arm_load_kernel(cpu, &musicpal_binfo);
}
-static QEMUMachine musicpal_machine = {
- .name = "musicpal",
- .desc = "Marvell 88w8618 / MusicPal (ARM926EJ-S)",
- .init = musicpal_init,
-};
-
-static void musicpal_machine_init(void)
+static void musicpal_machine_init(MachineClass *mc)
{
- qemu_register_machine(&musicpal_machine);
+ mc->desc = "Marvell 88w8618 / MusicPal (ARM926EJ-S)";
+ mc->init = musicpal_init;
}
-machine_init(musicpal_machine_init);
+DEFINE_MACHINE("musicpal", musicpal_machine_init)
static void mv88w8618_wlan_class_init(ObjectClass *klass, void *data)
{
diff --git a/hw/arm/netduino2.c b/hw/arm/netduino2.c
index 8f26780ef..a3b9e82ff 100644
--- a/hw/arm/netduino2.c
+++ b/hw/arm/netduino2.c
@@ -43,15 +43,10 @@ static void netduino2_init(MachineState *machine)
}
}
-static QEMUMachine netduino2_machine = {
- .name = "netduino2",
- .desc = "Netduino 2 Machine",
- .init = netduino2_init,
-};
-
-static void netduino2_machine_init(void)
+static void netduino2_machine_init(MachineClass *mc)
{
- qemu_register_machine(&netduino2_machine);
+ mc->desc = "Netduino 2 Machine";
+ mc->init = netduino2_init;
}
-machine_init(netduino2_machine_init);
+DEFINE_MACHINE("netduino2", netduino2_machine_init)
diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c
index a659e8525..2a8835ec0 100644
--- a/hw/arm/nseries.c
+++ b/hw/arm/nseries.c
@@ -1275,7 +1275,7 @@ static int n8x0_atag_setup(void *p, int model)
strcpy((void *) w, "hw-build"); /* char component[12] */
w += 6;
strcpy((void *) w, "QEMU ");
- pstrcat((void *) w, 12, qemu_get_version()); /* char version[12] */
+ pstrcat((void *) w, 12, qemu_hw_version()); /* char version[12] */
w += 6;
tag = (model == 810) ? "1.1.10-qemu" : "1.1.6-qemu";
@@ -1413,24 +1413,40 @@ static void n810_init(MachineState *machine)
n8x0_init(machine, &n810_binfo, 810);
}
-static QEMUMachine n800_machine = {
- .name = "n800",
- .desc = "Nokia N800 tablet aka. RX-34 (OMAP2420)",
- .init = n800_init,
- .default_boot_order = "",
+static void n800_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Nokia N800 tablet aka. RX-34 (OMAP2420)";
+ mc->init = n800_init;
+ mc->default_boot_order = "";
+}
+
+static const TypeInfo n800_type = {
+ .name = MACHINE_TYPE_NAME("n800"),
+ .parent = TYPE_MACHINE,
+ .class_init = n800_class_init,
};
-static QEMUMachine n810_machine = {
- .name = "n810",
- .desc = "Nokia N810 tablet aka. RX-44 (OMAP2420)",
- .init = n810_init,
- .default_boot_order = "",
+static void n810_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Nokia N810 tablet aka. RX-44 (OMAP2420)";
+ mc->init = n810_init;
+ mc->default_boot_order = "";
+}
+
+static const TypeInfo n810_type = {
+ .name = MACHINE_TYPE_NAME("n810"),
+ .parent = TYPE_MACHINE,
+ .class_init = n810_class_init,
};
static void nseries_machine_init(void)
{
- qemu_register_machine(&n800_machine);
- qemu_register_machine(&n810_machine);
+ type_register_static(&n800_type);
+ type_register_static(&n810_type);
}
-machine_init(nseries_machine_init);
+machine_init(nseries_machine_init)
diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c
index de2b28925..6b1c07659 100644
--- a/hw/arm/omap1.c
+++ b/hw/arm/omap1.c
@@ -258,8 +258,7 @@ static struct omap_mpu_timer_s *omap_mpu_timer_init(MemoryRegion *system_memory,
hwaddr base,
qemu_irq irq, omap_clk clk)
{
- struct omap_mpu_timer_s *s = (struct omap_mpu_timer_s *)
- g_malloc0(sizeof(struct omap_mpu_timer_s));
+ struct omap_mpu_timer_s *s = g_new0(struct omap_mpu_timer_s, 1);
s->irq = irq;
s->clk = clk;
@@ -388,8 +387,7 @@ static struct omap_watchdog_timer_s *omap_wd_timer_init(MemoryRegion *memory,
hwaddr base,
qemu_irq irq, omap_clk clk)
{
- struct omap_watchdog_timer_s *s = (struct omap_watchdog_timer_s *)
- g_malloc0(sizeof(struct omap_watchdog_timer_s));
+ struct omap_watchdog_timer_s *s = g_new0(struct omap_watchdog_timer_s, 1);
s->timer.irq = irq;
s->timer.clk = clk;
@@ -495,8 +493,7 @@ static struct omap_32khz_timer_s *omap_os_timer_init(MemoryRegion *memory,
hwaddr base,
qemu_irq irq, omap_clk clk)
{
- struct omap_32khz_timer_s *s = (struct omap_32khz_timer_s *)
- g_malloc0(sizeof(struct omap_32khz_timer_s));
+ struct omap_32khz_timer_s *s = g_new0(struct omap_32khz_timer_s, 1);
s->timer.irq = irq;
s->timer.clk = clk;
@@ -1236,8 +1233,7 @@ static struct omap_tipb_bridge_s *omap_tipb_bridge_init(
MemoryRegion *memory, hwaddr base,
qemu_irq abort_irq, omap_clk clk)
{
- struct omap_tipb_bridge_s *s = (struct omap_tipb_bridge_s *)
- g_malloc0(sizeof(struct omap_tipb_bridge_s));
+ struct omap_tipb_bridge_s *s = g_new0(struct omap_tipb_bridge_s, 1);
s->abort = abort_irq;
omap_tipb_bridge_reset(s);
@@ -2099,8 +2095,7 @@ static struct omap_mpuio_s *omap_mpuio_init(MemoryRegion *memory,
qemu_irq kbd_int, qemu_irq gpio_int, qemu_irq wakeup,
omap_clk clk)
{
- struct omap_mpuio_s *s = (struct omap_mpuio_s *)
- g_malloc0(sizeof(struct omap_mpuio_s));
+ struct omap_mpuio_s *s = g_new0(struct omap_mpuio_s, 1);
s->irq = gpio_int;
s->kbd_irq = kbd_int;
@@ -2292,8 +2287,7 @@ static struct omap_uwire_s *omap_uwire_init(MemoryRegion *system_memory,
qemu_irq dma,
omap_clk clk)
{
- struct omap_uwire_s *s = (struct omap_uwire_s *)
- g_malloc0(sizeof(struct omap_uwire_s));
+ struct omap_uwire_s *s = g_new0(struct omap_uwire_s, 1);
s->txirq = txirq;
s->rxirq = rxirq;
@@ -2932,8 +2926,7 @@ static struct omap_rtc_s *omap_rtc_init(MemoryRegion *system_memory,
qemu_irq timerirq, qemu_irq alarmirq,
omap_clk clk)
{
- struct omap_rtc_s *s = (struct omap_rtc_s *)
- g_malloc0(sizeof(struct omap_rtc_s));
+ struct omap_rtc_s *s = g_new0(struct omap_rtc_s, 1);
s->irq = timerirq;
s->alarm = alarmirq;
@@ -3468,8 +3461,7 @@ static struct omap_mcbsp_s *omap_mcbsp_init(MemoryRegion *system_memory,
qemu_irq txirq, qemu_irq rxirq,
qemu_irq *dma, omap_clk clk)
{
- struct omap_mcbsp_s *s = (struct omap_mcbsp_s *)
- g_malloc0(sizeof(struct omap_mcbsp_s));
+ struct omap_mcbsp_s *s = g_new0(struct omap_mcbsp_s, 1);
s->txirq = txirq;
s->rxirq = rxirq;
@@ -3648,8 +3640,7 @@ static void omap_lpg_clk_update(void *opaque, int line, int on)
static struct omap_lpg_s *omap_lpg_init(MemoryRegion *system_memory,
hwaddr base, omap_clk clk)
{
- struct omap_lpg_s *s = (struct omap_lpg_s *)
- g_malloc0(sizeof(struct omap_lpg_s));
+ struct omap_lpg_s *s = g_new0(struct omap_lpg_s, 1);
s->tm = timer_new_ms(QEMU_CLOCK_VIRTUAL, omap_lpg_tick, s);
@@ -3853,8 +3844,7 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory,
const char *core)
{
int i;
- struct omap_mpu_state_s *s = (struct omap_mpu_state_s *)
- g_malloc0(sizeof(struct omap_mpu_state_s));
+ struct omap_mpu_state_s *s = g_new0(struct omap_mpu_state_s, 1);
qemu_irq dma_irqs[6];
DriveInfo *dinfo;
SysBusDevice *busdev;
@@ -3882,7 +3872,7 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory,
s->sdram_size);
memory_region_add_subregion(system_memory, OMAP_EMIFF_BASE, &s->emiff_ram);
memory_region_init_ram(&s->imif_ram, NULL, "omap1.sram", s->sram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&s->imif_ram);
memory_region_add_subregion(system_memory, OMAP_IMIF_BASE, &s->imif_ram);
diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c
index e39b31729..98ee19f86 100644
--- a/hw/arm/omap2.c
+++ b/hw/arm/omap2.c
@@ -596,8 +596,7 @@ static const MemoryRegionOps omap_eac_ops = {
static struct omap_eac_s *omap_eac_init(struct omap_target_agent_s *ta,
qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk)
{
- struct omap_eac_s *s = (struct omap_eac_s *)
- g_malloc0(sizeof(struct omap_eac_s));
+ struct omap_eac_s *s = g_new0(struct omap_eac_s, 1);
s->irq = irq;
s->codec.rxdrq = *drq ++;
@@ -788,8 +787,7 @@ static struct omap_sti_s *omap_sti_init(struct omap_target_agent_s *ta,
hwaddr channel_base, qemu_irq irq, omap_clk clk,
CharDriverState *chr)
{
- struct omap_sti_s *s = (struct omap_sti_s *)
- g_malloc0(sizeof(struct omap_sti_s));
+ struct omap_sti_s *s = g_new0(struct omap_sti_s, 1);
s->irq = irq;
omap_sti_reset(s);
@@ -1806,8 +1804,7 @@ static struct omap_prcm_s *omap_prcm_init(struct omap_target_agent_s *ta,
qemu_irq mpu_int, qemu_irq dsp_int, qemu_irq iva_int,
struct omap_mpu_state_s *mpu)
{
- struct omap_prcm_s *s = (struct omap_prcm_s *)
- g_malloc0(sizeof(struct omap_prcm_s));
+ struct omap_prcm_s *s = g_new0(struct omap_prcm_s, 1);
s->irq[0] = mpu_int;
s->irq[1] = dsp_int;
@@ -2185,8 +2182,7 @@ static void omap_sysctl_reset(struct omap_sysctl_s *s)
static struct omap_sysctl_s *omap_sysctl_init(struct omap_target_agent_s *ta,
omap_clk iclk, struct omap_mpu_state_s *mpu)
{
- struct omap_sysctl_s *s = (struct omap_sysctl_s *)
- g_malloc0(sizeof(struct omap_sysctl_s));
+ struct omap_sysctl_s *s = g_new0(struct omap_sysctl_s, 1);
s->mpu = mpu;
omap_sysctl_reset(s);
@@ -2248,8 +2244,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem,
unsigned long sdram_size,
const char *core)
{
- struct omap_mpu_state_s *s = (struct omap_mpu_state_s *)
- g_malloc0(sizeof(struct omap_mpu_state_s));
+ struct omap_mpu_state_s *s = g_new0(struct omap_mpu_state_s, 1);
qemu_irq dma_irqs[4];
DriveInfo *dinfo;
int i;
@@ -2276,7 +2271,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem,
s->sdram_size);
memory_region_add_subregion(sysmem, OMAP2_Q2_BASE, &s->sdram);
memory_region_init_ram(&s->sram, NULL, "omap2.sram", s->sram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&s->sram);
memory_region_add_subregion(sysmem, OMAP2_SRAM_BASE, &s->sram);
diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c
index 4b0f7f9c4..8eaf8f331 100644
--- a/hw/arm/omap_sx1.c
+++ b/hw/arm/omap_sx1.c
@@ -122,7 +122,7 @@ static void sx1_init(MachineState *machine, const int version)
/* External Flash (EMIFS) */
memory_region_init_ram(flash, NULL, "omap_sx1.flash0-0", flash_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(flash);
memory_region_set_readonly(flash, true);
memory_region_add_subregion(address_space, OMAP_CS0_BASE, flash);
@@ -166,7 +166,7 @@ static void sx1_init(MachineState *machine, const int version)
(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);
+ &error_fatal);
vmstate_register_ram_global(flash_1);
memory_region_set_readonly(flash_1, true);
memory_region_add_subregion(address_space, OMAP_CS1_BASE, flash_1);
@@ -217,22 +217,38 @@ static void sx1_init_v2(MachineState *machine)
sx1_init(machine, 2);
}
-static QEMUMachine sx1_machine_v2 = {
- .name = "sx1",
- .desc = "Siemens SX1 (OMAP310) V2",
- .init = sx1_init_v2,
+static void sx1_machine_v2_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Siemens SX1 (OMAP310) V2";
+ mc->init = sx1_init_v2;
+}
+
+static const TypeInfo sx1_machine_v2_type = {
+ .name = MACHINE_TYPE_NAME("sx1"),
+ .parent = TYPE_MACHINE,
+ .class_init = sx1_machine_v2_class_init,
};
-static QEMUMachine sx1_machine_v1 = {
- .name = "sx1-v1",
- .desc = "Siemens SX1 (OMAP310) V1",
- .init = sx1_init_v1,
+static void sx1_machine_v1_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Siemens SX1 (OMAP310) V1";
+ mc->init = sx1_init_v1;
+}
+
+static const TypeInfo sx1_machine_v1_type = {
+ .name = MACHINE_TYPE_NAME("sx1-v1"),
+ .parent = TYPE_MACHINE,
+ .class_init = sx1_machine_v1_class_init,
};
static void sx1_machine_init(void)
{
- qemu_register_machine(&sx1_machine_v2);
- qemu_register_machine(&sx1_machine_v1);
+ type_register_static(&sx1_machine_v1_type);
+ type_register_static(&sx1_machine_v2_type);
}
-machine_init(sx1_machine_init);
+machine_init(sx1_machine_init)
diff --git a/hw/arm/palm.c b/hw/arm/palm.c
index 7f1cfb8f6..82ec99d93 100644
--- a/hw/arm/palm.c
+++ b/hw/arm/palm.c
@@ -213,7 +213,7 @@ static void palmte_init(MachineState *machine)
/* External Flash (EMIFS) */
memory_region_init_ram(flash, NULL, "palmte.flash", flash_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(flash);
memory_region_set_readonly(flash, true);
memory_region_add_subregion(address_space_mem, OMAP_CS0_BASE, flash);
@@ -269,15 +269,10 @@ static void palmte_init(MachineState *machine)
arm_load_kernel(mpu->cpu, &palmte_binfo);
}
-static QEMUMachine palmte_machine = {
- .name = "cheetah",
- .desc = "Palm Tungsten|E aka. Cheetah PDA (OMAP310)",
- .init = palmte_init,
-};
-
-static void palmte_machine_init(void)
+static void palmte_machine_init(MachineClass *mc)
{
- qemu_register_machine(&palmte_machine);
+ mc->desc = "Palm Tungsten|E aka. Cheetah PDA (OMAP310)";
+ mc->init = palmte_init;
}
-machine_init(palmte_machine_init);
+DEFINE_MACHINE("cheetah", palmte_machine_init)
diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
index d94e20777..79d22d91e 100644
--- a/hw/arm/pxa2xx.c
+++ b/hw/arm/pxa2xx.c
@@ -1731,8 +1731,7 @@ static PXA2xxI2SState *pxa2xx_i2s_init(MemoryRegion *sysmem,
hwaddr base,
qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma)
{
- PXA2xxI2SState *s = (PXA2xxI2SState *)
- g_malloc0(sizeof(PXA2xxI2SState));
+ PXA2xxI2SState *s = g_new0(PXA2xxI2SState, 1);
s->irq = irq;
s->rx_dma = rx_dma;
@@ -2061,7 +2060,7 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space,
PXA2xxState *s;
int i;
DriveInfo *dinfo;
- s = (PXA2xxState *) g_malloc0(sizeof(PXA2xxState));
+ s = g_new0(PXA2xxState, 1);
if (revision && strncmp(revision, "pxa27", 5)) {
fprintf(stderr, "Machine requires a PXA27x processor.\n");
@@ -2079,11 +2078,11 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space,
/* SDRAM & Internal Memory Storage */
memory_region_init_ram(&s->sdram, NULL, "pxa270.sdram", sdram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&s->sdram);
memory_region_add_subregion(address_space, PXA2XX_SDRAM_BASE, &s->sdram);
memory_region_init_ram(&s->internal, NULL, "pxa270.internal", 0x40000,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&s->internal);
memory_region_add_subregion(address_space, PXA2XX_INTERNAL_BASE,
&s->internal);
@@ -2157,7 +2156,7 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space,
vmstate_register(NULL, 0, &vmstate_pxa2xx_pm, s);
for (i = 0; pxa27x_ssp[i].io_base; i ++);
- s->ssp = (SSIBus **)g_malloc0(sizeof(SSIBus *) * i);
+ s->ssp = g_new0(SSIBus *, i);
for (i = 0; pxa27x_ssp[i].io_base; i ++) {
DeviceState *dev;
dev = sysbus_create_simple(TYPE_PXA2XX_SSP, pxa27x_ssp[i].io_base,
@@ -2202,7 +2201,7 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size)
int i;
DriveInfo *dinfo;
- s = (PXA2xxState *) g_malloc0(sizeof(PXA2xxState));
+ s = g_new0(PXA2xxState, 1);
s->cpu = cpu_arm_init("pxa255");
if (s->cpu == NULL) {
@@ -2213,11 +2212,11 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size)
/* SDRAM & Internal Memory Storage */
memory_region_init_ram(&s->sdram, NULL, "pxa255.sdram", sdram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&s->sdram);
memory_region_add_subregion(address_space, PXA2XX_SDRAM_BASE, &s->sdram);
memory_region_init_ram(&s->internal, NULL, "pxa255.internal",
- PXA2XX_INTERNAL_SIZE, &error_abort);
+ PXA2XX_INTERNAL_SIZE, &error_fatal);
vmstate_register_ram_global(&s->internal);
memory_region_add_subregion(address_space, PXA2XX_INTERNAL_BASE,
&s->internal);
@@ -2290,7 +2289,7 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size)
vmstate_register(NULL, 0, &vmstate_pxa2xx_pm, s);
for (i = 0; pxa255_ssp[i].io_base; i ++);
- s->ssp = (SSIBus **)g_malloc0(sizeof(SSIBus *) * i);
+ s->ssp = g_new0(SSIBus *, i);
for (i = 0; pxa255_ssp[i].io_base; i ++) {
DeviceState *dev;
dev = sysbus_create_simple(TYPE_PXA2XX_SSP, pxa255_ssp[i].io_base,
diff --git a/hw/arm/realview.c b/hw/arm/realview.c
index ef2788d3e..e14828db0 100644
--- a/hw/arm/realview.c
+++ b/hw/arm/realview.c
@@ -151,13 +151,13 @@ static void realview_init(MachineState *machine,
low_ram_size = ram_size - 0x20000000;
ram_size = 0x20000000;
memory_region_init_ram(ram_lo, NULL, "realview.lowmem", low_ram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(ram_lo);
memory_region_add_subregion(sysmem, 0x20000000, ram_lo);
}
memory_region_init_ram(ram_hi, NULL, "realview.highmem", ram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(ram_hi);
low_ram_size = ram_size;
if (low_ram_size > 0x10000000)
@@ -353,7 +353,7 @@ static void realview_init(MachineState *machine,
BootROM happens to be in ROM/flash or in memory that isn't clobbered
until after Linux boots the secondary CPUs. */
memory_region_init_ram(ram_hack, NULL, "realview.hack", 0x1000,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(ram_hack);
memory_region_add_subregion(sysmem, SMP_BOOT_ADDR, ram_hack);
@@ -399,41 +399,73 @@ static void realview_pbx_a9_init(MachineState *machine)
realview_init(machine, BOARD_PBX_A9);
}
-static QEMUMachine realview_eb_machine = {
- .name = "realview-eb",
- .desc = "ARM RealView Emulation Baseboard (ARM926EJ-S)",
- .init = realview_eb_init,
- .block_default_type = IF_SCSI,
+static void realview_eb_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "ARM RealView Emulation Baseboard (ARM926EJ-S)";
+ mc->init = realview_eb_init;
+ mc->block_default_type = IF_SCSI;
+}
+
+static const TypeInfo realview_eb_type = {
+ .name = MACHINE_TYPE_NAME("realview-eb"),
+ .parent = TYPE_MACHINE,
+ .class_init = realview_eb_class_init,
};
-static QEMUMachine realview_eb_mpcore_machine = {
- .name = "realview-eb-mpcore",
- .desc = "ARM RealView Emulation Baseboard (ARM11MPCore)",
- .init = realview_eb_mpcore_init,
- .block_default_type = IF_SCSI,
- .max_cpus = 4,
+static void realview_eb_mpcore_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "ARM RealView Emulation Baseboard (ARM11MPCore)";
+ mc->init = realview_eb_mpcore_init;
+ mc->block_default_type = IF_SCSI;
+ mc->max_cpus = 4;
+}
+
+static const TypeInfo realview_eb_mpcore_type = {
+ .name = MACHINE_TYPE_NAME("realview-eb-mpcore"),
+ .parent = TYPE_MACHINE,
+ .class_init = realview_eb_mpcore_class_init,
};
-static QEMUMachine realview_pb_a8_machine = {
- .name = "realview-pb-a8",
- .desc = "ARM RealView Platform Baseboard for Cortex-A8",
- .init = realview_pb_a8_init,
+static void realview_pb_a8_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "ARM RealView Platform Baseboard for Cortex-A8";
+ mc->init = realview_pb_a8_init;
+}
+
+static const TypeInfo realview_pb_a8_type = {
+ .name = MACHINE_TYPE_NAME("realview-pb-a8"),
+ .parent = TYPE_MACHINE,
+ .class_init = realview_pb_a8_class_init,
};
-static QEMUMachine realview_pbx_a9_machine = {
- .name = "realview-pbx-a9",
- .desc = "ARM RealView Platform Baseboard Explore for Cortex-A9",
- .init = realview_pbx_a9_init,
- .block_default_type = IF_SCSI,
- .max_cpus = 4,
+static void realview_pbx_a9_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "ARM RealView Platform Baseboard Explore for Cortex-A9";
+ mc->init = realview_pbx_a9_init;
+ mc->block_default_type = IF_SCSI;
+ mc->max_cpus = 4;
+}
+
+static const TypeInfo realview_pbx_a9_type = {
+ .name = MACHINE_TYPE_NAME("realview-pbx-a9"),
+ .parent = TYPE_MACHINE,
+ .class_init = realview_pbx_a9_class_init,
};
static void realview_machine_init(void)
{
- qemu_register_machine(&realview_eb_machine);
- qemu_register_machine(&realview_eb_mpcore_machine);
- qemu_register_machine(&realview_pb_a8_machine);
- qemu_register_machine(&realview_pbx_a9_machine);
+ type_register_static(&realview_eb_type);
+ type_register_static(&realview_eb_mpcore_type);
+ type_register_static(&realview_pb_a8_type);
+ type_register_static(&realview_pbx_a9_type);
}
-machine_init(realview_machine_init);
+machine_init(realview_machine_init)
diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c
index 5bf032a63..8d3cc0b6b 100644
--- a/hw/arm/spitz.c
+++ b/hw/arm/spitz.c
@@ -913,7 +913,7 @@ static void spitz_common_init(MachineState *machine,
sl_flash_register(mpu, (model == spitz) ? FLASH_128M : FLASH_1024M);
- memory_region_init_ram(rom, NULL, "spitz.rom", SPITZ_ROM, &error_abort);
+ memory_region_init_ram(rom, NULL, "spitz.rom", SPITZ_ROM, &error_fatal);
vmstate_register_ram_global(rom);
memory_region_set_readonly(rom, true);
memory_region_add_subregion(address_space_mem, 0, rom);
@@ -972,39 +972,71 @@ static void terrier_init(MachineState *machine)
spitz_common_init(machine, terrier, 0x33f);
}
-static QEMUMachine akitapda_machine = {
- .name = "akita",
- .desc = "Akita PDA (PXA270)",
- .init = akita_init,
+static void akitapda_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sharp SL-C1000 (Akita) PDA (PXA270)";
+ mc->init = akita_init;
+}
+
+static const TypeInfo akitapda_type = {
+ .name = MACHINE_TYPE_NAME("akita"),
+ .parent = TYPE_MACHINE,
+ .class_init = akitapda_class_init,
};
-static QEMUMachine spitzpda_machine = {
- .name = "spitz",
- .desc = "Spitz PDA (PXA270)",
- .init = spitz_init,
+static void spitzpda_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sharp SL-C3000 (Spitz) PDA (PXA270)";
+ mc->init = spitz_init;
+}
+
+static const TypeInfo spitzpda_type = {
+ .name = MACHINE_TYPE_NAME("spitz"),
+ .parent = TYPE_MACHINE,
+ .class_init = spitzpda_class_init,
};
-static QEMUMachine borzoipda_machine = {
- .name = "borzoi",
- .desc = "Borzoi PDA (PXA270)",
- .init = borzoi_init,
+static void borzoipda_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sharp SL-C3100 (Borzoi) PDA (PXA270)";
+ mc->init = borzoi_init;
+}
+
+static const TypeInfo borzoipda_type = {
+ .name = MACHINE_TYPE_NAME("borzoi"),
+ .parent = TYPE_MACHINE,
+ .class_init = borzoipda_class_init,
};
-static QEMUMachine terrierpda_machine = {
- .name = "terrier",
- .desc = "Terrier PDA (PXA270)",
- .init = terrier_init,
+static void terrierpda_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sharp SL-C3200 (Terrier) PDA (PXA270)";
+ mc->init = terrier_init;
+}
+
+static const TypeInfo terrierpda_type = {
+ .name = MACHINE_TYPE_NAME("terrier"),
+ .parent = TYPE_MACHINE,
+ .class_init = terrierpda_class_init,
};
static void spitz_machine_init(void)
{
- qemu_register_machine(&akitapda_machine);
- qemu_register_machine(&spitzpda_machine);
- qemu_register_machine(&borzoipda_machine);
- qemu_register_machine(&terrierpda_machine);
+ type_register_static(&akitapda_type);
+ type_register_static(&spitzpda_type);
+ type_register_static(&borzoipda_type);
+ type_register_static(&terrierpda_type);
}
-machine_init(spitz_machine_init);
+machine_init(spitz_machine_init)
static bool is_version_0(void *opaque, int version_id)
{
@@ -1060,10 +1092,6 @@ static VMStateDescription vmstate_spitz_kbd = {
},
};
-static Property spitz_keyboard_properties[] = {
- DEFINE_PROP_END_OF_LIST(),
-};
-
static void spitz_keyboard_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -1071,7 +1099,6 @@ static void spitz_keyboard_class_init(ObjectClass *klass, void *data)
k->init = spitz_keyboard_init;
dc->vmsd = &vmstate_spitz_kbd;
- dc->props = spitz_keyboard_properties;
}
static const TypeInfo spitz_keyboard_info = {
diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c
index cb515ec76..0114e0a7f 100644
--- a/hw/arm/stellaris.c
+++ b/hw/arm/stellaris.c
@@ -16,6 +16,7 @@
#include "net/net.h"
#include "hw/boards.h"
#include "exec/address-spaces.h"
+#include "sysemu/sysemu.h"
#define GPIO_A 0
#define GPIO_B 1
@@ -675,7 +676,7 @@ static int stellaris_sys_init(uint32_t base, qemu_irq irq,
{
ssys_state *s;
- s = (ssys_state *)g_malloc0(sizeof(ssys_state));
+ s = g_new0(ssys_state, 1);
s->irq = irq;
s->board = board;
/* Most devices come preprogrammed with a MAC address in the user data. */
@@ -1176,6 +1177,14 @@ static int stellaris_adc_init(SysBusDevice *sbd)
return 0;
}
+static
+void do_sys_reset(void *opaque, int n, int level)
+{
+ if (level) {
+ qemu_system_reset_request();
+ }
+}
+
/* Board init. */
static stellaris_board_info stellaris_boards[] = {
{ "LM3S811EVB",
@@ -1210,8 +1219,7 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model,
0x40024000, 0x40025000, 0x40026000};
static const int gpio_irq[7] = {0, 1, 2, 3, 4, 30, 31};
- qemu_irq *pic;
- DeviceState *gpio_dev[7];
+ DeviceState *gpio_dev[7], *nvic;
qemu_irq gpio_in[7][8];
qemu_irq gpio_out[7][8];
qemu_irq adc;
@@ -1231,22 +1239,29 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model,
/* Flash programming is done via the SCU, so pretend it is ROM. */
memory_region_init_ram(flash, NULL, "stellaris.flash", flash_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(flash);
memory_region_set_readonly(flash, true);
memory_region_add_subregion(system_memory, 0, flash);
memory_region_init_ram(sram, NULL, "stellaris.sram", sram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(sram);
memory_region_add_subregion(system_memory, 0x20000000, sram);
- pic = armv7m_init(system_memory, flash_size, NUM_IRQ_LINES,
+ nvic = armv7m_init(system_memory, flash_size, NUM_IRQ_LINES,
kernel_filename, cpu_model);
+ qdev_connect_gpio_out_named(nvic, "SYSRESETREQ", 0,
+ qemu_allocate_irq(&do_sys_reset, NULL, 0));
+
if (board->dc1 & (1 << 16)) {
dev = sysbus_create_varargs(TYPE_STELLARIS_ADC, 0x40038000,
- pic[14], pic[15], pic[16], pic[17], NULL);
+ qdev_get_gpio_in(nvic, 14),
+ qdev_get_gpio_in(nvic, 15),
+ qdev_get_gpio_in(nvic, 16),
+ qdev_get_gpio_in(nvic, 17),
+ NULL);
adc = qdev_get_gpio_in(dev, 0);
} else {
adc = NULL;
@@ -1255,19 +1270,21 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model,
if (board->dc2 & (0x10000 << i)) {
dev = sysbus_create_simple(TYPE_STELLARIS_GPTM,
0x40030000 + i * 0x1000,
- pic[timer_irq[i]]);
+ qdev_get_gpio_in(nvic, timer_irq[i]));
/* TODO: This is incorrect, but we get away with it because
the ADC output is only ever pulsed. */
qdev_connect_gpio_out(dev, 0, adc);
}
}
- stellaris_sys_init(0x400fe000, pic[28], board, nd_table[0].macaddr.a);
+ stellaris_sys_init(0x400fe000, qdev_get_gpio_in(nvic, 28),
+ board, nd_table[0].macaddr.a);
for (i = 0; i < 7; i++) {
if (board->dc4 & (1 << i)) {
gpio_dev[i] = sysbus_create_simple("pl061_luminary", gpio_addr[i],
- pic[gpio_irq[i]]);
+ qdev_get_gpio_in(nvic,
+ gpio_irq[i]));
for (j = 0; j < 8; j++) {
gpio_in[i][j] = qdev_get_gpio_in(gpio_dev[i], j);
gpio_out[i][j] = NULL;
@@ -1276,7 +1293,8 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model,
}
if (board->dc2 & (1 << 12)) {
- dev = sysbus_create_simple(TYPE_STELLARIS_I2C, 0x40020000, pic[8]);
+ dev = sysbus_create_simple(TYPE_STELLARIS_I2C, 0x40020000,
+ qdev_get_gpio_in(nvic, 8));
i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c");
if (board->peripherals & BP_OLED_I2C) {
i2c_create_slave(i2c, "ssd0303", 0x3d);
@@ -1286,11 +1304,12 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model,
for (i = 0; i < 4; i++) {
if (board->dc2 & (1 << i)) {
sysbus_create_simple("pl011_luminary", 0x4000c000 + i * 0x1000,
- pic[uart_irq[i]]);
+ qdev_get_gpio_in(nvic, uart_irq[i]));
}
}
if (board->dc2 & (1 << 4)) {
- dev = sysbus_create_simple("pl022", 0x40008000, pic[7]);
+ dev = sysbus_create_simple("pl022", 0x40008000,
+ qdev_get_gpio_in(nvic, 7));
if (board->peripherals & BP_OLED_SSI) {
void *bus;
DeviceState *sddev;
@@ -1326,7 +1345,7 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model,
qdev_set_nic_properties(enet, &nd_table[0]);
qdev_init_nofail(enet);
sysbus_mmio_map(SYS_BUS_DEVICE(enet), 0, 0x40048000);
- sysbus_connect_irq(SYS_BUS_DEVICE(enet), 0, pic[42]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(enet), 0, qdev_get_gpio_in(nvic, 42));
}
if (board->peripherals & BP_GAMEPAD) {
qemu_irq gpad_irq[5];
@@ -1366,25 +1385,41 @@ static void lm3s6965evb_init(MachineState *machine)
stellaris_init(kernel_filename, cpu_model, &stellaris_boards[1]);
}
-static QEMUMachine lm3s811evb_machine = {
- .name = "lm3s811evb",
- .desc = "Stellaris LM3S811EVB",
- .init = lm3s811evb_init,
+static void lm3s811evb_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Stellaris LM3S811EVB";
+ mc->init = lm3s811evb_init;
+}
+
+static const TypeInfo lm3s811evb_type = {
+ .name = MACHINE_TYPE_NAME("lm3s811evb"),
+ .parent = TYPE_MACHINE,
+ .class_init = lm3s811evb_class_init,
};
-static QEMUMachine lm3s6965evb_machine = {
- .name = "lm3s6965evb",
- .desc = "Stellaris LM3S6965EVB",
- .init = lm3s6965evb_init,
+static void lm3s6965evb_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Stellaris LM3S6965EVB";
+ mc->init = lm3s6965evb_init;
+}
+
+static const TypeInfo lm3s6965evb_type = {
+ .name = MACHINE_TYPE_NAME("lm3s6965evb"),
+ .parent = TYPE_MACHINE,
+ .class_init = lm3s6965evb_class_init,
};
static void stellaris_machine_init(void)
{
- qemu_register_machine(&lm3s811evb_machine);
- qemu_register_machine(&lm3s6965evb_machine);
+ type_register_static(&lm3s811evb_type);
+ type_register_static(&lm3s6965evb_type);
}
-machine_init(stellaris_machine_init);
+machine_init(stellaris_machine_init)
static void stellaris_i2c_class_init(ObjectClass *klass, void *data)
{
diff --git a/hw/arm/stm32f205_soc.c b/hw/arm/stm32f205_soc.c
index 0f3bdc77b..3f993406d 100644
--- a/hw/arm/stm32f205_soc.c
+++ b/hw/arm/stm32f205_soc.c
@@ -59,9 +59,8 @@ static void stm32f205_soc_initfn(Object *obj)
static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
{
STM32F205State *s = STM32F205_SOC(dev_soc);
- DeviceState *syscfgdev, *usartdev, *timerdev;
+ DeviceState *syscfgdev, *usartdev, *timerdev, *nvic;
SysBusDevice *syscfgbusdev, *usartbusdev, *timerbusdev;
- qemu_irq *pic;
Error *err = NULL;
int i;
@@ -71,7 +70,7 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
MemoryRegion *flash_alias = g_new(MemoryRegion, 1);
memory_region_init_ram(flash, NULL, "STM32F205.flash", FLASH_SIZE,
- &error_abort);
+ &error_fatal);
memory_region_init_alias(flash_alias, NULL, "STM32F205.flash.alias",
flash, 0, FLASH_SIZE);
@@ -84,12 +83,12 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
memory_region_add_subregion(system_memory, 0, flash_alias);
memory_region_init_ram(sram, NULL, "STM32F205.sram", SRAM_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(sram);
memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, sram);
- pic = armv7m_init(get_system_memory(), FLASH_SIZE, 96,
- s->kernel_filename, s->cpu_model);
+ nvic = armv7m_init(get_system_memory(), FLASH_SIZE, 96,
+ s->kernel_filename, s->cpu_model);
/* System configuration controller */
syscfgdev = DEVICE(&s->syscfg);
@@ -100,7 +99,7 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
}
syscfgbusdev = SYS_BUS_DEVICE(syscfgdev);
sysbus_mmio_map(syscfgbusdev, 0, 0x40013800);
- sysbus_connect_irq(syscfgbusdev, 0, pic[71]);
+ sysbus_connect_irq(syscfgbusdev, 0, qdev_get_gpio_in(nvic, 71));
/* Attach UART (uses USART registers) and USART controllers */
for (i = 0; i < STM_NUM_USARTS; i++) {
@@ -112,7 +111,8 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
}
usartbusdev = SYS_BUS_DEVICE(usartdev);
sysbus_mmio_map(usartbusdev, 0, usart_addr[i]);
- sysbus_connect_irq(usartbusdev, 0, pic[usart_irq[i]]);
+ sysbus_connect_irq(usartbusdev, 0,
+ qdev_get_gpio_in(nvic, usart_irq[i]));
}
/* Timer 2 to 5 */
@@ -126,7 +126,8 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
}
timerbusdev = SYS_BUS_DEVICE(timerdev);
sysbus_mmio_map(timerbusdev, 0, timer_addr[i]);
- sysbus_connect_irq(timerbusdev, 0, pic[timer_irq[i]]);
+ sysbus_connect_irq(timerbusdev, 0,
+ qdev_get_gpio_in(nvic, timer_irq[i]));
}
}
diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c
index da9fc1d51..9624ecb58 100644
--- a/hw/arm/strongarm.c
+++ b/hw/arm/strongarm.c
@@ -1588,7 +1588,7 @@ StrongARMState *sa1110_init(MemoryRegion *sysmem,
StrongARMState *s;
int i;
- s = g_malloc0(sizeof(StrongARMState));
+ s = g_new0(StrongARMState, 1);
if (!rev) {
rev = "sa1110-b5";
diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c
index 73572ebe0..02814d7ab 100644
--- a/hw/arm/tosa.c
+++ b/hw/arm/tosa.c
@@ -227,7 +227,7 @@ static void tosa_init(MachineState *machine)
mpu = pxa255_init(address_space_mem, tosa_binfo.ram_size);
- memory_region_init_ram(rom, NULL, "tosa.rom", TOSA_ROM, &error_abort);
+ memory_region_init_ram(rom, NULL, "tosa.rom", TOSA_ROM, &error_fatal);
vmstate_register_ram_global(rom);
memory_region_set_readonly(rom, true);
memory_region_add_subregion(address_space_mem, 0, rom);
@@ -252,18 +252,13 @@ static void tosa_init(MachineState *machine)
sl_bootparam_write(SL_PXA_PARAM_BASE);
}
-static QEMUMachine tosapda_machine = {
- .name = "tosa",
- .desc = "Tosa PDA (PXA255)",
- .init = tosa_init,
-};
-
-static void tosapda_machine_init(void)
+static void tosapda_machine_init(MachineClass *mc)
{
- qemu_register_machine(&tosapda_machine);
+ mc->desc = "Sharp SL-6000 (Tosa) PDA (PXA255)";
+ mc->init = tosa_init;
}
-machine_init(tosapda_machine_init);
+DEFINE_MACHINE("tosa", tosapda_machine_init)
static void tosa_dac_class_init(ObjectClass *klass, void *data)
{
diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c
index 6c69f4eaa..912c2908f 100644
--- a/hw/arm/versatilepb.c
+++ b/hw/arm/versatilepb.c
@@ -391,27 +391,43 @@ static void vab_init(MachineState *machine)
versatile_init(machine, 0x25e);
}
-static QEMUMachine versatilepb_machine = {
- .name = "versatilepb",
- .desc = "ARM Versatile/PB (ARM926EJ-S)",
- .init = vpb_init,
- .block_default_type = IF_SCSI,
+static void versatilepb_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "ARM Versatile/PB (ARM926EJ-S)";
+ mc->init = vpb_init;
+ mc->block_default_type = IF_SCSI;
+}
+
+static const TypeInfo versatilepb_type = {
+ .name = MACHINE_TYPE_NAME("versatilepb"),
+ .parent = TYPE_MACHINE,
+ .class_init = versatilepb_class_init,
};
-static QEMUMachine versatileab_machine = {
- .name = "versatileab",
- .desc = "ARM Versatile/AB (ARM926EJ-S)",
- .init = vab_init,
- .block_default_type = IF_SCSI,
+static void versatileab_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "ARM Versatile/AB (ARM926EJ-S)";
+ mc->init = vab_init;
+ mc->block_default_type = IF_SCSI;
+}
+
+static const TypeInfo versatileab_type = {
+ .name = MACHINE_TYPE_NAME("versatileab"),
+ .parent = TYPE_MACHINE,
+ .class_init = versatileab_class_init,
};
static void versatile_machine_init(void)
{
- qemu_register_machine(&versatilepb_machine);
- qemu_register_machine(&versatileab_machine);
+ type_register_static(&versatilepb_type);
+ type_register_static(&versatileab_type);
}
-machine_init(versatile_machine_init);
+machine_init(versatile_machine_init)
static void vpb_sic_class_init(ObjectClass *klass, void *data)
{
diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c
index da217884e..058abbde3 100644
--- a/hw/arm/vexpress.c
+++ b/hw/arm/vexpress.c
@@ -168,8 +168,8 @@ typedef struct {
} VexpressMachineState;
#define TYPE_VEXPRESS_MACHINE "vexpress"
-#define TYPE_VEXPRESS_A9_MACHINE "vexpress-a9"
-#define TYPE_VEXPRESS_A15_MACHINE "vexpress-a15"
+#define TYPE_VEXPRESS_A9_MACHINE MACHINE_TYPE_NAME("vexpress-a9")
+#define TYPE_VEXPRESS_A15_MACHINE MACHINE_TYPE_NAME("vexpress-a15")
#define VEXPRESS_MACHINE(obj) \
OBJECT_CHECK(VexpressMachineState, (obj), TYPE_VEXPRESS_MACHINE)
#define VEXPRESS_MACHINE_GET_CLASS(obj) \
@@ -391,7 +391,7 @@ static void a15_daughterboard_init(const VexpressMachineState *vms,
/* 0x2b0a0000: PL341 dynamic memory controller: not modelled */
/* 0x2e000000: system SRAM */
memory_region_init_ram(sram, NULL, "vexpress.a15sram", 0x10000,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(sram);
memory_region_add_subregion(sysmem, 0x2e000000, sram);
@@ -541,7 +541,7 @@ static void vexpress_common_init(MachineState *machine)
{
VexpressMachineState *vms = VEXPRESS_MACHINE(machine);
VexpressMachineClass *vmc = VEXPRESS_MACHINE_GET_CLASS(machine);
- VEDBoardInfo *daughterboard = vmc->daughterboard;;
+ VEDBoardInfo *daughterboard = vmc->daughterboard;
DeviceState *dev, *sysctl, *pl041;
qemu_irq pic[64];
uint32_t sys_id;
@@ -671,13 +671,13 @@ static void vexpress_common_init(MachineState *machine)
sram_size = 0x2000000;
memory_region_init_ram(sram, NULL, "vexpress.sram", sram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(sram);
memory_region_add_subregion(sysmem, map[VE_SRAM], sram);
vram_size = 0x800000;
memory_region_init_ram(vram, NULL, "vexpress.vram", vram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(vram);
memory_region_add_subregion(sysmem, map[VE_VIDEORAM], vram);
@@ -747,7 +747,6 @@ static void vexpress_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
- mc->name = TYPE_VEXPRESS_MACHINE;
mc->desc = "ARM Versatile Express";
mc->init = vexpress_common_init;
mc->block_default_type = IF_SCSI;
@@ -759,10 +758,9 @@ static void vexpress_a9_class_init(ObjectClass *oc, void *data)
MachineClass *mc = MACHINE_CLASS(oc);
VexpressMachineClass *vmc = VEXPRESS_MACHINE_CLASS(oc);
- mc->name = TYPE_VEXPRESS_A9_MACHINE;
mc->desc = "ARM Versatile Express for Cortex-A9";
- vmc->daughterboard = &a9_daughterboard;;
+ vmc->daughterboard = &a9_daughterboard;
}
static void vexpress_a15_class_init(ObjectClass *oc, void *data)
@@ -770,7 +768,6 @@ static void vexpress_a15_class_init(ObjectClass *oc, void *data)
MachineClass *mc = MACHINE_CLASS(oc);
VexpressMachineClass *vmc = VEXPRESS_MACHINE_CLASS(oc);
- mc->name = TYPE_VEXPRESS_A15_MACHINE;
mc->desc = "ARM Versatile Express for Cortex-A15";
vmc->daughterboard = &a15_daughterboard;
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index f36514031..3c2c5d6bf 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -114,7 +114,7 @@ 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;
+ hwaddr size = flash_memmap->size / 2;
dev = aml_device("FLS0");
aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015")));
@@ -159,7 +159,8 @@ static void acpi_dsdt_add_virtio(Aml *scope,
}
}
-static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq)
+static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq,
+ bool use_highmem)
{
Aml *method, *crs, *ifctx, *UUID, *ifctx1, *elsectx, *buf;
int i, bus_no;
@@ -179,6 +180,7 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq)
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")));
+ aml_append(dev, aml_name_decl("_CCA", aml_int(1)));
/* Declare the PCI Routing Table. */
Aml *rt_pkg = aml_package(nr_pcie_buses * PCI_NUM_PINS);
@@ -234,6 +236,17 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq)
AML_ENTIRE_RANGE, 0x0000, 0x0000, size_pio - 1, base_pio,
size_pio));
+ if (use_highmem) {
+ hwaddr base_mmio_high = memmap[VIRT_PCIE_MMIO_HIGH].base;
+ hwaddr size_mmio_high = memmap[VIRT_PCIE_MMIO_HIGH].size;
+
+ aml_append(rbuf,
+ aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
+ AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000,
+ base_mmio_high, base_mmio_high, 0x0000,
+ size_mmio_high));
+ }
+
aml_append(method, aml_name_decl("RBUF", rbuf));
aml_append(method, aml_return(rbuf));
aml_append(dev, method);
@@ -431,33 +444,47 @@ build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info,
madt = acpi_data_push(table_data, sizeof *madt);
+ 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;
+
for (i = 0; i < guest_info->smp_cpus; i++) {
AcpiMadtGenericInterrupt *gicc = acpi_data_push(table_data,
sizeof *gicc);
+ ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(i));
+
gicc->type = ACPI_APIC_GENERIC_INTERRUPT;
gicc->length = sizeof(*gicc);
- gicc->base_address = memmap[VIRT_GIC_CPU].base;
+ if (guest_info->gic_version == 2) {
+ gicc->base_address = memmap[VIRT_GIC_CPU].base;
+ }
gicc->cpu_interface_number = i;
- gicc->arm_mpidr = i;
+ gicc->arm_mpidr = armcpu->mp_affinity;
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);
+ if (guest_info->gic_version == 3) {
+ AcpiMadtGenericRedistributor *gicr = acpi_data_push(table_data,
+ sizeof *gicr);
+
+ gicr->type = ACPI_APIC_GENERIC_REDISTRIBUTOR;
+ gicr->length = sizeof(*gicr);
+ gicr->base_address = cpu_to_le64(memmap[VIRT_GIC_REDIST].base);
+ gicr->range_length = cpu_to_le32(memmap[VIRT_GIC_REDIST].size);
+ } else {
+ 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",
@@ -510,7 +537,8 @@ build_dsdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
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));
+ acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE),
+ guest_info->use_highmem);
aml_append(dsdt, scope);
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 484689264..9c6792cea 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -48,6 +48,10 @@
#include "hw/arm/sysbus-fdt.h"
#include "hw/platform-bus.h"
#include "hw/arm/fdt.h"
+#include "hw/intc/arm_gic_common.h"
+#include "kvm_arm.h"
+#include "hw/smbios/smbios.h"
+#include "qapi/visitor.h"
/* Number of external interrupt lines to configure the GIC with */
#define NUM_IRQS 256
@@ -77,9 +81,11 @@ typedef struct {
typedef struct {
MachineState parent;
bool secure;
+ bool highmem;
+ int32_t gic_version;
} VirtMachineState;
-#define TYPE_VIRT_MACHINE "virt"
+#define TYPE_VIRT_MACHINE MACHINE_TYPE_NAME("virt")
#define VIRT_MACHINE(obj) \
OBJECT_CHECK(VirtMachineState, (obj), TYPE_VIRT_MACHINE)
#define VIRT_MACHINE_GET_CLASS(obj) \
@@ -107,9 +113,13 @@ static const MemMapEntry a15memmap[] = {
[VIRT_GIC_DIST] = { 0x08000000, 0x00010000 },
[VIRT_GIC_CPU] = { 0x08010000, 0x00010000 },
[VIRT_GIC_V2M] = { 0x08020000, 0x00001000 },
+ /* The space in between here is reserved for GICv3 CPU/vCPU/HYP */
+ [VIRT_GIC_ITS] = { 0x08080000, 0x00020000 },
+ /* This redistributor space allows up to 2*64kB*123 CPUs */
+ [VIRT_GIC_REDIST] = { 0x080A0000, 0x00F60000 },
[VIRT_UART] = { 0x09000000, 0x00001000 },
[VIRT_RTC] = { 0x09010000, 0x00001000 },
- [VIRT_FW_CFG] = { 0x09020000, 0x0000000a },
+ [VIRT_FW_CFG] = { 0x09020000, 0x00000018 },
[VIRT_MMIO] = { 0x0a000000, 0x00000200 },
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
[VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
@@ -117,6 +127,8 @@ static const MemMapEntry a15memmap[] = {
[VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 },
[VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 },
[VIRT_MEM] = { 0x40000000, 30ULL * 1024 * 1024 * 1024 },
+ /* Second PCIe window, 512GB wide at the 512GB boundary */
+ [VIRT_PCIE_MMIO_HIGH] = { 0x8000000000ULL, 0x8000000000ULL },
};
static const int a15irqmap[] = {
@@ -249,7 +261,7 @@ static void fdt_add_psci_node(const VirtBoardInfo *vbi)
qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn);
}
-static void fdt_add_timer_nodes(const VirtBoardInfo *vbi)
+static void fdt_add_timer_nodes(const VirtBoardInfo *vbi, int gictype)
{
/* Note that on A15 h/w these interrupts are level-triggered,
* but for the GIC implementation provided by both QEMU and KVM
@@ -258,8 +270,11 @@ static void fdt_add_timer_nodes(const VirtBoardInfo *vbi)
ARMCPU *armcpu;
uint32_t irqflags = GIC_FDT_IRQ_FLAGS_EDGE_LO_HI;
- irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START,
- GIC_FDT_IRQ_PPI_CPU_WIDTH, (1 << vbi->smp_cpus) - 1);
+ if (gictype == 2) {
+ irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START,
+ GIC_FDT_IRQ_PPI_CPU_WIDTH,
+ (1 << vbi->smp_cpus) - 1);
+ }
qemu_fdt_add_subnode(vbi->fdt, "/timer");
@@ -282,9 +297,32 @@ static void fdt_add_timer_nodes(const VirtBoardInfo *vbi)
static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi)
{
int cpu;
+ int addr_cells = 1;
+
+ /*
+ * From Documentation/devicetree/bindings/arm/cpus.txt
+ * On ARM v8 64-bit systems value should be set to 2,
+ * that corresponds to the MPIDR_EL1 register size.
+ * If MPIDR_EL1[63:32] value is equal to 0 on all CPUs
+ * in the system, #address-cells can be set to 1, since
+ * MPIDR_EL1[63:32] bits are not used for CPUs
+ * identification.
+ *
+ * Here we actually don't know whether our system is 32- or 64-bit one.
+ * The simplest way to go is to examine affinity IDs of all our CPUs. If
+ * at least one of them has Aff3 populated, we set #address-cells to 2.
+ */
+ for (cpu = 0; cpu < vbi->smp_cpus; cpu++) {
+ ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(cpu));
+
+ if (armcpu->mp_affinity & ARM_AFF3_MASK) {
+ addr_cells = 2;
+ break;
+ }
+ }
qemu_fdt_add_subnode(vbi->fdt, "/cpus");
- qemu_fdt_setprop_cell(vbi->fdt, "/cpus", "#address-cells", 0x1);
+ qemu_fdt_setprop_cell(vbi->fdt, "/cpus", "#address-cells", addr_cells);
qemu_fdt_setprop_cell(vbi->fdt, "/cpus", "#size-cells", 0x0);
for (cpu = vbi->smp_cpus - 1; cpu >= 0; cpu--) {
@@ -301,7 +339,14 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi)
"enable-method", "psci");
}
- qemu_fdt_setprop_cell(vbi->fdt, nodename, "reg", armcpu->mp_affinity);
+ if (addr_cells == 2) {
+ qemu_fdt_setprop_u64(vbi->fdt, nodename, "reg",
+ armcpu->mp_affinity);
+ } else {
+ qemu_fdt_setprop_cell(vbi->fdt, nodename, "reg",
+ armcpu->mp_affinity);
+ }
+
g_free(nodename);
}
}
@@ -319,25 +364,36 @@ static void fdt_add_v2m_gic_node(VirtBoardInfo *vbi)
qemu_fdt_setprop_cell(vbi->fdt, "/intc/v2m", "phandle", vbi->v2m_phandle);
}
-static void fdt_add_gic_node(VirtBoardInfo *vbi)
+static void fdt_add_gic_node(VirtBoardInfo *vbi, int type)
{
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' */
- qemu_fdt_setprop_string(vbi->fdt, "/intc", "compatible",
- "arm,cortex-a15-gic");
qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#interrupt-cells", 3);
qemu_fdt_setprop(vbi->fdt, "/intc", "interrupt-controller", NULL, 0);
- qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc", "reg",
- 2, vbi->memmap[VIRT_GIC_DIST].base,
- 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", "#address-cells", 0x2);
qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#size-cells", 0x2);
qemu_fdt_setprop(vbi->fdt, "/intc", "ranges", NULL, 0);
+ if (type == 3) {
+ qemu_fdt_setprop_string(vbi->fdt, "/intc", "compatible",
+ "arm,gic-v3");
+ qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc", "reg",
+ 2, vbi->memmap[VIRT_GIC_DIST].base,
+ 2, vbi->memmap[VIRT_GIC_DIST].size,
+ 2, vbi->memmap[VIRT_GIC_REDIST].base,
+ 2, vbi->memmap[VIRT_GIC_REDIST].size);
+ } else {
+ /* 'cortex-a15-gic' means 'GIC v2' */
+ qemu_fdt_setprop_string(vbi->fdt, "/intc", "compatible",
+ "arm,cortex-a15-gic");
+ qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc", "reg",
+ 2, vbi->memmap[VIRT_GIC_DIST].base,
+ 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", vbi->gic_phandle);
}
@@ -360,29 +416,34 @@ static void create_v2m(VirtBoardInfo *vbi, qemu_irq *pic)
fdt_add_v2m_gic_node(vbi);
}
-static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic)
+static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic, int type, bool secure)
{
- /* We create a standalone GIC v2 */
+ /* We create a standalone GIC */
DeviceState *gicdev;
SysBusDevice *gicbusdev;
- const char *gictype = "arm_gic";
+ const char *gictype;
int i;
- if (kvm_irqchip_in_kernel()) {
- gictype = "kvm-arm-gic";
- }
+ gictype = (type == 3) ? gicv3_class_name() : gic_class_name();
gicdev = qdev_create(NULL, gictype);
- qdev_prop_set_uint32(gicdev, "revision", 2);
+ qdev_prop_set_uint32(gicdev, "revision", type);
qdev_prop_set_uint32(gicdev, "num-cpu", smp_cpus);
/* Note that the num-irq property counts both internal and external
* interrupts; there are always 32 of the former (mandated by GIC spec).
*/
qdev_prop_set_uint32(gicdev, "num-irq", NUM_IRQS + 32);
+ if (!kvm_irqchip_in_kernel()) {
+ qdev_prop_set_bit(gicdev, "has-security-extensions", secure);
+ }
qdev_init_nofail(gicdev);
gicbusdev = SYS_BUS_DEVICE(gicdev);
sysbus_mmio_map(gicbusdev, 0, vbi->memmap[VIRT_GIC_DIST].base);
- sysbus_mmio_map(gicbusdev, 1, vbi->memmap[VIRT_GIC_CPU].base);
+ if (type == 3) {
+ sysbus_mmio_map(gicbusdev, 1, vbi->memmap[VIRT_GIC_REDIST].base);
+ } else {
+ sysbus_mmio_map(gicbusdev, 1, vbi->memmap[VIRT_GIC_CPU].base);
+ }
/* Wire the outputs from each CPU's generic timer to the
* appropriate GIC PPI inputs, and the GIC's IRQ output to
@@ -390,15 +451,23 @@ static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic)
*/
for (i = 0; i < smp_cpus; i++) {
DeviceState *cpudev = DEVICE(qemu_get_cpu(i));
- int ppibase = NUM_IRQS + i * 32;
- /* physical timer; we wire it up to the non-secure timer's ID,
- * since a real A15 always has TrustZone but QEMU doesn't.
+ int ppibase = NUM_IRQS + i * GIC_INTERNAL + GIC_NR_SGIS;
+ int irq;
+ /* Mapping from the output timer irq lines from the CPU to the
+ * GIC PPI inputs we use for the virt board.
*/
- qdev_connect_gpio_out(cpudev, 0,
- qdev_get_gpio_in(gicdev, ppibase + 30));
- /* virtual timer */
- qdev_connect_gpio_out(cpudev, 1,
- qdev_get_gpio_in(gicdev, ppibase + 27));
+ const int timer_irq[] = {
+ [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ,
+ [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ,
+ [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ,
+ [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ,
+ };
+
+ for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) {
+ qdev_connect_gpio_out(cpudev, irq,
+ qdev_get_gpio_in(gicdev,
+ ppibase + timer_irq[irq]));
+ }
sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
sysbus_connect_irq(gicbusdev, i + smp_cpus,
@@ -409,9 +478,11 @@ static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic)
pic[i] = qdev_get_gpio_in(gicdev, i);
}
- fdt_add_gic_node(vbi);
+ fdt_add_gic_node(vbi, type);
- create_v2m(vbi, pic);
+ if (type == 2) {
+ create_v2m(vbi, pic);
+ }
}
static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic)
@@ -606,13 +677,13 @@ static void create_flash(const VirtBoardInfo *vbi)
g_free(nodename);
}
-static void create_fw_cfg(const VirtBoardInfo *vbi)
+static void create_fw_cfg(const VirtBoardInfo *vbi, AddressSpace *as)
{
hwaddr base = vbi->memmap[VIRT_FW_CFG].base;
hwaddr size = vbi->memmap[VIRT_FW_CFG].size;
char *nodename;
- fw_cfg_init_mem_wide(base + 8, base, 8);
+ fw_cfg_init_mem_wide(base + 8, base, 8, base + 16, as);
nodename = g_strdup_printf("/fw-cfg@%" PRIx64, base);
qemu_fdt_add_subnode(vbi->fdt, nodename);
@@ -658,10 +729,13 @@ 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)
+static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
+ bool use_highmem)
{
hwaddr base_mmio = vbi->memmap[VIRT_PCIE_MMIO].base;
hwaddr size_mmio = vbi->memmap[VIRT_PCIE_MMIO].size;
+ hwaddr base_mmio_high = vbi->memmap[VIRT_PCIE_MMIO_HIGH].base;
+ hwaddr size_mmio_high = vbi->memmap[VIRT_PCIE_MMIO_HIGH].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;
@@ -698,6 +772,16 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic)
mmio_reg, base_mmio, size_mmio);
memory_region_add_subregion(get_system_memory(), base_mmio, mmio_alias);
+ if (use_highmem) {
+ /* Map high MMIO space */
+ MemoryRegion *high_mmio_alias = g_new0(MemoryRegion, 1);
+
+ memory_region_init_alias(high_mmio_alias, OBJECT(dev), "pcie-mmio-high",
+ mmio_reg, base_mmio_high, size_mmio_high);
+ memory_region_add_subregion(get_system_memory(), base_mmio_high,
+ high_mmio_alias);
+ }
+
/* Map IO port space */
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, base_pio);
@@ -715,15 +799,30 @@ 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);
+ if (vbi->v2m_phandle) {
+ 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_pio, 2, size_pio,
- 1, FDT_PCI_RANGE_MMIO, 2, base_mmio,
- 2, base_mmio, 2, size_mmio);
+
+ if (use_highmem) {
+ qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "ranges",
+ 1, FDT_PCI_RANGE_IOPORT, 2, 0,
+ 2, base_pio, 2, size_pio,
+ 1, FDT_PCI_RANGE_MMIO, 2, base_mmio,
+ 2, base_mmio, 2, size_mmio,
+ 1, FDT_PCI_RANGE_MMIO_64BIT,
+ 2, base_mmio_high,
+ 2, base_mmio_high, 2, size_mmio_high);
+ } else {
+ qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "ranges",
+ 1, FDT_PCI_RANGE_IOPORT, 2, 0,
+ 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, vbi->gic_phandle, irq, nodename);
@@ -780,12 +879,42 @@ static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size)
return board->fdt;
}
+static void virt_build_smbios(VirtGuestInfo *guest_info)
+{
+ FWCfgState *fw_cfg = guest_info->fw_cfg;
+ uint8_t *smbios_tables, *smbios_anchor;
+ size_t smbios_tables_len, smbios_anchor_len;
+ const char *product = "QEMU Virtual Machine";
+
+ if (!fw_cfg) {
+ return;
+ }
+
+ if (kvm_enabled()) {
+ product = "KVM Virtual Machine";
+ }
+
+ smbios_set_defaults("QEMU", product,
+ "1.0", false, true, SMBIOS_ENTRY_POINT_30);
+
+ smbios_get_tables(NULL, 0, &smbios_tables, &smbios_tables_len,
+ &smbios_anchor, &smbios_anchor_len);
+
+ if (smbios_anchor) {
+ fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-tables",
+ smbios_tables, smbios_tables_len);
+ fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-anchor",
+ smbios_anchor, smbios_anchor_len);
+ }
+}
+
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);
+ virt_build_smbios(&guest_info_state->info);
}
static void machvirt_init(MachineState *machine)
@@ -793,7 +922,8 @@ static void machvirt_init(MachineState *machine)
VirtMachineState *vms = VIRT_MACHINE(machine);
qemu_irq pic[NUM_IRQS];
MemoryRegion *sysmem = get_system_memory();
- int n;
+ int gic_version = vms->gic_version;
+ int n, max_cpus;
MemoryRegion *ram = g_new(MemoryRegion, 1);
const char *cpu_model = machine->cpu_model;
VirtBoardInfo *vbi;
@@ -805,6 +935,18 @@ static void machvirt_init(MachineState *machine)
cpu_model = "cortex-a15";
}
+ /* We can probe only here because during property set
+ * KVM is not available yet
+ */
+ if (!gic_version) {
+ gic_version = kvm_arm_vgic_probe();
+ if (!gic_version) {
+ error_report("Unable to determine GIC version supported by host");
+ error_printf("KVM acceleration is probably not supported\n");
+ exit(1);
+ }
+ }
+
/* Separate the actual CPU model name from any appended features */
cpustr = g_strsplit(cpu_model, ",", 2);
@@ -815,6 +957,22 @@ static void machvirt_init(MachineState *machine)
exit(1);
}
+ /* The maximum number of CPUs depends on the GIC version, or on how
+ * many redistributors we can fit into the memory map.
+ */
+ if (gic_version == 3) {
+ max_cpus = vbi->memmap[VIRT_GIC_REDIST].size / 0x20000;
+ } else {
+ max_cpus = GIC_NCPU;
+ }
+
+ if (smp_cpus > max_cpus) {
+ error_report("Number of SMP CPUs requested (%d) exceeds max CPUs "
+ "supported by machine 'mach-virt' (%d)",
+ smp_cpus, max_cpus);
+ exit(1);
+ }
+
vbi->smp_cpus = smp_cpus;
if (machine->ram_size > vbi->memmap[VIRT_MEM].size) {
@@ -832,7 +990,7 @@ static void machvirt_init(MachineState *machine)
char *cpuopts = g_strdup(cpustr[1]);
if (!oc) {
- fprintf(stderr, "Unable to find CPU definition\n");
+ error_report("Unable to find CPU definition");
exit(1);
}
cpuobj = object_new(object_class_get_name(oc));
@@ -865,7 +1023,7 @@ static void machvirt_init(MachineState *machine)
object_property_set_bool(cpuobj, true, "realized", NULL);
}
g_strfreev(cpustr);
- fdt_add_timer_nodes(vbi);
+ fdt_add_timer_nodes(vbi, gic_version);
fdt_add_cpu_nodes(vbi);
fdt_add_psci_node(vbi);
@@ -875,13 +1033,13 @@ static void machvirt_init(MachineState *machine)
create_flash(vbi);
- create_gic(vbi, pic);
+ create_gic(vbi, pic, gic_version, vms->secure);
create_uart(vbi, pic);
create_rtc(vbi, pic);
- create_pcie(vbi, pic);
+ create_pcie(vbi, pic, vms->highmem);
/* Create mmio transports, so the user can create virtio backends
* (which will be automatically plugged in to the transports). If
@@ -889,13 +1047,15 @@ static void machvirt_init(MachineState *machine)
*/
create_virtio_devices(vbi, pic);
- create_fw_cfg(vbi);
+ create_fw_cfg(vbi, &address_space_memory);
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->use_highmem = vms->highmem;
+ guest_info->gic_version = gic_version;
guest_info_state->machine_done.notify = virt_guest_info_machine_done;
qemu_add_machine_init_done_notifier(&guest_info_state->machine_done);
@@ -933,31 +1093,93 @@ static void virt_set_secure(Object *obj, bool value, Error **errp)
vms->secure = value;
}
+static bool virt_get_highmem(Object *obj, Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(obj);
+
+ return vms->highmem;
+}
+
+static void virt_set_highmem(Object *obj, bool value, Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(obj);
+
+ vms->highmem = value;
+}
+
+static char *virt_get_gic_version(Object *obj, Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(obj);
+ const char *val = vms->gic_version == 3 ? "3" : "2";
+
+ return g_strdup(val);
+}
+
+static void virt_set_gic_version(Object *obj, const char *value, Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(obj);
+
+ if (!strcmp(value, "3")) {
+ vms->gic_version = 3;
+ } else if (!strcmp(value, "2")) {
+ vms->gic_version = 2;
+ } else if (!strcmp(value, "host")) {
+ vms->gic_version = 0; /* Will probe later */
+ } else {
+ error_report("Invalid gic-version option value");
+ error_printf("Allowed gic-version values are: 3, 2, host\n");
+ exit(1);
+ }
+}
+
static void virt_instance_init(Object *obj)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
- /* EL3 is enabled by default on virt */
- vms->secure = true;
+ /* EL3 is disabled by default on virt: this makes us consistent
+ * between KVM and TCG for this board, and it also allows us to
+ * boot UEFI blobs which assume no TrustZone support.
+ */
+ vms->secure = false;
object_property_add_bool(obj, "secure", virt_get_secure,
virt_set_secure, NULL);
object_property_set_description(obj, "secure",
"Set on/off to enable/disable the ARM "
"Security Extensions (TrustZone)",
NULL);
+
+ /* High memory is enabled by default */
+ vms->highmem = true;
+ object_property_add_bool(obj, "highmem", virt_get_highmem,
+ virt_set_highmem, NULL);
+ object_property_set_description(obj, "highmem",
+ "Set on/off to enable/disable using "
+ "physical address space above 32 bits",
+ NULL);
+ /* Default GIC type is v2 */
+ vms->gic_version = 2;
+ object_property_add_str(obj, "gic-version", virt_get_gic_version,
+ virt_set_gic_version, NULL);
+ object_property_set_description(obj, "gic-version",
+ "Set GIC version. "
+ "Valid values are 2, 3 and host", NULL);
}
static void virt_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
- mc->name = TYPE_VIRT_MACHINE;
mc->desc = "ARM Virtual Machine",
mc->init = machvirt_init;
- mc->max_cpus = 8;
+ /* Start max_cpus at the maximum QEMU supports. We'll further restrict
+ * it later in machvirt_init, where we have more information about the
+ * configuration of the particular instance.
+ */
+ mc->max_cpus = MAX_CPUMASK_BITS;
mc->has_dynamic_sysbus = true;
mc->block_default_type = IF_VIRTIO;
mc->no_cdrom = 1;
+ mc->pci_allow_0_address = true;
}
static const TypeInfo machvirt_info = {
diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c
index a4e7b5c63..1c1a44547 100644
--- a/hw/arm/xilinx_zynq.c
+++ b/hw/arm/xilinx_zynq.c
@@ -24,6 +24,7 @@
#include "hw/block/flash.h"
#include "sysemu/block-backend.h"
#include "hw/loader.h"
+#include "hw/misc/zynq-xadc.h"
#include "hw/ssi.h"
#include "qemu/error-report.h"
@@ -43,6 +44,45 @@ static const int dma_irqs[8] = {
46, 47, 48, 49, 72, 73, 74, 75
};
+#define BOARD_SETUP_ADDR 0x100
+
+#define SLCR_LOCK_OFFSET 0x004
+#define SLCR_UNLOCK_OFFSET 0x008
+#define SLCR_ARM_PLL_OFFSET 0x100
+
+#define SLCR_XILINX_UNLOCK_KEY 0xdf0d
+#define SLCR_XILINX_LOCK_KEY 0x767b
+
+#define ARMV7_IMM16(x) (extract32((x), 0, 12) | \
+ extract32((x), 12, 4) << 16)
+
+/* Write immediate val to address r0 + addr. r0 should contain base offset
+ * of the SLCR block. Clobbers r1.
+ */
+
+#define SLCR_WRITE(addr, val) \
+ 0xe3001000 + ARMV7_IMM16(extract32((val), 0, 16)), /* movw r1 ... */ \
+ 0xe3401000 + ARMV7_IMM16(extract32((val), 16, 16)), /* movt r1 ... */ \
+ 0xe5801000 + (addr)
+
+static void zynq_write_board_setup(ARMCPU *cpu,
+ const struct arm_boot_info *info)
+{
+ int n;
+ uint32_t board_setup_blob[] = {
+ 0xe3a004f8, /* mov r0, #0xf8000000 */
+ SLCR_WRITE(SLCR_UNLOCK_OFFSET, SLCR_XILINX_UNLOCK_KEY),
+ SLCR_WRITE(SLCR_ARM_PLL_OFFSET, 0x00014008),
+ SLCR_WRITE(SLCR_LOCK_OFFSET, SLCR_XILINX_LOCK_KEY),
+ 0xe12fff1e, /* bx lr */
+ };
+ for (n = 0; n < ARRAY_SIZE(board_setup_blob); n++) {
+ board_setup_blob[n] = tswap32(board_setup_blob[n]);
+ }
+ rom_add_blob_fixed("board-setup", board_setup_blob,
+ sizeof(board_setup_blob), BOARD_SETUP_ADDR);
+}
+
static struct arm_boot_info zynq_binfo = {};
static void gem_init(NICInfo *nd, uint32_t base, qemu_irq irq)
@@ -167,7 +207,7 @@ static void zynq_init(MachineState *machine)
/* 256K of on-chip memory */
memory_region_init_ram(ocm_ram, NULL, "zynq.ocm_ram", 256 << 10,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(ocm_ram);
memory_region_add_subregion(address_space_mem, 0xFFFC0000, ocm_ram);
@@ -225,6 +265,11 @@ static void zynq_init(MachineState *machine)
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xE0101000);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[79-IRQ_OFFSET]);
+ dev = qdev_create(NULL, TYPE_ZYNQ_XADC);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xF8007100);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[39-IRQ_OFFSET]);
+
dev = qdev_create(NULL, "pl330");
qdev_prop_set_uint8(dev, "num_chnls", 8);
qdev_prop_set_uint8(dev, "num_periph_req", 4);
@@ -252,21 +297,19 @@ static void zynq_init(MachineState *machine)
zynq_binfo.nb_cpus = 1;
zynq_binfo.board_id = 0xd32;
zynq_binfo.loader_start = 0;
+ zynq_binfo.board_setup_addr = BOARD_SETUP_ADDR;
+ zynq_binfo.write_board_setup = zynq_write_board_setup;
+
arm_load_kernel(ARM_CPU(first_cpu), &zynq_binfo);
}
-static QEMUMachine zynq_machine = {
- .name = "xilinx-zynq-a9",
- .desc = "Xilinx Zynq Platform Baseboard for Cortex-A9",
- .init = zynq_init,
- .block_default_type = IF_SCSI,
- .max_cpus = 1,
- .no_sdcard = 1,
-};
-
-static void zynq_machine_init(void)
+static void zynq_machine_init(MachineClass *mc)
{
- qemu_register_machine(&zynq_machine);
+ mc->desc = "Xilinx Zynq Platform Baseboard for Cortex-A9";
+ mc->init = zynq_init;
+ mc->block_default_type = IF_SCSI;
+ mc->max_cpus = 1;
+ mc->no_sdcard = 1;
}
-machine_init(zynq_machine_init);
+DEFINE_MACHINE("xilinx-zynq-a9", zynq_machine_init)
diff --git a/hw/arm/xlnx-ep108.c b/hw/arm/xlnx-ep108.c
index f94da86cb..85b978fa7 100644
--- a/hw/arm/xlnx-ep108.c
+++ b/hw/arm/xlnx-ep108.c
@@ -51,7 +51,7 @@ static void xlnx_ep108_init(MachineState *machine)
machine->ram_size = EP108_MAX_RAM_SIZE;
}
- if (machine->ram_size <= 0x08000000) {
+ if (machine->ram_size < 0x08000000) {
qemu_log("WARNING: RAM size " RAM_ADDR_FMT " is small for EP108",
machine->ram_size);
}
@@ -68,15 +68,10 @@ static void xlnx_ep108_init(MachineState *machine)
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)
+static void xlnx_ep108_machine_init(MachineClass *mc)
{
- qemu_register_machine(&xlnx_ep108_machine);
+ mc->desc = "Xilinx ZynqMP EP108 board";
+ mc->init = xlnx_ep108_init;
}
-machine_init(xlnx_ep108_machine_init);
+DEFINE_MACHINE("xlnx-ep108", xlnx_ep108_machine_init)
diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c
index 5157565f9..87553bbc6 100644
--- a/hw/arm/xlnx-zynqmp.c
+++ b/hw/arm/xlnx-zynqmp.c
@@ -28,6 +28,10 @@
#define GIC_DIST_ADDR 0xf9010000
#define GIC_CPU_ADDR 0xf9020000
+#define SATA_INTR 133
+#define SATA_ADDR 0xFD0C0000
+#define SATA_NUM_PORTS 2
+
static const uint64_t gem_addr[XLNX_ZYNQMP_NUM_GEMS] = {
0xFF0B0000, 0xFF0C0000, 0xFF0D0000, 0xFF0E0000,
};
@@ -44,6 +48,14 @@ static const int uart_intr[XLNX_ZYNQMP_NUM_UARTS] = {
21, 22,
};
+static const uint64_t sdhci_addr[XLNX_ZYNQMP_NUM_SDHCI] = {
+ 0xFF160000, 0xFF170000,
+};
+
+static const int sdhci_intr[XLNX_ZYNQMP_NUM_SDHCI] = {
+ 48, 49,
+};
+
typedef struct XlnxZynqMPGICRegion {
int region_index;
uint32_t address;
@@ -90,6 +102,16 @@ static void xlnx_zynqmp_init(Object *obj)
object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_CADENCE_UART);
qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default());
}
+
+ object_initialize(&s->sata, sizeof(s->sata), TYPE_SYSBUS_AHCI);
+ qdev_set_parent_bus(DEVICE(&s->sata), sysbus_get_default());
+
+ for (i = 0; i < XLNX_ZYNQMP_NUM_SDHCI; i++) {
+ object_initialize(&s->sdhci[i], sizeof(s->sdhci[i]),
+ TYPE_SYSBUS_SDHCI);
+ qdev_set_parent_bus(DEVICE(&s->sdhci[i]),
+ sysbus_get_default());
+ }
}
static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
@@ -101,12 +123,27 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
qemu_irq gic_spi[GIC_NUM_SPI_INTR];
Error *err = NULL;
+ /* Create the four OCM banks */
+ for (i = 0; i < XLNX_ZYNQMP_NUM_OCM_BANKS; i++) {
+ char *ocm_name = g_strdup_printf("zynqmp.ocm_ram_bank_%d", i);
+
+ memory_region_init_ram(&s->ocm_ram[i], NULL, ocm_name,
+ XLNX_ZYNQMP_OCM_RAM_SIZE, &error_fatal);
+ vmstate_register_ram_global(&s->ocm_ram[i]);
+ memory_region_add_subregion(get_system_memory(),
+ XLNX_ZYNQMP_OCM_RAM_0_ADDRESS +
+ i * XLNX_ZYNQMP_OCM_RAM_SIZE,
+ &s->ocm_ram[i]);
+
+ g_free(ocm_name);
+ }
+
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));
+ error_propagate(errp, err);
return;
}
assert(ARRAY_SIZE(xlnx_zynqmp_gic_regions) == XLNX_ZYNQMP_GIC_REGIONS);
@@ -147,16 +184,11 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
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;
- }
-
+ "reset-cbar", &error_abort);
object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, "realized",
&err);
if (err) {
- error_propagate((errp), (err));
+ error_propagate(errp, err);
return;
}
@@ -185,16 +217,11 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
g_free(name);
object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "reset-hivecs",
- &err);
- if (err != NULL) {
- error_propagate(errp, err);
- return;
- }
-
+ &error_abort);
object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "realized",
&err);
if (err) {
- error_propagate((errp), (err));
+ error_propagate(errp, err);
return;
}
}
@@ -217,7 +244,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
}
object_property_set_bool(OBJECT(&s->gem[i]), true, "realized", &err);
if (err) {
- error_propagate((errp), (err));
+ error_propagate(errp, err);
return;
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gem[i]), 0, gem_addr[i]);
@@ -228,13 +255,37 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
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));
+ 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]]);
}
+
+ object_property_set_int(OBJECT(&s->sata), SATA_NUM_PORTS, "num-ports",
+ &error_abort);
+ object_property_set_bool(OBJECT(&s->sata), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->sata), 0, SATA_ADDR);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->sata), 0, gic_spi[SATA_INTR]);
+
+ for (i = 0; i < XLNX_ZYNQMP_NUM_SDHCI; i++) {
+ object_property_set_bool(OBJECT(&s->sdhci[i]), true,
+ "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdhci[i]), 0,
+ sdhci_addr[i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci[i]), 0,
+ gic_spi[sdhci_intr[i]]);
+ }
}
static Property xlnx_zynqmp_props[] = {
diff --git a/hw/arm/z2.c b/hw/arm/z2.c
index 17355479a..b44eb76fc 100644
--- a/hw/arm/z2.c
+++ b/hw/arm/z2.c
@@ -372,15 +372,10 @@ static void z2_init(MachineState *machine)
arm_load_kernel(mpu->cpu, &z2_binfo);
}
-static QEMUMachine z2_machine = {
- .name = "z2",
- .desc = "Zipit Z2 (PXA27x)",
- .init = z2_init,
-};
-
-static void z2_machine_init(void)
+static void z2_machine_init(MachineClass *mc)
{
- qemu_register_machine(&z2_machine);
+ mc->desc = "Zipit Z2 (PXA27x)";
+ mc->init = z2_init;
}
-machine_init(z2_machine_init);
+DEFINE_MACHINE("z2", z2_machine_init)
diff --git a/hw/audio/adlib.c b/hw/audio/adlib.c
index 656eb3773..334935f8d 100644
--- a/hw/audio/adlib.c
+++ b/hw/audio/adlib.c
@@ -57,11 +57,6 @@ void YMF262UpdateOneQEMU (int which, INT16 *dst, int length);
#define SHIFT 1
#endif
-#define IO_READ_PROTO(name) \
- uint32_t name (void *opaque, uint32_t nport)
-#define IO_WRITE_PROTO(name) \
- void name (void *opaque, uint32_t nport, uint32_t val)
-
#define TYPE_ADLIB "adlib"
#define ADLIB(obj) OBJECT_CHECK(AdlibState, (obj), TYPE_ADLIB)
@@ -124,7 +119,7 @@ static void adlib_kill_timers (AdlibState *s)
}
}
-static IO_WRITE_PROTO (adlib_write)
+static void adlib_write(void *opaque, uint32_t nport, uint32_t val)
{
AdlibState *s = opaque;
int a = nport & 3;
@@ -141,7 +136,7 @@ static IO_WRITE_PROTO (adlib_write)
#endif
}
-static IO_READ_PROTO (adlib_read)
+static uint32_t adlib_read(void *opaque, uint32_t nport)
{
AdlibState *s = opaque;
uint8_t data;
diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c
index 8e7bcf503..592578b14 100644
--- a/hw/audio/es1370.c
+++ b/hw/audio/es1370.c
@@ -157,11 +157,6 @@ static const unsigned dac1_samplerate[] = { 5512, 11025, 22050, 44100 };
#define DAC2_CHANNEL 1
#define ADC_CHANNEL 2
-#define IO_READ_PROTO(n) \
-static uint32_t n (void *opaque, uint32_t addr)
-#define IO_WRITE_PROTO(n) \
-static void n (void *opaque, uint32_t addr, uint32_t val)
-
static void es1370_dac1_callback (void *opaque, int free);
static void es1370_dac2_callback (void *opaque, int free);
static void es1370_adc_callback (void *opaque, int avail);
@@ -474,7 +469,7 @@ static inline uint32_t es1370_fixup (ES1370State *s, uint32_t addr)
return addr;
}
-IO_WRITE_PROTO (es1370_writeb)
+static void es1370_writeb(void *opaque, uint32_t addr, uint32_t val)
{
ES1370State *s = opaque;
uint32_t shift, mask;
@@ -512,7 +507,7 @@ IO_WRITE_PROTO (es1370_writeb)
}
}
-IO_WRITE_PROTO (es1370_writew)
+static void es1370_writew(void *opaque, uint32_t addr, uint32_t val)
{
ES1370State *s = opaque;
addr = es1370_fixup (s, addr);
@@ -549,7 +544,7 @@ IO_WRITE_PROTO (es1370_writew)
}
}
-IO_WRITE_PROTO (es1370_writel)
+static void es1370_writel(void *opaque, uint32_t addr, uint32_t val)
{
ES1370State *s = opaque;
struct chan *d = &s->chan[0];
@@ -615,7 +610,7 @@ IO_WRITE_PROTO (es1370_writel)
}
}
-IO_READ_PROTO (es1370_readb)
+static uint32_t es1370_readb(void *opaque, uint32_t addr)
{
ES1370State *s = opaque;
uint32_t val;
@@ -650,7 +645,7 @@ IO_READ_PROTO (es1370_readb)
return val;
}
-IO_READ_PROTO (es1370_readw)
+static uint32_t es1370_readw(void *opaque, uint32_t addr)
{
ES1370State *s = opaque;
struct chan *d = &s->chan[0];
@@ -692,7 +687,7 @@ IO_READ_PROTO (es1370_readw)
return val;
}
-IO_READ_PROTO (es1370_readl)
+static uint32_t es1370_readl(void *opaque, uint32_t addr)
{
ES1370State *s = opaque;
uint32_t val;
diff --git a/hw/audio/fmopl.c b/hw/audio/fmopl.c
index adcef2d3b..81c0c1be2 100644
--- a/hw/audio/fmopl.c
+++ b/hw/audio/fmopl.c
@@ -1177,7 +1177,7 @@ void OPLResetChip(FM_OPL *OPL)
OPLWriteReg(OPL,0x03,0); /* Timer2 */
OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */
for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0);
- /* reset OPerator paramater */
+ /* reset operator parameter */
for( c = 0 ; c < OPL->max_ch ; c++ )
{
OPL_CH *CH = &OPL->P_CH[c];
diff --git a/hw/audio/gus.c b/hw/audio/gus.c
index 86223a954..e0c8a4ee1 100644
--- a/hw/audio/gus.c
+++ b/hw/audio/gus.c
@@ -41,11 +41,6 @@
#define GUS_ENDIANNESS 0
#endif
-#define IO_READ_PROTO(name) \
- static uint32_t name (void *opaque, uint32_t nport)
-#define IO_WRITE_PROTO(name) \
- static void name (void *opaque, uint32_t nport, uint32_t val)
-
#define TYPE_GUS "gus"
#define GUS(obj) OBJECT_CHECK (GUSState, (obj), TYPE_GUS)
@@ -64,14 +59,14 @@ typedef struct GUSState {
qemu_irq pic;
} GUSState;
-IO_READ_PROTO (gus_readb)
+static uint32_t gus_readb(void *opaque, uint32_t nport)
{
GUSState *s = opaque;
return gus_read (&s->emu, nport, 1);
}
-IO_WRITE_PROTO (gus_writeb)
+static void gus_writeb(void *opaque, uint32_t nport, uint32_t val)
{
GUSState *s = opaque;
diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c
index b052de5f7..995435f24 100644
--- a/hw/audio/sb16.c
+++ b/hw/audio/sb16.c
@@ -40,11 +40,6 @@
#define ldebug(...)
#endif
-#define IO_READ_PROTO(name) \
- uint32_t name (void *opaque, uint32_t nport)
-#define IO_WRITE_PROTO(name) \
- void name (void *opaque, uint32_t nport, uint32_t val)
-
static const char e3[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.";
#define TYPE_SB16 "sb16"
@@ -881,7 +876,7 @@ static void reset (SB16State *s)
legacy_reset (s);
}
-static IO_WRITE_PROTO (dsp_write)
+static void dsp_write(void *opaque, uint32_t nport, uint32_t val)
{
SB16State *s = opaque;
int iport;
@@ -959,7 +954,7 @@ static IO_WRITE_PROTO (dsp_write)
}
}
-static IO_READ_PROTO (dsp_read)
+static uint32_t dsp_read(void *opaque, uint32_t nport)
{
SB16State *s = opaque;
int iport, retval, ack = 0;
@@ -1058,14 +1053,14 @@ static void reset_mixer (SB16State *s)
}
}
-static IO_WRITE_PROTO (mixer_write_indexb)
+static void mixer_write_indexb(void *opaque, uint32_t nport, uint32_t val)
{
SB16State *s = opaque;
(void) nport;
s->mixer_nreg = val;
}
-static IO_WRITE_PROTO (mixer_write_datab)
+static void mixer_write_datab(void *opaque, uint32_t nport, uint32_t val)
{
SB16State *s = opaque;
@@ -1121,7 +1116,7 @@ static IO_WRITE_PROTO (mixer_write_datab)
s->mixer_regs[s->mixer_nreg] = val;
}
-static IO_READ_PROTO (mixer_read)
+static uint32_t mixer_read(void *opaque, uint32_t nport)
{
SB16State *s = opaque;
diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c
index 6106e4615..c57f293cc 100644
--- a/hw/block/dataplane/virtio-blk.c
+++ b/hw/block/dataplane/virtio-blk.c
@@ -45,7 +45,6 @@ struct VirtIOBlockDataPlane {
* use it).
*/
IOThread *iothread;
- IOThread internal_iothread_obj;
AioContext *ctx;
EventNotifier host_notifier; /* doorbell */
@@ -149,14 +148,14 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
*dataplane = NULL;
- if (!conf->data_plane && !conf->iothread) {
+ if (!conf->iothread) {
return;
}
/* Don't try if transport does not support notifiers. */
if (!k->set_guest_notifiers || !k->set_host_notifier) {
error_setg(errp,
- "device is incompatible with x-data-plane "
+ "device is incompatible with dataplane "
"(transport does not support notifiers)");
return;
}
@@ -179,16 +178,6 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
if (conf->iothread) {
s->iothread = conf->iothread;
object_ref(OBJECT(s->iothread));
- } else {
- /* Create per-device IOThread if none specified. This is for
- * x-data-plane option compatibility. If x-data-plane is removed we
- * can drop this.
- */
- object_initialize(&s->internal_iothread_obj,
- sizeof(s->internal_iothread_obj),
- TYPE_IOTHREAD);
- user_creatable_complete(OBJECT(&s->internal_iothread_obj), &error_abort);
- s->iothread = &s->internal_iothread_obj;
}
s->ctx = iothread_get_aio_context(s->iothread);
s->bh = aio_bh_new(s->ctx, notify_guest_bh, s);
@@ -283,7 +272,8 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
/* Get this show started by hooking up our callbacks */
aio_context_acquire(s->ctx);
- aio_set_event_notifier(s->ctx, &s->host_notifier, handle_notify);
+ aio_set_event_notifier(s->ctx, &s->host_notifier, true,
+ handle_notify);
aio_context_release(s->ctx);
return;
@@ -319,7 +309,7 @@ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s)
aio_context_acquire(s->ctx);
/* Stop notifications for new requests from guest */
- aio_set_event_notifier(s->ctx, &s->host_notifier, NULL);
+ aio_set_event_notifier(s->ctx, &s->host_notifier, true, NULL);
/* Drain and switch bs back to the QEMU main loop */
blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context());
diff --git a/hw/block/fdc.c b/hw/block/fdc.c
index 5e1b67ee4..4292eced3 100644
--- a/hw/block/fdc.c
+++ b/hw/block/fdc.c
@@ -192,6 +192,8 @@ typedef struct FDrive {
uint8_t ro; /* Is read-only */
uint8_t media_changed; /* Is media changed */
uint8_t media_rate; /* Data rate of medium */
+
+ bool media_inserted; /* Is there a medium in the tray */
} FDrive;
static void fd_init(FDrive *drv)
@@ -261,7 +263,7 @@ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect,
#endif
drv->head = head;
if (drv->track != track) {
- if (drv->blk != NULL && blk_is_inserted(drv->blk)) {
+ if (drv->media_inserted) {
drv->media_changed = 0;
}
ret = 1;
@@ -270,7 +272,7 @@ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect,
drv->sect = sect;
}
- if (drv->blk == NULL || !blk_is_inserted(drv->blk)) {
+ if (!drv->media_inserted) {
ret = 2;
}
@@ -296,7 +298,7 @@ static void fd_revalidate(FDrive *drv)
ro = blk_is_read_only(drv->blk);
pick_geometry(drv->blk, &nb_heads, &max_track,
&last_sect, drv->drive, &drive, &rate);
- if (!blk_is_inserted(drv->blk)) {
+ if (!drv->media_inserted) {
FLOPPY_DPRINTF("No disk in drive\n");
} else {
FLOPPY_DPRINTF("Floppy disk (%d h %d t %d s) %s\n", nb_heads,
@@ -692,7 +694,7 @@ static bool fdrive_media_changed_needed(void *opaque)
{
FDrive *drive = opaque;
- return (drive->blk != NULL && drive->media_changed != 1);
+ return (drive->media_inserted && drive->media_changed != 1);
}
static const VMStateDescription vmstate_fdrive_media_changed = {
@@ -1417,7 +1419,7 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction)
* recall us...
*/
DMA_hold_DREQ(fdctrl->dma_chann);
- DMA_schedule(fdctrl->dma_chann);
+ DMA_schedule();
} else {
/* Start transfer */
fdctrl_transfer_handler(fdctrl, fdctrl->dma_chann, 0,
@@ -2184,12 +2186,21 @@ static void fdctrl_change_cb(void *opaque, bool load)
{
FDrive *drive = opaque;
+ drive->media_inserted = load && drive->blk && blk_is_inserted(drive->blk);
+
drive->media_changed = 1;
fd_revalidate(drive);
}
+static bool fdctrl_is_tray_open(void *opaque)
+{
+ FDrive *drive = opaque;
+ return !drive->media_inserted;
+}
+
static const BlockDevOps fdctrl_block_ops = {
.change_media_cb = fdctrl_change_cb,
+ .is_tray_open = fdctrl_is_tray_open,
};
/* Init functions */
@@ -2217,6 +2228,7 @@ static void fdctrl_connect_drives(FDCtrl *fdctrl, Error **errp)
fdctrl_change_cb(drive, 0);
if (drive->blk) {
blk_set_dev_ops(drive->blk, &fdctrl_block_ops, drive);
+ drive->media_inserted = blk_is_inserted(drive->blk);
}
}
}
diff --git a/hw/block/nand.c b/hw/block/nand.c
index 61d2cec03..f0e34139f 100644
--- a/hw/block/nand.c
+++ b/hw/block/nand.c
@@ -522,8 +522,8 @@ void nand_setio(DeviceState *dev, uint32_t value)
if (s->ale) {
unsigned int shift = s->addrlen * 8;
- unsigned int mask = ~(0xff << shift);
- unsigned int v = value << shift;
+ uint64_t mask = ~(0xffull << shift);
+ uint64_t v = (uint64_t)value << shift;
s->addr = (s->addr & mask) | v;
s->addrlen ++;
@@ -712,7 +712,7 @@ static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s)
memset(s->storage + (PAGE(addr) << OOB_SHIFT),
0xff, OOB_SIZE << s->erase_shift);
i = SECTOR(addr);
- page = SECTOR(addr + (ADDR_SHIFT + s->erase_shift));
+ page = SECTOR(addr + (1 << (ADDR_SHIFT + s->erase_shift)));
for (; i < page; i ++)
if (blk_write(s->blk, i, iobuf, 1) < 0) {
printf("%s: write error in sector %" PRIu64 "\n", __func__, i);
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
index 40d488032..169e4fa7a 100644
--- a/hw/block/nvme.c
+++ b/hw/block/nvme.c
@@ -201,10 +201,11 @@ static void nvme_rw_cb(void *opaque, int ret)
NvmeCtrl *n = sq->ctrl;
NvmeCQueue *cq = n->cq[sq->cqid];
- block_acct_done(blk_get_stats(n->conf.blk), &req->acct);
if (!ret) {
+ block_acct_done(blk_get_stats(n->conf.blk), &req->acct);
req->status = NVME_SUCCESS;
} else {
+ block_acct_failed(blk_get_stats(n->conf.blk), &req->acct);
req->status = NVME_INTERNAL_DEV_ERROR;
}
if (req->has_sg) {
@@ -238,18 +239,22 @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
uint64_t data_size = (uint64_t)nlb << data_shift;
uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS);
int is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0;
+ enum BlockAcctType acct = is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ;
if ((slba + nlb) > ns->id_ns.nsze) {
+ block_acct_invalid(blk_get_stats(n->conf.blk), acct);
return NVME_LBA_RANGE | NVME_DNR;
}
+
if (nvme_map_prp(&req->qsg, prp1, prp2, data_size, n)) {
+ block_acct_invalid(blk_get_stats(n->conf.blk), acct);
return NVME_INVALID_FIELD | NVME_DNR;
}
+
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);
+ dma_acct_start(n->conf.blk, &req->acct, &req->qsg, acct);
req->aiocb = is_write ?
dma_blk_write(n->conf.blk, &req->qsg, aio_slba, nvme_rw_cb, req) :
dma_blk_read(n->conf.blk, &req->qsg, aio_slba, nvme_rw_cb, req);
@@ -805,7 +810,7 @@ static int nvme_init(PCIDevice *pci_dev)
n->num_namespaces = 1;
n->num_queues = 64;
- n->reg_size = 1 << qemu_fls(0x1004 + 2 * (n->num_queues + 1) * 4);
+ n->reg_size = pow2ceil(0x1004 + 2 * (n->num_queues + 1) * 4);
n->ns_size = bs_size / (uint64_t)n->num_namespaces;
n->namespaces = g_new0(NvmeNamespace, n->num_namespaces);
diff --git a/hw/block/onenand.c b/hw/block/onenand.c
index 1b2c89375..58eff508b 100644
--- a/hw/block/onenand.c
+++ b/hw/block/onenand.c
@@ -786,7 +786,7 @@ static int onenand_initfn(SysBusDevice *sbd)
s->otp = memset(g_malloc((64 + 2) << PAGE_SHIFT),
0xff, (64 + 2) << PAGE_SHIFT);
memory_region_init_ram(&s->ram, OBJECT(s), "onenand.ram",
- 0xc000 << s->shift, &error_abort);
+ 0xc000 << s->shift, &error_fatal);
vmstate_register_ram_global(&s->ram);
ram = memory_region_get_ram_ptr(&s->ram);
s->boot[0] = ram + (0x0000 << s->shift);
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index 5625a9fa7..b88b726be 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -30,7 +30,7 @@
VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s)
{
- VirtIOBlockReq *req = g_slice_new(VirtIOBlockReq);
+ VirtIOBlockReq *req = g_new(VirtIOBlockReq, 1);
req->dev = s;
req->qiov.size = 0;
req->in_len = 0;
@@ -42,7 +42,7 @@ VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s)
void virtio_blk_free_request(VirtIOBlockReq *req)
{
if (req) {
- g_slice_free(VirtIOBlockReq, req);
+ g_free(req);
}
}
@@ -72,11 +72,14 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
VirtIOBlock *s = req->dev;
if (action == BLOCK_ERROR_ACTION_STOP) {
+ /* Break the link as the next request is going to be parsed from the
+ * ring again. Otherwise we may end up doing a double completion! */
+ req->mr_next = NULL;
req->next = s->rq;
s->rq = req;
} else if (action == BLOCK_ERROR_ACTION_REPORT) {
virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
- block_acct_done(blk_get_stats(s->blk), &req->acct);
+ block_acct_failed(blk_get_stats(s->blk), &req->acct);
virtio_blk_free_request(req);
}
@@ -536,6 +539,8 @@ void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
if (!virtio_blk_sect_range_ok(req->dev, req->sector_num,
req->qiov.size)) {
virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
+ block_acct_invalid(blk_get_stats(req->dev->blk),
+ is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ);
virtio_blk_free_request(req);
return;
}
@@ -600,6 +605,8 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
return;
}
+ blk_io_plug(s->blk);
+
while ((req = virtio_blk_get_request(s))) {
virtio_blk_handle_request(req, &mrb);
}
@@ -607,6 +614,8 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
if (mrb.num_reqs) {
virtio_blk_submit_multireq(s->blk, &mrb);
}
+
+ blk_io_unplug(s->blk);
}
static void virtio_blk_dma_restart_bh(void *opaque)
@@ -840,10 +849,7 @@ static int virtio_blk_load_device(VirtIODevice *vdev, QEMUFile *f,
req->next = s->rq;
s->rq = req;
- virtqueue_map_sg(req->elem.in_sg, req->elem.in_addr,
- req->elem.in_num, 1);
- virtqueue_map_sg(req->elem.out_sg, req->elem.out_addr,
- req->elem.out_num, 0);
+ virtqueue_map(&req->elem);
}
return 0;
@@ -976,11 +982,10 @@ static Property virtio_blk_properties[] = {
DEFINE_PROP_STRING("serial", VirtIOBlock, conf.serial),
DEFINE_PROP_BIT("config-wce", VirtIOBlock, conf.config_wce, 0, true),
#ifdef __linux__
- DEFINE_PROP_BIT("scsi", VirtIOBlock, conf.scsi, 0, true),
+ DEFINE_PROP_BIT("scsi", VirtIOBlock, conf.scsi, 0, false),
#endif
DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0,
true),
- DEFINE_PROP_BIT("x-data-plane", VirtIOBlock, conf.data_plane, 0, false),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/block/xen_blkif.h b/hw/block/xen_blkif.h
index 711b69274..c68487cb3 100644
--- a/hw/block/xen_blkif.h
+++ b/hw/block/xen_blkif.h
@@ -85,8 +85,10 @@ static inline void blkif_get_x86_32_req(blkif_request_t *dst, blkif_x86_32_reque
d->nr_sectors = s->nr_sectors;
return;
}
- if (n > src->nr_segments)
- n = src->nr_segments;
+ /* prevent the compiler from optimizing the code and using src->nr_segments instead */
+ barrier();
+ if (n > dst->nr_segments)
+ n = dst->nr_segments;
for (i = 0; i < n; i++)
dst->seg[i] = src->seg[i];
}
@@ -106,8 +108,10 @@ static inline void blkif_get_x86_64_req(blkif_request_t *dst, blkif_x86_64_reque
d->nr_sectors = s->nr_sectors;
return;
}
- if (n > src->nr_segments)
- n = src->nr_segments;
+ /* prevent the compiler from optimizing the code and using src->nr_segments instead */
+ barrier();
+ if (n > dst->nr_segments)
+ n = dst->nr_segments;
for (i = 0; i < n; i++)
dst->seg[i] = src->seg[i];
}
diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c
index 267d8a8c7..814665034 100644
--- a/hw/block/xen_disk.c
+++ b/hw/block/xen_disk.c
@@ -24,7 +24,6 @@
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
-#include <signal.h>
#include <inttypes.h>
#include <time.h>
#include <fcntl.h>
@@ -76,7 +75,6 @@ struct ioreq {
off_t start;
QEMUIOVector v;
int presync;
- int postsync;
uint8_t mapped;
/* grant mapping */
@@ -145,7 +143,6 @@ static void ioreq_reset(struct ioreq *ioreq)
ioreq->status = 0;
ioreq->start = 0;
ioreq->presync = 0;
- ioreq->postsync = 0;
ioreq->mapped = 0;
memset(ioreq->domids, 0, sizeof(ioreq->domids));
@@ -521,12 +518,6 @@ static void qemu_aio_complete(void *opaque, int ret)
if (ioreq->aio_inflight > 0) {
return;
}
- if (ioreq->postsync) {
- ioreq->postsync = 0;
- ioreq->aio_inflight++;
- blk_aio_flush(ioreq->blkdev->blk, qemu_aio_complete, ioreq);
- return;
- }
ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY;
ioreq_unmap(ioreq);
@@ -538,7 +529,11 @@ static void qemu_aio_complete(void *opaque, int ret)
break;
}
case BLKIF_OP_READ:
- block_acct_done(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct);
+ if (ioreq->status == BLKIF_RSP_OKAY) {
+ block_acct_done(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct);
+ } else {
+ block_acct_failed(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct);
+ }
break;
case BLKIF_OP_DISCARD:
default:
@@ -577,7 +572,9 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq)
}
block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct,
- ioreq->v.size, BLOCK_ACCT_WRITE);
+ ioreq->v.size,
+ ioreq->req.operation == BLKIF_OP_WRITE ?
+ BLOCK_ACCT_WRITE : BLOCK_ACCT_FLUSH);
ioreq->aio_inflight++;
blk_aio_writev(blkdev->blk, ioreq->start / BLOCK_SIZE,
&ioreq->v, ioreq->v.size / BLOCK_SIZE,
@@ -721,6 +718,23 @@ static void blk_handle_requests(struct XenBlkDev *blkdev)
/* parse them */
if (ioreq_parse(ioreq) != 0) {
+
+ switch (ioreq->req.operation) {
+ case BLKIF_OP_READ:
+ block_acct_invalid(blk_get_stats(blkdev->blk),
+ BLOCK_ACCT_READ);
+ break;
+ case BLKIF_OP_WRITE:
+ block_acct_invalid(blk_get_stats(blkdev->blk),
+ BLOCK_ACCT_WRITE);
+ break;
+ case BLKIF_OP_FLUSH_DISKCACHE:
+ block_acct_invalid(blk_get_stats(blkdev->blk),
+ BLOCK_ACCT_FLUSH);
+ default:
+ break;
+ };
+
if (blk_send_response_one(ioreq)) {
xen_be_send_notify(&blkdev->xendev);
}
@@ -932,9 +946,11 @@ static int blk_connect(struct XenDevice *xendev)
blk_attach_dev_nofail(blkdev->blk, blkdev);
blkdev->file_size = blk_getlength(blkdev->blk);
if (blkdev->file_size < 0) {
+ BlockDriverState *bs = blk_bs(blkdev->blk);
+ const char *drv_name = bs ? bdrv_get_format_name(bs) : NULL;
xen_be_printf(&blkdev->xendev, 1, "blk_getlength: %d (%s) | drv %s\n",
(int)blkdev->file_size, strerror(-blkdev->file_size),
- bdrv_get_format_name(blk_bs(blkdev->blk)) ?: "-");
+ drv_name ?: "-");
blkdev->file_size = 0;
}
diff --git a/hw/bt/hci.c b/hw/bt/hci.c
index 7ea3dc6b7..2151d0128 100644
--- a/hw/bt/hci.c
+++ b/hw/bt/hci.c
@@ -23,6 +23,8 @@
#include "hw/usb.h"
#include "sysemu/bt.h"
#include "hw/bt.h"
+#include "qapi/qmp/qerror.h"
+#include "sysemu/replay.h"
struct bt_hci_s {
uint8_t *(*evt_packet)(void *opaque);
@@ -72,6 +74,8 @@ struct bt_hci_s {
struct HCIInfo info;
struct bt_device_s device;
+
+ Error *replay_blocker;
};
#define DEFAULT_RSSI_DBM 20
@@ -595,7 +599,7 @@ static void bt_hci_inquiry_result(struct bt_hci_s *hci,
static void bt_hci_mod_timer_1280ms(QEMUTimer *timer, int period)
{
timer_mod(timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- muldiv64(period << 7, get_ticks_per_sec(), 100));
+ (uint64_t)(period << 7) * 10000000);
}
static void bt_hci_inquiry_start(struct bt_hci_s *hci, int length)
@@ -1099,7 +1103,7 @@ static int bt_hci_mode_change(struct bt_hci_s *hci, uint16_t handle,
bt_hci_event_status(hci, HCI_SUCCESS);
timer_mod(link->acl_mode_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- muldiv64(interval * 625, get_ticks_per_sec(), 1000000));
+ ((uint64_t)interval * 625) * 1000);
bt_hci_lmp_mode_change_master(hci, link->link, mode, interval);
return 0;
@@ -1151,8 +1155,7 @@ static void bt_hci_reset(struct bt_hci_s *hci)
hci->event_mask[7] = 0x00;
hci->device.inquiry_scan = 0;
hci->device.page_scan = 0;
- if (hci->device.lmp_name)
- g_free((void *) hci->device.lmp_name);
+ g_free((void *) hci->device.lmp_name);
hci->device.lmp_name = NULL;
hci->device.class[0] = 0x00;
hci->device.class[1] = 0x00;
@@ -1829,8 +1832,7 @@ static void bt_submit_hci(struct HCIInfo *info,
case cmd_opcode_pack(OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME):
LENGTH_CHECK(change_local_name);
- if (hci->device.lmp_name)
- g_free((void *) hci->device.lmp_name);
+ g_free((void *) hci->device.lmp_name);
hci->device.lmp_name = g_strndup(PARAM(change_local_name, name),
sizeof(PARAM(change_local_name, name)));
bt_hci_event_complete_status(hci, HCI_SUCCESS);
@@ -2191,6 +2193,9 @@ struct HCIInfo *bt_new_hci(struct bt_scatternet_s *net)
s->device.handle_destroy = bt_hci_destroy;
+ error_setg(&s->replay_blocker, QERR_REPLAY_NOT_SUPPORTED, "-bt hci");
+ replay_add_blocker(s->replay_blocker);
+
return &s->info;
}
@@ -2231,8 +2236,7 @@ static void bt_hci_done(struct HCIInfo *info)
bt_device_done(&hci->device);
- if (hci->device.lmp_name)
- g_free((void *) hci->device.lmp_name);
+ g_free((void *) hci->device.lmp_name);
/* Be gentle and send DISCONNECT to all connected peers and those
* currently waiting for us to accept or reject a connection request.
diff --git a/hw/bt/sdp.c b/hw/bt/sdp.c
index c90374795..04eaecae6 100644
--- a/hw/bt/sdp.c
+++ b/hw/bt/sdp.c
@@ -42,7 +42,7 @@ struct bt_l2cap_sdp_state_s {
static ssize_t sdp_datalen(const uint8_t **element, ssize_t *left)
{
- size_t len = *(*element) ++ & SDP_DSIZE_MASK;
+ uint32_t len = *(*element) ++ & SDP_DSIZE_MASK;
if (!*left)
return -1;
@@ -150,12 +150,14 @@ static ssize_t sdp_svc_search(struct bt_l2cap_sdp_state_s *sdp,
if (seqlen < 3 || len < seqlen)
return -SDP_INVALID_SYNTAX;
len -= seqlen;
-
while (seqlen)
if (sdp_svc_match(sdp, &req, &seqlen))
return -SDP_INVALID_SYNTAX;
- } else if (sdp_svc_match(sdp, &req, &seqlen))
- return -SDP_INVALID_SYNTAX;
+ } else {
+ if (sdp_svc_match(sdp, &req, &len)) {
+ return -SDP_INVALID_SYNTAX;
+ }
+ }
if (len < 3)
return -SDP_INVALID_SYNTAX;
@@ -278,8 +280,11 @@ static ssize_t sdp_attr_get(struct bt_l2cap_sdp_state_s *sdp,
while (seqlen)
if (sdp_attr_match(record, &req, &seqlen))
return -SDP_INVALID_SYNTAX;
- } else if (sdp_attr_match(record, &req, &seqlen))
- return -SDP_INVALID_SYNTAX;
+ } else {
+ if (sdp_attr_match(record, &req, &len)) {
+ return -SDP_INVALID_SYNTAX;
+ }
+ }
if (len < 1)
return -SDP_INVALID_SYNTAX;
@@ -393,8 +398,11 @@ static ssize_t sdp_svc_search_attr_get(struct bt_l2cap_sdp_state_s *sdp,
while (seqlen)
if (sdp_svc_match(sdp, &req, &seqlen))
return -SDP_INVALID_SYNTAX;
- } else if (sdp_svc_match(sdp, &req, &seqlen))
- return -SDP_INVALID_SYNTAX;
+ } else {
+ if (sdp_svc_match(sdp, &req, &len)) {
+ return -SDP_INVALID_SYNTAX;
+ }
+ }
if (len < 3)
return -SDP_INVALID_SYNTAX;
@@ -413,8 +421,11 @@ static ssize_t sdp_svc_search_attr_get(struct bt_l2cap_sdp_state_s *sdp,
while (seqlen)
if (sdp_svc_attr_match(sdp, &req, &seqlen))
return -SDP_INVALID_SYNTAX;
- } else if (sdp_svc_attr_match(sdp, &req, &seqlen))
- return -SDP_INVALID_SYNTAX;
+ } else {
+ if (sdp_svc_attr_match(sdp, &req, &len)) {
+ return -SDP_INVALID_SYNTAX;
+ }
+ }
if (len < 1)
return -SDP_INVALID_SYNTAX;
diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
index 9d379e5b1..a217271be 100644
--- a/hw/char/cadence_uart.c
+++ b/hw/char/cadence_uart.c
@@ -374,6 +374,9 @@ static void uart_write(void *opaque, hwaddr offset,
DB_PRINT(" offset:%x data:%08x\n", (unsigned)offset, (unsigned)value);
offset >>= 2;
+ if (offset >= CADENCE_UART_R_MAX) {
+ return;
+ }
switch (offset) {
case R_IER: /* ier (wts imr) */
s->r[R_IMR] |= value;
diff --git a/hw/char/escc.c b/hw/char/escc.c
index ba653efd6..c9840e11d 100644
--- a/hw/char/escc.c
+++ b/hw/char/escc.c
@@ -842,13 +842,13 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src,
ChannelState *s = (ChannelState *)dev;
int qcode, keycode;
- assert(evt->kind == INPUT_EVENT_KIND_KEY);
- qcode = qemu_input_key_value_to_qcode(evt->key->key);
+ assert(evt->type == INPUT_EVENT_KIND_KEY);
+ qcode = qemu_input_key_value_to_qcode(evt->u.key->key);
trace_escc_sunkbd_event_in(qcode, QKeyCode_lookup[qcode],
- evt->key->down);
+ evt->u.key->down);
if (qcode == Q_KEY_CODE_CAPS_LOCK) {
- if (evt->key->down) {
+ if (evt->u.key->down) {
s->caps_lock_mode ^= 1;
if (s->caps_lock_mode == 2) {
return; /* Drop second press */
@@ -862,7 +862,7 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src,
}
if (qcode == Q_KEY_CODE_NUM_LOCK) {
- if (evt->key->down) {
+ if (evt->u.key->down) {
s->num_lock_mode ^= 1;
if (s->num_lock_mode == 2) {
return; /* Drop second press */
@@ -876,7 +876,7 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src,
}
keycode = qcode_to_keycode[qcode];
- if (!evt->key->down) {
+ if (!evt->u.key->down) {
keycode |= 0x80;
}
trace_escc_sunkbd_event_out(keycode);
@@ -1035,6 +1035,7 @@ static void escc_class_init(ObjectClass *klass, void *data)
dc->reset = escc_reset;
dc->vmsd = &vmstate_escc;
dc->props = escc_properties;
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
}
static const TypeInfo escc_info = {
diff --git a/hw/char/etraxfs_ser.c b/hw/char/etraxfs_ser.c
index 857c13621..562021e28 100644
--- a/hw/char/etraxfs_ser.c
+++ b/hw/char/etraxfs_ser.c
@@ -182,15 +182,13 @@ static void serial_receive(void *opaque, const uint8_t *buf, int size)
static int serial_can_receive(void *opaque)
{
ETRAXSerial *s = opaque;
- int r;
/* Is the receiver enabled? */
if (!(s->regs[RW_REC_CTRL] & (1 << 3))) {
return 0;
}
- r = sizeof(s->rx_fifo) - s->rx_fifo_len;
- return r;
+ return sizeof(s->rx_fifo) - s->rx_fifo_len;
}
static void serial_event(void *opaque, int event)
diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c
index 7614e5860..215f9622c 100644
--- a/hw/char/exynos4210_uart.c
+++ b/hw/char/exynos4210_uart.c
@@ -234,10 +234,8 @@ static int fifo_empty_elements_number(Exynos4210UartFIFO *q)
static void fifo_reset(Exynos4210UartFIFO *q)
{
- if (q->data != NULL) {
- g_free(q->data);
- q->data = NULL;
- }
+ g_free(q->data);
+ q->data = NULL;
q->data = (uint8_t *)g_malloc0(q->size);
diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c
index f3fbc776b..f30f9c24b 100644
--- a/hw/char/imx_serial.c
+++ b/hw/char/imx_serial.c
@@ -4,6 +4,7 @@
* Copyright (c) 2008 OKL
* Originally Written by Hans Jiang
* Copyright (c) 2011 NICTA Pty Ltd.
+ * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
@@ -17,63 +18,24 @@
* is a real serial device.
*/
-#include "hw/hw.h"
-#include "hw/sysbus.h"
+#include "hw/char/imx_serial.h"
#include "sysemu/sysemu.h"
#include "sysemu/char.h"
-#include "hw/arm/imx.h"
-//#define DEBUG_SERIAL 1
-#ifdef DEBUG_SERIAL
-#define DPRINTF(fmt, args...) \
-do { printf("imx_serial: " fmt , ##args); } while (0)
-#else
-#define DPRINTF(fmt, args...) do {} while (0)
-#endif
-
-/*
- * Define to 1 for messages about attempts to
- * access unimplemented registers or similar.
- */
-//#define DEBUG_IMPLEMENTATION 1
-#ifdef DEBUG_IMPLEMENTATION
-# define IPRINTF(fmt, args...) \
- do { fprintf(stderr, "imx_serial: " fmt, ##args); } while (0)
-#else
-# define IPRINTF(fmt, args...) do {} while (0)
+#ifndef DEBUG_IMX_UART
+#define DEBUG_IMX_UART 0
#endif
-#define TYPE_IMX_SERIAL "imx-serial"
-#define IMX_SERIAL(obj) OBJECT_CHECK(IMXSerialState, (obj), TYPE_IMX_SERIAL)
-
-typedef struct IMXSerialState {
- SysBusDevice parent_obj;
-
- MemoryRegion iomem;
- int32_t readbuff;
-
- uint32_t usr1;
- uint32_t usr2;
- uint32_t ucr1;
- uint32_t ucr2;
- uint32_t uts1;
-
- /*
- * The registers below are implemented just so that the
- * guest OS sees what it has written
- */
- uint32_t onems;
- uint32_t ufcr;
- uint32_t ubmr;
- uint32_t ubrc;
- uint32_t ucr3;
-
- qemu_irq irq;
- CharDriverState *chr;
-} IMXSerialState;
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX_UART) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_SERIAL, \
+ __func__, ##args); \
+ } \
+ } while (0)
static const VMStateDescription vmstate_imx_serial = {
- .name = "imx-serial",
+ .name = TYPE_IMX_SERIAL,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
@@ -91,61 +53,14 @@ static const VMStateDescription vmstate_imx_serial = {
},
};
-
-#define URXD_CHARRDY (1<<15) /* character read is valid */
-#define URXD_ERR (1<<14) /* Character has error */
-#define URXD_BRK (1<<11) /* Break received */
-
-#define USR1_PARTYER (1<<15) /* Parity Error */
-#define USR1_RTSS (1<<14) /* RTS pin status */
-#define USR1_TRDY (1<<13) /* Tx ready */
-#define USR1_RTSD (1<<12) /* RTS delta: pin changed state */
-#define USR1_ESCF (1<<11) /* Escape sequence interrupt */
-#define USR1_FRAMERR (1<<10) /* Framing error */
-#define USR1_RRDY (1<<9) /* receiver ready */
-#define USR1_AGTIM (1<<8) /* Aging timer interrupt */
-#define USR1_DTRD (1<<7) /* DTR changed */
-#define USR1_RXDS (1<<6) /* Receiver is idle */
-#define USR1_AIRINT (1<<5) /* Aysnch IR interrupt */
-#define USR1_AWAKE (1<<4) /* Falling edge detected on RXd pin */
-
-#define USR2_ADET (1<<15) /* Autobaud complete */
-#define USR2_TXFE (1<<14) /* Transmit FIFO empty */
-#define USR2_DTRF (1<<13) /* DTR/DSR transition */
-#define USR2_IDLE (1<<12) /* UART has been idle for too long */
-#define USR2_ACST (1<<11) /* Autobaud counter stopped */
-#define USR2_RIDELT (1<<10) /* Ring Indicator delta */
-#define USR2_RIIN (1<<9) /* Ring Indicator Input */
-#define USR2_IRINT (1<<8) /* Serial Infrared Interrupt */
-#define USR2_WAKE (1<<7) /* Start bit detected */
-#define USR2_DCDDELT (1<<6) /* Data Carrier Detect delta */
-#define USR2_DCDIN (1<<5) /* Data Carrier Detect Input */
-#define USR2_RTSF (1<<4) /* RTS transition */
-#define USR2_TXDC (1<<3) /* Transmission complete */
-#define USR2_BRCD (1<<2) /* Break condition detected */
-#define USR2_ORE (1<<1) /* Overrun error */
-#define USR2_RDR (1<<0) /* Receive data ready */
-
-#define UCR1_TRDYEN (1<<13) /* Tx Ready Interrupt Enable */
-#define UCR1_RRDYEN (1<<9) /* Rx Ready Interrupt Enable */
-#define UCR1_TXMPTYEN (1<<6) /* Tx Empty Interrupt Enable */
-#define UCR1_UARTEN (1<<0) /* UART Enable */
-
-#define UCR2_TXEN (1<<2) /* Transmitter enable */
-#define UCR2_RXEN (1<<1) /* Receiver enable */
-#define UCR2_SRST (1<<0) /* Reset complete */
-
-#define UTS1_TXEMPTY (1<<6)
-#define UTS1_RXEMPTY (1<<5)
-#define UTS1_TXFULL (1<<4)
-#define UTS1_RXFULL (1<<3)
-
static void imx_update(IMXSerialState *s)
{
uint32_t flags;
flags = (s->usr1 & s->ucr1) & (USR1_TRDY|USR1_RRDY);
- if (!(s->ucr1 & UCR1_TXMPTYEN)) {
+ if (s->ucr1 & UCR1_TXMPTYEN) {
+ flags |= (s->uts1 & UTS1_TXEMPTY);
+ } else {
flags &= ~USR1_TRDY;
}
@@ -192,7 +107,8 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset,
IMXSerialState *s = (IMXSerialState *)opaque;
uint32_t c;
- DPRINTF("read(offset=%x)\n", offset >> 2);
+ DPRINTF("read(offset=0x%" HWADDR_PRIx ")\n", offset);
+
switch (offset >> 2) {
case 0x0: /* URXD */
c = s->readbuff;
@@ -203,7 +119,9 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset,
s->usr2 &= ~USR2_RDR;
s->uts1 |= UTS1_RXEMPTY;
imx_update(s);
- qemu_chr_accept_input(s->chr);
+ if (s->chr) {
+ qemu_chr_accept_input(s->chr);
+ }
}
return c;
@@ -242,20 +160,20 @@ static uint64_t imx_serial_read(void *opaque, hwaddr offset,
return 0x0; /* TODO */
default:
- IPRINTF("imx_serial_read: bad offset: 0x%x\n", (int)offset);
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_SERIAL, __func__, offset);
return 0;
}
}
static void imx_serial_write(void *opaque, hwaddr offset,
- uint64_t value, unsigned size)
+ uint64_t value, unsigned size)
{
IMXSerialState *s = (IMXSerialState *)opaque;
unsigned char ch;
- DPRINTF("write(offset=%x, value = %x) to %s\n",
- offset >> 2,
- (unsigned int)value, s->chr ? s->chr->label : "NODEV");
+ DPRINTF("write(offset=0x%" HWADDR_PRIx ", value = 0x%x) to %s\n",
+ offset, (unsigned int)value, s->chr ? s->chr->label : "NODEV");
switch (offset >> 2) {
case 0x10: /* UTXD */
@@ -273,7 +191,9 @@ static void imx_serial_write(void *opaque, hwaddr offset,
case 0x20: /* UCR1 */
s->ucr1 = value & 0xffff;
+
DPRINTF("write(ucr1=%x)\n", (unsigned int)value);
+
imx_update(s);
break;
@@ -290,7 +210,9 @@ static void imx_serial_write(void *opaque, hwaddr offset,
}
if (value & UCR2_RXEN) {
if (!(s->ucr2 & UCR2_RXEN)) {
- qemu_chr_accept_input(s->chr);
+ if (s->chr) {
+ qemu_chr_accept_input(s->chr);
+ }
}
}
s->ucr2 = value & 0xffff;
@@ -298,25 +220,25 @@ static void imx_serial_write(void *opaque, hwaddr offset,
case 0x25: /* USR1 */
value &= USR1_AWAKE | USR1_AIRINT | USR1_DTRD | USR1_AGTIM |
- USR1_FRAMERR | USR1_ESCF | USR1_RTSD | USR1_PARTYER;
+ USR1_FRAMERR | USR1_ESCF | USR1_RTSD | USR1_PARTYER;
s->usr1 &= ~value;
break;
case 0x26: /* USR2 */
- /*
- * Writing 1 to some bits clears them; all other
- * values are ignored
- */
+ /*
+ * Writing 1 to some bits clears them; all other
+ * values are ignored
+ */
value &= USR2_ADET | USR2_DTRF | USR2_IDLE | USR2_ACST |
- USR2_RIDELT | USR2_IRINT | USR2_WAKE |
- USR2_DCDDELT | USR2_RTSF | USR2_BRCD | USR2_ORE;
+ USR2_RIDELT | USR2_IRINT | USR2_WAKE |
+ USR2_DCDDELT | USR2_RTSF | USR2_BRCD | USR2_ORE;
s->usr2 &= ~value;
break;
- /*
- * Linux expects to see what it writes to these registers
- * We don't currently alter the baud rate
- */
+ /*
+ * Linux expects to see what it writes to these registers
+ * We don't currently alter the baud rate
+ */
case 0x29: /* UBIR */
s->ubrc = value & 0xffff;
break;
@@ -339,12 +261,14 @@ static void imx_serial_write(void *opaque, hwaddr offset,
case 0x2d: /* UTS1 */
case 0x23: /* UCR4 */
- IPRINTF("Unimplemented Register %x written to\n", offset >> 2);
+ qemu_log_mask(LOG_UNIMP, "[%s]%s: Unimplemented reg 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_SERIAL, __func__, offset);
/* TODO */
break;
default:
- IPRINTF("imx_serial_write: Bad offset 0x%x\n", (int)offset);
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_SERIAL, __func__, offset);
}
}
@@ -357,7 +281,9 @@ static int imx_can_receive(void *opaque)
static void imx_put_data(void *opaque, uint32_t value)
{
IMXSerialState *s = (IMXSerialState *)opaque;
+
DPRINTF("received char\n");
+
s->usr1 |= USR1_RRDY;
s->usr2 |= USR2_RDR;
s->uts1 &= ~UTS1_RXEMPTY;
@@ -384,62 +310,30 @@ static const struct MemoryRegionOps imx_serial_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static int imx_serial_init(SysBusDevice *dev)
+static void imx_serial_realize(DeviceState *dev, Error **errp)
{
IMXSerialState *s = IMX_SERIAL(dev);
-
- memory_region_init_io(&s->iomem, OBJECT(s), &imx_serial_ops, s,
- "imx-serial", 0x1000);
- sysbus_init_mmio(dev, &s->iomem);
- sysbus_init_irq(dev, &s->irq);
-
if (s->chr) {
qemu_chr_add_handlers(s->chr, imx_can_receive, imx_receive,
imx_event, s);
} else {
- DPRINTF("No char dev for uart at 0x%lx\n",
- (unsigned long)s->iomem.ram_addr);
+ DPRINTF("No char dev for uart\n");
}
-
- return 0;
}
-void imx_serial_create(int uart, const hwaddr addr, qemu_irq irq)
+static void imx_serial_init(Object *obj)
{
- DeviceState *dev;
- SysBusDevice *bus;
- CharDriverState *chr;
- const char chr_name[] = "serial";
- char label[ARRAY_SIZE(chr_name) + 1];
-
- dev = qdev_create(NULL, TYPE_IMX_SERIAL);
-
- if (uart >= MAX_SERIAL_PORTS) {
- hw_error("Cannot assign uart %d: QEMU supports only %d ports\n",
- uart, MAX_SERIAL_PORTS);
- }
- chr = serial_hds[uart];
- if (!chr) {
- snprintf(label, ARRAY_SIZE(label), "%s%d", chr_name, uart);
- chr = qemu_chr_new(label, "null", NULL);
- if (!(chr)) {
- hw_error("Can't assign serial port to imx-uart%d.\n", uart);
- }
- }
-
- qdev_prop_set_chr(dev, "chardev", chr);
- bus = SYS_BUS_DEVICE(dev);
- qdev_init_nofail(dev);
- if (addr != (hwaddr)-1) {
- sysbus_mmio_map(bus, 0, addr);
- }
- sysbus_connect_irq(bus, 0, irq);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ IMXSerialState *s = IMX_SERIAL(obj);
+ memory_region_init_io(&s->iomem, obj, &imx_serial_ops, s,
+ TYPE_IMX_SERIAL, 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->irq);
}
-
-static Property imx32_serial_properties[] = {
+static Property imx_serial_properties[] = {
DEFINE_PROP_CHR("chardev", IMXSerialState, chr),
DEFINE_PROP_END_OF_LIST(),
};
@@ -447,21 +341,21 @@ static Property imx32_serial_properties[] = {
static void imx_serial_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = imx_serial_init;
+ dc->realize = imx_serial_realize;
dc->vmsd = &vmstate_imx_serial;
dc->reset = imx_serial_reset_at_boot;
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
dc->desc = "i.MX series UART";
- dc->props = imx32_serial_properties;
+ dc->props = imx_serial_properties;
}
static const TypeInfo imx_serial_info = {
- .name = TYPE_IMX_SERIAL,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(IMXSerialState),
- .class_init = imx_serial_class_init,
+ .name = TYPE_IMX_SERIAL,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(IMXSerialState),
+ .instance_init = imx_serial_init,
+ .class_init = imx_serial_class_init,
};
static void imx_serial_register_types(void)
diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c
index 98fd44e66..cda22eea5 100644
--- a/hw/char/mcf_uart.c
+++ b/hw/char/mcf_uart.c
@@ -126,7 +126,7 @@ static void mcf_uart_do_tx(mcf_uart_state *s)
static void mcf_do_command(mcf_uart_state *s, uint8_t cmd)
{
/* Misc command. */
- switch ((cmd >> 4) & 3) {
+ switch ((cmd >> 4) & 7) {
case 0: /* No-op. */
break;
case 1: /* Reset mode register pointer. */
diff --git a/hw/char/omap_uart.c b/hw/char/omap_uart.c
index 88f20943e..278ce36cb 100644
--- a/hw/char/omap_uart.c
+++ b/hw/char/omap_uart.c
@@ -55,8 +55,7 @@ struct omap_uart_s *omap_uart_init(hwaddr base,
qemu_irq txdma, qemu_irq rxdma,
const char *label, CharDriverState *chr)
{
- struct omap_uart_s *s = (struct omap_uart_s *)
- g_malloc0(sizeof(struct omap_uart_s));
+ struct omap_uart_s *s = g_new0(struct omap_uart_s, 1);
s->base = base;
s->fclk = fclk;
diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c
index be9705871..497b0afd9 100644
--- a/hw/char/virtio-serial-bus.c
+++ b/hw/char/virtio-serial-bus.c
@@ -705,10 +705,7 @@ static int fetch_active_ports_list(QEMUFile *f, int version_id,
qemu_get_buffer(f, (unsigned char *)&port->elem,
sizeof(port->elem));
- virtqueue_map_sg(port->elem.in_sg, port->elem.in_addr,
- port->elem.in_num, 1);
- virtqueue_map_sg(port->elem.out_sg, port->elem.out_addr,
- port->elem.out_num, 1);
+ virtqueue_map(&port->elem);
/*
* Port was throttled on source machine. Let's
diff --git a/hw/core/loader.c b/hw/core/loader.c
index 216eeeb91..eb67f05ee 100644
--- a/hw/core/loader.c
+++ b/hw/core/loader.c
@@ -594,8 +594,7 @@ static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr,
ret = hdr->ih_size;
out:
- if (data)
- g_free(data);
+ g_free(data);
close(fd);
return ret;
}
@@ -741,7 +740,7 @@ static void *rom_set_mr(Rom *rom, Object *owner, const char *name)
memory_region_init_resizeable_ram(rom->mr, owner, name,
rom->datasize, rom->romsize,
fw_cfg_resized,
- &error_abort);
+ &error_fatal);
memory_region_set_readonly(rom->mr, true);
vmstate_register_ram_global(rom->mr);
diff --git a/hw/core/machine.c b/hw/core/machine.c
index ac4654e9d..f4d317088 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -226,6 +226,20 @@ static void machine_set_usb(Object *obj, bool value, Error **errp)
ms->usb_disabled = !value;
}
+static bool machine_get_igd_gfx_passthru(Object *obj, Error **errp)
+{
+ MachineState *ms = MACHINE(obj);
+
+ return ms->igd_gfx_passthru;
+}
+
+static void machine_set_igd_gfx_passthru(Object *obj, bool value, Error **errp)
+{
+ MachineState *ms = MACHINE(obj);
+
+ ms->igd_gfx_passthru = value;
+}
+
static char *machine_get_firmware(Object *obj, Error **errp)
{
MachineState *ms = MACHINE(obj);
@@ -269,6 +283,21 @@ static bool machine_get_suppress_vmdesc(Object *obj, Error **errp)
return ms->suppress_vmdesc;
}
+static void machine_set_enforce_config_section(Object *obj, bool value,
+ Error **errp)
+{
+ MachineState *ms = MACHINE(obj);
+
+ ms->enforce_config_section = value;
+}
+
+static bool machine_get_enforce_config_section(Object *obj, Error **errp)
+{
+ MachineState *ms = MACHINE(obj);
+
+ return ms->enforce_config_section;
+}
+
static int error_on_sysbus_device(SysBusDevice *sbdev, void *opaque)
{
error_report("Option '-device %s' cannot be handled by this machine",
@@ -302,6 +331,17 @@ static void machine_class_init(ObjectClass *oc, void *data)
mc->default_ram_size = 128 * M_BYTE;
}
+static void machine_class_base_init(ObjectClass *oc, void *data)
+{
+ if (!object_class_is_abstract(oc)) {
+ MachineClass *mc = MACHINE_CLASS(oc);
+ const char *cname = object_class_get_name(oc);
+ assert(g_str_has_suffix(cname, TYPE_MACHINE_SUFFIX));
+ mc->name = g_strndup(cname,
+ strlen(cname) - strlen(TYPE_MACHINE_SUFFIX));
+ }
+}
+
static void machine_initfn(Object *obj)
{
MachineState *ms = MACHINE(obj);
@@ -388,6 +428,12 @@ static void machine_initfn(Object *obj)
object_property_set_description(obj, "usb",
"Set on/off to enable/disable usb",
NULL);
+ object_property_add_bool(obj, "igd-passthru",
+ machine_get_igd_gfx_passthru,
+ machine_set_igd_gfx_passthru, NULL);
+ object_property_set_description(obj, "igd-passthru",
+ "Set on/off to enable/disable igd passthrou",
+ NULL);
object_property_add_str(obj, "firmware",
machine_get_firmware,
machine_set_firmware, NULL);
@@ -406,6 +452,12 @@ static void machine_initfn(Object *obj)
object_property_set_description(obj, "suppress-vmdesc",
"Set on to disable self-describing migration",
NULL);
+ object_property_add_bool(obj, "enforce-config-section",
+ machine_get_enforce_config_section,
+ machine_set_enforce_config_section, NULL);
+ object_property_set_description(obj, "enforce-config-section",
+ "Set on to enforce configuration section migration",
+ NULL);
/* Register notifier when init is done for sysbus sanity checks */
ms->sysbus_notifier.notify = machine_init_notify;
@@ -431,11 +483,6 @@ bool machine_usb(MachineState *machine)
return machine->usb;
}
-bool machine_iommu(MachineState *machine)
-{
- return machine->iommu;
-}
-
bool machine_kernel_irqchip_allowed(MachineState *machine)
{
return machine->kernel_irqchip_allowed;
@@ -472,6 +519,7 @@ static const TypeInfo machine_info = {
.abstract = true,
.class_size = sizeof(MachineClass),
.class_init = machine_class_init,
+ .class_base_init = machine_class_base_init,
.instance_size = sizeof(MachineState),
.instance_init = machine_initfn,
.instance_finalize = machine_finalize,
diff --git a/hw/core/null-machine.c b/hw/core/null-machine.c
index 1ec7c3bbe..f36fbf231 100644
--- a/hw/core/null-machine.c
+++ b/hw/core/null-machine.c
@@ -19,17 +19,11 @@ static void machine_none_init(MachineState *machine)
{
}
-static QEMUMachine machine_none = {
- .name = "none",
- .desc = "empty machine",
- .init = machine_none_init,
- .max_cpus = 0,
-};
-
-static void register_machines(void)
+static void machine_none_machine_init(MachineClass *mc)
{
- qemu_register_machine(&machine_none);
+ mc->desc = "empty machine";
+ mc->init = machine_none_init;
+ mc->max_cpus = 0;
}
-machine_init(register_machines);
-
+DEFINE_MACHINE("none", machine_none_machine_init)
diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c
index 8437bd6e8..edf077cfd 100644
--- a/hw/core/ptimer.c
+++ b/hw/core/ptimer.c
@@ -9,6 +9,7 @@
#include "qemu/timer.h"
#include "hw/ptimer.h"
#include "qemu/host-utils.h"
+#include "sysemu/replay.h"
struct ptimer_state
{
@@ -27,7 +28,7 @@ struct ptimer_state
static void ptimer_trigger(ptimer_state *s)
{
if (s->bh) {
- qemu_bh_schedule(s->bh);
+ replay_bh_schedule_event(s->bh);
}
}
diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
index 04fd80a4d..33e245e12 100644
--- a/hw/core/qdev-properties.c
+++ b/hw/core/qdev-properties.c
@@ -422,9 +422,7 @@ static void set_string(Object *obj, Visitor *v, void *opaque,
error_propagate(errp, local_err);
return;
}
- if (*ptr) {
- g_free(*ptr);
- }
+ g_free(*ptr);
*ptr = str;
}
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index b2f404a76..b3ad46775 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -325,6 +325,11 @@ void qdev_reset_all(DeviceState *dev)
qdev_walk_children(dev, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL);
}
+void qdev_reset_all_fn(void *opaque)
+{
+ qdev_reset_all(DEVICE(opaque));
+}
+
void qbus_reset_all(BusState *bus)
{
qbus_walk_children(bus, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL);
@@ -417,17 +422,21 @@ void qdev_init_gpio_in_named(DeviceState *dev, qemu_irq_handler handler,
{
int i;
NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name);
- char *propname = g_strdup_printf("%s[*]", name ? name : "unnamed-gpio-in");
assert(gpio_list->num_out == 0 || !name);
gpio_list->in = qemu_extend_irqs(gpio_list->in, gpio_list->num_in, handler,
dev, n);
+ if (!name) {
+ name = "unnamed-gpio-in";
+ }
for (i = gpio_list->num_in; i < gpio_list->num_in + n; i++) {
+ gchar *propname = g_strdup_printf("%s[%u]", name, i);
+
object_property_add_child(OBJECT(dev), propname,
OBJECT(gpio_list->in[i]), &error_abort);
+ g_free(propname);
}
- g_free(propname);
gpio_list->num_in += n;
}
@@ -442,20 +451,25 @@ void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins,
{
int i;
NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name);
- char *propname = g_strdup_printf("%s[*]", name ? name : "unnamed-gpio-out");
assert(gpio_list->num_in == 0 || !name);
- gpio_list->num_out += n;
+ if (!name) {
+ name = "unnamed-gpio-out";
+ }
+ memset(pins, 0, sizeof(*pins) * n);
for (i = 0; i < n; ++i) {
- memset(&pins[i], 0, sizeof(*pins));
+ gchar *propname = g_strdup_printf("%s[%u]", name,
+ gpio_list->num_out + i);
+
object_property_add_link(OBJECT(dev), propname, TYPE_IRQ,
(Object **)&pins[i],
object_property_allow_set_link,
OBJ_PROP_LINK_UNREF_ON_RELEASE,
&error_abort);
+ g_free(propname);
}
- g_free(propname);
+ gpio_list->num_out += n;
}
void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n)
@@ -506,7 +520,7 @@ qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n)
return ret;
}
-/* disconnect a GPIO ouput, returning the disconnected input (if any) */
+/* disconnect a GPIO output, returning the disconnected input (if any) */
static qemu_irq qdev_disconnect_gpio_out_named(DeviceState *dev,
const char *name, int n)
diff --git a/hw/cpu/Makefile.objs b/hw/cpu/Makefile.objs
index 6381238cc..0954a1872 100644
--- a/hw/cpu/Makefile.objs
+++ b/hw/cpu/Makefile.objs
@@ -2,5 +2,4 @@ obj-$(CONFIG_ARM11MPCORE) += arm11mpcore.o
obj-$(CONFIG_REALVIEW) += realview_mpcore.o
obj-$(CONFIG_A9MPCORE) += a9mpcore.o
obj-$(CONFIG_A15MPCORE) += a15mpcore.o
-obj-$(CONFIG_ICC_BUS) += icc_bus.o
diff --git a/hw/cpu/a15mpcore.c b/hw/cpu/a15mpcore.c
index acc419e11..94e8cc1a6 100644
--- a/hw/cpu/a15mpcore.c
+++ b/hw/cpu/a15mpcore.c
@@ -20,6 +20,7 @@
#include "hw/cpu/a15mpcore.h"
#include "sysemu/kvm.h"
+#include "kvm_arm.h"
static void a15mp_priv_set_irq(void *opaque, int irq, int level)
{
@@ -33,16 +34,11 @@ static void a15mp_priv_initfn(Object *obj)
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
A15MPPrivState *s = A15MPCORE_PRIV(obj);
DeviceState *gicdev;
- const char *gictype = "arm_gic";
-
- if (kvm_irqchip_in_kernel()) {
- gictype = "kvm-arm-gic";
- }
memory_region_init(&s->container, obj, "a15mp-priv-container", 0x8000);
sysbus_init_mmio(sbd, &s->container);
- object_initialize(&s->gic, sizeof(s->gic), gictype);
+ object_initialize(&s->gic, sizeof(s->gic), gic_class_name());
gicdev = DEVICE(&s->gic);
qdev_set_parent_bus(gicdev, sysbus_get_default());
qdev_prop_set_uint32(gicdev, "revision", 2);
@@ -56,10 +52,23 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp)
SysBusDevice *busdev;
int i;
Error *err = NULL;
+ bool has_el3;
+ Object *cpuobj;
gicdev = DEVICE(&s->gic);
qdev_prop_set_uint32(gicdev, "num-cpu", s->num_cpu);
qdev_prop_set_uint32(gicdev, "num-irq", s->num_irq);
+
+ if (!kvm_irqchip_in_kernel()) {
+ /* Make the GIC's TZ support match the CPUs. We assume that
+ * either all the CPUs have TZ, or none do.
+ */
+ cpuobj = OBJECT(qemu_get_cpu(0));
+ has_el3 = object_property_find(cpuobj, "has_el3", NULL) &&
+ object_property_get_bool(cpuobj, "has_el3", &error_abort);
+ qdev_prop_set_bit(gicdev, "has-security-extensions", has_el3);
+ }
+
object_property_set_bool(OBJECT(&s->gic), true, "realized", &err);
if (err != NULL) {
error_propagate(errp, err);
@@ -79,14 +88,21 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp)
for (i = 0; i < s->num_cpu; i++) {
DeviceState *cpudev = DEVICE(qemu_get_cpu(i));
int ppibase = s->num_irq - 32 + i * 32;
- /* physical timer; we wire it up to the non-secure timer's ID,
- * since a real A15 always has TrustZone but QEMU doesn't.
+ int irq;
+ /* Mapping from the output timer irq lines from the CPU to the
+ * GIC PPI inputs used on the A15:
*/
- qdev_connect_gpio_out(cpudev, 0,
- qdev_get_gpio_in(gicdev, ppibase + 30));
- /* virtual timer */
- qdev_connect_gpio_out(cpudev, 1,
- qdev_get_gpio_in(gicdev, ppibase + 27));
+ const int timer_irq[] = {
+ [GTIMER_PHYS] = 30,
+ [GTIMER_VIRT] = 27,
+ [GTIMER_HYP] = 26,
+ [GTIMER_SEC] = 29,
+ };
+ for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) {
+ qdev_connect_gpio_out(cpudev, irq,
+ qdev_get_gpio_in(gicdev,
+ ppibase + timer_irq[irq]));
+ }
}
/* Memory map (addresses are offsets from PERIPHBASE):
diff --git a/hw/cpu/a9mpcore.c b/hw/cpu/a9mpcore.c
index c09358c6e..869818cd8 100644
--- a/hw/cpu/a9mpcore.c
+++ b/hw/cpu/a9mpcore.c
@@ -49,6 +49,8 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp)
*wdtbusdev;
Error *err = NULL;
int i;
+ bool has_el3;
+ Object *cpuobj;
scudev = DEVICE(&s->scu);
qdev_prop_set_uint32(scudev, "num-cpu", s->num_cpu);
@@ -62,6 +64,15 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp)
gicdev = DEVICE(&s->gic);
qdev_prop_set_uint32(gicdev, "num-cpu", s->num_cpu);
qdev_prop_set_uint32(gicdev, "num-irq", s->num_irq);
+
+ /* Make the GIC's TZ support match the CPUs. We assume that
+ * either all the CPUs have TZ, or none do.
+ */
+ cpuobj = OBJECT(qemu_get_cpu(0));
+ has_el3 = object_property_find(cpuobj, "has_el3", NULL) &&
+ object_property_get_bool(cpuobj, "has_el3", &error_abort);
+ qdev_prop_set_bit(gicdev, "has-security-extensions", has_el3);
+
object_property_set_bool(OBJECT(&s->gic), true, "realized", &err);
if (err != NULL) {
error_propagate(errp, err);
diff --git a/hw/cpu/icc_bus.c b/hw/cpu/icc_bus.c
deleted file mode 100644
index 6646ea2b3..000000000
--- a/hw/cpu/icc_bus.c
+++ /dev/null
@@ -1,118 +0,0 @@
-/* icc_bus.c
- * emulate x86 ICC (Interrupt Controller Communications) bus
- *
- * Copyright (c) 2013 Red Hat, Inc
- *
- * Authors:
- * Igor Mammedov <imammedo@redhat.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 "hw/cpu/icc_bus.h"
-#include "hw/sysbus.h"
-
-/* icc-bridge implementation */
-
-static const TypeInfo icc_bus_info = {
- .name = TYPE_ICC_BUS,
- .parent = TYPE_BUS,
- .instance_size = sizeof(ICCBus),
-};
-
-
-/* icc-device implementation */
-
-static void icc_device_realize(DeviceState *dev, Error **errp)
-{
- ICCDeviceClass *idc = ICC_DEVICE_GET_CLASS(dev);
-
- /* convert to QOM */
- if (idc->realize) {
- idc->realize(dev, errp);
- }
-
-}
-
-static void icc_device_class_init(ObjectClass *oc, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(oc);
-
- dc->realize = icc_device_realize;
- dc->bus_type = TYPE_ICC_BUS;
-}
-
-static const TypeInfo icc_device_info = {
- .name = TYPE_ICC_DEVICE,
- .parent = TYPE_DEVICE,
- .abstract = true,
- .instance_size = sizeof(ICCDevice),
- .class_size = sizeof(ICCDeviceClass),
- .class_init = icc_device_class_init,
-};
-
-
-/* icc-bridge implementation */
-
-typedef struct ICCBridgeState {
- /*< private >*/
- SysBusDevice parent_obj;
- /*< public >*/
-
- ICCBus icc_bus;
- MemoryRegion apic_container;
-} ICCBridgeState;
-
-#define ICC_BRIDGE(obj) OBJECT_CHECK(ICCBridgeState, (obj), TYPE_ICC_BRIDGE)
-
-static void icc_bridge_init(Object *obj)
-{
- ICCBridgeState *s = ICC_BRIDGE(obj);
- SysBusDevice *sb = SYS_BUS_DEVICE(obj);
-
- qbus_create_inplace(&s->icc_bus, sizeof(s->icc_bus), TYPE_ICC_BUS,
- DEVICE(s), "icc");
-
- /* Do not change order of registering regions,
- * APIC must be first registered region, board maps it by 0 index
- */
- memory_region_init(&s->apic_container, obj, "icc-apic-container",
- APIC_SPACE_SIZE);
- sysbus_init_mmio(sb, &s->apic_container);
- s->icc_bus.apic_address_space = &s->apic_container;
-}
-
-static void icc_bridge_class_init(ObjectClass *oc, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(oc);
-
- set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
-}
-
-static const TypeInfo icc_bridge_info = {
- .name = TYPE_ICC_BRIDGE,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_init = icc_bridge_init,
- .instance_size = sizeof(ICCBridgeState),
- .class_init = icc_bridge_class_init,
-};
-
-
-static void icc_bus_register_types(void)
-{
- type_register_static(&icc_bus_info);
- type_register_static(&icc_device_info);
- type_register_static(&icc_bridge_info);
-}
-
-type_init(icc_bus_register_types)
diff --git a/hw/cris/axis_dev88.c b/hw/cris/axis_dev88.c
index 3cae480fa..de880d173 100644
--- a/hw/cris/axis_dev88.c
+++ b/hw/cris/axis_dev88.c
@@ -138,7 +138,7 @@ static void tempsensor_clkedge(struct tempsensor_t *s,
s->count = 16;
if ((s->regs[0] & 0xff) == 0) {
- /* 25 degrees celcius. */
+ /* 25 degrees celsius. */
s->shiftreg = 0x0b9f;
} else if ((s->regs[0] & 0xff) == 0xff) {
/* Sensor ID, 0x8100 LM70. */
@@ -277,7 +277,7 @@ void axisdev88_init(MachineState *machine)
/* The ETRAX-FS has 128Kb on chip ram, the docs refer to it as the
internal memory. */
memory_region_init_ram(phys_intmem, NULL, "axisdev88.chipram", INTMEM_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(phys_intmem);
memory_region_add_subregion(address_space_mem, 0x38000000, phys_intmem);
@@ -351,16 +351,11 @@ void axisdev88_init(MachineState *machine)
}
}
-static QEMUMachine axisdev88_machine = {
- .name = "axis-dev88",
- .desc = "AXIS devboard 88",
- .init = axisdev88_init,
- .is_default = 1,
-};
-
-static void axisdev88_machine_init(void)
+static void axisdev88_machine_init(MachineClass *mc)
{
- qemu_register_machine(&axisdev88_machine);
+ mc->desc = "AXIS devboard 88";
+ mc->init = axisdev88_init;
+ mc->is_default = 1;
}
-machine_init(axisdev88_machine_init);
+DEFINE_MACHINE("axis-dev88", axisdev88_machine_init)
diff --git a/hw/cris/boot.c b/hw/cris/boot.c
index 622f353c9..1cfa339c2 100644
--- a/hw/cris/boot.c
+++ b/hw/cris/boot.c
@@ -72,7 +72,7 @@ void cris_load_image(CRISCPU *cpu, struct cris_load_info *li)
/* Boots a kernel elf binary, os/linux-2.6/vmlinux from the axis
devboard SDK. */
image_size = load_elf(li->image_filename, translate_kernel_address, NULL,
- &entry, NULL, &high, 0, ELF_MACHINE, 0);
+ &entry, NULL, &high, 0, EM_CRIS, 0);
li->entry = entry;
if (image_size < 0) {
/* Takes a kimage from the axis devboard SDK. */
diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs
index dd8ea76d1..f0cf431a0 100644
--- a/hw/display/Makefile.objs
+++ b/hw/display/Makefile.objs
@@ -35,6 +35,10 @@ 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) += virtio-gpu.o virtio-gpu-3d.o
obj-$(CONFIG_VIRTIO_PCI) += virtio-gpu-pci.o
obj-$(CONFIG_VIRTIO_VGA) += virtio-vga.o
+virtio-gpu.o-cflags := $(VIRGL_CFLAGS)
+virtio-gpu.o-libs += $(VIRGL_LIBS)
+virtio-gpu-3d.o-cflags := $(VIRGL_CFLAGS)
+virtio-gpu-3d.o-libs += $(VIRGL_LIBS)
diff --git a/hw/display/cg3.c b/hw/display/cg3.c
index 2d3bd7081..e309fbe92 100644
--- a/hw/display/cg3.c
+++ b/hw/display/cg3.c
@@ -281,7 +281,7 @@ static void cg3_initfn(Object *obj)
CG3State *s = CG3(obj);
memory_region_init_ram(&s->rom, obj, "cg3.prom", FCODE_MAX_ROM_SIZE,
- &error_abort);
+ &error_fatal);
memory_region_set_readonly(&s->rom, true);
sysbus_init_mmio(sbd, &s->rom);
@@ -310,7 +310,7 @@ static void cg3_realizefn(DeviceState *dev, Error **errp)
}
memory_region_init_ram(&s->vram_mem, NULL, "cg3.vram", s->vram_size,
- &error_abort);
+ &error_fatal);
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/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c
index 603ef5056..45239e836 100644
--- a/hw/display/exynos4210_fimd.c
+++ b/hw/display/exynos4210_fimd.c
@@ -1354,9 +1354,7 @@ static void exynos4210_fimd_reset(DeviceState *d)
fimd_update_get_alpha(s, w);
}
- if (s->ifb != NULL) {
- g_free(s->ifb);
- }
+ g_free(s->ifb);
s->ifb = NULL;
exynos4210_fimd_invalidate(s);
diff --git a/hw/display/milkymist-tmu2.c b/hw/display/milkymist-tmu2.c
index 3e1d0b9c2..e2de28176 100644
--- a/hw/display/milkymist-tmu2.c
+++ b/hw/display/milkymist-tmu2.c
@@ -30,8 +30,8 @@
#include "qemu/error-report.h"
#include <X11/Xlib.h>
-#include <GL/gl.h>
-#include <GL/glx.h>
+#include <epoxy/gl.h>
+#include <epoxy/glx.h>
enum {
R_CTL = 0,
diff --git a/hw/display/omap_dss.c b/hw/display/omap_dss.c
index f1fef2767..b1c7af581 100644
--- a/hw/display/omap_dss.c
+++ b/hw/display/omap_dss.c
@@ -1051,8 +1051,7 @@ struct omap_dss_s *omap_dss_init(struct omap_target_agent_s *ta,
omap_clk fck1, omap_clk fck2, omap_clk ck54m,
omap_clk ick1, omap_clk ick2)
{
- struct omap_dss_s *s = (struct omap_dss_s *)
- g_malloc0(sizeof(struct omap_dss_s));
+ struct omap_dss_s *s = g_new0(struct omap_dss_s, 1);
s->irq = irq;
s->drq = drq;
diff --git a/hw/display/omap_lcdc.c b/hw/display/omap_lcdc.c
index a7c6cd79b..678f9a1b4 100644
--- a/hw/display/omap_lcdc.c
+++ b/hw/display/omap_lcdc.c
@@ -403,8 +403,7 @@ struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem,
struct omap_dma_lcd_channel_s *dma,
omap_clk clk)
{
- struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *)
- g_malloc0(sizeof(struct omap_lcd_panel_s));
+ struct omap_lcd_panel_s *s = g_new0(struct omap_lcd_panel_s, 1);
s->irq = irq;
s->dma = dma;
diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c
index a542087fc..7e4ef1ed0 100644
--- a/hw/display/qxl-render.c
+++ b/hw/display/qxl-render.c
@@ -159,7 +159,7 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
/*
* use ssd.lock to protect render_update_cookie_num.
* qxl_render_update is called by io thread or vcpu thread, and the completion
- * callbacks are called by spice_server thread, defering to bh called from the
+ * callbacks are called by spice_server thread, deferring to bh called from the
* io thread.
*/
void qxl_render_update(PCIQXLDevice *qxl)
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index 2288238d0..8a3040cbb 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -1970,14 +1970,14 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp)
qxl->rom_size = qxl_rom_size();
memory_region_init_ram(&qxl->rom_bar, OBJECT(qxl), "qxl.vrom",
- qxl->rom_size, &error_abort);
+ qxl->rom_size, &error_fatal);
vmstate_register_ram(&qxl->rom_bar, &qxl->pci.qdev);
init_qxl_rom(qxl);
init_qxl_ram(qxl);
qxl->guest_surfaces.cmds = g_new0(QXLPHYSICAL, qxl->ssd.num_surfaces);
memory_region_init_ram(&qxl->vram_bar, OBJECT(qxl), "qxl.vram",
- qxl->vram_size, &error_abort);
+ qxl->vram_size, &error_fatal);
vmstate_register_ram(&qxl->vram_bar, &qxl->pci.qdev);
memory_region_init_alias(&qxl->vram32_bar, OBJECT(qxl), "qxl.vram32",
&qxl->vram_bar, 0, qxl->vram32_size);
@@ -2079,7 +2079,7 @@ static void qxl_realize_secondary(PCIDevice *dev, Error **errp)
qxl->id = device_id++;
qxl_init_ramsize(qxl);
memory_region_init_ram(&qxl->vga.vram, OBJECT(dev), "qxl.vgavram",
- qxl->vga.vram_size, &error_abort);
+ qxl->vga.vram_size, &error_fatal);
vmstate_register_ram(&qxl->vga.vram, &qxl->pci.qdev);
qxl->vga.vram_ptr = memory_region_get_ram_ptr(&qxl->vga.vram);
qxl->vga.con = graphic_console_init(DEVICE(dev), 0, &qxl_ops, qxl);
@@ -2156,7 +2156,7 @@ static int qxl_post_load(void *opaque, int version)
qxl_create_guest_primary(d, 1, QXL_SYNC);
/* replay surface-create and cursor-set commands */
- cmds = g_malloc0(sizeof(QXLCommandExt) * (d->ssd.num_surfaces + 1));
+ cmds = g_new0(QXLCommandExt, d->ssd.num_surfaces + 1);
for (in = 0, out = 0; in < d->ssd.num_surfaces; in++) {
if (d->guest_surfaces.cmds[in] == 0) {
continue;
diff --git a/hw/display/sm501.c b/hw/display/sm501.c
index 15a5ba800..3c3f97824 100644
--- a/hw/display/sm501.c
+++ b/hw/display/sm501.c
@@ -1411,7 +1411,7 @@ void sm501_init(MemoryRegion *address_space_mem, uint32_t base,
/* allocate local memory */
memory_region_init_ram(&s->local_mem_region, NULL, "sm501.local",
- local_mem_bytes, &error_abort);
+ local_mem_bytes, &error_fatal);
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);
diff --git a/hw/display/tc6393xb.c b/hw/display/tc6393xb.c
index f5f3f3e69..516af1a5c 100644
--- a/hw/display/tc6393xb.c
+++ b/hw/display/tc6393xb.c
@@ -584,7 +584,7 @@ TC6393xbState *tc6393xb_init(MemoryRegion *sysmem, uint32_t base, qemu_irq irq)
memory_region_add_subregion(sysmem, base, &s->iomem);
memory_region_init_ram(&s->vram, NULL, "tc6393xb.vram", 0x100000,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&s->vram);
s->vram_ptr = memory_region_get_ram_ptr(&s->vram);
memory_region_add_subregion(sysmem, base + 0x100000, &s->vram);
diff --git a/hw/display/tcx.c b/hw/display/tcx.c
index 327ce3019..d720ea666 100644
--- a/hw/display/tcx.c
+++ b/hw/display/tcx.c
@@ -944,57 +944,55 @@ static void tcx_initfn(Object *obj)
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
TCXState *s = TCX(obj);
- memory_region_init_ram(&s->rom, OBJECT(s), "tcx.prom", FCODE_MAX_ROM_SIZE,
- &error_abort);
+ memory_region_init_ram(&s->rom, obj, "tcx.prom", FCODE_MAX_ROM_SIZE,
+ &error_fatal);
memory_region_set_readonly(&s->rom, true);
sysbus_init_mmio(sbd, &s->rom);
/* 2/STIP : Stippler */
- memory_region_init_io(&s->stip, OBJECT(s), &tcx_stip_ops, s, "tcx.stip",
+ memory_region_init_io(&s->stip, obj, &tcx_stip_ops, s, "tcx.stip",
TCX_STIP_NREGS);
sysbus_init_mmio(sbd, &s->stip);
/* 3/BLIT : Blitter */
- memory_region_init_io(&s->blit, OBJECT(s), &tcx_blit_ops, s, "tcx.blit",
+ memory_region_init_io(&s->blit, obj, &tcx_blit_ops, s, "tcx.blit",
TCX_BLIT_NREGS);
sysbus_init_mmio(sbd, &s->blit);
/* 5/RSTIP : Raw Stippler */
- memory_region_init_io(&s->rstip, OBJECT(s), &tcx_rstip_ops, s, "tcx.rstip",
+ memory_region_init_io(&s->rstip, obj, &tcx_rstip_ops, s, "tcx.rstip",
TCX_RSTIP_NREGS);
sysbus_init_mmio(sbd, &s->rstip);
/* 6/RBLIT : Raw Blitter */
- memory_region_init_io(&s->rblit, OBJECT(s), &tcx_rblit_ops, s, "tcx.rblit",
+ memory_region_init_io(&s->rblit, obj, &tcx_rblit_ops, s, "tcx.rblit",
TCX_RBLIT_NREGS);
sysbus_init_mmio(sbd, &s->rblit);
/* 7/TEC : ??? */
- memory_region_init_io(&s->tec, OBJECT(s), &tcx_dummy_ops, s,
- "tcx.tec", TCX_TEC_NREGS);
+ memory_region_init_io(&s->tec, obj, &tcx_dummy_ops, s, "tcx.tec",
+ TCX_TEC_NREGS);
sysbus_init_mmio(sbd, &s->tec);
/* 8/CMAP : DAC */
- memory_region_init_io(&s->dac, OBJECT(s), &tcx_dac_ops, s,
- "tcx.dac", TCX_DAC_NREGS);
+ memory_region_init_io(&s->dac, obj, &tcx_dac_ops, s, "tcx.dac",
+ TCX_DAC_NREGS);
sysbus_init_mmio(sbd, &s->dac);
/* 9/THC : Cursor */
- memory_region_init_io(&s->thc, OBJECT(s), &tcx_thc_ops, s, "tcx.thc",
+ memory_region_init_io(&s->thc, obj, &tcx_thc_ops, s, "tcx.thc",
TCX_THC_NREGS);
sysbus_init_mmio(sbd, &s->thc);
/* 11/DHC : ??? */
- memory_region_init_io(&s->dhc, OBJECT(s), &tcx_dummy_ops, s, "tcx.dhc",
+ memory_region_init_io(&s->dhc, obj, &tcx_dummy_ops, s, "tcx.dhc",
TCX_DHC_NREGS);
sysbus_init_mmio(sbd, &s->dhc);
/* 12/ALT : ??? */
- memory_region_init_io(&s->alt, OBJECT(s), &tcx_dummy_ops, s, "tcx.alt",
+ memory_region_init_io(&s->alt, obj, &tcx_dummy_ops, s, "tcx.alt",
TCX_ALT_NREGS);
sysbus_init_mmio(sbd, &s->alt);
-
- return;
}
static void tcx_realizefn(DeviceState *dev, Error **errp)
@@ -1007,7 +1005,7 @@ static void tcx_realizefn(DeviceState *dev, Error **errp)
char *fcode_filename;
memory_region_init_ram(&s->vram_mem, OBJECT(s), "tcx.vram",
- s->vram_size * (1 + 4 + 4), &error_abort);
+ s->vram_size * (1 + 4 + 4), &error_fatal);
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);
diff --git a/hw/display/vga.c b/hw/display/vga.c
index b35d523e6..679070e64 100644
--- a/hw/display/vga.c
+++ b/hw/display/vga.c
@@ -140,6 +140,13 @@ static uint32_t expand4[256];
static uint16_t expand2[256];
static uint8_t expand4to8[16];
+static void vbe_update_vgaregs(VGACommonState *s);
+
+static inline bool vbe_enabled(VGACommonState *s)
+{
+ return s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED;
+}
+
static void vga_update_memory_access(VGACommonState *s)
{
hwaddr base, offset, size;
@@ -177,6 +184,7 @@ static void vga_update_memory_access(VGACommonState *s)
size = 0x8000;
break;
}
+ assert(offset + size <= s->vram_size);
memory_region_init_alias(&s->chain4_alias, memory_region_owner(&s->vram),
"vga.chain4", &s->vram, offset, size);
memory_region_add_subregion_overlap(s->legacy_address_space, base,
@@ -476,6 +484,7 @@ void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
printf("vga: write SR%x = 0x%02x\n", s->sr_index, val);
#endif
s->sr[s->sr_index] = val & sr_mask[s->sr_index];
+ vbe_update_vgaregs(s);
if (s->sr_index == VGA_SEQ_CLOCK_MODE) {
s->update_retrace_info(s);
}
@@ -507,6 +516,7 @@ void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
printf("vga: write GR%x = 0x%02x\n", s->gr_index, val);
#endif
s->gr[s->gr_index] = val & gr_mask[s->gr_index];
+ vbe_update_vgaregs(s);
vga_update_memory_access(s);
break;
case VGA_CRT_IM:
@@ -525,10 +535,12 @@ void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
if (s->cr_index == VGA_CRTC_OVERFLOW) {
s->cr[VGA_CRTC_OVERFLOW] = (s->cr[VGA_CRTC_OVERFLOW] & ~0x10) |
(val & 0x10);
+ vbe_update_vgaregs(s);
}
return;
}
s->cr[s->cr_index] = val;
+ vbe_update_vgaregs(s);
switch(s->cr_index) {
case VGA_CRTC_H_TOTAL:
@@ -561,7 +573,7 @@ static void vbe_fixup_regs(VGACommonState *s)
uint16_t *r = s->vbe_regs;
uint32_t bits, linelength, maxy, offset;
- if (!(r[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) {
+ if (!vbe_enabled(s)) {
/* vbe is turned off -- nothing to do */
return;
}
@@ -636,6 +648,49 @@ static void vbe_fixup_regs(VGACommonState *s)
s->vbe_start_addr = offset / 4;
}
+/* we initialize the VGA graphic mode */
+static void vbe_update_vgaregs(VGACommonState *s)
+{
+ int h, shift_control;
+
+ if (!vbe_enabled(s)) {
+ /* vbe is turned off -- nothing to do */
+ return;
+ }
+
+ /* graphic mode + memory map 1 */
+ s->gr[VGA_GFX_MISC] = (s->gr[VGA_GFX_MISC] & ~0x0c) | 0x04 |
+ VGA_GR06_GRAPHICS_MODE;
+ s->cr[VGA_CRTC_MODE] |= 3; /* no CGA modes */
+ s->cr[VGA_CRTC_OFFSET] = s->vbe_line_offset >> 3;
+ /* width */
+ s->cr[VGA_CRTC_H_DISP] =
+ (s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 3) - 1;
+ /* height (only meaningful if < 1024) */
+ h = s->vbe_regs[VBE_DISPI_INDEX_YRES] - 1;
+ s->cr[VGA_CRTC_V_DISP_END] = h;
+ s->cr[VGA_CRTC_OVERFLOW] = (s->cr[VGA_CRTC_OVERFLOW] & ~0x42) |
+ ((h >> 7) & 0x02) | ((h >> 3) & 0x40);
+ /* line compare to 1023 */
+ s->cr[VGA_CRTC_LINE_COMPARE] = 0xff;
+ s->cr[VGA_CRTC_OVERFLOW] |= 0x10;
+ s->cr[VGA_CRTC_MAX_SCAN] |= 0x40;
+
+ if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
+ shift_control = 0;
+ s->sr[VGA_SEQ_CLOCK_MODE] &= ~8; /* no double line */
+ } else {
+ shift_control = 2;
+ /* set chain 4 mode */
+ s->sr[VGA_SEQ_MEMORY_MODE] |= VGA_SR04_CHN_4M;
+ /* activate all planes */
+ s->sr[VGA_SEQ_PLANE_WRITE] |= VGA_SR02_ALL_PLANES;
+ }
+ s->gr[VGA_GFX_MODE] = (s->gr[VGA_GFX_MODE] & ~0x60) |
+ (shift_control << 5);
+ s->cr[VGA_CRTC_MAX_SCAN] &= ~0x9f; /* no double scan */
+}
+
static uint32_t vbe_ioport_read_index(void *opaque, uint32_t addr)
{
VGACommonState *s = opaque;
@@ -712,13 +767,10 @@ void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val)
case VBE_DISPI_INDEX_Y_OFFSET:
s->vbe_regs[s->vbe_index] = val;
vbe_fixup_regs(s);
+ vbe_update_vgaregs(s);
break;
case VBE_DISPI_INDEX_BANK:
- if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
- val &= (s->vbe_bank_mask >> 2);
- } else {
- val &= s->vbe_bank_mask;
- }
+ val &= s->vbe_bank_mask;
s->vbe_regs[s->vbe_index] = val;
s->bank_offset = (val << 16);
vga_update_memory_access(s);
@@ -726,52 +778,19 @@ void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val)
case VBE_DISPI_INDEX_ENABLE:
if ((val & VBE_DISPI_ENABLED) &&
!(s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) {
- int h, shift_control;
s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = 0;
s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
s->vbe_regs[VBE_DISPI_INDEX_ENABLE] |= VBE_DISPI_ENABLED;
vbe_fixup_regs(s);
+ vbe_update_vgaregs(s);
/* clear the screen */
if (!(val & VBE_DISPI_NOCLEARMEM)) {
memset(s->vram_ptr, 0,
s->vbe_regs[VBE_DISPI_INDEX_YRES] * s->vbe_line_offset);
}
-
- /* we initialize the VGA graphic mode */
- /* graphic mode + memory map 1 */
- s->gr[VGA_GFX_MISC] = (s->gr[VGA_GFX_MISC] & ~0x0c) | 0x04 |
- VGA_GR06_GRAPHICS_MODE;
- s->cr[VGA_CRTC_MODE] |= 3; /* no CGA modes */
- s->cr[VGA_CRTC_OFFSET] = s->vbe_line_offset >> 3;
- /* width */
- s->cr[VGA_CRTC_H_DISP] =
- (s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 3) - 1;
- /* height (only meaningful if < 1024) */
- h = s->vbe_regs[VBE_DISPI_INDEX_YRES] - 1;
- s->cr[VGA_CRTC_V_DISP_END] = h;
- s->cr[VGA_CRTC_OVERFLOW] = (s->cr[VGA_CRTC_OVERFLOW] & ~0x42) |
- ((h >> 7) & 0x02) | ((h >> 3) & 0x40);
- /* line compare to 1023 */
- s->cr[VGA_CRTC_LINE_COMPARE] = 0xff;
- s->cr[VGA_CRTC_OVERFLOW] |= 0x10;
- s->cr[VGA_CRTC_MAX_SCAN] |= 0x40;
-
- if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
- shift_control = 0;
- s->sr[VGA_SEQ_CLOCK_MODE] &= ~8; /* no double line */
- } else {
- shift_control = 2;
- /* set chain 4 mode */
- s->sr[VGA_SEQ_MEMORY_MODE] |= VGA_SR04_CHN_4M;
- /* activate all planes */
- s->sr[VGA_SEQ_PLANE_WRITE] |= VGA_SR02_ALL_PLANES;
- }
- s->gr[VGA_GFX_MODE] = (s->gr[VGA_GFX_MODE] & ~0x60) |
- (shift_control << 5);
- s->cr[VGA_CRTC_MAX_SCAN] &= ~0x9f; /* no double scan */
} else {
s->bank_offset = 0;
}
@@ -817,13 +836,21 @@ uint32_t vga_mem_readb(VGACommonState *s, hwaddr addr)
if (s->sr[VGA_SEQ_MEMORY_MODE] & VGA_SR04_CHN_4M) {
/* chain 4 mode : simplest access */
+ assert(addr < s->vram_size);
ret = s->vram_ptr[addr];
} else if (s->gr[VGA_GFX_MODE] & 0x10) {
/* odd/even mode (aka text mode mapping) */
plane = (s->gr[VGA_GFX_PLANE_READ] & 2) | (addr & 1);
- ret = s->vram_ptr[((addr & ~1) << 1) | plane];
+ addr = ((addr & ~1) << 1) | plane;
+ if (addr >= s->vram_size) {
+ return 0xff;
+ }
+ ret = s->vram_ptr[addr];
} else {
/* standard VGA latched access */
+ if (addr * sizeof(uint32_t) >= s->vram_size) {
+ return 0xff;
+ }
s->latch = ((uint32_t *)s->vram_ptr)[addr];
if (!(s->gr[VGA_GFX_MODE] & 0x08)) {
@@ -880,6 +907,7 @@ void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val)
plane = addr & 3;
mask = (1 << plane);
if (s->sr[VGA_SEQ_PLANE_WRITE] & mask) {
+ assert(addr < s->vram_size);
s->vram_ptr[addr] = val;
#ifdef DEBUG_VGA_MEM
printf("vga: chain4: [0x" TARGET_FMT_plx "]\n", addr);
@@ -893,6 +921,9 @@ void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val)
mask = (1 << plane);
if (s->sr[VGA_SEQ_PLANE_WRITE] & mask) {
addr = ((addr & ~1) << 1) | plane;
+ if (addr >= s->vram_size) {
+ return;
+ }
s->vram_ptr[addr] = val;
#ifdef DEBUG_VGA_MEM
printf("vga: odd/even: [0x" TARGET_FMT_plx "]\n", addr);
@@ -966,6 +997,9 @@ void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val)
mask = s->sr[VGA_SEQ_PLANE_WRITE];
s->plane_updated |= mask; /* only used to detect font change */
write_mask = mask16[mask];
+ if (addr * sizeof(uint32_t) >= s->vram_size) {
+ return;
+ }
((uint32_t *)s->vram_ptr)[addr] =
(((uint32_t *)s->vram_ptr)[addr] & ~write_mask) |
(val & write_mask);
@@ -1044,7 +1078,7 @@ static void vga_get_offsets(VGACommonState *s,
{
uint32_t start_addr, line_offset, line_compare;
- if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
+ if (vbe_enabled(s)) {
line_offset = s->vbe_line_offset;
start_addr = s->vbe_start_addr;
line_compare = 65535;
@@ -1369,7 +1403,7 @@ static int vga_get_bpp(VGACommonState *s)
{
int ret;
- if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
+ if (vbe_enabled(s)) {
ret = s->vbe_regs[VBE_DISPI_INDEX_BPP];
} else {
ret = 0;
@@ -1381,7 +1415,7 @@ static void vga_get_resolution(VGACommonState *s, int *pwidth, int *pheight)
{
int width, height;
- if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
+ if (vbe_enabled(s)) {
width = s->vbe_regs[VBE_DISPI_INDEX_XRES];
height = s->vbe_regs[VBE_DISPI_INDEX_YRES];
} else {
@@ -2139,7 +2173,7 @@ void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate)
s->is_vbe_vmstate = 1;
memory_region_init_ram(&s->vram, obj, "vga.vram", s->vram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram(&s->vram, global_vmstate ? NULL : DEVICE(obj));
xen_register_framebuffer(&s->vram);
s->vram_ptr = memory_region_get_ram_ptr(&s->vram);
diff --git a/hw/display/virtio-gpu-3d.c b/hw/display/virtio-gpu-3d.c
new file mode 100644
index 000000000..28dccfdec
--- /dev/null
+++ b/hw/display/virtio-gpu-3d.c
@@ -0,0 +1,598 @@
+/*
+ * 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 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu-common.h"
+#include "qemu/iov.h"
+#include "trace.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-gpu.h"
+
+#ifdef CONFIG_VIRGL
+
+#include "virglrenderer.h"
+
+static struct virgl_renderer_callbacks virtio_gpu_3d_cbs;
+
+static void virgl_cmd_create_resource_2d(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_resource_create_2d c2d;
+ struct virgl_renderer_resource_create_args args;
+
+ VIRTIO_GPU_FILL_CMD(c2d);
+ trace_virtio_gpu_cmd_res_create_2d(c2d.resource_id, c2d.format,
+ c2d.width, c2d.height);
+
+ args.handle = c2d.resource_id;
+ args.target = 2;
+ args.format = c2d.format;
+ args.bind = (1 << 1);
+ args.width = c2d.width;
+ args.height = c2d.height;
+ args.depth = 1;
+ args.array_size = 1;
+ args.last_level = 0;
+ args.nr_samples = 0;
+ args.flags = VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP;
+ virgl_renderer_resource_create(&args, NULL, 0);
+}
+
+static void virgl_cmd_create_resource_3d(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_resource_create_3d c3d;
+ struct virgl_renderer_resource_create_args args;
+
+ VIRTIO_GPU_FILL_CMD(c3d);
+ trace_virtio_gpu_cmd_res_create_3d(c3d.resource_id, c3d.format,
+ c3d.width, c3d.height, c3d.depth);
+
+ args.handle = c3d.resource_id;
+ args.target = c3d.target;
+ args.format = c3d.format;
+ args.bind = c3d.bind;
+ args.width = c3d.width;
+ args.height = c3d.height;
+ args.depth = c3d.depth;
+ args.array_size = c3d.array_size;
+ args.last_level = c3d.last_level;
+ args.nr_samples = c3d.nr_samples;
+ args.flags = c3d.flags;
+ virgl_renderer_resource_create(&args, NULL, 0);
+}
+
+static void virgl_cmd_resource_unref(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_resource_unref unref;
+
+ VIRTIO_GPU_FILL_CMD(unref);
+ trace_virtio_gpu_cmd_res_unref(unref.resource_id);
+
+ virgl_renderer_resource_unref(unref.resource_id);
+}
+
+static void virgl_cmd_context_create(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_ctx_create cc;
+
+ VIRTIO_GPU_FILL_CMD(cc);
+ trace_virtio_gpu_cmd_ctx_create(cc.hdr.ctx_id,
+ cc.debug_name);
+
+ virgl_renderer_context_create(cc.hdr.ctx_id, cc.nlen,
+ cc.debug_name);
+}
+
+static void virgl_cmd_context_destroy(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_ctx_destroy cd;
+
+ VIRTIO_GPU_FILL_CMD(cd);
+ trace_virtio_gpu_cmd_ctx_destroy(cd.hdr.ctx_id);
+
+ virgl_renderer_context_destroy(cd.hdr.ctx_id);
+}
+
+static void virtio_gpu_rect_update(VirtIOGPU *g, int idx, int x, int y,
+ int width, int height)
+{
+ if (!g->scanout[idx].con) {
+ return;
+ }
+
+ dpy_gl_update(g->scanout[idx].con, x, y, width, height);
+}
+
+static void virgl_cmd_resource_flush(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_resource_flush rf;
+ 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);
+
+ for (i = 0; i < VIRTIO_GPU_MAX_SCANOUT; i++) {
+ if (g->scanout[i].resource_id != rf.resource_id) {
+ continue;
+ }
+ virtio_gpu_rect_update(g, i, rf.r.x, rf.r.y, rf.r.width, rf.r.height);
+ }
+}
+
+static void virgl_cmd_set_scanout(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_set_scanout ss;
+ struct virgl_renderer_resource_info info;
+ int ret;
+
+ 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);
+
+ if (ss.scanout_id >= VIRTIO_GPU_MAX_SCANOUT) {
+ 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;
+ }
+ g->enable = 1;
+
+ memset(&info, 0, sizeof(info));
+
+ if (ss.resource_id && ss.r.width && ss.r.height) {
+ ret = virgl_renderer_resource_get_info(ss.resource_id, &info);
+ if (ret == -1) {
+ 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;
+ }
+ qemu_console_resize(g->scanout[ss.scanout_id].con,
+ ss.r.width, ss.r.height);
+ virgl_renderer_force_ctx_0();
+ dpy_gl_scanout(g->scanout[ss.scanout_id].con, info.tex_id,
+ info.flags & 1 /* FIXME: Y_0_TOP */,
+ ss.r.x, ss.r.y, ss.r.width, ss.r.height);
+ } else {
+ if (ss.scanout_id != 0) {
+ dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL);
+ }
+ dpy_gl_scanout(g->scanout[ss.scanout_id].con, 0, false,
+ 0, 0, 0, 0);
+ }
+ g->scanout[ss.scanout_id].resource_id = ss.resource_id;
+}
+
+static void virgl_cmd_submit_3d(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_cmd_submit cs;
+ void *buf;
+ size_t s;
+
+ VIRTIO_GPU_FILL_CMD(cs);
+ trace_virtio_gpu_cmd_ctx_submit(cs.hdr.ctx_id, cs.size);
+
+ buf = g_malloc(cs.size);
+ s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num,
+ sizeof(cs), buf, cs.size);
+ if (s != cs.size) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: size mismatch (%zd/%d)",
+ __func__, s, cs.size);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+ return;
+ }
+
+ if (virtio_gpu_stats_enabled(g->conf)) {
+ g->stats.req_3d++;
+ g->stats.bytes_3d += cs.size;
+ }
+
+ virgl_renderer_submit_cmd(buf, cs.hdr.ctx_id, cs.size / 4);
+
+ g_free(buf);
+}
+
+static void virgl_cmd_transfer_to_host_2d(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_transfer_to_host_2d t2d;
+ struct virtio_gpu_box box;
+
+ VIRTIO_GPU_FILL_CMD(t2d);
+ trace_virtio_gpu_cmd_res_xfer_toh_2d(t2d.resource_id);
+
+ box.x = t2d.r.x;
+ box.y = t2d.r.y;
+ box.z = 0;
+ box.w = t2d.r.width;
+ box.h = t2d.r.height;
+ box.d = 1;
+
+ virgl_renderer_transfer_write_iov(t2d.resource_id,
+ 0,
+ 0,
+ 0,
+ 0,
+ (struct virgl_box *)&box,
+ t2d.offset, NULL, 0);
+}
+
+static void virgl_cmd_transfer_to_host_3d(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_transfer_host_3d t3d;
+
+ VIRTIO_GPU_FILL_CMD(t3d);
+ trace_virtio_gpu_cmd_res_xfer_toh_3d(t3d.resource_id);
+
+ virgl_renderer_transfer_write_iov(t3d.resource_id,
+ t3d.hdr.ctx_id,
+ t3d.level,
+ t3d.stride,
+ t3d.layer_stride,
+ (struct virgl_box *)&t3d.box,
+ t3d.offset, NULL, 0);
+}
+
+static void
+virgl_cmd_transfer_from_host_3d(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_transfer_host_3d tf3d;
+
+ VIRTIO_GPU_FILL_CMD(tf3d);
+ trace_virtio_gpu_cmd_res_xfer_fromh_3d(tf3d.resource_id);
+
+ virgl_renderer_transfer_read_iov(tf3d.resource_id,
+ tf3d.hdr.ctx_id,
+ tf3d.level,
+ tf3d.stride,
+ tf3d.layer_stride,
+ (struct virgl_box *)&tf3d.box,
+ tf3d.offset, NULL, 0);
+}
+
+
+static void virgl_resource_attach_backing(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_resource_attach_backing att_rb;
+ struct iovec *res_iovs;
+ int ret;
+
+ VIRTIO_GPU_FILL_CMD(att_rb);
+ trace_virtio_gpu_cmd_res_back_attach(att_rb.resource_id);
+
+ ret = virtio_gpu_create_mapping_iov(&att_rb, cmd, &res_iovs);
+ if (ret != 0) {
+ cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+ return;
+ }
+
+ virgl_renderer_resource_attach_iov(att_rb.resource_id,
+ res_iovs, att_rb.nr_entries);
+}
+
+static void virgl_resource_detach_backing(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_resource_detach_backing detach_rb;
+ struct iovec *res_iovs = NULL;
+ int num_iovs = 0;
+
+ VIRTIO_GPU_FILL_CMD(detach_rb);
+ trace_virtio_gpu_cmd_res_back_detach(detach_rb.resource_id);
+
+ virgl_renderer_resource_detach_iov(detach_rb.resource_id,
+ &res_iovs,
+ &num_iovs);
+ if (res_iovs == NULL || num_iovs == 0) {
+ return;
+ }
+ virtio_gpu_cleanup_mapping_iov(res_iovs, num_iovs);
+}
+
+
+static void virgl_cmd_ctx_attach_resource(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_ctx_resource att_res;
+
+ VIRTIO_GPU_FILL_CMD(att_res);
+ trace_virtio_gpu_cmd_ctx_res_attach(att_res.hdr.ctx_id,
+ att_res.resource_id);
+
+ virgl_renderer_ctx_attach_resource(att_res.hdr.ctx_id, att_res.resource_id);
+}
+
+static void virgl_cmd_ctx_detach_resource(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_ctx_resource det_res;
+
+ VIRTIO_GPU_FILL_CMD(det_res);
+ trace_virtio_gpu_cmd_ctx_res_detach(det_res.hdr.ctx_id,
+ det_res.resource_id);
+
+ virgl_renderer_ctx_detach_resource(det_res.hdr.ctx_id, det_res.resource_id);
+}
+
+static void virgl_cmd_get_capset_info(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_get_capset_info info;
+ struct virtio_gpu_resp_capset_info resp;
+
+ VIRTIO_GPU_FILL_CMD(info);
+
+ if (info.capset_index == 0) {
+ resp.capset_id = VIRTIO_GPU_CAPSET_VIRGL;
+ virgl_renderer_get_cap_set(resp.capset_id,
+ &resp.capset_max_version,
+ &resp.capset_max_size);
+ } else {
+ resp.capset_max_version = 0;
+ resp.capset_max_size = 0;
+ }
+ resp.hdr.type = VIRTIO_GPU_RESP_OK_CAPSET_INFO;
+ virtio_gpu_ctrl_response(g, cmd, &resp.hdr, sizeof(resp));
+}
+
+static void virgl_cmd_get_capset(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_get_capset gc;
+ struct virtio_gpu_resp_capset *resp;
+ uint32_t max_ver, max_size;
+ VIRTIO_GPU_FILL_CMD(gc);
+
+ virgl_renderer_get_cap_set(gc.capset_id, &max_ver,
+ &max_size);
+ resp = g_malloc(sizeof(*resp) + max_size);
+
+ resp->hdr.type = VIRTIO_GPU_RESP_OK_CAPSET;
+ virgl_renderer_fill_caps(gc.capset_id,
+ gc.capset_version,
+ (void *)resp->capset_data);
+ virtio_gpu_ctrl_response(g, cmd, &resp->hdr, sizeof(*resp) + max_size);
+ g_free(resp);
+}
+
+void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ VIRTIO_GPU_FILL_CMD(cmd->cmd_hdr);
+
+ virgl_renderer_force_ctx_0();
+ switch (cmd->cmd_hdr.type) {
+ case VIRTIO_GPU_CMD_CTX_CREATE:
+ virgl_cmd_context_create(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_CTX_DESTROY:
+ virgl_cmd_context_destroy(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D:
+ virgl_cmd_create_resource_2d(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_CREATE_3D:
+ virgl_cmd_create_resource_3d(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_SUBMIT_3D:
+ virgl_cmd_submit_3d(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D:
+ virgl_cmd_transfer_to_host_2d(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D:
+ virgl_cmd_transfer_to_host_3d(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D:
+ virgl_cmd_transfer_from_host_3d(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING:
+ virgl_resource_attach_backing(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING:
+ virgl_resource_detach_backing(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_SET_SCANOUT:
+ virgl_cmd_set_scanout(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_FLUSH:
+ virgl_cmd_resource_flush(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_UNREF:
+ virgl_cmd_resource_unref(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE:
+ /* TODO add security */
+ virgl_cmd_ctx_attach_resource(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE:
+ /* TODO add security */
+ virgl_cmd_ctx_detach_resource(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_GET_CAPSET_INFO:
+ virgl_cmd_get_capset_info(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_GET_CAPSET:
+ virgl_cmd_get_capset(g, cmd);
+ break;
+
+ case VIRTIO_GPU_CMD_GET_DISPLAY_INFO:
+ virtio_gpu_get_display_info(g, cmd);
+ break;
+ default:
+ cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+ break;
+ }
+
+ if (cmd->finished) {
+ return;
+ }
+ if (cmd->error) {
+ fprintf(stderr, "%s: ctrl 0x%x, error 0x%x\n", __func__,
+ cmd->cmd_hdr.type, cmd->error);
+ virtio_gpu_ctrl_response_nodata(g, cmd, cmd->error);
+ return;
+ }
+ if (!(cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_FENCE)) {
+ virtio_gpu_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA);
+ return;
+ }
+
+ trace_virtio_gpu_fence_ctrl(cmd->cmd_hdr.fence_id, cmd->cmd_hdr.type);
+ virgl_renderer_create_fence(cmd->cmd_hdr.fence_id, cmd->cmd_hdr.type);
+}
+
+static void virgl_write_fence(void *opaque, uint32_t fence)
+{
+ VirtIOGPU *g = opaque;
+ struct virtio_gpu_ctrl_command *cmd, *tmp;
+
+ QTAILQ_FOREACH_SAFE(cmd, &g->fenceq, next, tmp) {
+ /*
+ * the guest can end up emitting fences out of order
+ * so we should check all fenced cmds not just the first one.
+ */
+ if (cmd->cmd_hdr.fence_id > fence) {
+ continue;
+ }
+ trace_virtio_gpu_fence_resp(cmd->cmd_hdr.fence_id);
+ virtio_gpu_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA);
+ QTAILQ_REMOVE(&g->fenceq, cmd, next);
+ g_free(cmd);
+ g->inflight--;
+ if (virtio_gpu_stats_enabled(g->conf)) {
+ fprintf(stderr, "inflight: %3d (-)\r", g->inflight);
+ }
+ }
+}
+
+static virgl_renderer_gl_context
+virgl_create_context(void *opaque, int scanout_idx,
+ struct virgl_renderer_gl_ctx_param *params)
+{
+ VirtIOGPU *g = opaque;
+ QEMUGLContext ctx;
+ QEMUGLParams qparams;
+
+ qparams.major_ver = params->major_ver;
+ qparams.minor_ver = params->minor_ver;
+
+ ctx = dpy_gl_ctx_create(g->scanout[scanout_idx].con, &qparams);
+ return (virgl_renderer_gl_context)ctx;
+}
+
+static void virgl_destroy_context(void *opaque, virgl_renderer_gl_context ctx)
+{
+ VirtIOGPU *g = opaque;
+ QEMUGLContext qctx = (QEMUGLContext)ctx;
+
+ dpy_gl_ctx_destroy(g->scanout[0].con, qctx);
+}
+
+static int virgl_make_context_current(void *opaque, int scanout_idx,
+ virgl_renderer_gl_context ctx)
+{
+ VirtIOGPU *g = opaque;
+ QEMUGLContext qctx = (QEMUGLContext)ctx;
+
+ return dpy_gl_ctx_make_current(g->scanout[scanout_idx].con, qctx);
+}
+
+static struct virgl_renderer_callbacks virtio_gpu_3d_cbs = {
+ .version = 1,
+ .write_fence = virgl_write_fence,
+ .create_gl_context = virgl_create_context,
+ .destroy_gl_context = virgl_destroy_context,
+ .make_current = virgl_make_context_current,
+};
+
+static void virtio_gpu_print_stats(void *opaque)
+{
+ VirtIOGPU *g = opaque;
+
+ if (g->stats.requests) {
+ fprintf(stderr, "stats: vq req %4d, %3d -- 3D %4d (%5d)\n",
+ g->stats.requests,
+ g->stats.max_inflight,
+ g->stats.req_3d,
+ g->stats.bytes_3d);
+ g->stats.requests = 0;
+ g->stats.max_inflight = 0;
+ g->stats.req_3d = 0;
+ g->stats.bytes_3d = 0;
+ } else {
+ fprintf(stderr, "stats: idle\r");
+ }
+ timer_mod(g->print_stats, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000);
+}
+
+static void virtio_gpu_fence_poll(void *opaque)
+{
+ VirtIOGPU *g = opaque;
+
+ virgl_renderer_poll();
+ if (g->inflight) {
+ timer_mod(g->fence_poll, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 10);
+ }
+}
+
+void virtio_gpu_virgl_fence_poll(VirtIOGPU *g)
+{
+ virtio_gpu_fence_poll(g);
+}
+
+void virtio_gpu_virgl_reset(VirtIOGPU *g)
+{
+ int i;
+
+ /* virgl_renderer_reset() ??? */
+ for (i = 0; i < g->conf.max_outputs; i++) {
+ if (i != 0) {
+ dpy_gfx_replace_surface(g->scanout[i].con, NULL);
+ }
+ dpy_gl_scanout(g->scanout[i].con, 0, false, 0, 0, 0, 0);
+ }
+}
+
+int virtio_gpu_virgl_init(VirtIOGPU *g)
+{
+ int ret;
+
+ ret = virgl_renderer_init(g, 0, &virtio_gpu_3d_cbs);
+ if (ret != 0) {
+ return ret;
+ }
+
+ g->fence_poll = timer_new_ms(QEMU_CLOCK_VIRTUAL,
+ virtio_gpu_fence_poll, g);
+
+ if (virtio_gpu_stats_enabled(g->conf)) {
+ g->print_stats = timer_new_ms(QEMU_CLOCK_VIRTUAL,
+ virtio_gpu_print_stats, g);
+ timer_mod(g->print_stats, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000);
+ }
+ return 0;
+}
+
+#endif /* CONFIG_VIRGL */
diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c
index 5bc62cf34..eef137f81 100644
--- a/hw/display/virtio-gpu-pci.c
+++ b/hw/display/virtio-gpu-pci.c
@@ -6,8 +6,8 @@
* 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.
+ * 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"
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index a67d927f5..a836ce385 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -7,7 +7,7 @@
* Dave Airlie <airlied@redhat.com>
* Gerd Hoffmann <kraxel@redhat.com>
*
- * This work is licensed under the terms of the GNU GPL, version 2.
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
@@ -22,6 +22,23 @@
static struct virtio_gpu_simple_resource*
virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id);
+#ifdef CONFIG_VIRGL
+#include "virglrenderer.h"
+#define VIRGL(_g, _virgl, _simple, ...) \
+ do { \
+ if (_g->use_virgl_renderer) { \
+ _virgl(__VA_ARGS__); \
+ } else { \
+ _simple(__VA_ARGS__); \
+ } \
+ } while (0)
+#else
+#define VIRGL(_g, _virgl, _simple, ...) \
+ do { \
+ _simple(__VA_ARGS__); \
+ } while (0)
+#endif
+
static void update_cursor_data_simple(VirtIOGPU *g,
struct virtio_gpu_scanout *s,
uint32_t resource_id)
@@ -45,16 +62,49 @@ static void update_cursor_data_simple(VirtIOGPU *g,
pixels * sizeof(uint32_t));
}
+#ifdef CONFIG_VIRGL
+
+static void update_cursor_data_virgl(VirtIOGPU *g,
+ struct virtio_gpu_scanout *s,
+ uint32_t resource_id)
+{
+ uint32_t width, height;
+ uint32_t pixels, *data;
+
+ data = virgl_renderer_get_cursor_data(resource_id, &width, &height);
+ if (!data) {
+ return;
+ }
+
+ if (width != s->current_cursor->width ||
+ height != s->current_cursor->height) {
+ return;
+ }
+
+ pixels = s->current_cursor->width * s->current_cursor->height;
+ memcpy(s->current_cursor->data, data, pixels * sizeof(uint32_t));
+ free(data);
+}
+
+#endif
+
static void update_cursor(VirtIOGPU *g, struct virtio_gpu_update_cursor *cursor)
{
struct virtio_gpu_scanout *s;
+ bool move = cursor->hdr.type != VIRTIO_GPU_CMD_MOVE_CURSOR;
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) {
+ trace_virtio_gpu_update_cursor(cursor->pos.scanout_id,
+ cursor->pos.x,
+ cursor->pos.y,
+ move ? "move" : "update",
+ cursor->resource_id);
+
+ if (move) {
if (!s->current_cursor) {
s->current_cursor = cursor_alloc(64, 64);
}
@@ -63,7 +113,8 @@ static void update_cursor(VirtIOGPU *g, struct virtio_gpu_update_cursor *cursor)
s->current_cursor->hot_y = cursor->hot_y;
if (cursor->resource_id > 0) {
- update_cursor_data_simple(g, s, cursor->resource_id);
+ VIRGL(g, update_cursor_data_virgl, update_cursor_data_simple,
+ g, s, cursor->resource_id);
}
dpy_cursor_define(s->con, s->current_cursor);
}
@@ -92,9 +143,23 @@ static void virtio_gpu_set_config(VirtIODevice *vdev, const uint8_t *config)
static uint64_t virtio_gpu_get_features(VirtIODevice *vdev, uint64_t features,
Error **errp)
{
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+
+ if (virtio_gpu_virgl_enabled(g->conf)) {
+ features |= (1 << VIRTIO_GPU_FEATURE_VIRGL);
+ }
return features;
}
+static void virtio_gpu_set_features(VirtIODevice *vdev, uint64_t features)
+{
+ static const uint32_t virgl = (1 << VIRTIO_GPU_FEATURE_VIRGL);
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+
+ g->use_virgl_renderer = ((features & virgl) == virgl);
+ trace_virtio_gpu_features(g->use_virgl_renderer);
+}
+
static void virtio_gpu_notify_event(VirtIOGPU *g, uint32_t event_type)
{
g->virtio_config.events_read |= event_type;
@@ -563,7 +628,6 @@ int virtio_gpu_create_mapping_iov(struct virtio_gpu_resource_attach_backing *ab,
__func__, ab->resource_id, i);
virtio_gpu_cleanup_mapping_iov(*iov, i);
g_free(ents);
- g_free(*iov);
*iov = NULL;
return -1;
}
@@ -580,12 +644,12 @@ void virtio_gpu_cleanup_mapping_iov(struct iovec *iov, uint32_t count)
cpu_physical_memory_unmap(iov[i].iov_base, iov[i].iov_len, 1,
iov[i].iov_len);
}
+ g_free(iov);
}
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;
}
@@ -699,25 +763,43 @@ static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
return;
}
+#ifdef CONFIG_VIRGL
+ if (!g->renderer_inited && g->use_virgl_renderer) {
+ virtio_gpu_virgl_init(g);
+ g->renderer_inited = true;
+ }
+#endif
+
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++;
+ if (virtio_gpu_stats_enabled(g->conf)) {
+ g->stats.requests++;
+ }
- virtio_gpu_simple_process_cmd(g, cmd);
+ VIRGL(g, virtio_gpu_virgl_process_cmd, 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;
+ g->inflight++;
+ if (virtio_gpu_stats_enabled(g->conf)) {
+ if (g->stats.max_inflight < g->inflight) {
+ g->stats.max_inflight = g->inflight;
+ }
+ fprintf(stderr, "inflight: %3d (+)\r", g->inflight);
}
- fprintf(stderr, "inflight: %3d (+)\r", g->stats.inflight);
cmd = g_new(struct virtio_gpu_ctrl_command, 1);
}
}
g_free(cmd);
+
+#ifdef CONFIG_VIRGL
+ if (g->use_virgl_renderer) {
+ virtio_gpu_virgl_fence_poll(g);
+ }
+#endif
}
static void virtio_gpu_ctrl_bh(void *opaque)
@@ -804,6 +886,7 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
VirtIOGPU *g = VIRTIO_GPU(qdev);
+ bool have_virgl;
int i;
g->config_size = sizeof(struct virtio_gpu_config);
@@ -814,8 +897,25 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
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->use_virgl_renderer = false;
+#if !defined(CONFIG_VIRGL) || defined(HOST_WORDS_BIGENDIAN)
+ have_virgl = false;
+#else
+ have_virgl = display_opengl;
+#endif
+ if (!have_virgl) {
+ g->conf.flags &= ~(1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED);
+ }
+
+ if (virtio_gpu_virgl_enabled(g->conf)) {
+ /* use larger control queue in 3d mode */
+ g->ctrl_vq = virtio_add_queue(vdev, 256, virtio_gpu_handle_ctrl_cb);
+ g->cursor_vq = virtio_add_queue(vdev, 16, virtio_gpu_handle_cursor_cb);
+ g->virtio_config.num_capsets = 1;
+ } else {
+ 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);
@@ -869,10 +969,23 @@ static void virtio_gpu_reset(VirtIODevice *vdev)
g->scanout[i].ds = NULL;
}
g->enabled_output_bitmask = 1;
+
+#ifdef CONFIG_VIRGL
+ if (g->use_virgl_renderer) {
+ virtio_gpu_virgl_reset(g);
+ g->use_virgl_renderer = 0;
+ }
+#endif
}
static Property virtio_gpu_properties[] = {
DEFINE_PROP_UINT32("max_outputs", VirtIOGPU, conf.max_outputs, 1),
+#ifdef CONFIG_VIRGL
+ DEFINE_PROP_BIT("virgl", VirtIOGPU, conf.flags,
+ VIRTIO_GPU_FLAG_VIRGL_ENABLED, true),
+ DEFINE_PROP_BIT("stats", VirtIOGPU, conf.flags,
+ VIRTIO_GPU_FLAG_STATS_ENABLED, false),
+#endif
DEFINE_PROP_END_OF_LIST(),
};
@@ -885,6 +998,7 @@ static void virtio_gpu_class_init(ObjectClass *klass, void *data)
vdc->get_config = virtio_gpu_get_config;
vdc->set_config = virtio_gpu_set_config;
vdc->get_features = virtio_gpu_get_features;
+ vdc->set_features = virtio_gpu_set_features;
vdc->reset = virtio_gpu_reset;
@@ -917,3 +1031,14 @@ 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);
+
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_transfer_host_3d) != 72);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_create_3d) != 72);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_create) != 96);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_destroy) != 24);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_resource) != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_cmd_submit) != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_get_capset_info) != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_capset_info) != 40);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_get_capset) != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_capset) != 24);
diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c
index 7f397d3c2..935403785 100644
--- a/hw/display/vmware_vga.c
+++ b/hw/display/vmware_vga.c
@@ -488,10 +488,10 @@ static inline int vmsvga_fill_rect(struct vmsvga_state_s *s,
#endif
struct vmsvga_cursor_definition_s {
- int width;
- int height;
+ uint32_t width;
+ uint32_t height;
int id;
- int bpp;
+ uint32_t bpp;
int hot_x;
int hot_y;
uint32_t mask[1024];
@@ -658,7 +658,10 @@ static void vmsvga_fifo_run(struct vmsvga_state_s *s)
cursor.bpp = vmsvga_fifo_read(s);
args = SVGA_BITMAP_SIZE(x, y) + SVGA_PIXMAP_SIZE(x, y, cursor.bpp);
- if (SVGA_BITMAP_SIZE(x, y) > sizeof cursor.mask ||
+ if (cursor.width > 256 ||
+ cursor.height > 256 ||
+ cursor.bpp > 32 ||
+ SVGA_BITMAP_SIZE(x, y) > sizeof cursor.mask ||
SVGA_PIXMAP_SIZE(x, y, cursor.bpp) > sizeof cursor.image) {
goto badcmd;
}
@@ -1244,7 +1247,7 @@ static void vmsvga_init(DeviceState *dev, struct vmsvga_state_s *s,
s->fifo_size = SVGA_FIFO_SIZE;
memory_region_init_ram(&s->fifo_ram, NULL, "vmsvga.fifo", s->fifo_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&s->fifo_ram);
s->fifo_ptr = memory_region_get_ram_ptr(&s->fifo_ram);
diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c
index 5e324ef62..4e2a27a3d 100644
--- a/hw/display/xenfb.c
+++ b/hw/display/xenfb.c
@@ -784,18 +784,20 @@ static void xenfb_invalidate(void *opaque)
static void xenfb_handle_events(struct XenFB *xenfb)
{
- uint32_t prod, cons;
+ uint32_t prod, cons, out_cons;
struct xenfb_page *page = xenfb->c.page;
prod = page->out_prod;
- if (prod == page->out_cons)
+ out_cons = page->out_cons;
+ if (prod == out_cons)
return;
xen_rmb(); /* ensure we see ring contents up to prod */
- for (cons = page->out_cons; cons != prod; cons++) {
+ for (cons = out_cons; cons != prod; cons++) {
union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons);
+ uint8_t type = event->type;
int x, y, w, h;
- switch (event->type) {
+ switch (type) {
case XENFB_TYPE_UPDATE:
if (xenfb->up_count == UP_QUEUE)
xenfb->up_fullscreen = 1;
diff --git a/hw/dma/i82374.c b/hw/dma/i82374.c
index b8ad2e64e..f63097154 100644
--- a/hw/dma/i82374.c
+++ b/hw/dma/i82374.c
@@ -38,7 +38,6 @@ do { fprintf(stderr, "i82374 ERROR: " fmt , ## __VA_ARGS__); } while (0)
typedef struct I82374State {
uint8_t commands[8];
- qemu_irq out;
PortioList port_list;
} I82374State;
@@ -101,7 +100,7 @@ static uint32_t i82374_read_descriptor(void *opaque, uint32_t nport)
static void i82374_realize(I82374State *s, Error **errp)
{
- DMA_init(1, &s->out);
+ DMA_init(1);
memset(s->commands, 0, sizeof(s->commands));
}
@@ -145,8 +144,6 @@ static void i82374_isa_realize(DeviceState *dev, Error **errp)
isa->iobase);
i82374_realize(s, errp);
-
- qdev_init_gpio_out(dev, &s->out, 1);
}
static Property i82374_properties[] = {
diff --git a/hw/dma/i8257.c b/hw/dma/i8257.c
index a414029be..13984244a 100644
--- a/hw/dma/i8257.c
+++ b/hw/dma/i8257.c
@@ -59,7 +59,6 @@ static struct dma_cont {
uint8_t flip_flop;
int dshift;
struct dma_regs regs[4];
- qemu_irq *cpu_request_exit;
MemoryRegion channel_io;
MemoryRegion cont_io;
} dma_controllers[2];
@@ -358,6 +357,7 @@ static void channel_run (int ncont, int ichan)
}
static QEMUBH *dma_bh;
+static bool dma_bh_scheduled;
static void DMA_run (void)
{
@@ -390,12 +390,15 @@ static void DMA_run (void)
running = 0;
out:
- if (rearm)
+ if (rearm) {
qemu_bh_schedule_idle(dma_bh);
+ dma_bh_scheduled = true;
+ }
}
static void DMA_run_bh(void *unused)
{
+ dma_bh_scheduled = false;
DMA_run();
}
@@ -458,12 +461,14 @@ int DMA_write_memory (int nchan, void *buf, int pos, int len)
return len;
}
-/* request the emulator to transfer a new DMA memory block ASAP */
-void DMA_schedule(int nchan)
+/* request the emulator to transfer a new DMA memory block ASAP (even
+ * if the idle bottom half would not have exited the iothread yet).
+ */
+void DMA_schedule(void)
{
- struct dma_cont *d = &dma_controllers[nchan > 3];
-
- qemu_irq_pulse(*d->cpu_request_exit);
+ if (dma_bh_scheduled) {
+ qemu_notify_event();
+ }
}
static void dma_reset(void *opaque)
@@ -515,13 +520,11 @@ static const MemoryRegionOps cont_io_ops = {
/* dshift = 0: 8 bit DMA, 1 = 16 bit DMA */
static void dma_init2(struct dma_cont *d, int base, int dshift,
- int page_base, int pageh_base,
- qemu_irq *cpu_request_exit)
+ int page_base, int pageh_base)
{
int i;
d->dshift = dshift;
- d->cpu_request_exit = cpu_request_exit;
memory_region_init_io(&d->channel_io, NULL, &channel_io_ops, d,
"dma-chan", 8 << d->dshift);
@@ -585,12 +588,10 @@ static const VMStateDescription vmstate_dma = {
}
};
-void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit)
+void DMA_init(int high_page_enable)
{
- dma_init2(&dma_controllers[0], 0x00, 0, 0x80,
- high_page_enable ? 0x480 : -1, cpu_request_exit);
- dma_init2(&dma_controllers[1], 0xc0, 1, 0x88,
- high_page_enable ? 0x488 : -1, cpu_request_exit);
+ dma_init2(&dma_controllers[0], 0x00, 0, 0x80, high_page_enable ? 0x480 : -1);
+ dma_init2(&dma_controllers[1], 0xc0, 1, 0x88, high_page_enable ? 0x488 : -1);
vmstate_register (NULL, 0, &vmstate_dma, &dma_controllers[0]);
vmstate_register (NULL, 1, &vmstate_dma, &dma_controllers[1]);
diff --git a/hw/dma/omap_dma.c b/hw/dma/omap_dma.c
index 97c57a03c..db6873099 100644
--- a/hw/dma/omap_dma.c
+++ b/hw/dma/omap_dma.c
@@ -1626,8 +1626,7 @@ struct soc_dma_s *omap_dma_init(hwaddr base, qemu_irq *irqs,
enum omap_dma_model model)
{
int num_irqs, memsize, i;
- struct omap_dma_s *s = (struct omap_dma_s *)
- g_malloc0(sizeof(struct omap_dma_s));
+ struct omap_dma_s *s = g_new0(struct omap_dma_s, 1);
if (model <= omap_dma_3_1) {
num_irqs = 6;
@@ -2061,8 +2060,7 @@ struct soc_dma_s *omap_dma4_init(hwaddr base, qemu_irq *irqs,
int chans, omap_clk iclk, omap_clk fclk)
{
int i;
- struct omap_dma_s *s = (struct omap_dma_s *)
- g_malloc0(sizeof(struct omap_dma_s));
+ struct omap_dma_s *s = g_new0(struct omap_dma_s, 1);
s->model = omap_dma_4;
s->chans = chans;
diff --git a/hw/dma/pxa2xx_dma.c b/hw/dma/pxa2xx_dma.c
index d4501fb4c..54cdb25a3 100644
--- a/hw/dma/pxa2xx_dma.c
+++ b/hw/dma/pxa2xx_dma.c
@@ -459,9 +459,8 @@ static int pxa2xx_dma_init(SysBusDevice *sbd)
return -1;
}
- s->chan = g_malloc0(sizeof(PXA2xxDMAChannel) * s->channels);
+ s->chan = g_new0(PXA2xxDMAChannel, s->channels);
- memset(s->chan, 0, sizeof(PXA2xxDMAChannel) * s->channels);
for (i = 0; i < s->channels; i ++)
s->chan[i].state = DCSR_STOPINTR;
diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c
index cf842a3cc..b1cfa1135 100644
--- a/hw/dma/xilinx_axidma.c
+++ b/hw/dma/xilinx_axidma.c
@@ -133,7 +133,7 @@ struct XilinxAXIDMA {
};
/*
- * Helper calls to extract info from desriptors and other trivial
+ * Helper calls to extract info from descriptors and other trivial
* state from regs.
*/
static inline int stream_desc_sof(struct SDesc *d)
diff --git a/hw/gpio/Makefile.objs b/hw/gpio/Makefile.objs
index 1abcf1798..52233f7e2 100644
--- a/hw/gpio/Makefile.objs
+++ b/hw/gpio/Makefile.objs
@@ -5,3 +5,4 @@ common-obj-$(CONFIG_ZAURUS) += zaurus.o
common-obj-$(CONFIG_E500) += mpc8xxx.o
obj-$(CONFIG_OMAP) += omap_gpio.o
+obj-$(CONFIG_IMX) += imx_gpio.o
diff --git a/hw/gpio/imx_gpio.c b/hw/gpio/imx_gpio.c
new file mode 100644
index 000000000..3170585a2
--- /dev/null
+++ b/hw/gpio/imx_gpio.c
@@ -0,0 +1,341 @@
+/*
+ * i.MX processors GPIO emulation.
+ *
+ * Copyright (C) 2015 Jean-Christophe Dubois <jcd@tribudubois.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/gpio/imx_gpio.h"
+
+#ifndef DEBUG_IMX_GPIO
+#define DEBUG_IMX_GPIO 0
+#endif
+
+typedef enum IMXGPIOLevel {
+ IMX_GPIO_LEVEL_LOW = 0,
+ IMX_GPIO_LEVEL_HIGH = 1,
+} IMXGPIOLevel;
+
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX_GPIO) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_GPIO, \
+ __func__, ##args); \
+ } \
+ } while (0)
+
+static const char *imx_gpio_reg_name(uint32_t reg)
+{
+ switch (reg) {
+ case DR_ADDR:
+ return "DR";
+ case GDIR_ADDR:
+ return "GDIR";
+ case PSR_ADDR:
+ return "PSR";
+ case ICR1_ADDR:
+ return "ICR1";
+ case ICR2_ADDR:
+ return "ICR2";
+ case IMR_ADDR:
+ return "IMR";
+ case ISR_ADDR:
+ return "ISR";
+ case EDGE_SEL_ADDR:
+ return "EDGE_SEL";
+ default:
+ return "[?]";
+ }
+}
+
+static void imx_gpio_update_int(IMXGPIOState *s)
+{
+ qemu_set_irq(s->irq, (s->isr & s->imr) ? 1 : 0);
+}
+
+static void imx_gpio_set_int_line(IMXGPIOState *s, int line, IMXGPIOLevel level)
+{
+ /* if this signal isn't configured as an input signal, nothing to do */
+ if (!extract32(s->gdir, line, 1)) {
+ return;
+ }
+
+ /* When set, EDGE_SEL overrides the ICR config */
+ if (extract32(s->edge_sel, line, 1)) {
+ /* we detect interrupt on rising and falling edge */
+ if (extract32(s->psr, line, 1) != level) {
+ /* level changed */
+ s->isr = deposit32(s->isr, line, 1, 1);
+ }
+ } else if (extract64(s->icr, 2*line + 1, 1)) {
+ /* interrupt is edge sensitive */
+ if (extract32(s->psr, line, 1) != level) {
+ /* level changed */
+ if (extract64(s->icr, 2*line, 1) != level) {
+ s->isr = deposit32(s->isr, line, 1, 1);
+ }
+ }
+ } else {
+ /* interrupt is level sensitive */
+ if (extract64(s->icr, 2*line, 1) == level) {
+ s->isr = deposit32(s->isr, line, 1, 1);
+ }
+ }
+}
+
+static void imx_gpio_set(void *opaque, int line, int level)
+{
+ IMXGPIOState *s = IMX_GPIO(opaque);
+ IMXGPIOLevel imx_level = level ? IMX_GPIO_LEVEL_HIGH : IMX_GPIO_LEVEL_LOW;
+
+ imx_gpio_set_int_line(s, line, imx_level);
+
+ /* this is an input signal, so set PSR */
+ s->psr = deposit32(s->psr, line, 1, imx_level);
+
+ imx_gpio_update_int(s);
+}
+
+static void imx_gpio_set_all_int_lines(IMXGPIOState *s)
+{
+ int i;
+
+ for (i = 0; i < IMX_GPIO_PIN_COUNT; i++) {
+ IMXGPIOLevel imx_level = extract32(s->psr, i, 1);
+ imx_gpio_set_int_line(s, i, imx_level);
+ }
+
+ imx_gpio_update_int(s);
+}
+
+static inline void imx_gpio_set_all_output_lines(IMXGPIOState *s)
+{
+ int i;
+
+ for (i = 0; i < IMX_GPIO_PIN_COUNT; i++) {
+ /*
+ * if the line is set as output, then forward the line
+ * level to its user.
+ */
+ if (extract32(s->gdir, i, 1) && s->output[i]) {
+ qemu_set_irq(s->output[i], extract32(s->dr, i, 1));
+ }
+ }
+}
+
+static uint64_t imx_gpio_read(void *opaque, hwaddr offset, unsigned size)
+{
+ IMXGPIOState *s = IMX_GPIO(opaque);
+ uint32_t reg_value = 0;
+
+ switch (offset) {
+ case DR_ADDR:
+ /*
+ * depending on the "line" configuration, the bit values
+ * are coming either from DR or PSR
+ */
+ reg_value = (s->dr & s->gdir) | (s->psr & ~s->gdir);
+ break;
+
+ case GDIR_ADDR:
+ reg_value = s->gdir;
+ break;
+
+ case PSR_ADDR:
+ reg_value = s->psr & ~s->gdir;
+ break;
+
+ case ICR1_ADDR:
+ reg_value = extract64(s->icr, 0, 32);
+ break;
+
+ case ICR2_ADDR:
+ reg_value = extract64(s->icr, 32, 32);
+ break;
+
+ case IMR_ADDR:
+ reg_value = s->imr;
+ break;
+
+ case ISR_ADDR:
+ reg_value = s->isr;
+ break;
+
+ case EDGE_SEL_ADDR:
+ if (s->has_edge_sel) {
+ reg_value = s->edge_sel;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: EDGE_SEL register not "
+ "present on this version of GPIO device\n",
+ TYPE_IMX_GPIO, __func__);
+ }
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_GPIO, __func__, offset);
+ break;
+ }
+
+ DPRINTF("(%s) = 0x%" PRIx32 "\n", imx_gpio_reg_name(offset), reg_value);
+
+ return reg_value;
+}
+
+static void imx_gpio_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ IMXGPIOState *s = IMX_GPIO(opaque);
+
+ DPRINTF("(%s, value = 0x%" PRIx32 ")\n", imx_gpio_reg_name(offset),
+ (uint32_t)value);
+
+ switch (offset) {
+ case DR_ADDR:
+ s->dr = value;
+ imx_gpio_set_all_output_lines(s);
+ break;
+
+ case GDIR_ADDR:
+ s->gdir = value;
+ imx_gpio_set_all_output_lines(s);
+ imx_gpio_set_all_int_lines(s);
+ break;
+
+ case ICR1_ADDR:
+ s->icr = deposit64(s->icr, 0, 32, value);
+ imx_gpio_set_all_int_lines(s);
+ break;
+
+ case ICR2_ADDR:
+ s->icr = deposit64(s->icr, 32, 32, value);
+ imx_gpio_set_all_int_lines(s);
+ break;
+
+ case IMR_ADDR:
+ s->imr = value;
+ imx_gpio_update_int(s);
+ break;
+
+ case ISR_ADDR:
+ s->isr |= ~value;
+ imx_gpio_set_all_int_lines(s);
+ break;
+
+ case EDGE_SEL_ADDR:
+ if (s->has_edge_sel) {
+ s->edge_sel = value;
+ imx_gpio_set_all_int_lines(s);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: EDGE_SEL register not "
+ "present on this version of GPIO device\n",
+ TYPE_IMX_GPIO, __func__);
+ }
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_GPIO, __func__, offset);
+ break;
+ }
+
+ return;
+}
+
+static const MemoryRegionOps imx_gpio_ops = {
+ .read = imx_gpio_read,
+ .write = imx_gpio_write,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_imx_gpio = {
+ .name = TYPE_IMX_GPIO,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(dr, IMXGPIOState),
+ VMSTATE_UINT32(gdir, IMXGPIOState),
+ VMSTATE_UINT32(psr, IMXGPIOState),
+ VMSTATE_UINT64(icr, IMXGPIOState),
+ VMSTATE_UINT32(imr, IMXGPIOState),
+ VMSTATE_UINT32(isr, IMXGPIOState),
+ VMSTATE_BOOL(has_edge_sel, IMXGPIOState),
+ VMSTATE_UINT32(edge_sel, IMXGPIOState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property imx_gpio_properties[] = {
+ DEFINE_PROP_BOOL("has-edge-sel", IMXGPIOState, has_edge_sel, true),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void imx_gpio_reset(DeviceState *dev)
+{
+ IMXGPIOState *s = IMX_GPIO(dev);
+
+ s->dr = 0;
+ s->gdir = 0;
+ s->psr = 0;
+ s->icr = 0;
+ s->imr = 0;
+ s->isr = 0;
+ s->edge_sel = 0;
+
+ imx_gpio_set_all_output_lines(s);
+ imx_gpio_update_int(s);
+}
+
+static void imx_gpio_realize(DeviceState *dev, Error **errp)
+{
+ IMXGPIOState *s = IMX_GPIO(dev);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &imx_gpio_ops, s,
+ TYPE_IMX_GPIO, IMX_GPIO_MEM_SIZE);
+
+ qdev_init_gpio_in(DEVICE(s), imx_gpio_set, IMX_GPIO_PIN_COUNT);
+ qdev_init_gpio_out(DEVICE(s), s->output, IMX_GPIO_PIN_COUNT);
+
+ sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
+}
+
+static void imx_gpio_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = imx_gpio_realize;
+ dc->reset = imx_gpio_reset;
+ dc->props = imx_gpio_properties;
+ dc->vmsd = &vmstate_imx_gpio;
+ dc->desc = "i.MX GPIO controller";
+}
+
+static const TypeInfo imx_gpio_info = {
+ .name = TYPE_IMX_GPIO,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(IMXGPIOState),
+ .class_init = imx_gpio_class_init,
+};
+
+static void imx_gpio_register_types(void)
+{
+ type_register_static(&imx_gpio_info);
+}
+
+type_init(imx_gpio_register_types)
diff --git a/hw/gpio/omap_gpio.c b/hw/gpio/omap_gpio.c
index d92f8cfba..3c538985e 100644
--- a/hw/gpio/omap_gpio.c
+++ b/hw/gpio/omap_gpio.c
@@ -710,8 +710,8 @@ static int omap2_gpio_init(SysBusDevice *sbd)
} else {
s->modulecount = 6;
}
- s->modules = g_malloc0(s->modulecount * sizeof(struct omap2_gpio_s));
- s->handler = g_malloc0(s->modulecount * 32 * sizeof(qemu_irq));
+ s->modules = g_new0(struct omap2_gpio_s, s->modulecount);
+ s->handler = g_new0(qemu_irq, s->modulecount * 32);
qdev_init_gpio_in(dev, omap2_gpio_set, s->modulecount * 32);
qdev_init_gpio_out(dev, s->handler, s->modulecount * 32);
for (i = 0; i < s->modulecount; i++) {
diff --git a/hw/gpio/zaurus.c b/hw/gpio/zaurus.c
index 24a77272d..aa8ee5fa0 100644
--- a/hw/gpio/zaurus.c
+++ b/hw/gpio/zaurus.c
@@ -235,10 +235,6 @@ static const VMStateDescription vmstate_scoop_regs = {
},
};
-static Property scoop_sysbus_properties[] = {
- DEFINE_PROP_END_OF_LIST(),
-};
-
static void scoop_sysbus_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -247,7 +243,6 @@ static void scoop_sysbus_class_init(ObjectClass *klass, void *data)
k->init = scoop_init;
dc->desc = "Scoop2 Sharp custom ASIC";
dc->vmsd = &vmstate_scoop_regs;
- dc->props = scoop_sysbus_properties;
}
static const TypeInfo scoop_sysbus_info = {
diff --git a/hw/i2c/Makefile.objs b/hw/i2c/Makefile.objs
index 0f130608c..aeb8f38d7 100644
--- a/hw/i2c/Makefile.objs
+++ b/hw/i2c/Makefile.objs
@@ -4,4 +4,5 @@ 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
+common-obj-$(CONFIG_IMX_I2C) += imx_i2c.o
obj-$(CONFIG_OMAP) += omap_i2c.o
diff --git a/hw/i2c/imx_i2c.c b/hw/i2c/imx_i2c.c
new file mode 100644
index 000000000..cb62c7a2c
--- /dev/null
+++ b/hw/i2c/imx_i2c.c
@@ -0,0 +1,335 @@
+/*
+ * i.MX I2C Bus Serial Interface Emulation
+ *
+ * Copyright (C) 2013 Jean-Christophe Dubois. <jcd@tribudubois.net>
+ *
+ * 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/i2c/imx_i2c.h"
+#include "hw/i2c/i2c.h"
+
+#ifndef DEBUG_IMX_I2C
+#define DEBUG_IMX_I2C 0
+#endif
+
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX_I2C) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_I2C, \
+ __func__, ##args); \
+ } \
+ } while (0)
+
+static const char *imx_i2c_get_regname(unsigned offset)
+{
+ switch (offset) {
+ case IADR_ADDR:
+ return "IADR";
+ case IFDR_ADDR:
+ return "IFDR";
+ case I2CR_ADDR:
+ return "I2CR";
+ case I2SR_ADDR:
+ return "I2SR";
+ case I2DR_ADDR:
+ return "I2DR";
+ default:
+ return "[?]";
+ }
+}
+
+static inline bool imx_i2c_is_enabled(IMXI2CState *s)
+{
+ return s->i2cr & I2CR_IEN;
+}
+
+static inline bool imx_i2c_interrupt_is_enabled(IMXI2CState *s)
+{
+ return s->i2cr & I2CR_IIEN;
+}
+
+static inline bool imx_i2c_is_master(IMXI2CState *s)
+{
+ return s->i2cr & I2CR_MSTA;
+}
+
+static void imx_i2c_reset(DeviceState *dev)
+{
+ IMXI2CState *s = IMX_I2C(dev);
+
+ if (s->address != ADDR_RESET) {
+ i2c_end_transfer(s->bus);
+ }
+
+ s->address = ADDR_RESET;
+ s->iadr = IADR_RESET;
+ s->ifdr = IFDR_RESET;
+ s->i2cr = I2CR_RESET;
+ s->i2sr = I2SR_RESET;
+ s->i2dr_read = I2DR_RESET;
+ s->i2dr_write = I2DR_RESET;
+}
+
+static inline void imx_i2c_raise_interrupt(IMXI2CState *s)
+{
+ /*
+ * raise an interrupt if the device is enabled and it is configured
+ * to generate some interrupts.
+ */
+ if (imx_i2c_is_enabled(s) && imx_i2c_interrupt_is_enabled(s)) {
+ s->i2sr |= I2SR_IIF;
+ qemu_irq_raise(s->irq);
+ }
+}
+
+static uint64_t imx_i2c_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ uint16_t value;
+ IMXI2CState *s = IMX_I2C(opaque);
+
+ switch (offset) {
+ case IADR_ADDR:
+ value = s->iadr;
+ break;
+ case IFDR_ADDR:
+ value = s->ifdr;
+ break;
+ case I2CR_ADDR:
+ value = s->i2cr;
+ break;
+ case I2SR_ADDR:
+ value = s->i2sr;
+ break;
+ case I2DR_ADDR:
+ value = s->i2dr_read;
+
+ if (imx_i2c_is_master(s)) {
+ int ret = 0xff;
+
+ if (s->address == ADDR_RESET) {
+ /* something is wrong as the address is not set */
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read "
+ "without specifying the slave address\n",
+ TYPE_IMX_I2C, __func__);
+ } else if (s->i2cr & I2CR_MTX) {
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read "
+ "but MTX is set\n", TYPE_IMX_I2C, __func__);
+ } else {
+ /* get the next byte */
+ ret = i2c_recv(s->bus);
+
+ if (ret >= 0) {
+ imx_i2c_raise_interrupt(s);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: read failed "
+ "for device 0x%02x\n", TYPE_IMX_I2C,
+ __func__, s->address);
+ ret = 0xff;
+ }
+ }
+
+ s->i2dr_read = ret;
+ } else {
+ qemu_log_mask(LOG_UNIMP, "[%s]%s: slave mode not implemented\n",
+ TYPE_IMX_I2C, __func__);
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_I2C, __func__, offset);
+ value = 0;
+ break;
+ }
+
+ DPRINTF("read %s [0x%" HWADDR_PRIx "] -> 0x%02x\n",
+ imx_i2c_get_regname(offset), offset, value);
+
+ return (uint64_t)value;
+}
+
+static void imx_i2c_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ IMXI2CState *s = IMX_I2C(opaque);
+
+ DPRINTF("write %s [0x%" HWADDR_PRIx "] <- 0x%02x\n",
+ imx_i2c_get_regname(offset), offset, (int)value);
+
+ value &= 0xff;
+
+ switch (offset) {
+ case IADR_ADDR:
+ s->iadr = value & IADR_MASK;
+ /* i2c_set_slave_address(s->bus, (uint8_t)s->iadr); */
+ break;
+ case IFDR_ADDR:
+ s->ifdr = value & IFDR_MASK;
+ break;
+ case I2CR_ADDR:
+ if (imx_i2c_is_enabled(s) && ((value & I2CR_IEN) == 0)) {
+ /* This is a soft reset. IADR is preserved during soft resets */
+ uint16_t iadr = s->iadr;
+ imx_i2c_reset(DEVICE(s));
+ s->iadr = iadr;
+ } else { /* normal write */
+ s->i2cr = value & I2CR_MASK;
+
+ if (imx_i2c_is_master(s)) {
+ /* set the bus to busy */
+ s->i2sr |= I2SR_IBB;
+ } else { /* slave mode */
+ /* bus is not busy anymore */
+ s->i2sr &= ~I2SR_IBB;
+
+ /*
+ * if we unset the master mode then it ends the ongoing
+ * transfer if any
+ */
+ if (s->address != ADDR_RESET) {
+ i2c_end_transfer(s->bus);
+ s->address = ADDR_RESET;
+ }
+ }
+
+ if (s->i2cr & I2CR_RSTA) { /* Restart */
+ /* if this is a restart then it ends the ongoing transfer */
+ if (s->address != ADDR_RESET) {
+ i2c_end_transfer(s->bus);
+ s->address = ADDR_RESET;
+ s->i2cr &= ~I2CR_RSTA;
+ }
+ }
+ }
+ break;
+ case I2SR_ADDR:
+ /*
+ * if the user writes 0 to IIF then lower the interrupt and
+ * reset the bit
+ */
+ if ((s->i2sr & I2SR_IIF) && !(value & I2SR_IIF)) {
+ s->i2sr &= ~I2SR_IIF;
+ qemu_irq_lower(s->irq);
+ }
+
+ /*
+ * if the user writes 0 to IAL, reset the bit
+ */
+ if ((s->i2sr & I2SR_IAL) && !(value & I2SR_IAL)) {
+ s->i2sr &= ~I2SR_IAL;
+ }
+
+ break;
+ case I2DR_ADDR:
+ /* if the device is not enabled, nothing to do */
+ if (!imx_i2c_is_enabled(s)) {
+ break;
+ }
+
+ s->i2dr_write = value & I2DR_MASK;
+
+ if (imx_i2c_is_master(s)) {
+ /* If this is the first write cycle then it is the slave addr */
+ if (s->address == ADDR_RESET) {
+ if (i2c_start_transfer(s->bus, extract32(s->i2dr_write, 1, 7),
+ extract32(s->i2dr_write, 0, 1))) {
+ /* if non zero is returned, the adress is not valid */
+ s->i2sr |= I2SR_RXAK;
+ } else {
+ s->address = s->i2dr_write;
+ s->i2sr &= ~I2SR_RXAK;
+ imx_i2c_raise_interrupt(s);
+ }
+ } else { /* This is a normal data write */
+ if (i2c_send(s->bus, s->i2dr_write)) {
+ /* if the target return non zero then end the transfer */
+ s->i2sr |= I2SR_RXAK;
+ s->address = ADDR_RESET;
+ i2c_end_transfer(s->bus);
+ } else {
+ s->i2sr &= ~I2SR_RXAK;
+ imx_i2c_raise_interrupt(s);
+ }
+ }
+ } else {
+ qemu_log_mask(LOG_UNIMP, "[%s]%s: slave mode not implemented\n",
+ TYPE_IMX_I2C, __func__);
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_I2C, __func__, offset);
+ break;
+ }
+}
+
+static const MemoryRegionOps imx_i2c_ops = {
+ .read = imx_i2c_read,
+ .write = imx_i2c_write,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 2,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription imx_i2c_vmstate = {
+ .name = TYPE_IMX_I2C,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT16(address, IMXI2CState),
+ VMSTATE_UINT16(iadr, IMXI2CState),
+ VMSTATE_UINT16(ifdr, IMXI2CState),
+ VMSTATE_UINT16(i2cr, IMXI2CState),
+ VMSTATE_UINT16(i2sr, IMXI2CState),
+ VMSTATE_UINT16(i2dr_read, IMXI2CState),
+ VMSTATE_UINT16(i2dr_write, IMXI2CState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void imx_i2c_realize(DeviceState *dev, Error **errp)
+{
+ IMXI2CState *s = IMX_I2C(dev);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &imx_i2c_ops, s, TYPE_IMX_I2C,
+ IMX_I2C_MEM_SIZE);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
+ sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
+ s->bus = i2c_init_bus(DEVICE(dev), "i2c");
+}
+
+static void imx_i2c_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &imx_i2c_vmstate;
+ dc->reset = imx_i2c_reset;
+ dc->realize = imx_i2c_realize;
+}
+
+static const TypeInfo imx_i2c_type_info = {
+ .name = TYPE_IMX_I2C,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(IMXI2CState),
+ .class_init = imx_i2c_class_init,
+};
+
+static void imx_i2c_register_types(void)
+{
+ type_register_static(&imx_i2c_type_info);
+}
+
+type_init(imx_i2c_register_types)
diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs
index bd4f147f9..c250deb84 100644
--- a/hw/i386/Makefile.objs
+++ b/hw/i386/Makefile.objs
@@ -1,5 +1,5 @@
obj-$(CONFIG_KVM) += kvm/
-obj-y += multiboot.o smbios.o
+obj-y += multiboot.o
obj-y += pc.o pc_piix.o pc_q35.o
obj-y += pc_sysfw.o
obj-y += intel_iommu.o
@@ -7,8 +7,15 @@ obj-$(CONFIG_XEN) += ../xenpv/ xen/
obj-y += kvmvapic.o
obj-y += acpi-build.o
+obj-y += pci-assign-load-rom.o
+
+gen-hex-y += hw/i386/acpi-dsdt.hex
+gen-hex-y += hw/i386/q35-acpi-dsdt.hex
+
hw/i386/acpi-build.o: hw/i386/acpi-build.c \
- hw/i386/acpi-dsdt.hex hw/i386/q35-acpi-dsdt.hex
+ $(gen-hex-y)
+
+-include $(gen-hex-y:.hex=.d)
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 46eddb8e4..95e0c657a 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -169,6 +169,7 @@ static void acpi_get_pm_info(AcpiPmInfo *pm)
Object *obj = NULL;
QObject *o;
+ pm->cpu_hp_io_base = 0;
pm->pcihp_io_base = 0;
pm->pcihp_io_len = 0;
if (piix) {
diff --git a/hw/i386/acpi-dsdt.dsl b/hw/i386/acpi-dsdt.dsl
index a2d84ecf8..8dba096dd 100644
--- a/hw/i386/acpi-dsdt.dsl
+++ b/hw/i386/acpi-dsdt.dsl
@@ -43,7 +43,6 @@ DefinitionBlock (
#include "acpi-dsdt-hpet.dsl"
-
/****************************************************************
* PIIX4 PM
****************************************************************/
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index 08055a8d8..3fe27fa51 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -22,6 +22,7 @@
#include "hw/sysbus.h"
#include "exec/address-spaces.h"
#include "intel_iommu_internal.h"
+#include "hw/pci/pci.h"
/*#define DEBUG_INTEL_IOMMU*/
#ifdef DEBUG_INTEL_IOMMU
@@ -166,19 +167,17 @@ static gboolean vtd_hash_remove_by_page(gpointer key, gpointer value,
*/
static void vtd_reset_context_cache(IntelIOMMUState *s)
{
- VTDAddressSpace **pvtd_as;
VTDAddressSpace *vtd_as;
- uint32_t bus_it;
+ VTDBus *vtd_bus;
+ GHashTableIter bus_it;
uint32_t devfn_it;
+ g_hash_table_iter_init(&bus_it, s->vtd_as_by_busptr);
+
VTD_DPRINTF(CACHE, "global context_cache_gen=1");
- for (bus_it = 0; bus_it < VTD_PCI_BUS_MAX; ++bus_it) {
- pvtd_as = s->address_spaces[bus_it];
- if (!pvtd_as) {
- continue;
- }
+ while (g_hash_table_iter_next (&bus_it, NULL, (void**)&vtd_bus)) {
for (devfn_it = 0; devfn_it < VTD_PCI_DEVFN_MAX; ++devfn_it) {
- vtd_as = pvtd_as[devfn_it];
+ vtd_as = vtd_bus->dev_as[devfn_it];
if (!vtd_as) {
continue;
}
@@ -754,12 +753,13 @@ static inline bool vtd_is_interrupt_addr(hwaddr addr)
* @is_write: The access is a write operation
* @entry: IOMMUTLBEntry that contain the addr to be translated and result
*/
-static void vtd_do_iommu_translate(VTDAddressSpace *vtd_as, uint8_t bus_num,
+static void vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus,
uint8_t devfn, hwaddr addr, bool is_write,
IOMMUTLBEntry *entry)
{
IntelIOMMUState *s = vtd_as->iommu_state;
VTDContextEntry ce;
+ uint8_t bus_num = pci_bus_num(bus);
VTDContextCacheEntry *cc_entry = &vtd_as->context_cache_entry;
uint64_t slpte;
uint32_t level;
@@ -874,6 +874,29 @@ static void vtd_context_global_invalidate(IntelIOMMUState *s)
}
}
+
+/* Find the VTD address space currently associated with a given bus number,
+ */
+static VTDBus *vtd_find_as_from_bus_num(IntelIOMMUState *s, uint8_t bus_num)
+{
+ VTDBus *vtd_bus = s->vtd_as_by_bus_num[bus_num];
+ if (!vtd_bus) {
+ /* Iterate over the registered buses to find the one
+ * which currently hold this bus number, and update the bus_num lookup table:
+ */
+ GHashTableIter iter;
+
+ g_hash_table_iter_init(&iter, s->vtd_as_by_busptr);
+ while (g_hash_table_iter_next (&iter, NULL, (void**)&vtd_bus)) {
+ if (pci_bus_num(vtd_bus->bus) == bus_num) {
+ s->vtd_as_by_bus_num[bus_num] = vtd_bus;
+ return vtd_bus;
+ }
+ }
+ }
+ return vtd_bus;
+}
+
/* Do a context-cache device-selective invalidation.
* @func_mask: FM field after shifting
*/
@@ -882,7 +905,7 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s,
uint16_t func_mask)
{
uint16_t mask;
- VTDAddressSpace **pvtd_as;
+ VTDBus *vtd_bus;
VTDAddressSpace *vtd_as;
uint16_t devfn;
uint16_t devfn_it;
@@ -903,11 +926,11 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s,
}
VTD_DPRINTF(INV, "device-selective invalidation source 0x%"PRIx16
" mask %"PRIu16, source_id, mask);
- pvtd_as = s->address_spaces[VTD_SID_TO_BUS(source_id)];
- if (pvtd_as) {
+ vtd_bus = vtd_find_as_from_bus_num(s, VTD_SID_TO_BUS(source_id));
+ if (vtd_bus) {
devfn = VTD_SID_TO_DEVFN(source_id);
for (devfn_it = 0; devfn_it < VTD_PCI_DEVFN_MAX; ++devfn_it) {
- vtd_as = pvtd_as[devfn_it];
+ vtd_as = vtd_bus->dev_as[devfn_it];
if (vtd_as && ((devfn_it & mask) == (devfn & mask))) {
VTD_DPRINTF(INV, "invalidate context-cahce of devfn 0x%"PRIx16,
devfn_it);
@@ -1805,11 +1828,11 @@ static IOMMUTLBEntry vtd_iommu_translate(MemoryRegion *iommu, hwaddr addr,
return ret;
}
- vtd_do_iommu_translate(vtd_as, vtd_as->bus_num, vtd_as->devfn, addr,
+ vtd_do_iommu_translate(vtd_as, vtd_as->bus, vtd_as->devfn, addr,
is_write, &ret);
VTD_DPRINTF(MMU,
"bus %"PRIu8 " slot %"PRIu8 " func %"PRIu8 " devfn %"PRIu8
- " gpa 0x%"PRIx64 " hpa 0x%"PRIx64, vtd_as->bus_num,
+ " gpa 0x%"PRIx64 " hpa 0x%"PRIx64, pci_bus_num(vtd_as->bus),
VTD_PCI_SLOT(vtd_as->devfn), VTD_PCI_FUNC(vtd_as->devfn),
vtd_as->devfn, addr, ret.translated_addr);
return ret;
@@ -1839,6 +1862,38 @@ static Property vtd_properties[] = {
DEFINE_PROP_END_OF_LIST(),
};
+
+VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn)
+{
+ uintptr_t key = (uintptr_t)bus;
+ VTDBus *vtd_bus = g_hash_table_lookup(s->vtd_as_by_busptr, &key);
+ VTDAddressSpace *vtd_dev_as;
+
+ if (!vtd_bus) {
+ /* No corresponding free() */
+ vtd_bus = g_malloc0(sizeof(VTDBus) + sizeof(VTDAddressSpace *) * VTD_PCI_DEVFN_MAX);
+ vtd_bus->bus = bus;
+ key = (uintptr_t)bus;
+ g_hash_table_insert(s->vtd_as_by_busptr, &key, vtd_bus);
+ }
+
+ vtd_dev_as = vtd_bus->dev_as[devfn];
+
+ if (!vtd_dev_as) {
+ vtd_bus->dev_as[devfn] = vtd_dev_as = g_malloc0(sizeof(VTDAddressSpace));
+
+ vtd_dev_as->bus = bus;
+ vtd_dev_as->devfn = (uint8_t)devfn;
+ vtd_dev_as->iommu_state = s;
+ vtd_dev_as->context_cache_entry.context_cache_gen = 0;
+ memory_region_init_iommu(&vtd_dev_as->iommu, OBJECT(s),
+ &s->iommu_ops, "intel_iommu", UINT64_MAX);
+ address_space_init(&vtd_dev_as->as,
+ &vtd_dev_as->iommu, "intel_iommu");
+ }
+ return vtd_dev_as;
+}
+
/* Do the initialization. It will also be called when reset, so pay
* attention when adding new initialization stuff.
*/
@@ -1931,13 +1986,15 @@ static void vtd_realize(DeviceState *dev, Error **errp)
IntelIOMMUState *s = INTEL_IOMMU_DEVICE(dev);
VTD_DPRINTF(GENERAL, "");
- memset(s->address_spaces, 0, sizeof(s->address_spaces));
+ memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num));
memory_region_init_io(&s->csrmem, OBJECT(s), &vtd_mem_ops, s,
"intel_iommu", DMAR_REG_SIZE);
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->csrmem);
/* No corresponding destroy */
s->iotlb = g_hash_table_new_full(vtd_uint64_hash, vtd_uint64_equal,
g_free, g_free);
+ s->vtd_as_by_busptr = g_hash_table_new_full(vtd_uint64_hash, vtd_uint64_equal,
+ g_free, g_free);
vtd_init(s);
}
diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c
index efdf16584..0593a3f1f 100644
--- a/hw/i386/kvm/clock.c
+++ b/hw/i386/kvm/clock.c
@@ -17,7 +17,7 @@
#include "qemu/host-utils.h"
#include "sysemu/sysemu.h"
#include "sysemu/kvm.h"
-#include "sysemu/cpus.h"
+#include "kvm_i386.h"
#include "hw/sysbus.h"
#include "hw/kvm/clock.h"
@@ -125,21 +125,7 @@ static void kvmclock_vm_state_change(void *opaque, int running,
return;
}
- cpu_synchronize_all_states();
- /* In theory, the cpu_synchronize_all_states() call above wouldn't
- * affect the rest of the code, as the VCPU state inside CPUState
- * is supposed to always match the VCPU state on the kernel side.
- *
- * In practice, calling cpu_synchronize_state() too soon will load the
- * kernel-side APIC state into X86CPU.apic_state too early, APIC state
- * won't be reloaded later because CPUState.vcpu_dirty==true, and
- * outdated APIC state may be migrated to another host.
- *
- * The real fix would be to make sure outdated APIC state is read
- * from the kernel again when necessary. While this is not fixed, we
- * need the cpu_clean_all_dirty() call below.
- */
- cpu_clean_all_dirty();
+ kvm_synchronize_all_tsc();
ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
if (ret < 0) {
diff --git a/hw/i386/kvm/ioapic.c b/hw/i386/kvm/ioapic.c
index d2a6c4cf6..b7390ca0d 100644
--- a/hw/i386/kvm/ioapic.c
+++ b/hw/i386/kvm/ioapic.c
@@ -10,6 +10,7 @@
* See the COPYING file in the top-level directory.
*/
+#include "monitor/monitor.h"
#include "hw/i386/pc.h"
#include "hw/i386/ioapic_internal.h"
#include "hw/i386/apic_internal.h"
@@ -110,6 +111,15 @@ static void kvm_ioapic_put(IOAPICCommonState *s)
}
}
+void kvm_ioapic_dump_state(Monitor *mon, const QDict *qdict)
+{
+ IOAPICCommonState s;
+
+ kvm_ioapic_get(&s);
+
+ ioapic_print_redtbl(mon, &s);
+}
+
static void kvm_ioapic_reset(DeviceState *dev)
{
IOAPICCommonState *s = IOAPIC_COMMON(dev);
diff --git a/hw/i386/kvm/pci-assign.c b/hw/i386/kvm/pci-assign.c
index 74d22f4fd..0fd69230a 100644
--- a/hw/i386/kvm/pci-assign.c
+++ b/hw/i386/kvm/pci-assign.c
@@ -22,7 +22,6 @@
*/
#include <stdio.h>
#include <unistd.h>
-#include <sys/io.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -37,6 +36,7 @@
#include "hw/pci/pci.h"
#include "hw/pci/msi.h"
#include "kvm_i386.h"
+#include "hw/pci/pci-assign.h"
#define MSIX_PAGE_SIZE 0x1000
@@ -48,17 +48,6 @@
#define IORESOURCE_PREFETCH 0x00002000 /* No side effects */
#define IORESOURCE_MEM_64 0x00100000
-//#define DEVICE_ASSIGNMENT_DEBUG
-
-#ifdef DEVICE_ASSIGNMENT_DEBUG
-#define DEBUG(fmt, ...) \
- do { \
- fprintf(stderr, "%s: " fmt, __func__ , __VA_ARGS__); \
- } while (0)
-#else
-#define DEBUG(fmt, ...)
-#endif
-
typedef struct PCIRegion {
int type; /* Memory or port I/O */
int valid;
@@ -990,7 +979,7 @@ static void assigned_dev_update_msi(PCIDevice *pci_dev)
MSIMessage msg = msi_get_message(pci_dev, 0);
int virq;
- virq = kvm_irqchip_add_msi_route(kvm_state, msg);
+ virq = kvm_irqchip_add_msi_route(kvm_state, msg, pci_dev);
if (virq < 0) {
perror("assigned_dev_update_msi: kvm_irqchip_add_msi_route");
return;
@@ -1028,7 +1017,7 @@ static void assigned_dev_update_msi_msg(PCIDevice *pci_dev)
}
kvm_irqchip_update_msi_route(kvm_state, assigned_dev->msi_virq[0],
- msi_get_message(pci_dev, 0));
+ msi_get_message(pci_dev, 0), pci_dev);
}
static bool assigned_dev_msix_masked(MSIXTableEntry *entry)
@@ -1094,7 +1083,7 @@ static int assigned_dev_update_msix_mmio(PCIDevice *pci_dev)
msg.address = entry->addr_lo | ((uint64_t)entry->addr_hi << 32);
msg.data = entry->data;
- r = kvm_irqchip_add_msi_route(kvm_state, msg);
+ r = kvm_irqchip_add_msi_route(kvm_state, msg, pci_dev);
if (r < 0) {
return r;
}
@@ -1494,7 +1483,7 @@ static int assigned_device_pci_cap_init(PCIDevice *pci_dev, Error **errp)
* error bits, leave the rest. */
status = pci_get_long(pci_dev->config + pos + PCI_X_STATUS);
status &= ~(PCI_X_STATUS_BUS | PCI_X_STATUS_DEVFN);
- status |= (pci_bus_num(pci_dev->bus) << 8) | pci_dev->devfn;
+ status |= pci_requester_id(pci_dev);
status &= ~(PCI_X_STATUS_SPL_DISC | PCI_X_STATUS_UNX_SPL |
PCI_X_STATUS_SPL_ERR);
pci_set_long(pci_dev->config + pos + PCI_X_STATUS, status);
@@ -1613,7 +1602,8 @@ static void assigned_dev_msix_mmio_write(void *opaque, hwaddr addr,
msg.data = entry->data;
ret = kvm_irqchip_update_msi_route(kvm_state,
- adev->msi_virq[i], msg);
+ adev->msi_virq[i], msg,
+ pdev);
if (ret) {
error_report("Error updating irq routing entry (%d)", ret);
}
@@ -1896,73 +1886,15 @@ static void assign_register_types(void)
type_init(assign_register_types)
-/*
- * Scan the assigned devices for the devices that have an option ROM, and then
- * load the corresponding ROM data to RAM. If an error occurs while loading an
- * option ROM, we just ignore that option ROM and continue with the next one.
- */
static void assigned_dev_load_option_rom(AssignedDevice *dev)
{
- char name[32], rom_file[64];
- FILE *fp;
- uint8_t val;
- struct stat st;
- void *ptr;
-
- /* If loading ROM from file, pci handles it */
- if (dev->dev.romfile || !dev->dev.rom_bar) {
- return;
- }
+ int size = 0;
- snprintf(rom_file, sizeof(rom_file),
- "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/rom",
- dev->host.domain, dev->host.bus, dev->host.slot,
- dev->host.function);
+ pci_assign_dev_load_option_rom(&dev->dev, OBJECT(dev), &size,
+ dev->host.domain, dev->host.bus,
+ dev->host.slot, dev->host.function);
- if (stat(rom_file, &st)) {
- return;
- }
-
- if (access(rom_file, F_OK)) {
- error_report("pci-assign: Insufficient privileges for %s", rom_file);
- return;
- }
-
- /* Write "1" to the ROM file to enable it */
- fp = fopen(rom_file, "r+");
- if (fp == NULL) {
- return;
+ if (!size) {
+ error_report("pci-assign: Invalid ROM.");
}
- val = 1;
- if (fwrite(&val, 1, 1, fp) != 1) {
- goto close_rom;
- }
- fseek(fp, 0, SEEK_SET);
-
- snprintf(name, sizeof(name), "%s.rom",
- object_get_typename(OBJECT(dev)));
- memory_region_init_ram(&dev->dev.rom, OBJECT(dev), name, st.st_size,
- &error_abort);
- vmstate_register_ram(&dev->dev.rom, &dev->dev.qdev);
- ptr = memory_region_get_ram_ptr(&dev->dev.rom);
- memset(ptr, 0xff, st.st_size);
-
- if (!fread(ptr, 1, st.st_size, fp)) {
- error_report("pci-assign: Cannot read from host %s", rom_file);
- error_printf("Device option ROM contents are probably invalid "
- "(check dmesg).\nSkip option ROM probe with rombar=0, "
- "or load from file with romfile=\n");
- goto close_rom;
- }
-
- pci_register_bar(&dev->dev, PCI_ROM_SLOT, 0, &dev->dev.rom);
- dev->dev.has_rom = true;
-close_rom:
- /* Write "0" to disable ROM */
- fseek(fp, 0, SEEK_SET);
- val = 0;
- if (!fwrite(&val, 1, 1, fp)) {
- DEBUG("%s\n", "Failed to disable pci-sysfs rom file");
- }
- fclose(fp);
}
diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c
index c6d34b254..f0922da68 100644
--- a/hw/i386/kvmvapic.c
+++ b/hw/i386/kvmvapic.c
@@ -634,13 +634,18 @@ static int vapic_prepare(VAPICROMState *s)
static void vapic_write(void *opaque, hwaddr addr, uint64_t data,
unsigned int size)
{
- CPUState *cs = current_cpu;
- X86CPU *cpu = X86_CPU(cs);
- CPUX86State *env = &cpu->env;
- hwaddr rom_paddr;
VAPICROMState *s = opaque;
+ X86CPU *cpu;
+ CPUX86State *env;
+ hwaddr rom_paddr;
- cpu_synchronize_state(cs);
+ if (!current_cpu) {
+ return;
+ }
+
+ cpu_synchronize_state(current_cpu);
+ cpu = X86_CPU(current_cpu);
+ env = &cpu->env;
/*
* The VAPIC supports two PIO-based hypercalls, both via port 0x7E.
diff --git a/hw/i386/multiboot.c b/hw/i386/multiboot.c
index 1adbe9e25..6774a1932 100644
--- a/hw/i386/multiboot.c
+++ b/hw/i386/multiboot.c
@@ -195,7 +195,7 @@ int load_multiboot(FWCfgState *fw_cfg,
}
kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
- &elf_low, &elf_high, 0, ELF_MACHINE, 0);
+ &elf_low, &elf_high, 0, I386_ELF_MACHINE, 0);
if (kernel_size < 0) {
fprintf(stderr, "Error while loading elf kernel\n");
exit(1);
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 7661ea9cd..5e20e07b6 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -33,7 +33,7 @@
#include "hw/pci/pci_bus.h"
#include "hw/nvram/fw_cfg.h"
#include "hw/timer/hpet.h"
-#include "hw/i386/smbios.h"
+#include "hw/smbios/smbios.h"
#include "hw/loader.h"
#include "elf.h"
#include "multiboot.h"
@@ -59,7 +59,6 @@
#include "qemu/error-report.h"
#include "hw/acpi/acpi.h"
#include "hw/acpi/cpu_hotplug.h"
-#include "hw/cpu/icc_bus.h"
#include "hw/boards.h"
#include "hw/pci/pci_host.h"
#include "acpi-build.h"
@@ -428,26 +427,24 @@ static void pc_cmos_init_late(void *opaque)
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,
+void pc_cmos_init(PCMachineState *pcms,
BusState *idebus0, BusState *idebus1,
ISADevice *s)
{
int val;
static pc_cmos_init_late_arg arg;
- PCMachineState *pc_machine = PC_MACHINE(machine);
Error *local_err = NULL;
/* various important CMOS locations needed by PC/Bochs bios */
/* memory size */
/* base memory (first MiB) */
- val = MIN(ram_size / 1024, 640);
+ val = MIN(pcms->below_4g_mem_size / 1024, 640);
rtc_set_memory(s, 0x15, val);
rtc_set_memory(s, 0x16, val >> 8);
/* extended memory (next 64MiB) */
- if (ram_size > 1024 * 1024) {
- val = (ram_size - 1024 * 1024) / 1024;
+ if (pcms->below_4g_mem_size > 1024 * 1024) {
+ val = (pcms->below_4g_mem_size - 1024 * 1024) / 1024;
} else {
val = 0;
}
@@ -458,8 +455,8 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
rtc_set_memory(s, 0x30, val);
rtc_set_memory(s, 0x31, val >> 8);
/* memory between 16MiB and 4GiB */
- if (ram_size > 16 * 1024 * 1024) {
- val = (ram_size - 16 * 1024 * 1024) / 65536;
+ if (pcms->below_4g_mem_size > 16 * 1024 * 1024) {
+ val = (pcms->below_4g_mem_size - 16 * 1024 * 1024) / 65536;
} else {
val = 0;
}
@@ -468,7 +465,7 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
rtc_set_memory(s, 0x34, val);
rtc_set_memory(s, 0x35, val >> 8);
/* memory above 4GiB */
- val = above_4g_mem_size / 65536;
+ val = pcms->above_4g_mem_size / 65536;
rtc_set_memory(s, 0x5b, val);
rtc_set_memory(s, 0x5c, val >> 8);
rtc_set_memory(s, 0x5d, val >> 16);
@@ -476,15 +473,15 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
/* set the number of CPU */
rtc_set_memory(s, 0x5f, smp_cpus - 1);
- object_property_add_link(OBJECT(machine), "rtc_state",
+ object_property_add_link(OBJECT(pcms), "rtc_state",
TYPE_ISA_DEVICE,
- (Object **)&pc_machine->rtc,
+ (Object **)&pcms->rtc,
object_property_allow_set_link,
OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort);
- object_property_set_link(OBJECT(machine), OBJECT(s),
+ object_property_set_link(OBJECT(pcms), OBJECT(s),
"rtc_state", &error_abort);
- set_boot_dev(s, boot_device, &local_err);
+ set_boot_dev(s, MACHINE(pcms)->boot_order, &local_err);
if (local_err) {
error_report_err(local_err);
exit(1);
@@ -718,16 +715,52 @@ static unsigned int pc_apic_id_limit(unsigned int max_cpus)
return x86_cpu_apic_id_from_index(max_cpus - 1) + 1;
}
-static FWCfgState *bochs_bios_init(void)
+static void pc_build_smbios(FWCfgState *fw_cfg)
{
- FWCfgState *fw_cfg;
uint8_t *smbios_tables, *smbios_anchor;
size_t smbios_tables_len, smbios_anchor_len;
+ struct smbios_phys_mem_area *mem_array;
+ unsigned i, array_count;
+
+ smbios_tables = smbios_get_table_legacy(&smbios_tables_len);
+ if (smbios_tables) {
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES,
+ smbios_tables, smbios_tables_len);
+ }
+
+ /* build the array of physical mem area from e820 table */
+ mem_array = g_malloc0(sizeof(*mem_array) * e820_get_num_entries());
+ for (i = 0, array_count = 0; i < e820_get_num_entries(); i++) {
+ uint64_t addr, len;
+
+ if (e820_get_entry(i, E820_RAM, &addr, &len)) {
+ mem_array[array_count].address = addr;
+ mem_array[array_count].length = len;
+ array_count++;
+ }
+ }
+ smbios_get_tables(mem_array, array_count,
+ &smbios_tables, &smbios_tables_len,
+ &smbios_anchor, &smbios_anchor_len);
+ g_free(mem_array);
+
+ if (smbios_anchor) {
+ fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-tables",
+ smbios_tables, smbios_tables_len);
+ fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-anchor",
+ smbios_anchor, smbios_anchor_len);
+ }
+}
+
+static FWCfgState *bochs_bios_init(AddressSpace *as)
+{
+ FWCfgState *fw_cfg;
uint64_t *numa_fw_cfg;
int i, j;
unsigned int apic_id_limit = pc_apic_id_limit(max_cpus);
- fw_cfg = fw_cfg_init_io(BIOS_CFG_IOPORT);
+ fw_cfg = fw_cfg_init_io_dma(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 4, as);
+
/* FW_CFG_MAX_CPUS is a bit confusing/problematic on x86:
*
* SeaBIOS needs FW_CFG_MAX_CPUS for CPU hotplug, but the CPU hotplug
@@ -748,20 +781,7 @@ static FWCfgState *bochs_bios_init(void)
acpi_tables, acpi_tables_len);
fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, kvm_allows_irq0_override());
- smbios_tables = smbios_get_table_legacy(&smbios_tables_len);
- if (smbios_tables) {
- fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES,
- smbios_tables, smbios_tables_len);
- }
-
- smbios_get_tables(&smbios_tables, &smbios_tables_len,
- &smbios_anchor, &smbios_anchor_len);
- if (smbios_anchor) {
- fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-tables",
- smbios_tables, smbios_tables_len);
- fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-anchor",
- smbios_anchor, smbios_anchor_len);
- }
+ pc_build_smbios(fw_cfg);
fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE,
&e820_reserve, sizeof(e820_reserve));
@@ -809,11 +829,8 @@ static long get_file_size(FILE *f)
return size;
}
-static void load_linux(FWCfgState *fw_cfg,
- const char *kernel_filename,
- const char *initrd_filename,
- const char *kernel_cmdline,
- hwaddr max_ram_size)
+static void load_linux(PCMachineState *pcms,
+ FWCfgState *fw_cfg)
{
uint16_t protocol;
int setup_size, kernel_size, initrd_size = 0, cmdline_size;
@@ -822,6 +839,10 @@ static void load_linux(FWCfgState *fw_cfg,
hwaddr real_addr, prot_addr, cmdline_addr, initrd_addr = 0;
FILE *f;
char *vmode;
+ MachineState *machine = MACHINE(pcms);
+ const char *kernel_filename = machine->kernel_filename;
+ const char *initrd_filename = machine->initrd_filename;
+ const char *kernel_cmdline = machine->kernel_cmdline;
/* Align to 16 bytes as a paranoia measure */
cmdline_size = (strlen(kernel_cmdline)+16) & ~15;
@@ -886,8 +907,8 @@ static void load_linux(FWCfgState *fw_cfg,
initrd_max = 0x37ffffff;
}
- if (initrd_max >= max_ram_size - acpi_data_size) {
- initrd_max = max_ram_size - acpi_data_size - 1;
+ if (initrd_max >= pcms->below_4g_mem_size - acpi_data_size) {
+ initrd_max = pcms->below_4g_mem_size - acpi_data_size - 1;
}
fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr);
@@ -965,6 +986,10 @@ static void load_linux(FWCfgState *fw_cfg,
setup_size = 4;
}
setup_size = (setup_size+1)*512;
+ if (setup_size > kernel_size) {
+ fprintf(stderr, "qemu: invalid kernel header\n");
+ exit(1);
+ }
kernel_size -= setup_size;
setup = g_malloc(setup_size);
@@ -1031,23 +1056,16 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level)
}
static X86CPU *pc_new_cpu(const char *cpu_model, int64_t apic_id,
- DeviceState *icc_bridge, Error **errp)
+ Error **errp)
{
X86CPU *cpu = NULL;
Error *local_err = NULL;
- if (icc_bridge == NULL) {
- error_setg(&local_err, "Invalid icc-bridge value");
- goto out;
- }
-
cpu = cpu_x86_create(cpu_model, &local_err);
if (local_err != NULL) {
goto out;
}
- qdev_set_parent_bus(DEVICE(cpu), qdev_get_child_bus(icc_bridge, "icc"));
-
object_property_set_int(OBJECT(cpu), apic_id, "apic-id", &local_err);
object_property_set_bool(OBJECT(cpu), true, "realized", &local_err);
@@ -1060,12 +1078,10 @@ out:
return cpu;
}
-static const char *current_cpu_model;
-
void pc_hot_add_cpu(const int64_t id, Error **errp)
{
- DeviceState *icc_bridge;
X86CPU *cpu;
+ MachineState *machine = MACHINE(qdev_get_machine());
int64_t apic_id = x86_cpu_apic_id_from_index(id);
Error *local_err = NULL;
@@ -1093,9 +1109,7 @@ void pc_hot_add_cpu(const int64_t id, Error **errp)
return;
}
- icc_bridge = DEVICE(object_resolve_path_type("icc-bridge",
- TYPE_ICC_BRIDGE, NULL));
- cpu = pc_new_cpu(current_cpu_model, apic_id, icc_bridge, &local_err);
+ cpu = pc_new_cpu(machine->cpu_model, apic_id, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -1103,22 +1117,22 @@ void pc_hot_add_cpu(const int64_t id, Error **errp)
object_unref(OBJECT(cpu));
}
-void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge)
+void pc_cpus_init(PCMachineState *pcms)
{
int i;
X86CPU *cpu = NULL;
+ MachineState *machine = MACHINE(pcms);
Error *error = NULL;
unsigned long apic_id_limit;
/* init CPUs */
- if (cpu_model == NULL) {
+ if (machine->cpu_model == NULL) {
#ifdef TARGET_X86_64
- cpu_model = "qemu64";
+ machine->cpu_model = "qemu64";
#else
- cpu_model = "qemu32";
+ machine->cpu_model = "qemu32";
#endif
}
- current_cpu_model = cpu_model;
apic_id_limit = pc_apic_id_limit(max_cpus);
if (apic_id_limit > ACPI_CPU_HOTPLUG_ID_LIMIT) {
@@ -1128,8 +1142,8 @@ void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge)
}
for (i = 0; i < smp_cpus; i++) {
- cpu = pc_new_cpu(cpu_model, x86_cpu_apic_id_from_index(i),
- icc_bridge, &error);
+ cpu = pc_new_cpu(machine->cpu_model, x86_cpu_apic_id_from_index(i),
+ &error);
if (error) {
error_report_err(error);
exit(1);
@@ -1137,13 +1151,6 @@ void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge)
object_unref(OBJECT(cpu));
}
- /* map APIC MMIO area if CPU has APIC */
- if (cpu && cpu->apic_state) {
- /* XXX: what if the base changes? */
- sysbus_mmio_map_overlap(SYS_BUS_DEVICE(icc_bridge), 0,
- APIC_DEFAULT_ADDRESS, 0x1000);
- }
-
/* tell smbios about cpuid version and features */
smbios_set_cpuid(cpu->env.cpuid_version, cpu->env.features[FEAT_1_EDX]);
}
@@ -1189,15 +1196,14 @@ void pc_guest_info_machine_done(Notifier *notifier, void *data)
acpi_setup(&guest_info_state->info);
}
-PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
- ram_addr_t above_4g_mem_size)
+PcGuestInfo *pc_guest_info_init(PCMachineState *pcms)
{
PcGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state);
PcGuestInfo *guest_info = &guest_info_state->info;
int i, j;
- guest_info->ram_size_below_4g = below_4g_mem_size;
- guest_info->ram_size = below_4g_mem_size + above_4g_mem_size;
+ guest_info->ram_size_below_4g = pcms->below_4g_mem_size;
+ guest_info->ram_size = pcms->below_4g_mem_size + pcms->above_4g_mem_size;
guest_info->apic_id_limit = pc_apic_id_limit(max_cpus);
guest_info->apic_xrupt_override = kvm_allows_irq0_override();
guest_info->numa_nodes = nb_numa_nodes;
@@ -1264,22 +1270,18 @@ void pc_acpi_init(const char *default_dsdt)
}
}
-FWCfgState *xen_load_linux(const char *kernel_filename,
- const char *kernel_cmdline,
- const char *initrd_filename,
- ram_addr_t below_4g_mem_size,
+FWCfgState *xen_load_linux(PCMachineState *pcms,
PcGuestInfo *guest_info)
{
int i;
FWCfgState *fw_cfg;
- assert(kernel_filename != NULL);
+ assert(MACHINE(pcms)->kernel_filename != NULL);
fw_cfg = fw_cfg_init_io(BIOS_CFG_IOPORT);
rom_set_fw(fw_cfg);
- load_linux(fw_cfg, kernel_filename, initrd_filename,
- kernel_cmdline, below_4g_mem_size);
+ load_linux(pcms, fw_cfg);
for (i = 0; i < nb_option_roms; i++) {
assert(!strcmp(option_rom[i].name, "linuxboot.bin") ||
!strcmp(option_rom[i].name, "multiboot.bin"));
@@ -1289,10 +1291,8 @@ FWCfgState *xen_load_linux(const char *kernel_filename,
return fw_cfg;
}
-FWCfgState *pc_memory_init(MachineState *machine,
+FWCfgState *pc_memory_init(PCMachineState *pcms,
MemoryRegion *system_memory,
- ram_addr_t below_4g_mem_size,
- ram_addr_t above_4g_mem_size,
MemoryRegion *rom_memory,
MemoryRegion **ram_memory,
PcGuestInfo *guest_info)
@@ -1301,9 +1301,10 @@ FWCfgState *pc_memory_init(MachineState *machine,
MemoryRegion *ram, *option_rom_mr;
MemoryRegion *ram_below_4g, *ram_above_4g;
FWCfgState *fw_cfg;
- PCMachineState *pcms = PC_MACHINE(machine);
+ MachineState *machine = MACHINE(pcms);
- assert(machine->ram_size == below_4g_mem_size + above_4g_mem_size);
+ assert(machine->ram_size == pcms->below_4g_mem_size +
+ pcms->above_4g_mem_size);
linux_boot = (machine->kernel_filename != NULL);
@@ -1317,16 +1318,17 @@ FWCfgState *pc_memory_init(MachineState *machine,
*ram_memory = ram;
ram_below_4g = g_malloc(sizeof(*ram_below_4g));
memory_region_init_alias(ram_below_4g, NULL, "ram-below-4g", ram,
- 0, below_4g_mem_size);
+ 0, pcms->below_4g_mem_size);
memory_region_add_subregion(system_memory, 0, ram_below_4g);
- e820_add_entry(0, below_4g_mem_size, E820_RAM);
- if (above_4g_mem_size > 0) {
+ e820_add_entry(0, pcms->below_4g_mem_size, E820_RAM);
+ if (pcms->above_4g_mem_size > 0) {
ram_above_4g = g_malloc(sizeof(*ram_above_4g));
memory_region_init_alias(ram_above_4g, NULL, "ram-above-4g", ram,
- below_4g_mem_size, above_4g_mem_size);
+ pcms->below_4g_mem_size,
+ pcms->above_4g_mem_size);
memory_region_add_subregion(system_memory, 0x100000000ULL,
ram_above_4g);
- e820_add_entry(0x100000000ULL, above_4g_mem_size, E820_RAM);
+ e820_add_entry(0x100000000ULL, pcms->above_4g_mem_size, E820_RAM);
}
if (!guest_info->has_reserved_memory &&
@@ -1359,7 +1361,7 @@ FWCfgState *pc_memory_init(MachineState *machine,
}
pcms->hotplug_memory.base =
- ROUND_UP(0x100000000ULL + above_4g_mem_size, 1ULL << 30);
+ ROUND_UP(0x100000000ULL + pcms->above_4g_mem_size, 1ULL << 30);
if (pcms->enforce_aligned_dimm) {
/* size hotplug region assuming 1G page max alignment per slot */
@@ -1384,25 +1386,31 @@ FWCfgState *pc_memory_init(MachineState *machine,
option_rom_mr = g_malloc(sizeof(*option_rom_mr));
memory_region_init_ram(option_rom_mr, NULL, "pc.rom", PC_ROM_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(option_rom_mr);
memory_region_add_subregion_overlap(rom_memory,
PC_ROM_MIN_VGA,
option_rom_mr,
1);
- fw_cfg = bochs_bios_init();
+ fw_cfg = bochs_bios_init(&address_space_memory);
+
rom_set_fw(fw_cfg);
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));
+ PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
+ uint64_t res_mem_end = pcms->hotplug_memory.base;
+
+ if (!pcmc->broken_reserved_end) {
+ res_mem_end += memory_region_size(&pcms->hotplug_memory.mr);
+ }
+ *val = cpu_to_le64(ROUND_UP(res_mem_end, 0x1ULL << 30));
fw_cfg_add_file(fw_cfg, "etc/reserved-memory-end", val, sizeof(*val));
}
if (linux_boot) {
- load_linux(fw_cfg, machine->kernel_filename, machine->initrd_filename,
- machine->kernel_cmdline, below_4g_mem_size);
+ load_linux(pcms, fw_cfg);
}
for (i = 0; i < nb_option_roms; i++) {
@@ -1431,15 +1439,6 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus)
return dev;
}
-static void cpu_request_exit(void *opaque, int irq, int level)
-{
- CPUState *cpu = current_cpu;
-
- if (cpu && level) {
- cpu_exit(cpu);
- }
-}
-
static const MemoryRegionOps ioport80_io_ops = {
.write = ioport80_write,
.read = ioport80_read,
@@ -1474,7 +1473,6 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
qemu_irq rtc_irq = NULL;
qemu_irq *a20_line;
ISADevice *i8042, *port92, *vmmouse, *pit = NULL;
- qemu_irq *cpu_exit_irq;
MemoryRegion *ioport80_io = g_new(MemoryRegion, 1);
MemoryRegion *ioportF0_io = g_new(MemoryRegion, 1);
@@ -1551,8 +1549,7 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
port92 = isa_create_simple(isa_bus, "port92");
port92_init(port92, &a20_line[1]);
- cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
- DMA_init(0, cpu_exit_irq);
+ DMA_init(0);
for(i = 0; i < MAX_FD; i++) {
fd[i] = drive_get(IF_FLOPPY, 0, i);
@@ -1798,9 +1795,9 @@ static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v,
return;
}
if (value > (1ULL << 32)) {
- error_set(&error, ERROR_CLASS_GENERIC_ERROR,
- "Machine option 'max-ram-below-4g=%"PRIu64
- "' expects size less than or equal to 4G", value);
+ error_setg(&error,
+ "Machine option 'max-ram-below-4g=%"PRIu64
+ "' expects size less than or equal to 4G", value);
error_propagate(errp, error);
return;
}
@@ -1886,47 +1883,66 @@ static void pc_machine_initfn(Object *obj)
object_property_add(obj, PC_MACHINE_MEMHP_REGION_SIZE, "int",
pc_machine_get_hotplug_memory_region_size,
- NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, &error_abort);
pcms->max_ram_below_4g = 1ULL << 32; /* 4G */
object_property_add(obj, PC_MACHINE_MAX_RAM_BELOW_4G, "size",
pc_machine_get_max_ram_below_4g,
pc_machine_set_max_ram_below_4g,
- NULL, NULL, NULL);
+ NULL, NULL, &error_abort);
object_property_set_description(obj, PC_MACHINE_MAX_RAM_BELOW_4G,
"Maximum ram below the 4G boundary (32bit boundary)",
- NULL);
+ &error_abort);
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);
+ NULL, NULL, &error_abort);
object_property_set_description(obj, PC_MACHINE_SMM,
"Enable SMM (pc & q35)",
- NULL);
+ &error_abort);
pcms->vmport = ON_OFF_AUTO_AUTO;
object_property_add(obj, PC_MACHINE_VMPORT, "OnOffAuto",
pc_machine_get_vmport,
pc_machine_set_vmport,
- NULL, NULL, NULL);
+ NULL, NULL, &error_abort);
object_property_set_description(obj, PC_MACHINE_VMPORT,
"Enable vmport (pc & q35)",
- NULL);
+ &error_abort);
pcms->enforce_aligned_dimm = true;
object_property_add_bool(obj, PC_MACHINE_ENFORCE_ALIGNED_DIMM,
pc_machine_get_aligned_dimm,
- NULL, NULL);
+ NULL, &error_abort);
+}
+
+static void pc_machine_reset(void)
+{
+ CPUState *cs;
+ X86CPU *cpu;
+
+ qemu_devices_reset();
+
+ /* Reset APIC after devices have been reset to cancel
+ * any changes that qemu_devices_reset() might have done.
+ */
+ CPU_FOREACH(cs) {
+ cpu = X86_CPU(cs);
+
+ if (cpu->apic_state) {
+ device_reset(cpu->apic_state);
+ }
+ }
}
static unsigned pc_cpu_index_to_socket_id(unsigned cpu_index)
{
- unsigned pkg_id, core_id, smt_id;
+ X86CPUTopoInfo topo;
x86_topo_ids_from_idx(smp_cores, smp_threads, cpu_index,
- &pkg_id, &core_id, &smt_id);
- return pkg_id;
+ &topo);
+ return topo.pkg_id;
}
static void pc_machine_class_init(ObjectClass *oc, void *data)
@@ -1938,6 +1954,10 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
pcmc->get_hotplug_handler = mc->get_hotplug_handler;
mc->get_hotplug_handler = pc_get_hotpug_handler;
mc->cpu_index_to_socket_id = pc_cpu_index_to_socket_id;
+ mc->default_boot_order = "cad";
+ mc->hot_add_cpu = pc_hot_add_cpu;
+ mc->max_cpus = 255;
+ mc->reset = pc_machine_reset;
hc->plug = pc_machine_device_plug_cb;
hc->unplug_request = pc_machine_device_unplug_request_cb;
hc->unplug = pc_machine_device_unplug_cb;
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index a896624f8..2e41efe1b 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -28,7 +28,7 @@
#include "hw/loader.h"
#include "hw/i386/pc.h"
#include "hw/i386/apic.h"
-#include "hw/i386/smbios.h"
+#include "hw/smbios/smbios.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_ids.h"
#include "hw/usb.h"
@@ -39,7 +39,6 @@
#include "hw/kvm/clock.h"
#include "sysemu/sysemu.h"
#include "hw/sysbus.h"
-#include "hw/cpu/icc_bus.h"
#include "sysemu/arch_init.h"
#include "sysemu/block-backend.h"
#include "hw/i2c/smbus.h"
@@ -50,7 +49,8 @@
#include "cpu.h"
#include "qemu/error-report.h"
#ifdef CONFIG_XEN
-# include <xen/hvm/hvm_info_table.h>
+#include <xen/hvm/hvm_info_table.h>
+#include "hw/xen/xen_pt.h"
#endif
#include "migration/migration.h"
@@ -76,13 +76,13 @@ static bool has_reserved_memory = true;
static bool kvmclock_enabled = true;
/* PC hardware initialisation */
-static void pc_init1(MachineState *machine)
+static void pc_init1(MachineState *machine,
+ const char *host_type, const char *pci_type)
{
- PCMachineState *pc_machine = PC_MACHINE(machine);
+ PCMachineState *pcms = PC_MACHINE(machine);
MemoryRegion *system_memory = get_system_memory();
MemoryRegion *system_io = get_system_io();
int i;
- ram_addr_t below_4g_mem_size, above_4g_mem_size;
PCIBus *pci_bus;
ISABus *isa_bus;
PCII440FXState *i440fx_state;
@@ -97,7 +97,6 @@ static void pc_init1(MachineState *machine)
MemoryRegion *ram_memory;
MemoryRegion *pci_memory;
MemoryRegion *rom_memory;
- DeviceState *icc_bridge;
PcGuestInfo *guest_info;
ram_addr_t lowmem;
@@ -117,35 +116,30 @@ static void pc_init1(MachineState *machine)
/* Handle the machine opt max-ram-below-4g. It is basically doing
* min(qemu limit, user limit).
*/
- if (lowmem > pc_machine->max_ram_below_4g) {
- lowmem = pc_machine->max_ram_below_4g;
+ if (lowmem > pcms->max_ram_below_4g) {
+ lowmem = pcms->max_ram_below_4g;
if (machine->ram_size - lowmem > lowmem &&
lowmem & ((1ULL << 30) - 1)) {
error_report("Warning: Large machine and max_ram_below_4g(%"PRIu64
") not a multiple of 1G; possible bad performance.",
- pc_machine->max_ram_below_4g);
+ pcms->max_ram_below_4g);
}
}
if (machine->ram_size >= lowmem) {
- above_4g_mem_size = machine->ram_size - lowmem;
- below_4g_mem_size = lowmem;
+ pcms->above_4g_mem_size = machine->ram_size - lowmem;
+ pcms->below_4g_mem_size = lowmem;
} else {
- above_4g_mem_size = 0;
- below_4g_mem_size = machine->ram_size;
+ pcms->above_4g_mem_size = 0;
+ pcms->below_4g_mem_size = machine->ram_size;
}
- if (xen_enabled() && xen_hvm_init(&below_4g_mem_size, &above_4g_mem_size,
- &ram_memory) != 0) {
+ if (xen_enabled() && xen_hvm_init(pcms, &ram_memory) != 0) {
fprintf(stderr, "xen hardware virtual machine initialisation failed\n");
exit(1);
}
- icc_bridge = qdev_create(NULL, TYPE_ICC_BRIDGE);
- object_property_add_child(qdev_get_machine(), "icc-bridge",
- OBJECT(icc_bridge), NULL);
-
- pc_cpus_init(machine->cpu_model, icc_bridge);
+ pc_cpus_init(pcms);
if (kvm_enabled() && kvmclock_enabled) {
kvmclock_create();
@@ -160,7 +154,7 @@ static void pc_init1(MachineState *machine)
rom_memory = system_memory;
}
- guest_info = pc_guest_info_init(below_4g_mem_size, above_4g_mem_size);
+ guest_info = pc_guest_info_init(pcms);
guest_info->has_acpi_build = has_acpi_build;
guest_info->legacy_acpi_table_size = legacy_acpi_table_size;
@@ -173,21 +167,17 @@ static void pc_init1(MachineState *machine)
MachineClass *mc = MACHINE_GET_CLASS(machine);
/* These values are guest ABI, do not change */
smbios_set_defaults("QEMU", "Standard PC (i440FX + PIIX, 1996)",
- mc->name, smbios_legacy_mode, smbios_uuid_encoded);
+ mc->name, smbios_legacy_mode, smbios_uuid_encoded,
+ SMBIOS_ENTRY_POINT_21);
}
/* allocate ram and load rom/bios */
if (!xen_enabled()) {
- pc_memory_init(machine, system_memory,
- below_4g_mem_size, above_4g_mem_size,
+ pc_memory_init(pcms, system_memory,
rom_memory, &ram_memory, guest_info);
} else if (machine->kernel_filename != NULL) {
/* For xen HVM direct kernel boot, load linux here */
- xen_load_linux(machine->kernel_filename,
- machine->kernel_cmdline,
- machine->initrd_filename,
- below_4g_mem_size,
- guest_info);
+ xen_load_linux(pcms, guest_info);
}
gsi_state = g_malloc0(sizeof(*gsi_state));
@@ -200,10 +190,12 @@ static void pc_init1(MachineState *machine)
}
if (pci_enabled) {
- pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, &isa_bus, gsi,
+ pci_bus = i440fx_init(host_type,
+ pci_type,
+ &i440fx_state, &piix3_devfn, &isa_bus, gsi,
system_memory, system_io, machine->ram_size,
- below_4g_mem_size,
- above_4g_mem_size,
+ pcms->below_4g_mem_size,
+ pcms->above_4g_mem_size,
pci_memory, ram_memory);
} else {
pci_bus = NULL;
@@ -228,20 +220,19 @@ static void pc_init1(MachineState *machine)
if (pci_enabled) {
ioapic_init_gsi(gsi_state, "i440fx");
}
- qdev_init_nofail(icc_bridge);
pc_register_ferr_irq(gsi[13]);
pc_vga_init(isa_bus, pci_enabled ? pci_bus : NULL);
- assert(pc_machine->vmport != ON_OFF_AUTO_MAX);
- if (pc_machine->vmport == ON_OFF_AUTO_AUTO) {
- pc_machine->vmport = xen_enabled() ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON;
+ assert(pcms->vmport != ON_OFF_AUTO_MAX);
+ if (pcms->vmport == ON_OFF_AUTO_AUTO) {
+ pcms->vmport = xen_enabled() ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON;
}
/* init basic PC hardware */
pc_basic_device_init(isa_bus, gsi, &rtc_state, true,
- (pc_machine->vmport != ON_OFF_AUTO_ON), 0x4);
+ (pcms->vmport != ON_OFF_AUTO_ON), 0x4);
pc_nic_init(isa_bus, pci_bus);
@@ -271,8 +262,7 @@ static void pc_init1(MachineState *machine)
}
}
- pc_cmos_init(below_4g_mem_size, above_4g_mem_size, machine->boot_order,
- machine, idebus[0], idebus[1], rtc_state);
+ pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state);
if (pci_enabled && usb_enabled()) {
pci_create_simple(pci_bus, piix3_devfn + 2, "piix3-usb-uhci");
@@ -286,13 +276,13 @@ static void pc_init1(MachineState *machine)
/* TODO: Populate SPD eeprom data. */
smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100,
gsi[9], smi_irq,
- pc_machine_is_smm_enabled(pc_machine),
+ pc_machine_is_smm_enabled(pcms),
&piix4_pm);
smbus_eeprom_init(smbus, 8, NULL, 0);
object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP,
TYPE_HOTPLUG_HANDLER,
- (Object **)&pc_machine->acpi_dev,
+ (Object **)&pcms->acpi_dev,
object_property_allow_set_link,
OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort);
object_property_set_link(OBJECT(machine), OBJECT(piix4_pm),
@@ -304,6 +294,13 @@ static void pc_init1(MachineState *machine)
}
}
+/* Looking for a pc_compat_2_4() function? It doesn't exist.
+ * pc_compat_*() functions that run on machine-init time and
+ * change global QEMU state are deprecated. Please don't create
+ * one, and implement any pc-*-2.4 (and newer) compat code in
+ * HW_COMPAT_*, PC_COMPAT_*, or * pc_*_machine_options().
+ */
+
static void pc_compat_2_3(MachineState *machine)
{
PCMachineState *pcms = PC_MACHINE(machine);
@@ -319,24 +316,6 @@ 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);
- x86_cpu_compat_set_features("Conroe", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Penryn", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Nehalem", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Westmere", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("SandyBridge", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Haswell", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Broadwell", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Opteron_G1", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Opteron_G2", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Opteron_G3", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Opteron_G4", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Opteron_G5", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Haswell", FEAT_1_ECX, 0, CPUID_EXT_F16C);
- x86_cpu_compat_set_features("Haswell", FEAT_1_ECX, 0, CPUID_EXT_RDRAND);
- x86_cpu_compat_set_features("Broadwell", FEAT_1_ECX, 0, CPUID_EXT_F16C);
- x86_cpu_compat_set_features("Broadwell", FEAT_1_ECX, 0, CPUID_EXT_RDRAND);
machine->suppress_vmdesc = true;
}
@@ -346,9 +325,7 @@ static void pc_compat_2_1(MachineState *machine)
pc_compat_2_2(machine);
smbios_uuid_encoded = false;
- x86_cpu_compat_set_features("coreduo", FEAT_1_ECX, CPUID_EXT_VMX, 0);
- x86_cpu_compat_set_features("core2duo", FEAT_1_ECX, CPUID_EXT_VMX, 0);
- x86_cpu_compat_kvm_no_autodisable(FEAT_8000_0001_ECX, CPUID_EXT3_SVM);
+ x86_cpu_change_kvm_default("svm", NULL);
pcms->enforce_aligned_dimm = false;
}
@@ -384,7 +361,7 @@ static void pc_compat_1_7(MachineState *machine)
gigabyte_align = false;
option_rom_has_mr = true;
legacy_acpi_table_size = 6414;
- x86_cpu_compat_kvm_no_autoenable(FEAT_1_ECX, CPUID_EXT_X2APIC);
+ x86_cpu_change_kvm_default("x2apic", NULL);
}
static void pc_compat_1_6(MachineState *machine)
@@ -402,8 +379,6 @@ static void pc_compat_1_5(MachineState *machine)
static void pc_compat_1_4(MachineState *machine)
{
pc_compat_1_5(machine);
- x86_cpu_compat_set_features("n270", FEAT_1_ECX, 0, CPUID_EXT_MOVBE);
- x86_cpu_compat_set_features("Westmere", FEAT_1_ECX, 0, CPUID_EXT_PCLMULQDQ);
}
static void pc_compat_1_3(MachineState *machine)
@@ -416,7 +391,7 @@ static void pc_compat_1_3(MachineState *machine)
static void pc_compat_1_2(MachineState *machine)
{
pc_compat_1_3(machine);
- x86_cpu_compat_kvm_no_autoenable(FEAT_KVM, 1 << KVM_FEATURE_PV_EOI);
+ x86_cpu_change_kvm_default("kvm-pv-eoi", NULL);
}
/* PC compat function for pc-0.10 to pc-0.13 */
@@ -439,17 +414,32 @@ static void pc_init_isa(MachineState *machine)
if (!machine->cpu_model) {
machine->cpu_model = "486";
}
- x86_cpu_compat_kvm_no_autoenable(FEAT_KVM, 1 << KVM_FEATURE_PV_EOI);
+ x86_cpu_change_kvm_default("kvm-pv-eoi", NULL);
enable_compat_apic_id_mode();
- pc_init1(machine);
+ pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, TYPE_I440FX_PCI_DEVICE);
}
#ifdef CONFIG_XEN
+static void pc_xen_hvm_init_pci(MachineState *machine)
+{
+ const char *pci_type = has_igd_gfx_passthru ?
+ TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE : TYPE_I440FX_PCI_DEVICE;
+
+ pc_init1(machine,
+ TYPE_I440FX_PCI_HOST_BRIDGE,
+ pci_type);
+}
+
static void pc_xen_hvm_init(MachineState *machine)
{
PCIBus *bus;
- pc_init1(machine);
+ if (!xen_enabled()) {
+ error_report("xenfv machine requires the xen accelerator");
+ exit(1);
+ }
+
+ pc_xen_hvm_init_pci(machine);
bus = pci_find_primary_bus();
if (bus != NULL) {
@@ -465,27 +455,42 @@ static void pc_xen_hvm_init(MachineState *machine)
if (compat) { \
compat(machine); \
} \
- pc_init1(machine); \
+ pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \
+ TYPE_I440FX_PCI_DEVICE); \
} \
DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn)
static void pc_i440fx_machine_options(MachineClass *m)
{
- pc_default_machine_options(m);
m->family = "pc_piix";
m->desc = "Standard PC (i440FX + PIIX, 1996)";
m->hot_add_cpu = pc_hot_add_cpu;
+ m->default_machine_opts = "firmware=bios-256k.bin";
+ m->default_display = "std";
}
-static void pc_i440fx_2_4_machine_options(MachineClass *m)
+static void pc_i440fx_2_5_machine_options(MachineClass *m)
{
pc_i440fx_machine_options(m);
- m->default_machine_opts = "firmware=bios-256k.bin";
- m->default_display = "std";
m->alias = "pc";
m->is_default = 1;
}
+DEFINE_I440FX_MACHINE(v2_5, "pc-i440fx-2.5", NULL,
+ pc_i440fx_2_5_machine_options);
+
+
+static void pc_i440fx_2_4_machine_options(MachineClass *m)
+{
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
+ pc_i440fx_2_5_machine_options(m);
+ m->hw_version = "2.4.0";
+ m->alias = NULL;
+ m->is_default = 0;
+ pcmc->broken_reserved_end = true;
+ SET_MACHINE_COMPAT(m, PC_COMPAT_2_4);
+}
+
DEFINE_I440FX_MACHINE(v2_4, "pc-i440fx-2.4", NULL,
pc_i440fx_2_4_machine_options)
@@ -493,6 +498,7 @@ DEFINE_I440FX_MACHINE(v2_4, "pc-i440fx-2.4", NULL,
static void pc_i440fx_2_3_machine_options(MachineClass *m)
{
pc_i440fx_2_4_machine_options(m);
+ m->hw_version = "2.3.0";
m->alias = NULL;
m->is_default = 0;
SET_MACHINE_COMPAT(m, PC_COMPAT_2_3);
@@ -505,6 +511,7 @@ DEFINE_I440FX_MACHINE(v2_3, "pc-i440fx-2.3", pc_compat_2_3,
static void pc_i440fx_2_2_machine_options(MachineClass *m)
{
pc_i440fx_2_3_machine_options(m);
+ m->hw_version = "2.2.0";
SET_MACHINE_COMPAT(m, PC_COMPAT_2_2);
}
@@ -515,6 +522,7 @@ DEFINE_I440FX_MACHINE(v2_2, "pc-i440fx-2.2", pc_compat_2_2,
static void pc_i440fx_2_1_machine_options(MachineClass *m)
{
pc_i440fx_2_2_machine_options(m);
+ m->hw_version = "2.1.0";
m->default_display = NULL;
SET_MACHINE_COMPAT(m, PC_COMPAT_2_1);
}
@@ -527,6 +535,7 @@ DEFINE_I440FX_MACHINE(v2_1, "pc-i440fx-2.1", pc_compat_2_1,
static void pc_i440fx_2_0_machine_options(MachineClass *m)
{
pc_i440fx_2_1_machine_options(m);
+ m->hw_version = "2.0.0";
SET_MACHINE_COMPAT(m, PC_COMPAT_2_0);
}
@@ -537,6 +546,7 @@ DEFINE_I440FX_MACHINE(v2_0, "pc-i440fx-2.0", pc_compat_2_0,
static void pc_i440fx_1_7_machine_options(MachineClass *m)
{
pc_i440fx_2_0_machine_options(m);
+ m->hw_version = "1.7.0";
m->default_machine_opts = NULL;
SET_MACHINE_COMPAT(m, PC_COMPAT_1_7);
}
@@ -548,6 +558,7 @@ DEFINE_I440FX_MACHINE(v1_7, "pc-i440fx-1.7", pc_compat_1_7,
static void pc_i440fx_1_6_machine_options(MachineClass *m)
{
pc_i440fx_1_7_machine_options(m);
+ m->hw_version = "1.6.0";
SET_MACHINE_COMPAT(m, PC_COMPAT_1_6);
}
@@ -558,6 +569,7 @@ DEFINE_I440FX_MACHINE(v1_6, "pc-i440fx-1.6", pc_compat_1_6,
static void pc_i440fx_1_5_machine_options(MachineClass *m)
{
pc_i440fx_1_6_machine_options(m);
+ m->hw_version = "1.5.0";
SET_MACHINE_COMPAT(m, PC_COMPAT_1_5);
}
@@ -568,6 +580,7 @@ DEFINE_I440FX_MACHINE(v1_5, "pc-i440fx-1.5", pc_compat_1_5,
static void pc_i440fx_1_4_machine_options(MachineClass *m)
{
pc_i440fx_1_5_machine_options(m);
+ m->hw_version = "1.4.0";
m->hot_add_cpu = NULL;
SET_MACHINE_COMPAT(m, PC_COMPAT_1_4);
}
@@ -600,6 +613,7 @@ DEFINE_I440FX_MACHINE(v1_4, "pc-i440fx-1.4", pc_compat_1_4,
static void pc_i440fx_1_3_machine_options(MachineClass *m)
{
pc_i440fx_1_4_machine_options(m);
+ m->hw_version = "1.3.0";
SET_MACHINE_COMPAT(m, PC_COMPAT_1_3);
}
@@ -638,6 +652,7 @@ DEFINE_I440FX_MACHINE(v1_3, "pc-1.3", pc_compat_1_3,
static void pc_i440fx_1_2_machine_options(MachineClass *m)
{
pc_i440fx_1_3_machine_options(m);
+ m->hw_version = "1.2.0";
SET_MACHINE_COMPAT(m, PC_COMPAT_1_2);
}
@@ -680,6 +695,7 @@ DEFINE_I440FX_MACHINE(v1_2, "pc-1.2", pc_compat_1_2,
static void pc_i440fx_1_1_machine_options(MachineClass *m)
{
pc_i440fx_1_2_machine_options(m);
+ m->hw_version = "1.1.0";
SET_MACHINE_COMPAT(m, PC_COMPAT_1_1);
}
@@ -906,10 +922,121 @@ static void pc_i440fx_0_10_machine_options(MachineClass *m)
DEFINE_I440FX_MACHINE(v0_10, "pc-0.10", pc_compat_0_13,
pc_i440fx_0_10_machine_options);
+typedef struct {
+ uint16_t gpu_device_id;
+ uint16_t pch_device_id;
+ uint8_t pch_revision_id;
+} IGDDeviceIDInfo;
+
+/* In real world different GPU should have different PCH. But actually
+ * the different PCH DIDs likely map to different PCH SKUs. We do the
+ * same thing for the GPU. For PCH, the different SKUs are going to be
+ * all the same silicon design and implementation, just different
+ * features turn on and off with fuses. The SW interfaces should be
+ * consistent across all SKUs in a given family (eg LPT). But just same
+ * features may not be supported.
+ *
+ * Most of these different PCH features probably don't matter to the
+ * Gfx driver, but obviously any difference in display port connections
+ * will so it should be fine with any PCH in case of passthrough.
+ *
+ * So currently use one PCH version, 0x8c4e, to cover all HSW(Haswell)
+ * scenarios, 0x9cc3 for BDW(Broadwell).
+ */
+static const IGDDeviceIDInfo igd_combo_id_infos[] = {
+ /* HSW Classic */
+ {0x0402, 0x8c4e, 0x04}, /* HSWGT1D, HSWD_w7 */
+ {0x0406, 0x8c4e, 0x04}, /* HSWGT1M, HSWM_w7 */
+ {0x0412, 0x8c4e, 0x04}, /* HSWGT2D, HSWD_w7 */
+ {0x0416, 0x8c4e, 0x04}, /* HSWGT2M, HSWM_w7 */
+ {0x041E, 0x8c4e, 0x04}, /* HSWGT15D, HSWD_w7 */
+ /* HSW ULT */
+ {0x0A06, 0x8c4e, 0x04}, /* HSWGT1UT, HSWM_w7 */
+ {0x0A16, 0x8c4e, 0x04}, /* HSWGT2UT, HSWM_w7 */
+ {0x0A26, 0x8c4e, 0x06}, /* HSWGT3UT, HSWM_w7 */
+ {0x0A2E, 0x8c4e, 0x04}, /* HSWGT3UT28W, HSWM_w7 */
+ {0x0A1E, 0x8c4e, 0x04}, /* HSWGT2UX, HSWM_w7 */
+ {0x0A0E, 0x8c4e, 0x04}, /* HSWGT1ULX, HSWM_w7 */
+ /* HSW CRW */
+ {0x0D26, 0x8c4e, 0x04}, /* HSWGT3CW, HSWM_w7 */
+ {0x0D22, 0x8c4e, 0x04}, /* HSWGT3CWDT, HSWD_w7 */
+ /* HSW Server */
+ {0x041A, 0x8c4e, 0x04}, /* HSWSVGT2, HSWD_w7 */
+ /* HSW SRVR */
+ {0x040A, 0x8c4e, 0x04}, /* HSWSVGT1, HSWD_w7 */
+ /* BSW */
+ {0x1606, 0x9cc3, 0x03}, /* BDWULTGT1, BDWM_w7 */
+ {0x1616, 0x9cc3, 0x03}, /* BDWULTGT2, BDWM_w7 */
+ {0x1626, 0x9cc3, 0x03}, /* BDWULTGT3, BDWM_w7 */
+ {0x160E, 0x9cc3, 0x03}, /* BDWULXGT1, BDWM_w7 */
+ {0x161E, 0x9cc3, 0x03}, /* BDWULXGT2, BDWM_w7 */
+ {0x1602, 0x9cc3, 0x03}, /* BDWHALOGT1, BDWM_w7 */
+ {0x1612, 0x9cc3, 0x03}, /* BDWHALOGT2, BDWM_w7 */
+ {0x1622, 0x9cc3, 0x03}, /* BDWHALOGT3, BDWM_w7 */
+ {0x162B, 0x9cc3, 0x03}, /* BDWHALO28W, BDWM_w7 */
+ {0x162A, 0x9cc3, 0x03}, /* BDWGT3WRKS, BDWM_w7 */
+ {0x162D, 0x9cc3, 0x03}, /* BDWGT3SRVR, BDWM_w7 */
+};
+
+static void isa_bridge_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ dc->desc = "ISA bridge faked to support IGD PT";
+ k->vendor_id = PCI_VENDOR_ID_INTEL;
+ k->class_id = PCI_CLASS_BRIDGE_ISA;
+};
+
+static TypeInfo isa_bridge_info = {
+ .name = "igd-passthrough-isa-bridge",
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PCIDevice),
+ .class_init = isa_bridge_class_init,
+};
+
+static void pt_graphics_register_types(void)
+{
+ type_register_static(&isa_bridge_info);
+}
+type_init(pt_graphics_register_types)
+
+void igd_passthrough_isa_bridge_create(PCIBus *bus, uint16_t gpu_dev_id)
+{
+ struct PCIDevice *bridge_dev;
+ int i, num;
+ uint16_t pch_dev_id = 0xffff;
+ uint8_t pch_rev_id;
+
+ num = ARRAY_SIZE(igd_combo_id_infos);
+ for (i = 0; i < num; i++) {
+ if (gpu_dev_id == igd_combo_id_infos[i].gpu_device_id) {
+ pch_dev_id = igd_combo_id_infos[i].pch_device_id;
+ pch_rev_id = igd_combo_id_infos[i].pch_revision_id;
+ }
+ }
+
+ if (pch_dev_id == 0xffff) {
+ return;
+ }
+
+ /* Currently IGD drivers always need to access PCH by 1f.0. */
+ bridge_dev = pci_create_simple(bus, PCI_DEVFN(0x1f, 0),
+ "igd-passthrough-isa-bridge");
+
+ /*
+ * Note that vendor id is always PCI_VENDOR_ID_INTEL.
+ */
+ if (!bridge_dev) {
+ fprintf(stderr, "set igd-passthrough-isa-bridge failed!\n");
+ return;
+ }
+ pci_config_set_device_id(bridge_dev->config, pch_dev_id);
+ pci_config_set_revision(bridge_dev->config, pch_rev_id);
+}
static void isapc_machine_options(MachineClass *m)
{
- pc_common_machine_options(m);
m->desc = "ISA-only PC";
m->max_cpus = 1;
}
@@ -921,7 +1048,6 @@ DEFINE_PC_MACHINE(isapc, "isapc", pc_init_isa,
#ifdef CONFIG_XEN
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";
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 974aead5a..133bc68fa 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -39,11 +39,10 @@
#include "hw/pci-host/q35.h"
#include "exec/address-spaces.h"
#include "hw/i386/ich9.h"
-#include "hw/i386/smbios.h"
+#include "hw/smbios/smbios.h"
#include "hw/ide/pci.h"
#include "hw/ide/ahci.h"
#include "hw/usb.h"
-#include "hw/cpu/icc_bus.h"
#include "qemu/error-report.h"
#include "migration/migration.h"
@@ -65,8 +64,7 @@ static bool has_reserved_memory = true;
/* PC hardware initialisation */
static void pc_q35_init(MachineState *machine)
{
- PCMachineState *pc_machine = PC_MACHINE(machine);
- ram_addr_t below_4g_mem_size, above_4g_mem_size;
+ PCMachineState *pcms = PC_MACHINE(machine);
Q35PCIHost *q35_host;
PCIHostState *phb;
PCIBus *host_bus;
@@ -84,7 +82,6 @@ static void pc_q35_init(MachineState *machine)
int i;
ICH9LPCState *ich9_lpc;
PCIDevice *ahci;
- DeviceState *icc_bridge;
PcGuestInfo *guest_info;
ram_addr_t lowmem;
DriveInfo *hd[MAX_SATA_PORTS];
@@ -108,35 +105,30 @@ static void pc_q35_init(MachineState *machine)
/* Handle the machine opt max-ram-below-4g. It is basically doing
* min(qemu limit, user limit).
*/
- if (lowmem > pc_machine->max_ram_below_4g) {
- lowmem = pc_machine->max_ram_below_4g;
+ if (lowmem > pcms->max_ram_below_4g) {
+ lowmem = pcms->max_ram_below_4g;
if (machine->ram_size - lowmem > lowmem &&
lowmem & ((1ULL << 30) - 1)) {
error_report("Warning: Large machine and max_ram_below_4g(%"PRIu64
") not a multiple of 1G; possible bad performance.",
- pc_machine->max_ram_below_4g);
+ pcms->max_ram_below_4g);
}
}
if (machine->ram_size >= lowmem) {
- above_4g_mem_size = machine->ram_size - lowmem;
- below_4g_mem_size = lowmem;
+ pcms->above_4g_mem_size = machine->ram_size - lowmem;
+ pcms->below_4g_mem_size = lowmem;
} else {
- above_4g_mem_size = 0;
- below_4g_mem_size = machine->ram_size;
+ pcms->above_4g_mem_size = 0;
+ pcms->below_4g_mem_size = machine->ram_size;
}
- if (xen_enabled() && xen_hvm_init(&below_4g_mem_size, &above_4g_mem_size,
- &ram_memory) != 0) {
+ if (xen_enabled() && xen_hvm_init(pcms, &ram_memory) != 0) {
fprintf(stderr, "xen hardware virtual machine initialisation failed\n");
exit(1);
}
- icc_bridge = qdev_create(NULL, TYPE_ICC_BRIDGE);
- object_property_add_child(qdev_get_machine(), "icc-bridge",
- OBJECT(icc_bridge), NULL);
-
- pc_cpus_init(machine->cpu_model, icc_bridge);
+ pc_cpus_init(pcms);
pc_acpi_init("q35-acpi-dsdt.aml");
kvmclock_create();
@@ -151,7 +143,7 @@ static void pc_q35_init(MachineState *machine)
rom_memory = get_system_memory();
}
- guest_info = pc_guest_info_init(below_4g_mem_size, above_4g_mem_size);
+ guest_info = pc_guest_info_init(pcms);
guest_info->isapc_ram_fw = false;
guest_info->has_acpi_build = has_acpi_build;
guest_info->has_reserved_memory = has_reserved_memory;
@@ -165,13 +157,13 @@ static void pc_q35_init(MachineState *machine)
if (smbios_defaults) {
/* 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);
+ mc->name, smbios_legacy_mode, smbios_uuid_encoded,
+ SMBIOS_ENTRY_POINT_21);
}
/* allocate ram and load rom/bios */
if (!xen_enabled()) {
- pc_memory_init(machine, get_system_memory(),
- below_4g_mem_size, above_4g_mem_size,
+ pc_memory_init(pcms, get_system_memory(),
rom_memory, &ram_memory, guest_info);
}
@@ -193,8 +185,8 @@ static void pc_q35_init(MachineState *machine)
q35_host->mch.pci_address_space = pci_memory;
q35_host->mch.system_memory = get_system_memory();
q35_host->mch.address_space_io = get_system_io();
- q35_host->mch.below_4g_mem_size = below_4g_mem_size;
- q35_host->mch.above_4g_mem_size = above_4g_mem_size;
+ q35_host->mch.below_4g_mem_size = pcms->below_4g_mem_size;
+ q35_host->mch.above_4g_mem_size = pcms->above_4g_mem_size;
q35_host->mch.guest_info = guest_info;
/* pci */
qdev_init_nofail(DEVICE(q35_host));
@@ -207,7 +199,7 @@ static void pc_q35_init(MachineState *machine)
object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP,
TYPE_HOTPLUG_HANDLER,
- (Object **)&pc_machine->acpi_dev,
+ (Object **)&pcms->acpi_dev,
object_property_allow_set_link,
OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort);
object_property_set_link(OBJECT(machine), OBJECT(lpc),
@@ -238,21 +230,20 @@ static void pc_q35_init(MachineState *machine)
if (pci_enabled) {
ioapic_init_gsi(gsi_state, "q35");
}
- qdev_init_nofail(icc_bridge);
pc_register_ferr_irq(gsi[13]);
- assert(pc_machine->vmport != ON_OFF_AUTO_MAX);
- if (pc_machine->vmport == ON_OFF_AUTO_AUTO) {
- pc_machine->vmport = xen_enabled() ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON;
+ assert(pcms->vmport != ON_OFF_AUTO_MAX);
+ if (pcms->vmport == ON_OFF_AUTO_AUTO) {
+ pcms->vmport = xen_enabled() ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON;
}
/* init basic PC hardware */
pc_basic_device_init(isa_bus, gsi, &rtc_state, !mc->no_floppy,
- (pc_machine->vmport != ON_OFF_AUTO_ON), 0xff0104);
+ (pcms->vmport != ON_OFF_AUTO_ON), 0xff0104);
/* connect pm stuff to lpc */
- ich9_lpc_pm_init(lpc, pc_machine_is_smm_enabled(pc_machine), !mc->no_tco);
+ ich9_lpc_pm_init(lpc, pc_machine_is_smm_enabled(pcms), !mc->no_tco);
/* ahci and SATA device, for q35 1 ahci controller is built-in */
ahci = pci_create_simple_multifunction(host_bus,
@@ -276,8 +267,7 @@ static void pc_q35_init(MachineState *machine)
0xb100),
8, NULL, 0);
- pc_cmos_init(below_4g_mem_size, above_4g_mem_size, machine->boot_order,
- machine, idebus[0], idebus[1], rtc_state);
+ pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state);
/* the rest devices to which pci devfn is automatically assigned */
pc_vga_init(isa_bus, host_bus);
@@ -287,6 +277,13 @@ static void pc_q35_init(MachineState *machine)
}
}
+/* Looking for a pc_compat_2_4() function? It doesn't exist.
+ * pc_compat_*() functions that run on machine-init time and
+ * change global QEMU state are deprecated. Please don't create
+ * one, and implement any pc-*-2.4 (and newer) compat code in
+ * HW_COMPAT_*, PC_COMPAT_*, or * pc_*_machine_options().
+ */
+
static void pc_compat_2_3(MachineState *machine)
{
PCMachineState *pcms = PC_MACHINE(machine);
@@ -302,24 +299,6 @@ 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);
- x86_cpu_compat_set_features("Conroe", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Penryn", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Nehalem", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Westmere", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("SandyBridge", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Haswell", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Broadwell", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Opteron_G1", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Opteron_G2", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Opteron_G3", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Opteron_G4", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Opteron_G5", FEAT_1_EDX, 0, CPUID_VME);
- x86_cpu_compat_set_features("Haswell", FEAT_1_ECX, 0, CPUID_EXT_F16C);
- x86_cpu_compat_set_features("Haswell", FEAT_1_ECX, 0, CPUID_EXT_RDRAND);
- x86_cpu_compat_set_features("Broadwell", FEAT_1_ECX, 0, CPUID_EXT_F16C);
- x86_cpu_compat_set_features("Broadwell", FEAT_1_ECX, 0, CPUID_EXT_RDRAND);
machine->suppress_vmdesc = true;
}
@@ -330,9 +309,7 @@ static void pc_compat_2_1(MachineState *machine)
pc_compat_2_2(machine);
pcms->enforce_aligned_dimm = false;
smbios_uuid_encoded = false;
- x86_cpu_compat_set_features("coreduo", FEAT_1_ECX, CPUID_EXT_VMX, 0);
- x86_cpu_compat_set_features("core2duo", FEAT_1_ECX, CPUID_EXT_VMX, 0);
- x86_cpu_compat_kvm_no_autodisable(FEAT_8000_0001_ECX, CPUID_EXT3_SVM);
+ x86_cpu_change_kvm_default("svm", NULL);
}
static void pc_compat_2_0(MachineState *machine)
@@ -349,7 +326,7 @@ static void pc_compat_1_7(MachineState *machine)
smbios_defaults = false;
gigabyte_align = false;
option_rom_has_mr = true;
- x86_cpu_compat_kvm_no_autoenable(FEAT_1_ECX, CPUID_EXT_X2APIC);
+ x86_cpu_change_kvm_default("x2apic", NULL);
}
static void pc_compat_1_6(MachineState *machine)
@@ -367,8 +344,6 @@ static void pc_compat_1_5(MachineState *machine)
static void pc_compat_1_4(MachineState *machine)
{
pc_compat_1_5(machine);
- x86_cpu_compat_set_features("n270", FEAT_1_ECX, 0, CPUID_EXT_MOVBE);
- x86_cpu_compat_set_features("Westmere", FEAT_1_ECX, 0, CPUID_EXT_PCLMULQDQ);
}
#define DEFINE_Q35_MACHINE(suffix, name, compatfn, optionfn) \
@@ -385,23 +360,35 @@ static void pc_compat_1_4(MachineState *machine)
static void pc_q35_machine_options(MachineClass *m)
{
- 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_2_4_machine_options(MachineClass *m)
-{
- 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;
+}
+
+static void pc_q35_2_5_machine_options(MachineClass *m)
+{
+ pc_q35_machine_options(m);
m->alias = "q35";
}
+DEFINE_Q35_MACHINE(v2_5, "pc-q35-2.5", NULL,
+ pc_q35_2_5_machine_options);
+
+static void pc_q35_2_4_machine_options(MachineClass *m)
+{
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
+ pc_q35_2_5_machine_options(m);
+ m->hw_version = "2.4.0";
+ m->alias = NULL;
+ pcmc->broken_reserved_end = true;
+ SET_MACHINE_COMPAT(m, PC_COMPAT_2_4);
+}
+
DEFINE_Q35_MACHINE(v2_4, "pc-q35-2.4", NULL,
pc_q35_2_4_machine_options);
@@ -409,6 +396,7 @@ DEFINE_Q35_MACHINE(v2_4, "pc-q35-2.4", NULL,
static void pc_q35_2_3_machine_options(MachineClass *m)
{
pc_q35_2_4_machine_options(m);
+ m->hw_version = "2.3.0";
m->no_floppy = 0;
m->no_tco = 1;
m->alias = NULL;
@@ -422,6 +410,7 @@ DEFINE_Q35_MACHINE(v2_3, "pc-q35-2.3", pc_compat_2_3,
static void pc_q35_2_2_machine_options(MachineClass *m)
{
pc_q35_2_3_machine_options(m);
+ m->hw_version = "2.2.0";
SET_MACHINE_COMPAT(m, PC_COMPAT_2_2);
}
@@ -432,6 +421,7 @@ DEFINE_Q35_MACHINE(v2_2, "pc-q35-2.2", pc_compat_2_2,
static void pc_q35_2_1_machine_options(MachineClass *m)
{
pc_q35_2_2_machine_options(m);
+ m->hw_version = "2.1.0";
m->default_display = NULL;
SET_MACHINE_COMPAT(m, PC_COMPAT_2_1);
}
@@ -443,6 +433,7 @@ DEFINE_Q35_MACHINE(v2_1, "pc-q35-2.1", pc_compat_2_1,
static void pc_q35_2_0_machine_options(MachineClass *m)
{
pc_q35_2_1_machine_options(m);
+ m->hw_version = "2.0.0";
SET_MACHINE_COMPAT(m, PC_COMPAT_2_0);
}
@@ -453,6 +444,7 @@ DEFINE_Q35_MACHINE(v2_0, "pc-q35-2.0", pc_compat_2_0,
static void pc_q35_1_7_machine_options(MachineClass *m)
{
pc_q35_2_0_machine_options(m);
+ m->hw_version = "1.7.0";
m->default_machine_opts = NULL;
SET_MACHINE_COMPAT(m, PC_COMPAT_1_7);
}
@@ -464,6 +456,7 @@ DEFINE_Q35_MACHINE(v1_7, "pc-q35-1.7", pc_compat_1_7,
static void pc_q35_1_6_machine_options(MachineClass *m)
{
pc_q35_machine_options(m);
+ m->hw_version = "1.6.0";
SET_MACHINE_COMPAT(m, PC_COMPAT_1_6);
}
@@ -474,6 +467,7 @@ DEFINE_Q35_MACHINE(v1_6, "pc-q35-1.6", pc_compat_1_6,
static void pc_q35_1_5_machine_options(MachineClass *m)
{
pc_q35_1_6_machine_options(m);
+ m->hw_version = "1.5.0";
SET_MACHINE_COMPAT(m, PC_COMPAT_1_5);
}
@@ -484,6 +478,7 @@ DEFINE_Q35_MACHINE(v1_5, "pc-q35-1.5", pc_compat_1_5,
static void pc_q35_1_4_machine_options(MachineClass *m)
{
pc_q35_1_5_machine_options(m);
+ m->hw_version = "1.4.0";
m->hot_add_cpu = NULL;
SET_MACHINE_COMPAT(m, PC_COMPAT_1_4);
}
diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c
index 662d99768..579461fa1 100644
--- a/hw/i386/pc_sysfw.c
+++ b/hw/i386/pc_sysfw.c
@@ -56,7 +56,7 @@ static void pc_isa_bios_init(MemoryRegion *rom_memory,
isa_bios_size = MIN(flash_size, 128 * 1024);
isa_bios = g_malloc(sizeof(*isa_bios));
memory_region_init_ram(isa_bios, NULL, "isa-bios", isa_bios_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(isa_bios);
memory_region_add_subregion_overlap(rom_memory,
0x100000 - isa_bios_size,
@@ -193,7 +193,7 @@ static void old_pc_system_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw)
goto bios_error;
}
bios = g_malloc(sizeof(*bios));
- memory_region_init_ram(bios, NULL, "pc.bios", bios_size, &error_abort);
+ memory_region_init_ram(bios, NULL, "pc.bios", bios_size, &error_fatal);
vmstate_register_ram_global(bios);
if (!isapc_ram_fw) {
memory_region_set_readonly(bios, true);
diff --git a/hw/i386/pci-assign-load-rom.c b/hw/i386/pci-assign-load-rom.c
new file mode 100644
index 000000000..e40b586b9
--- /dev/null
+++ b/hw/i386/pci-assign-load-rom.c
@@ -0,0 +1,87 @@
+/*
+ * This is splited from hw/i386/kvm/pci-assign.c
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "hw/hw.h"
+#include "hw/i386/pc.h"
+#include "qemu/error-report.h"
+#include "ui/console.h"
+#include "hw/loader.h"
+#include "monitor/monitor.h"
+#include "qemu/range.h"
+#include "sysemu/sysemu.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci-assign.h"
+
+/*
+ * Scan the assigned devices for the devices that have an option ROM, and then
+ * load the corresponding ROM data to RAM. If an error occurs while loading an
+ * option ROM, we just ignore that option ROM and continue with the next one.
+ */
+void *pci_assign_dev_load_option_rom(PCIDevice *dev, struct Object *owner,
+ int *size, unsigned int domain,
+ unsigned int bus, unsigned int slot,
+ unsigned int function)
+{
+ char name[32], rom_file[64];
+ FILE *fp;
+ uint8_t val;
+ struct stat st;
+ void *ptr = NULL;
+
+ /* If loading ROM from file, pci handles it */
+ if (dev->romfile || !dev->rom_bar) {
+ return NULL;
+ }
+
+ snprintf(rom_file, sizeof(rom_file),
+ "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/rom",
+ domain, bus, slot, function);
+
+ if (stat(rom_file, &st)) {
+ return NULL;
+ }
+
+ /* Write "1" to the ROM file to enable it */
+ fp = fopen(rom_file, "r+");
+ if (fp == NULL) {
+ error_report("pci-assign: Cannot open %s: %s", rom_file, strerror(errno));
+ return NULL;
+ }
+ val = 1;
+ if (fwrite(&val, 1, 1, fp) != 1) {
+ goto close_rom;
+ }
+ fseek(fp, 0, SEEK_SET);
+
+ snprintf(name, sizeof(name), "%s.rom", object_get_typename(owner));
+ memory_region_init_ram(&dev->rom, owner, name, st.st_size, &error_abort);
+ vmstate_register_ram(&dev->rom, &dev->qdev);
+ ptr = memory_region_get_ram_ptr(&dev->rom);
+ memset(ptr, 0xff, st.st_size);
+
+ if (!fread(ptr, 1, st.st_size, fp)) {
+ error_report("pci-assign: Cannot read from host %s", rom_file);
+ error_printf("Device option ROM contents are probably invalid "
+ "(check dmesg).\nSkip option ROM probe with rombar=0, "
+ "or load from file with romfile=\n");
+ goto close_rom;
+ }
+
+ pci_register_bar(dev, PCI_ROM_SLOT, 0, &dev->rom);
+ dev->has_rom = true;
+ *size = st.st_size;
+close_rom:
+ /* Write "0" to disable ROM */
+ fseek(fp, 0, SEEK_SET);
+ val = 0;
+ if (!fwrite(&val, 1, 1, fp)) {
+ DEBUG("%s\n", "Failed to disable pci-sysfs rom file");
+ }
+ fclose(fp);
+
+ return ptr;
+}
diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl
index 16eaca3fa..7be7b37b8 100644
--- a/hw/i386/q35-acpi-dsdt.dsl
+++ b/hw/i386/q35-acpi-dsdt.dsl
@@ -22,6 +22,7 @@
* Based on acpi-dsdt.dsl, but heavily modified for q35 chipset.
*/
+
ACPI_EXTRACT_ALL_CODE Q35AcpiDsdtAmlCode
DefinitionBlock (
diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c
index 28b324a6f..de83f4e3e 100644
--- a/hw/i386/xen/xen_platform.c
+++ b/hw/i386/xen/xen_platform.c
@@ -23,8 +23,6 @@
* THE SOFTWARE.
*/
-#include <assert.h>
-
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/ide.h"
@@ -35,6 +33,7 @@
#include "trace.h"
#include "exec/address-spaces.h"
#include "sysemu/block-backend.h"
+#include "qemu/error-report.h"
#include <xenguest.h>
@@ -384,11 +383,17 @@ static const VMStateDescription vmstate_xen_platform = {
}
};
-static int xen_platform_initfn(PCIDevice *dev)
+static void xen_platform_realize(PCIDevice *dev, Error **errp)
{
PCIXenPlatformState *d = XEN_PLATFORM(dev);
uint8_t *pci_conf;
+ /* Device will crash on reset if xen is not initialized */
+ if (!xen_enabled()) {
+ error_setg(errp, "xen-platform device requires the Xen accelerator");
+ return;
+ }
+
pci_conf = dev->config;
pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
@@ -406,8 +411,6 @@ static int xen_platform_initfn(PCIDevice *dev)
&d->mmio_bar);
platform_fixed_ioport_init(d);
-
- return 0;
}
static void platform_reset(DeviceState *dev)
@@ -422,7 +425,7 @@ static void xen_platform_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- k->init = xen_platform_initfn;
+ k->realize = xen_platform_realize;
k->vendor_id = PCI_VENDOR_ID_XEN;
k->device_id = PCI_DEVICE_ID_XEN_PLATFORM;
k->class_id = PCI_CLASS_OTHERS << 8 | 0x80;
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index 378ad60c3..cdc92997b 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -25,7 +25,6 @@
#include <hw/pci/msi.h>
#include <hw/i386/pc.h>
#include <hw/pci/pci.h>
-#include <hw/sysbus.h>
#include "qemu/error-report.h"
#include "sysemu/block-backend.h"
@@ -47,7 +46,7 @@ do { \
static void check_cmd(AHCIState *s, int port);
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 bool ahci_write_fis_d2h(AHCIDevice *ad);
static void ahci_init_d2h(AHCIDevice *ad);
static int ahci_dma_prepare_buf(IDEDMA *dma, int32_t limit);
static bool ahci_map_clb_address(AHCIDevice *ad);
@@ -121,9 +120,9 @@ static uint32_t ahci_port_read(AHCIState *s, int port, int offset)
static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev)
{
- AHCIPCIState *d = container_of(s, AHCIPCIState, ahci);
- PCIDevice *pci_dev =
- (PCIDevice *)object_dynamic_cast(OBJECT(d), TYPE_PCI_DEVICE);
+ DeviceState *dev_state = s->container;
+ PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state),
+ TYPE_PCI_DEVICE);
DPRINTF(0, "raise irq\n");
@@ -136,9 +135,9 @@ static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev)
static void ahci_irq_lower(AHCIState *s, AHCIDevice *dev)
{
- AHCIPCIState *d = container_of(s, AHCIPCIState, ahci);
- PCIDevice *pci_dev =
- (PCIDevice *)object_dynamic_cast(OBJECT(d), TYPE_PCI_DEVICE);
+ DeviceState *dev_state = s->container;
+ PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state),
+ TYPE_PCI_DEVICE);
DPRINTF(0, "lower irq\n");
@@ -296,7 +295,6 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val)
if ((pr->cmd & PORT_CMD_FIS_ON) &&
!s->dev[port].init_d2h_sent) {
ahci_init_d2h(&s->dev[port]);
- s->dev[port].init_d2h_sent = true;
}
check_cmd(s, port);
@@ -380,17 +378,23 @@ static uint64_t ahci_mem_read(void *opaque, hwaddr addr, unsigned size)
int ofst = addr - aligned;
uint64_t lo = ahci_mem_read_32(opaque, aligned);
uint64_t hi;
+ uint64_t val;
/* if < 8 byte read does not cross 4 byte boundary */
if (ofst + size <= 4) {
- return lo >> (ofst * 8);
+ val = lo >> (ofst * 8);
+ } else {
+ 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);
+ val = (hi << 32 | 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);
+ DPRINTF(-1, "addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 ", size=%d\n",
+ addr, val, size);
+ return val;
}
@@ -399,6 +403,9 @@ static void ahci_mem_write(void *opaque, hwaddr addr,
{
AHCIState *s = opaque;
+ DPRINTF(-1, "addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 ", size=%d\n",
+ addr, val, size);
+
/* Only aligned reads are allowed on AHCI */
if (addr & 3) {
fprintf(stderr, "ahci: Mis-aligned write to addr 0x"
@@ -539,20 +546,33 @@ static void ahci_check_cmd_bh(void *opaque)
static void ahci_init_d2h(AHCIDevice *ad)
{
- uint8_t init_fis[20];
IDEState *ide_state = &ad->port.ifs[0];
+ AHCIPortRegs *pr = &ad->port_regs;
- memset(init_fis, 0, sizeof(init_fis));
-
- init_fis[4] = 1;
- init_fis[12] = 1;
+ if (ad->init_d2h_sent) {
+ return;
+ }
- if (ide_state->drive_kind == IDE_CD) {
- init_fis[5] = ide_state->lcyl;
- init_fis[6] = ide_state->hcyl;
+ if (ahci_write_fis_d2h(ad)) {
+ ad->init_d2h_sent = true;
+ /* We're emulating receiving the first Reg H2D Fis from the device;
+ * Update the SIG register, but otherwise proceed as normal. */
+ pr->sig = ((uint32_t)ide_state->hcyl << 24) |
+ (ide_state->lcyl << 16) |
+ (ide_state->sector << 8) |
+ (ide_state->nsector & 0xFF);
}
+}
+
+static void ahci_set_signature(AHCIDevice *ad, uint32_t sig)
+{
+ IDEState *s = &ad->port.ifs[0];
+ s->hcyl = sig >> 24 & 0xFF;
+ s->lcyl = sig >> 16 & 0xFF;
+ s->sector = sig >> 8 & 0xFF;
+ s->nsector = sig & 0xFF;
- ahci_write_fis_d2h(ad, init_fis);
+ DPRINTF(ad->port_no, "set hcyl:lcyl:sect:nsect = 0x%08x\n", sig);
}
static void ahci_reset_port(AHCIState *s, int port)
@@ -603,17 +623,11 @@ static void ahci_reset_port(AHCIState *s, int port)
}
s->dev[port].port_state = STATE_RUN;
- if (!ide_state->blk) {
- pr->sig = 0;
- ide_state->status = SEEK_STAT | WRERR_STAT;
- } else if (ide_state->drive_kind == IDE_CD) {
- pr->sig = SATA_SIGNATURE_CDROM;
- ide_state->lcyl = 0x14;
- ide_state->hcyl = 0xeb;
- DPRINTF(port, "set lcyl = %d\n", ide_state->lcyl);
+ if (ide_state->drive_kind == IDE_CD) {
+ ahci_set_signature(d, SATA_SIGNATURE_CDROM);\
ide_state->status = SEEK_STAT | WRERR_STAT | READY_STAT;
} else {
- pr->sig = SATA_SIGNATURE_DISK;
+ ahci_set_signature(d, SATA_SIGNATURE_DISK);
ide_state->status = SEEK_STAT | WRERR_STAT;
}
@@ -647,6 +661,10 @@ static bool ahci_map_fis_address(AHCIDevice *ad)
static void ahci_unmap_fis_address(AHCIDevice *ad)
{
+ if (ad->res_fis == NULL) {
+ DPRINTF(ad->port_no, "Attempt to unmap NULL FIS address\n");
+ return;
+ }
dma_memory_unmap(ad->hba->as, ad->res_fis, 256,
DMA_DIRECTION_FROM_DEVICE, 256);
ad->res_fis = NULL;
@@ -663,6 +681,10 @@ static bool ahci_map_clb_address(AHCIDevice *ad)
static void ahci_unmap_clb_address(AHCIDevice *ad)
{
+ if (ad->lst == NULL) {
+ DPRINTF(ad->port_no, "Attempt to unmap NULL CLB address\n");
+ return;
+ }
dma_memory_unmap(ad->hba->as, ad->lst, 1024,
DMA_DIRECTION_FROM_DEVICE, 1024);
ad->lst = NULL;
@@ -749,7 +771,7 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len)
ahci_trigger_irq(ad->hba, ad, PORT_IRQ_PIOS_FIS);
}
-static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
+static bool ahci_write_fis_d2h(AHCIDevice *ad)
{
AHCIPortRegs *pr = &ad->port_regs;
uint8_t *d2h_fis;
@@ -757,7 +779,7 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
IDEState *s = &ad->port.ifs[0];
if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) {
- return;
+ return false;
}
d2h_fis = &ad->res_fis[RES_FIS_RFIS];
@@ -790,6 +812,7 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
}
ahci_trigger_irq(ad->hba, ad, PORT_IRQ_D2H_REG_FIS);
+ return true;
}
static int prdt_tbl_entry_size(const AHCI_SG *tbl)
@@ -798,8 +821,21 @@ static int prdt_tbl_entry_size(const AHCI_SG *tbl)
return (le32_to_cpu(tbl->flags_size) & AHCI_PRDT_SIZE_MASK) + 1;
}
+/**
+ * Fetch entries in a guest-provided PRDT and convert it into a QEMU SGlist.
+ * @ad: The AHCIDevice for whom we are building the SGList.
+ * @sglist: The SGList target to add PRD entries to.
+ * @cmd: The AHCI Command Header that describes where the PRDT is.
+ * @limit: The remaining size of the S/ATA transaction, in bytes.
+ * @offset: The number of bytes already transferred, in bytes.
+ *
+ * The AHCI PRDT can describe up to 256GiB. S/ATA only support transactions of
+ * up to 32MiB as of ATA8-ACS3 rev 1b, assuming a 512 byte sector size. We stop
+ * building the sglist from the PRDT as soon as we hit @limit bytes,
+ * which is <= INT32_MAX/2GiB.
+ */
static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist,
- AHCICmdHdr *cmd, int64_t limit, int32_t offset)
+ AHCICmdHdr *cmd, int64_t limit, uint64_t offset)
{
uint16_t opts = le16_to_cpu(cmd->opts);
uint16_t prdtl = le16_to_cpu(cmd->prdtl);
@@ -817,14 +853,6 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist,
IDEBus *bus = &ad->port;
BusState *qbus = BUS(bus);
- /*
- * Note: AHCI PRDT can describe up to 256GiB. SATA/ATA only support
- * transactions of up to 32MiB as of ATA8-ACS3 rev 1b, assuming a
- * 512 byte sector size. We limit the PRDT in this implementation to
- * a reasonably large 2GiB, which can accommodate the maximum transfer
- * request for sector sizes up to 32K.
- */
-
if (!prdtl) {
DPRINTF(ad->port_no, "no sg list given by guest: 0x%08x\n", opts);
return -1;
@@ -874,13 +902,6 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist,
qemu_sglist_add(sglist, le64_to_cpu(tbl[i].addr),
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");
- qemu_sglist_destroy(sglist);
- r = -1;
- goto out;
- }
}
}
@@ -897,6 +918,7 @@ static void ncq_err(NCQTransferState *ncq_tfs)
ide_state->error = ABRT_ERR;
ide_state->status = READY_STAT | ERR_STAT;
ncq_tfs->drive->port_regs.scr_err |= (1 << ncq_tfs->tag);
+ ncq_tfs->used = 0;
}
static void ncq_finish(NCQTransferState *ncq_tfs)
@@ -1397,7 +1419,7 @@ static void ahci_cmd_done(IDEDMA *dma)
DPRINTF(ad->port_no, "cmd done\n");
/* update d2h status */
- ahci_write_fis_d2h(ad, NULL);
+ ahci_write_fis_d2h(ad);
if (!ad->check_bh) {
/* maybe we still have something to process, check later */
@@ -1421,7 +1443,17 @@ static const IDEDMAOps ahci_dma_ops = {
.cmd_done = ahci_cmd_done,
};
-void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports)
+void ahci_init(AHCIState *s, DeviceState *qdev)
+{
+ s->container = qdev;
+ /* XXX BAR size should be 1k, but that breaks, so bump it to 4k for now */
+ memory_region_init_io(&s->mem, OBJECT(qdev), &ahci_mem_ops, s,
+ "ahci", AHCI_MEM_BAR_SIZE);
+ memory_region_init_io(&s->idp, OBJECT(qdev), &ahci_idp_ops, s,
+ "ahci-idp", 32);
+}
+
+void ahci_realize(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports)
{
qemu_irq *irqs;
int i;
@@ -1430,14 +1462,7 @@ void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports)
s->ports = ports;
s->dev = g_new0(AHCIDevice, ports);
ahci_reg_init(s);
- /* XXX BAR size should be 1k, but that breaks, so bump it to 4k for now */
- memory_region_init_io(&s->mem, OBJECT(qdev), &ahci_mem_ops, s,
- "ahci", AHCI_MEM_BAR_SIZE);
- memory_region_init_io(&s->idp, OBJECT(qdev), &ahci_idp_ops, s,
- "ahci-idp", 32);
-
irqs = qemu_allocate_irqs(ahci_irq_set, s, s->ports);
-
for (i = 0; i < s->ports; i++) {
AHCIDevice *ad = &s->dev[i];
@@ -1617,18 +1642,6 @@ const VMStateDescription vmstate_ahci = {
},
};
-#define TYPE_SYSBUS_AHCI "sysbus-ahci"
-#define SYSBUS_AHCI(obj) OBJECT_CHECK(SysbusAHCIState, (obj), TYPE_SYSBUS_AHCI)
-
-typedef struct SysbusAHCIState {
- /*< private >*/
- SysBusDevice parent_obj;
- /*< public >*/
-
- AHCIState ahci;
- uint32_t num_ports;
-} SysbusAHCIState;
-
static const VMStateDescription vmstate_sysbus_ahci = {
.name = "sysbus-ahci",
.fields = (VMStateField[]) {
@@ -1644,17 +1657,24 @@ static void sysbus_ahci_reset(DeviceState *dev)
ahci_reset(&s->ahci);
}
-static void sysbus_ahci_realize(DeviceState *dev, Error **errp)
+static void sysbus_ahci_init(Object *obj)
{
- SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
- SysbusAHCIState *s = SYSBUS_AHCI(dev);
+ SysbusAHCIState *s = SYSBUS_AHCI(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
- ahci_init(&s->ahci, dev, &address_space_memory, s->num_ports);
+ ahci_init(&s->ahci, DEVICE(obj));
sysbus_init_mmio(sbd, &s->ahci.mem);
sysbus_init_irq(sbd, &s->ahci.irq);
}
+static void sysbus_ahci_realize(DeviceState *dev, Error **errp)
+{
+ SysbusAHCIState *s = SYSBUS_AHCI(dev);
+
+ ahci_realize(&s->ahci, dev, &address_space_memory, s->num_ports);
+}
+
static Property sysbus_ahci_properties[] = {
DEFINE_PROP_UINT32("num-ports", SysbusAHCIState, num_ports, 1),
DEFINE_PROP_END_OF_LIST(),
@@ -1675,12 +1695,108 @@ static const TypeInfo sysbus_ahci_info = {
.name = TYPE_SYSBUS_AHCI,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(SysbusAHCIState),
+ .instance_init = sysbus_ahci_init,
.class_init = sysbus_ahci_class_init,
};
+#define ALLWINNER_AHCI_BISTAFR ((0xa0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_BISTCR ((0xa4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_BISTFCTR ((0xa8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_BISTSR ((0xac - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_BISTDECR ((0xb0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_DIAGNR0 ((0xb4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_DIAGNR1 ((0xb8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_OOBR ((0xbc - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_PHYCS0R ((0xc0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_PHYCS1R ((0xc4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_PHYCS2R ((0xc8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_TIMER1MS ((0xe0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_GPARAM1R ((0xe8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_GPARAM2R ((0xec - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_PPARAMR ((0xf0 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_TESTR ((0xf4 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_VERSIONR ((0xf8 - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_IDR ((0xfc - ALLWINNER_AHCI_MMIO_OFF) / 4)
+#define ALLWINNER_AHCI_RWCR ((0xfc - ALLWINNER_AHCI_MMIO_OFF) / 4)
+
+static uint64_t allwinner_ahci_mem_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ AllwinnerAHCIState *a = opaque;
+ uint64_t val = a->regs[addr/4];
+
+ switch (addr / 4) {
+ case ALLWINNER_AHCI_PHYCS0R:
+ val |= 0x2 << 28;
+ break;
+ case ALLWINNER_AHCI_PHYCS2R:
+ val &= ~(0x1 << 24);
+ break;
+ }
+ DPRINTF(-1, "addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 ", size=%d\n",
+ addr, val, size);
+ return val;
+}
+
+static void allwinner_ahci_mem_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ AllwinnerAHCIState *a = opaque;
+
+ DPRINTF(-1, "addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 ", size=%d\n",
+ addr, val, size);
+ a->regs[addr/4] = val;
+}
+
+static const MemoryRegionOps allwinner_ahci_mem_ops = {
+ .read = allwinner_ahci_mem_read,
+ .write = allwinner_ahci_mem_write,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void allwinner_ahci_init(Object *obj)
+{
+ SysbusAHCIState *s = SYSBUS_AHCI(obj);
+ AllwinnerAHCIState *a = ALLWINNER_AHCI(obj);
+
+ memory_region_init_io(&a->mmio, OBJECT(obj), &allwinner_ahci_mem_ops, a,
+ "allwinner-ahci", ALLWINNER_AHCI_MMIO_SIZE);
+ memory_region_add_subregion(&s->ahci.mem, ALLWINNER_AHCI_MMIO_OFF,
+ &a->mmio);
+}
+
+static const VMStateDescription vmstate_allwinner_ahci = {
+ .name = "allwinner-ahci",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, AllwinnerAHCIState,
+ ALLWINNER_AHCI_MMIO_SIZE/4),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void allwinner_ahci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &vmstate_allwinner_ahci;
+}
+
+static const TypeInfo allwinner_ahci_info = {
+ .name = TYPE_ALLWINNER_AHCI,
+ .parent = TYPE_SYSBUS_AHCI,
+ .instance_size = sizeof(AllwinnerAHCIState),
+ .instance_init = allwinner_ahci_init,
+ .class_init = allwinner_ahci_class_init,
+};
+
static void sysbus_ahci_register_types(void)
{
type_register_static(&sysbus_ahci_info);
+ type_register_static(&allwinner_ahci_info);
}
type_init(sysbus_ahci_register_types)
diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h
index 79a463d93..bc777ed5c 100644
--- a/hw/ide/ahci.h
+++ b/hw/ide/ahci.h
@@ -24,6 +24,8 @@
#ifndef HW_IDE_AHCI_H
#define HW_IDE_AHCI_H
+#include <hw/sysbus.h>
+
#define AHCI_MEM_BAR_SIZE 0x1000
#define AHCI_MAX_PORTS 32
#define AHCI_MAX_SG 168 /* hardware max is 64K */
@@ -285,6 +287,8 @@ struct AHCIDevice {
};
typedef struct AHCIState {
+ DeviceState *container;
+
AHCIDevice *dev;
AHCIControlRegs control_regs;
MemoryRegion mem;
@@ -362,11 +366,40 @@ typedef struct SDBFIS {
uint32_t payload;
} QEMU_PACKED SDBFIS;
-void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports);
+void ahci_realize(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports);
+void ahci_init(AHCIState *s, DeviceState *qdev);
void ahci_uninit(AHCIState *s);
void ahci_reset(AHCIState *s);
void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **hd);
+#define TYPE_SYSBUS_AHCI "sysbus-ahci"
+#define SYSBUS_AHCI(obj) OBJECT_CHECK(SysbusAHCIState, (obj), TYPE_SYSBUS_AHCI)
+
+typedef struct SysbusAHCIState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+
+ AHCIState ahci;
+ uint32_t num_ports;
+} SysbusAHCIState;
+
+#define TYPE_ALLWINNER_AHCI "allwinner-ahci"
+#define ALLWINNER_AHCI(obj) OBJECT_CHECK(AllwinnerAHCIState, (obj), \
+ TYPE_ALLWINNER_AHCI)
+
+#define ALLWINNER_AHCI_MMIO_OFF 0x80
+#define ALLWINNER_AHCI_MMIO_SIZE 0x80
+
+struct AllwinnerAHCIState {
+ /*< private >*/
+ SysbusAHCIState parent_obj;
+ /*< public >*/
+
+ MemoryRegion mmio;
+ uint32_t regs[ALLWINNER_AHCI_MMIO_SIZE/4];
+};
+
#endif /* HW_IDE_AHCI_H */
diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index 79dd16710..65f8dd457 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -105,33 +105,99 @@ static void cd_data_to_raw(uint8_t *buf, int lba)
memset(buf, 0, 288);
}
-static int cd_read_sector(IDEState *s, int lba, uint8_t *buf, int sector_size)
+static int
+cd_read_sector_sync(IDEState *s)
{
int ret;
+ block_acct_start(blk_get_stats(s->blk), &s->acct,
+ 4 * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
- switch(sector_size) {
+#ifdef DEBUG_IDE_ATAPI
+ printf("cd_read_sector_sync: lba=%d\n", s->lba);
+#endif
+
+ switch (s->cd_sector_size) {
case 2048:
- block_acct_start(blk_get_stats(s->blk), &s->acct,
- 4 * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
- ret = blk_read(s->blk, (int64_t)lba << 2, buf, 4);
- block_acct_done(blk_get_stats(s->blk), &s->acct);
+ ret = blk_read(s->blk, (int64_t)s->lba << 2,
+ s->io_buffer, 4);
break;
case 2352:
- block_acct_start(blk_get_stats(s->blk), &s->acct,
- 4 * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
- ret = blk_read(s->blk, (int64_t)lba << 2, buf + 16, 4);
- block_acct_done(blk_get_stats(s->blk), &s->acct);
- if (ret < 0)
- return ret;
- cd_data_to_raw(buf, lba);
+ ret = blk_read(s->blk, (int64_t)s->lba << 2,
+ s->io_buffer + 16, 4);
+ if (ret >= 0) {
+ cd_data_to_raw(s->io_buffer, s->lba);
+ }
break;
default:
- ret = -EIO;
- break;
+ block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ);
+ return -EIO;
+ }
+
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->blk), &s->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+ s->lba++;
+ s->io_buffer_index = 0;
}
+
return ret;
}
+static void cd_read_sector_cb(void *opaque, int ret)
+{
+ IDEState *s = opaque;
+
+#ifdef DEBUG_IDE_ATAPI
+ printf("cd_read_sector_cb: lba=%d ret=%d\n", s->lba, ret);
+#endif
+
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->blk), &s->acct);
+ ide_atapi_io_error(s, ret);
+ return;
+ }
+
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+
+ if (s->cd_sector_size == 2352) {
+ cd_data_to_raw(s->io_buffer, s->lba);
+ }
+
+ s->lba++;
+ s->io_buffer_index = 0;
+ s->status &= ~BUSY_STAT;
+
+ ide_atapi_cmd_reply_end(s);
+}
+
+static int cd_read_sector(IDEState *s)
+{
+ if (s->cd_sector_size != 2048 && s->cd_sector_size != 2352) {
+ block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ);
+ return -EINVAL;
+ }
+
+ s->iov.iov_base = (s->cd_sector_size == 2352) ?
+ s->io_buffer + 16 : s->io_buffer;
+
+ s->iov.iov_len = 4 * BDRV_SECTOR_SIZE;
+ qemu_iovec_init_external(&s->qiov, &s->iov, 1);
+
+#ifdef DEBUG_IDE_ATAPI
+ printf("cd_read_sector: lba=%d\n", s->lba);
+#endif
+
+ block_acct_start(blk_get_stats(s->blk), &s->acct,
+ 4 * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
+
+ ide_buffered_readv(s, (int64_t)s->lba << 2, &s->qiov, 4,
+ cd_read_sector_cb, s);
+
+ s->status |= BUSY_STAT;
+ return 0;
+}
+
void ide_atapi_cmd_ok(IDEState *s)
{
s->error = 0;
@@ -167,6 +233,17 @@ void ide_atapi_io_error(IDEState *s, int ret)
}
}
+static uint16_t atapi_byte_count_limit(IDEState *s)
+{
+ uint16_t bcl;
+
+ bcl = s->lcyl | (s->hcyl << 8);
+ if (bcl == 0xffff) {
+ return 0xfffe;
+ }
+ return bcl;
+}
+
/* The whole ATAPI transfer logic is handled in this function */
void ide_atapi_cmd_reply_end(IDEState *s)
{
@@ -182,18 +259,27 @@ void ide_atapi_cmd_reply_end(IDEState *s)
ide_atapi_cmd_ok(s);
ide_set_irq(s->bus);
#ifdef DEBUG_IDE_ATAPI
- printf("status=0x%x\n", s->status);
+ printf("end of transfer, status=0x%x\n", s->status);
#endif
} else {
/* see if a new sector must be read */
if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) {
- ret = cd_read_sector(s, s->lba, s->io_buffer, s->cd_sector_size);
- if (ret < 0) {
- ide_atapi_io_error(s, ret);
+ if (!s->elementary_transfer_size) {
+ ret = cd_read_sector(s);
+ if (ret < 0) {
+ ide_atapi_io_error(s, ret);
+ }
return;
+ } else {
+ /* rebuffering within an elementary transfer is
+ * only possible with a sync request because we
+ * end up with a race condition otherwise */
+ ret = cd_read_sector_sync(s);
+ if (ret < 0) {
+ ide_atapi_io_error(s, ret);
+ return;
+ }
}
- s->lba++;
- s->io_buffer_index = 0;
}
if (s->elementary_transfer_size > 0) {
/* there are some data left to transmit in this elementary
@@ -209,12 +295,10 @@ void ide_atapi_cmd_reply_end(IDEState *s)
} else {
/* a new transfer is needed */
s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO;
- byte_count_limit = s->lcyl | (s->hcyl << 8);
+ byte_count_limit = atapi_byte_count_limit(s);
#ifdef DEBUG_IDE_ATAPI
printf("byte_count_limit=%d\n", byte_count_limit);
#endif
- if (byte_count_limit == 0xffff)
- byte_count_limit--;
size = s->packet_transfer_size;
if (size > byte_count_limit) {
/* byte count limit must be even if this case */
@@ -275,7 +359,6 @@ static void ide_atapi_cmd_read_pio(IDEState *s, int lba, int nb_sectors,
s->io_buffer_index = sector_size;
s->cd_sector_size = sector_size;
- s->status = READY_STAT | SEEK_STAT;
ide_atapi_cmd_reply_end(s);
}
@@ -351,13 +434,17 @@ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret)
s->bus->dma->iov.iov_len = n * 4 * 512;
qemu_iovec_init_external(&s->bus->dma->qiov, &s->bus->dma->iov, 1);
- s->bus->dma->aiocb = blk_aio_readv(s->blk, (int64_t)s->lba << 2,
- &s->bus->dma->qiov, n * 4,
- ide_atapi_cmd_read_dma_cb, s);
+ s->bus->dma->aiocb = ide_buffered_readv(s, (int64_t)s->lba << 2,
+ &s->bus->dma->qiov, n * 4,
+ ide_atapi_cmd_read_dma_cb, s);
return;
eot:
- block_acct_done(blk_get_stats(s->blk), &s->acct);
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->blk), &s->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+ }
ide_set_inactive(s, false);
}
@@ -1169,20 +1256,28 @@ enum {
* 4.1.8)
*/
CHECK_READY = 0x02,
+
+ /*
+ * Commands flagged with NONDATA do not in any circumstances return
+ * any data via ide_atapi_cmd_reply. These commands are exempt from
+ * the normal byte_count_limit constraints.
+ * See ATA8-ACS3 "7.21.5 Byte Count Limit"
+ */
+ NONDATA = 0x04,
};
-static const struct {
+static const struct AtapiCmd {
void (*handler)(IDEState *s, uint8_t *buf);
int flags;
} atapi_cmd_table[0x100] = {
- [ 0x00 ] = { cmd_test_unit_ready, CHECK_READY },
+ [ 0x00 ] = { cmd_test_unit_ready, CHECK_READY | NONDATA },
[ 0x03 ] = { cmd_request_sense, ALLOW_UA },
[ 0x12 ] = { cmd_inquiry, ALLOW_UA },
- [ 0x1b ] = { cmd_start_stop_unit, 0 }, /* [1] */
- [ 0x1e ] = { cmd_prevent_allow_medium_removal, 0 },
+ [ 0x1b ] = { cmd_start_stop_unit, NONDATA }, /* [1] */
+ [ 0x1e ] = { cmd_prevent_allow_medium_removal, NONDATA },
[ 0x25 ] = { cmd_read_cdvd_capacity, CHECK_READY },
[ 0x28 ] = { cmd_read, /* (10) */ CHECK_READY },
- [ 0x2b ] = { cmd_seek, CHECK_READY },
+ [ 0x2b ] = { cmd_seek, CHECK_READY | NONDATA },
[ 0x43 ] = { cmd_read_toc_pma_atip, CHECK_READY },
[ 0x46 ] = { cmd_get_configuration, ALLOW_UA },
[ 0x4a ] = { cmd_get_event_status_notification, ALLOW_UA },
@@ -1190,7 +1285,7 @@ static const struct {
[ 0x5a ] = { cmd_mode_sense, /* (10) */ 0 },
[ 0xa8 ] = { cmd_read, /* (12) */ CHECK_READY },
[ 0xad ] = { cmd_read_dvd_structure, CHECK_READY },
- [ 0xbb ] = { cmd_set_speed, 0 },
+ [ 0xbb ] = { cmd_set_speed, NONDATA },
[ 0xbd ] = { cmd_mechanism_status, 0 },
[ 0xbe ] = { cmd_read_cd, CHECK_READY },
/* [1] handler detects and reports not ready condition itself */
@@ -1198,9 +1293,9 @@ static const struct {
void ide_atapi_cmd(IDEState *s)
{
- uint8_t *buf;
+ uint8_t *buf = s->io_buffer;
+ const struct AtapiCmd *cmd = &atapi_cmd_table[s->io_buffer[0]];
- buf = s->io_buffer;
#ifdef DEBUG_IDE_ATAPI
{
int i;
@@ -1211,14 +1306,14 @@ void ide_atapi_cmd(IDEState *s)
printf("\n");
}
#endif
+
/*
* If there's a UNIT_ATTENTION condition pending, only command flagged with
* ALLOW_UA are allowed to complete. with other commands getting a CHECK
* condition response unless a higher priority status, defined by the drive
* here, is pending.
*/
- if (s->sense_key == UNIT_ATTENTION &&
- !(atapi_cmd_table[s->io_buffer[0]].flags & ALLOW_UA)) {
+ if (s->sense_key == UNIT_ATTENTION && !(cmd->flags & ALLOW_UA)) {
ide_atapi_cmd_check_status(s);
return;
}
@@ -1229,7 +1324,7 @@ void ide_atapi_cmd(IDEState *s)
* GET_EVENT_STATUS_NOTIFICATION to detect such tray open/close
* states rely on this behavior.
*/
- if (!(atapi_cmd_table[s->io_buffer[0]].flags & ALLOW_UA) &&
+ if (!(cmd->flags & ALLOW_UA) &&
!s->tray_open && blk_is_inserted(s->blk) && s->cdrom_changed) {
if (s->cdrom_changed == 1) {
@@ -1244,16 +1339,29 @@ void ide_atapi_cmd(IDEState *s)
}
/* Report a Not Ready condition if appropriate for the command */
- if ((atapi_cmd_table[s->io_buffer[0]].flags & CHECK_READY) &&
+ if ((cmd->flags & CHECK_READY) &&
(!media_present(s) || !blk_is_inserted(s->blk)))
{
ide_atapi_cmd_error(s, NOT_READY, ASC_MEDIUM_NOT_PRESENT);
return;
}
+ /* Nondata commands permit the byte_count_limit to be 0.
+ * If this is a data-transferring PIO command and BCL is 0,
+ * we abort at the /ATA/ level, not the ATAPI level.
+ * See ATA8 ACS3 section 7.17.6.49 and 7.21.5 */
+ if (cmd->handler && !(cmd->flags & NONDATA)) {
+ /* TODO: Check IDENTIFY data word 125 for default BCL (currently 0) */
+ if (!(atapi_byte_count_limit(s) || s->atapi_dma)) {
+ /* TODO: Move abort back into core.c and make static inline again */
+ ide_abort_command(s);
+ return;
+ }
+ }
+
/* Execute the command */
- if (atapi_cmd_table[s->io_buffer[0]].handler) {
- atapi_cmd_table[s->io_buffer[0]].handler(s, buf);
+ if (cmd->handler) {
+ cmd->handler(s, buf);
return;
}
diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c
index 66fb9d96d..27f3da21a 100644
--- a/hw/ide/cmd646.c
+++ b/hw/ide/cmd646.c
@@ -417,6 +417,7 @@ static void cmd646_ide_class_init(ObjectClass *klass, void *data)
k->config_read = cmd646_pci_config_read;
k->config_write = cmd646_pci_config_write;
dc->props = cmd646_ide_properties;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
}
static const TypeInfo cmd646_ide_info = {
diff --git a/hw/ide/core.c b/hw/ide/core.c
index 1cc6945d8..da3baab1e 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -457,7 +457,7 @@ BlockAIOCB *ide_issue_trim(BlockBackend *blk,
return &iocb->common;
}
-static inline void ide_abort_command(IDEState *s)
+void ide_abort_command(IDEState *s)
{
ide_transfer_stop(s);
s->status = READY_STAT | ERR_STAT;
@@ -561,6 +561,53 @@ static bool ide_sect_range_ok(IDEState *s,
return true;
}
+static void ide_buffered_readv_cb(void *opaque, int ret)
+{
+ IDEBufferedRequest *req = opaque;
+ if (!req->orphaned) {
+ if (!ret) {
+ qemu_iovec_from_buf(req->original_qiov, 0, req->iov.iov_base,
+ req->original_qiov->size);
+ }
+ req->original_cb(req->original_opaque, ret);
+ }
+ QLIST_REMOVE(req, list);
+ qemu_vfree(req->iov.iov_base);
+ g_free(req);
+}
+
+#define MAX_BUFFERED_REQS 16
+
+BlockAIOCB *ide_buffered_readv(IDEState *s, int64_t sector_num,
+ QEMUIOVector *iov, int nb_sectors,
+ BlockCompletionFunc *cb, void *opaque)
+{
+ BlockAIOCB *aioreq;
+ IDEBufferedRequest *req;
+ int c = 0;
+
+ QLIST_FOREACH(req, &s->buffered_requests, list) {
+ c++;
+ }
+ if (c > MAX_BUFFERED_REQS) {
+ return blk_abort_aio_request(s->blk, cb, opaque, -EIO);
+ }
+
+ req = g_new0(IDEBufferedRequest, 1);
+ req->original_qiov = iov;
+ req->original_cb = cb;
+ req->original_opaque = opaque;
+ req->iov.iov_base = qemu_blockalign(blk_bs(s->blk), iov->size);
+ req->iov.iov_len = iov->size;
+ qemu_iovec_init_external(&req->qiov, &req->iov, 1);
+
+ aioreq = blk_aio_readv(s->blk, sector_num, &req->qiov, nb_sectors,
+ ide_buffered_readv_cb, req);
+
+ QLIST_INSERT_HEAD(&s->buffered_requests, req, list);
+ return aioreq;
+}
+
static void ide_sector_read(IDEState *s);
static void ide_sector_read_cb(void *opaque, int ret)
@@ -574,7 +621,6 @@ static void ide_sector_read_cb(void *opaque, int ret)
if (ret == -ECANCELED) {
return;
}
- block_acct_done(blk_get_stats(s->blk), &s->acct);
if (ret != 0) {
if (ide_handle_rw_error(s, -ret, IDE_RETRY_PIO |
IDE_RETRY_READ)) {
@@ -582,6 +628,8 @@ static void ide_sector_read_cb(void *opaque, int ret)
}
}
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+
n = s->nsector;
if (n > s->req_nb_sectors) {
n = s->req_nb_sectors;
@@ -621,6 +669,7 @@ static void ide_sector_read(IDEState *s)
if (!ide_sect_range_ok(s, sector_num, n)) {
ide_rw_error(s);
+ block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ);
return;
}
@@ -630,8 +679,8 @@ static void ide_sector_read(IDEState *s)
block_acct_start(blk_get_stats(s->blk), &s->acct,
n * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
- s->pio_aiocb = blk_aio_readv(s->blk, sector_num, &s->qiov, n,
- ide_sector_read_cb, s);
+ s->pio_aiocb = ide_buffered_readv(s, sector_num, &s->qiov, n,
+ ide_sector_read_cb, s);
}
void dma_buf_commit(IDEState *s, uint32_t tx_bytes)
@@ -672,6 +721,7 @@ static int ide_handle_rw_error(IDEState *s, int error, int op)
assert(s->bus->retry_unit == s->unit);
s->bus->error_status = op;
} else if (action == BLOCK_ERROR_ACTION_REPORT) {
+ block_acct_failed(blk_get_stats(s->blk), &s->acct);
if (op & IDE_RETRY_DMA) {
ide_dma_error(s);
} else {
@@ -750,6 +800,7 @@ static void ide_dma_cb(void *opaque, int ret)
if ((s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) &&
!ide_sect_range_ok(s, sector_num, n)) {
ide_dma_error(s);
+ block_acct_invalid(blk_get_stats(s->blk), s->acct.type);
return;
}
@@ -826,7 +877,6 @@ static void ide_sector_write_cb(void *opaque, int ret)
if (ret == -ECANCELED) {
return;
}
- block_acct_done(blk_get_stats(s->blk), &s->acct);
s->pio_aiocb = NULL;
s->status &= ~BUSY_STAT;
@@ -837,6 +887,8 @@ static void ide_sector_write_cb(void *opaque, int ret)
}
}
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+
n = s->nsector;
if (n > s->req_nb_sectors) {
n = s->req_nb_sectors;
@@ -887,6 +939,7 @@ static void ide_sector_write(IDEState *s)
if (!ide_sect_range_ok(s, sector_num, n)) {
ide_rw_error(s);
+ block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_WRITE);
return;
}
@@ -895,7 +948,7 @@ static void ide_sector_write(IDEState *s)
qemu_iovec_init_external(&s->qiov, &s->iov, 1);
block_acct_start(blk_get_stats(s->blk), &s->acct,
- n * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ);
+ n * BDRV_SECTOR_SIZE, BLOCK_ACCT_WRITE);
s->pio_aiocb = blk_aio_writev(s->blk, sector_num, &s->qiov, n,
ide_sector_write_cb, s);
}
@@ -2312,7 +2365,7 @@ int ide_init_drive(IDEState *s, BlockBackend *blk, IDEDriveKind kind,
if (version) {
pstrcpy(s->version, sizeof(s->version), version);
} else {
- pstrcpy(s->version, sizeof(s->version), qemu_get_version());
+ pstrcpy(s->version, sizeof(s->version), qemu_hw_version());
}
ide_reset(s);
diff --git a/hw/ide/ich.c b/hw/ide/ich.c
index 350c7f1c7..16925fa25 100644
--- a/hw/ide/ich.c
+++ b/hw/ide/ich.c
@@ -97,6 +97,13 @@ static void pci_ich9_reset(DeviceState *dev)
ahci_reset(&d->ahci);
}
+static void pci_ich9_ahci_init(Object *obj)
+{
+ struct AHCIPCIState *d = ICH_AHCI(obj);
+
+ ahci_init(&d->ahci, DEVICE(obj));
+}
+
static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp)
{
struct AHCIPCIState *d;
@@ -104,7 +111,7 @@ static void pci_ich9_ahci_realize(PCIDevice *dev, Error **errp)
uint8_t *sata_cap;
d = ICH_AHCI(dev);
- ahci_init(&d->ahci, DEVICE(dev), pci_get_address_space(dev), 6);
+ ahci_realize(&d->ahci, DEVICE(dev), pci_get_address_space(dev), 6);
pci_config_set_prog_interface(dev->config, AHCI_PROGMODE_MAJOR_REV_1);
@@ -171,6 +178,7 @@ static const TypeInfo ich_ahci_info = {
.name = TYPE_ICH9_AHCI,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(AHCIPCIState),
+ .instance_init = pci_ich9_ahci_init,
.class_init = ich_ahci_class_init,
};
diff --git a/hw/ide/internal.h b/hw/ide/internal.h
index 7288a677a..2d1e2d2d2 100644
--- a/hw/ide/internal.h
+++ b/hw/ide/internal.h
@@ -343,6 +343,16 @@ enum ide_dma_cmd {
#define ide_cmd_is_read(s) \
((s)->dma_cmd == IDE_DMA_READ)
+typedef struct IDEBufferedRequest {
+ QLIST_ENTRY(IDEBufferedRequest) list;
+ struct iovec iov;
+ QEMUIOVector qiov;
+ QEMUIOVector *original_qiov;
+ BlockCompletionFunc *original_cb;
+ void *original_opaque;
+ bool orphaned;
+} IDEBufferedRequest;
+
/* NOTE: IDEState represents in fact one drive */
struct IDEState {
IDEBus *bus;
@@ -396,8 +406,9 @@ struct IDEState {
BlockAIOCB *pio_aiocb;
struct iovec iov;
QEMUIOVector qiov;
+ QLIST_HEAD(, IDEBufferedRequest) buffered_requests;
/* ATA DMA state */
- int32_t io_buffer_offset;
+ uint64_t io_buffer_offset;
int32_t io_buffer_size;
QEMUSGList sg;
/* PIO transfer handling */
@@ -538,6 +549,7 @@ 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_abort_command(IDEState *s);
void ide_atapi_cmd_ok(IDEState *s);
void ide_atapi_cmd_error(IDEState *s, int sense_key, int asc);
@@ -571,6 +583,9 @@ void ide_set_inactive(IDEState *s, bool more);
BlockAIOCB *ide_issue_trim(BlockBackend *blk,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
BlockCompletionFunc *cb, void *opaque);
+BlockAIOCB *ide_buffered_readv(IDEState *s, int64_t sector_num,
+ QEMUIOVector *iov, int nb_sectors,
+ BlockCompletionFunc *cb, void *opaque);
/* hw/ide/atapi.c */
void ide_atapi_cmd(IDEState *s);
diff --git a/hw/ide/macio.c b/hw/ide/macio.c
index 66ac2baa9..3ee962f83 100644
--- a/hw/ide/macio.c
+++ b/hw/ide/macio.c
@@ -286,7 +286,11 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
return;
done:
- block_acct_done(blk_get_stats(s->blk), &s->acct);
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->blk), &s->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+ }
io->dma_end(opaque);
return;
@@ -348,7 +352,11 @@ static void pmac_ide_transfer_cb(void *opaque, int ret)
done:
if (s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) {
- block_acct_done(blk_get_stats(s->blk), &s->acct);
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->blk), &s->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->blk), &s->acct);
+ }
}
io->dma_end(opaque);
}
@@ -590,6 +598,7 @@ static void macio_ide_class_init(ObjectClass *oc, void *data)
dc->realize = macio_ide_realizefn;
dc->reset = macio_ide_reset;
dc->vmsd = &vmstate_pmac;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
}
static const TypeInfo macio_ide_type_info = {
diff --git a/hw/ide/pci.c b/hw/ide/pci.c
index d31ff885b..37dbc291d 100644
--- a/hw/ide/pci.c
+++ b/hw/ide/pci.c
@@ -103,13 +103,6 @@ static int32_t bmdma_prepare_buf(IDEDMA *dma, int32_t limit)
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
- * for LBA48 (65,536 sectors) and 32K sector sizes. */
- if (s->sg.size > INT32_MAX) {
- error_report("IDE: sglist describes more than 2GiB.");
- break;
- }
bm->cur_prd_addr += l;
bm->cur_prd_len -= l;
s->io_buffer_size += l;
@@ -240,6 +233,22 @@ void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val)
/* Ignore writes to SSBM if it keeps the old value */
if ((val & BM_CMD_START) != (bm->cmd & BM_CMD_START)) {
if (!(val & BM_CMD_START)) {
+ /* First invoke the callbacks of all buffered requests
+ * and flag those requests as orphaned. Ideally there
+ * are no unbuffered (Scatter Gather DMA Requests or
+ * write requests) pending and we can avoid to drain. */
+ IDEBufferedRequest *req;
+ IDEState *s = idebus_active_if(bm->bus);
+ QLIST_FOREACH(req, &s->buffered_requests, list) {
+ if (!req->orphaned) {
+#ifdef DEBUG_IDE
+ printf("%s: invoking cb %p of buffered request %p with"
+ " -ECANCELED\n", __func__, req->original_cb, req);
+#endif
+ req->original_cb(req->original_opaque, -ECANCELED);
+ }
+ req->orphaned = true;
+ }
/*
* We can't cancel Scatter Gather DMA in the middle of the
* operation or a partial (not full) DMA transfer would reach
@@ -253,6 +262,9 @@ void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val)
* aio operation with preadv/pwritev.
*/
if (bm->bus->dma->aiocb) {
+#ifdef DEBUG_IDE
+ printf("%s: draining all remaining requests", __func__);
+#endif
blk_drain_all();
assert(bm->bus->dma->aiocb == NULL);
}
diff --git a/hw/input/Makefile.objs b/hw/input/Makefile.objs
index 624ba7ea4..7715d7230 100644
--- a/hw/input/Makefile.objs
+++ b/hw/input/Makefile.objs
@@ -8,9 +8,9 @@ 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
+ifeq ($(CONFIG_LINUX),y)
common-obj-$(CONFIG_VIRTIO) += virtio-input-host.o
endif
diff --git a/hw/input/adb.c b/hw/input/adb.c
index a18eea265..09eead96b 100644
--- a/hw/input/adb.c
+++ b/hw/input/adb.c
@@ -362,6 +362,7 @@ static void adb_kbd_class_init(ObjectClass *oc, void *data)
akc->parent_realize = dc->realize;
dc->realize = adb_kbd_realizefn;
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
adc->devreq = adb_kbd_request;
dc->reset = adb_kbd_reset;
@@ -566,6 +567,7 @@ static void adb_mouse_class_init(ObjectClass *oc, void *data)
amc->parent_realize = dc->realize;
dc->realize = adb_mouse_realizefn;
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
adc->devreq = adb_mouse_request;
dc->reset = adb_mouse_reset;
diff --git a/hw/input/hid.c b/hw/input/hid.c
index 21ebd9e71..e39269fc7 100644
--- a/hw/input/hid.c
+++ b/hw/input/hid.c
@@ -119,33 +119,33 @@ static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
assert(hs->n < QUEUE_LENGTH);
e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
- switch (evt->kind) {
+ switch (evt->type) {
case INPUT_EVENT_KIND_REL:
- if (evt->rel->axis == INPUT_AXIS_X) {
- e->xdx += evt->rel->value;
- } else if (evt->rel->axis == INPUT_AXIS_Y) {
- e->ydy += evt->rel->value;
+ if (evt->u.rel->axis == INPUT_AXIS_X) {
+ e->xdx += evt->u.rel->value;
+ } else if (evt->u.rel->axis == INPUT_AXIS_Y) {
+ e->ydy += evt->u.rel->value;
}
break;
case INPUT_EVENT_KIND_ABS:
- if (evt->rel->axis == INPUT_AXIS_X) {
- e->xdx = evt->rel->value;
- } else if (evt->rel->axis == INPUT_AXIS_Y) {
- e->ydy = evt->rel->value;
+ if (evt->u.rel->axis == INPUT_AXIS_X) {
+ e->xdx = evt->u.rel->value;
+ } else if (evt->u.rel->axis == INPUT_AXIS_Y) {
+ e->ydy = evt->u.rel->value;
}
break;
case INPUT_EVENT_KIND_BTN:
- if (evt->btn->down) {
- e->buttons_state |= bmap[evt->btn->button];
- if (evt->btn->button == INPUT_BUTTON_WHEEL_UP) {
+ if (evt->u.btn->down) {
+ e->buttons_state |= bmap[evt->u.btn->button];
+ if (evt->u.btn->button == INPUT_BUTTON_WHEEL_UP) {
e->dz--;
- } else if (evt->btn->button == INPUT_BUTTON_WHEEL_DOWN) {
+ } else if (evt->u.btn->button == INPUT_BUTTON_WHEEL_DOWN) {
e->dz++;
}
} else {
- e->buttons_state &= ~bmap[evt->btn->button];
+ e->buttons_state &= ~bmap[evt->u.btn->button];
}
break;
@@ -223,8 +223,8 @@ static void hid_keyboard_event(DeviceState *dev, QemuConsole *src,
int scancodes[3], i, count;
int slot;
- count = qemu_input_key_value_to_scancode(evt->key->key,
- evt->key->down,
+ count = qemu_input_key_value_to_scancode(evt->u.key->key,
+ evt->u.key->down,
scancodes);
if (hs->n + count > QUEUE_LENGTH) {
fprintf(stderr, "usb-kbd: warning: key event queue full\n");
diff --git a/hw/input/milkymist-softusb.c b/hw/input/milkymist-softusb.c
index 7b0f4db88..8a02d35ca 100644
--- a/hw/input/milkymist-softusb.c
+++ b/hw/input/milkymist-softusb.c
@@ -255,12 +255,12 @@ static int milkymist_softusb_init(SysBusDevice *dev)
/* register pmem and dmem */
memory_region_init_ram(&s->pmem, OBJECT(s), "milkymist-softusb.pmem",
- s->pmem_size, &error_abort);
+ s->pmem_size, &error_fatal);
vmstate_register_ram_global(&s->pmem);
s->pmem_ptr = memory_region_get_ram_ptr(&s->pmem);
sysbus_init_mmio(dev, &s->pmem);
memory_region_init_ram(&s->dmem, OBJECT(s), "milkymist-softusb.dmem",
- s->dmem_size, &error_abort);
+ s->dmem_size, &error_fatal);
vmstate_register_ram_global(&s->dmem);
s->dmem_ptr = memory_region_get_ram_ptr(&s->dmem);
sysbus_init_mmio(dev, &s->dmem);
diff --git a/hw/input/ps2.c b/hw/input/ps2.c
index fdbe565e6..3d6d4961d 100644
--- a/hw/input/ps2.c
+++ b/hw/input/ps2.c
@@ -183,8 +183,8 @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
int scancodes[3], i, count;
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
- count = qemu_input_key_value_to_scancode(evt->key->key,
- evt->key->down,
+ count = qemu_input_key_value_to_scancode(evt->u.key->key,
+ evt->u.key->down,
scancodes);
for (i = 0; i < count; i++) {
ps2_put_keycode(s, scancodes[i]);
@@ -393,25 +393,25 @@ static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
return;
- switch (evt->kind) {
+ switch (evt->type) {
case INPUT_EVENT_KIND_REL:
- if (evt->rel->axis == INPUT_AXIS_X) {
- s->mouse_dx += evt->rel->value;
- } else if (evt->rel->axis == INPUT_AXIS_Y) {
- s->mouse_dy -= evt->rel->value;
+ if (evt->u.rel->axis == INPUT_AXIS_X) {
+ s->mouse_dx += evt->u.rel->value;
+ } else if (evt->u.rel->axis == INPUT_AXIS_Y) {
+ s->mouse_dy -= evt->u.rel->value;
}
break;
case INPUT_EVENT_KIND_BTN:
- if (evt->btn->down) {
- s->mouse_buttons |= bmap[evt->btn->button];
- if (evt->btn->button == INPUT_BUTTON_WHEEL_UP) {
+ if (evt->u.btn->down) {
+ s->mouse_buttons |= bmap[evt->u.btn->button];
+ if (evt->u.btn->button == INPUT_BUTTON_WHEEL_UP) {
s->mouse_dz--;
- } else if (evt->btn->button == INPUT_BUTTON_WHEEL_DOWN) {
+ } else if (evt->u.btn->button == INPUT_BUTTON_WHEEL_DOWN) {
s->mouse_dz++;
}
} else {
- s->mouse_buttons &= ~bmap[evt->btn->button];
+ s->mouse_buttons &= ~bmap[evt->u.btn->button];
}
break;
diff --git a/hw/input/stellaris_input.c b/hw/input/stellaris_input.c
index 0609e8086..4d86c04e5 100644
--- a/hw/input/stellaris_input.c
+++ b/hw/input/stellaris_input.c
@@ -69,14 +69,14 @@ static const VMStateDescription vmstate_stellaris_gamepad = {
}
};
-/* Returns an array 5 ouput slots. */
+/* Returns an array of 5 output slots. */
void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode)
{
gamepad_state *s;
int i;
- s = (gamepad_state *)g_malloc0(sizeof (gamepad_state));
- s->buttons = (gamepad_button *)g_malloc0(n * sizeof (gamepad_button));
+ s = g_new0(gamepad_state, 1);
+ s->buttons = g_new0(gamepad_button, n);
for (i = 0; i < n; i++) {
s->buttons[i].irq = irq[i];
s->buttons[i].keycode = keycode[i];
diff --git a/hw/input/tsc210x.c b/hw/input/tsc210x.c
index fae338563..1073bbfec 100644
--- a/hw/input/tsc210x.c
+++ b/hw/input/tsc210x.c
@@ -1086,9 +1086,7 @@ uWireSlave *tsc2102_init(qemu_irq pint)
{
TSC210xState *s;
- s = (TSC210xState *)
- g_malloc0(sizeof(TSC210xState));
- memset(s, 0, sizeof(TSC210xState));
+ s = g_new0(TSC210xState, 1);
s->x = 160;
s->y = 160;
s->pressure = 0;
@@ -1135,9 +1133,7 @@ uWireSlave *tsc2301_init(qemu_irq penirq, qemu_irq kbirq, qemu_irq dav)
{
TSC210xState *s;
- s = (TSC210xState *)
- g_malloc0(sizeof(TSC210xState));
- memset(s, 0, sizeof(TSC210xState));
+ s = g_new0(TSC210xState, 1);
s->x = 400;
s->y = 240;
s->pressure = 0;
diff --git a/hw/input/virtio-input-hid.c b/hw/input/virtio-input-hid.c
index 4d85dad4d..bdd479cd0 100644
--- a/hw/input/virtio-input-hid.c
+++ b/hw/input/virtio-input-hid.c
@@ -191,44 +191,45 @@ static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src,
virtio_input_event event;
int qcode;
- switch (evt->kind) {
+ switch (evt->type) {
case INPUT_EVENT_KIND_KEY:
- qcode = qemu_input_key_value_to_qcode(evt->key->key);
+ qcode = qemu_input_key_value_to_qcode(evt->u.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);
+ event.value = cpu_to_le32(evt->u.key->down ? 1 : 0);
virtio_input_send(vinput, &event);
} else {
- if (evt->key->down) {
+ if (evt->u.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]) {
+ if (keymap_button[evt->u.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);
+ event.code = cpu_to_le16(keymap_button[evt->u.btn->button]);
+ event.value = cpu_to_le32(evt->u.btn->down ? 1 : 0);
virtio_input_send(vinput, &event);
} else {
- if (evt->btn->down) {
+ if (evt->u.btn->down) {
fprintf(stderr, "%s: unmapped button: %d [%s]\n", __func__,
- evt->btn->button, InputButton_lookup[evt->btn->button]);
+ evt->u.btn->button,
+ InputButton_lookup[evt->u.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);
+ event.code = cpu_to_le16(axismap_rel[evt->u.rel->axis]);
+ event.value = cpu_to_le32(evt->u.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);
+ event.code = cpu_to_le16(axismap_abs[evt->u.abs->axis]);
+ event.value = cpu_to_le32(evt->u.abs->value);
virtio_input_send(vinput, &event);
break;
default:
diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
index 092d8a80a..004b0c25e 100644
--- a/hw/intc/Makefile.objs
+++ b/hw/intc/Makefile.objs
@@ -12,10 +12,12 @@ 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_ARM_GIC) += arm_gicv3_common.o
common-obj-$(CONFIG_OPENPIC) += openpic.o
obj-$(CONFIG_APIC) += apic.o apic_common.o
obj-$(CONFIG_ARM_GIC_KVM) += arm_gic_kvm.o
+obj-$(call land,$(CONFIG_ARM_GIC_KVM),$(TARGET_AARCH64)) += arm_gicv3_kvm.o
obj-$(CONFIG_STELLARIS) += armv7m_nvic.o
obj-$(CONFIG_EXYNOS4) += exynos4210_gic.o exynos4210_combiner.o
obj-$(CONFIG_GRLIB) += grlib_irqmp.o
diff --git a/hw/intc/apic.c b/hw/intc/apic.c
index 77b639cce..0a840b851 100644
--- a/hw/intc/apic.c
+++ b/hw/intc/apic.c
@@ -51,14 +51,6 @@ static int apic_ffs_bit(uint32_t value)
return ctz32(value);
}
-static inline void apic_set_bit(uint32_t *tab, int index)
-{
- int i, mask;
- i = index >> 5;
- mask = 1 << (index & 0x1f);
- tab[i] |= mask;
-}
-
static inline void apic_reset_bit(uint32_t *tab, int index)
{
int i, mask;
@@ -67,14 +59,6 @@ static inline void apic_reset_bit(uint32_t *tab, int index)
tab[i] &= ~mask;
}
-static inline int apic_get_bit(uint32_t *tab, int index)
-{
- int i, mask;
- i = index >> 5;
- mask = 1 << (index & 0x1f);
- return !!(tab[i] & mask);
-}
-
/* return -1 if no bit is set */
static int get_highest_priority_int(uint32_t *tab)
{
@@ -318,7 +302,7 @@ static uint8_t apic_get_tpr(APICCommonState *s)
return s->tpr >> 4;
}
-static int apic_get_ppr(APICCommonState *s)
+int apic_get_ppr(APICCommonState *s)
{
int tpr, isrv, ppr;
@@ -739,7 +723,7 @@ static uint32_t apic_mem_readl(void *opaque, hwaddr addr)
val = s->divide_conf;
break;
default:
- s->esr |= ESR_ILLEGAL_ADDRESS;
+ s->esr |= APIC_ESR_ILLEGAL_ADDRESS;
val = 0;
break;
}
@@ -852,7 +836,7 @@ static void apic_mem_writel(void *opaque, hwaddr addr, uint32_t val)
}
break;
default:
- s->esr |= ESR_ILLEGAL_ADDRESS;
+ s->esr |= APIC_ESR_ILLEGAL_ADDRESS;
break;
}
}
diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c
index 0032b97c5..ad959c4e7 100644
--- a/hw/intc/apic_common.c
+++ b/hw/intc/apic_common.c
@@ -296,7 +296,6 @@ static void apic_common_realize(DeviceState *dev, Error **errp)
APICCommonClass *info;
static DeviceState *vapic;
static int apic_no;
- static bool mmio_registered;
if (apic_no >= MAX_APICS) {
error_setg(errp, "%s initialization failed.",
@@ -307,11 +306,6 @@ static void apic_common_realize(DeviceState *dev, Error **errp)
info = APIC_COMMON_GET_CLASS(s);
info->realize(dev, errp);
- if (!mmio_registered) {
- ICCBus *b = ICC_BUS(qdev_get_parent_bus(dev));
- memory_region_add_subregion(b->apic_address_space, 0, &s->io_memory);
- mmio_registered = true;
- }
/* Note: We need at least 1M to map the VAPIC option ROM */
if (!vapic && s->vapic_control & VAPIC_ENABLE_MASK &&
@@ -425,13 +419,12 @@ static Property apic_properties_common[] = {
static void apic_common_class_init(ObjectClass *klass, void *data)
{
- ICCDeviceClass *idc = ICC_DEVICE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
dc->vmsd = &vmstate_apic_common;
dc->reset = apic_reset_common;
dc->props = apic_properties_common;
- idc->realize = apic_common_realize;
+ dc->realize = apic_common_realize;
/*
* Reason: APIC and CPU need to be wired up by
* x86_cpu_apic_create()
@@ -441,7 +434,7 @@ static void apic_common_class_init(ObjectClass *klass, void *data)
static const TypeInfo apic_common_type = {
.name = TYPE_APIC_COMMON,
- .parent = TYPE_ICC_DEVICE,
+ .parent = TYPE_DEVICE,
.instance_size = sizeof(APICCommonState),
.class_size = sizeof(APICCommonClass),
.class_init = apic_common_class_init,
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index 454bfd7df..13e297d52 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -35,8 +35,6 @@ static const uint8_t gic_id[] = {
0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1
};
-#define NUM_CPU(s) ((s)->num_cpu)
-
static inline int gic_get_current_cpu(GICState *s)
{
if (s->num_cpu > 1) {
@@ -64,7 +62,7 @@ void gic_update(GICState *s)
int cpu;
int cm;
- for (cpu = 0; cpu < NUM_CPU(s); cpu++) {
+ for (cpu = 0; cpu < s->num_cpu; cpu++) {
cm = 1 << cpu;
s->current_pending[cpu] = 1023;
if (!(s->ctlr & (GICD_CTLR_EN_GRP0 | GICD_CTLR_EN_GRP1))
@@ -219,15 +217,99 @@ static uint16_t gic_get_current_pending_irq(GICState *s, int cpu,
return pending_irq;
}
-static void gic_set_running_irq(GICState *s, int cpu, int irq)
+static int gic_get_group_priority(GICState *s, int cpu, int irq)
{
- s->running_irq[cpu] = irq;
- if (irq == 1023) {
- s->running_priority[cpu] = 0x100;
+ /* Return the group priority of the specified interrupt
+ * (which is the top bits of its priority, with the number
+ * of bits masked determined by the applicable binary point register).
+ */
+ int bpr;
+ uint32_t mask;
+
+ if (gic_has_groups(s) &&
+ !(s->cpu_ctlr[cpu] & GICC_CTLR_CBPR) &&
+ GIC_TEST_GROUP(irq, (1 << cpu))) {
+ bpr = s->abpr[cpu];
} else {
- s->running_priority[cpu] = GIC_GET_PRIORITY(irq, cpu);
+ bpr = s->bpr[cpu];
}
- gic_update(s);
+
+ /* a BPR of 0 means the group priority bits are [7:1];
+ * a BPR of 1 means they are [7:2], and so on down to
+ * a BPR of 7 meaning no group priority bits at all.
+ */
+ mask = ~0U << ((bpr & 7) + 1);
+
+ return GIC_GET_PRIORITY(irq, cpu) & mask;
+}
+
+static void gic_activate_irq(GICState *s, int cpu, int irq)
+{
+ /* Set the appropriate Active Priority Register bit for this IRQ,
+ * and update the running priority.
+ */
+ int prio = gic_get_group_priority(s, cpu, irq);
+ int preemption_level = prio >> (GIC_MIN_BPR + 1);
+ int regno = preemption_level / 32;
+ int bitno = preemption_level % 32;
+
+ if (gic_has_groups(s) && GIC_TEST_GROUP(irq, (1 << cpu))) {
+ s->nsapr[regno][cpu] |= (1 << bitno);
+ } else {
+ s->apr[regno][cpu] |= (1 << bitno);
+ }
+
+ s->running_priority[cpu] = prio;
+ GIC_SET_ACTIVE(irq, 1 << cpu);
+}
+
+static int gic_get_prio_from_apr_bits(GICState *s, int cpu)
+{
+ /* Recalculate the current running priority for this CPU based
+ * on the set bits in the Active Priority Registers.
+ */
+ int i;
+ for (i = 0; i < GIC_NR_APRS; i++) {
+ uint32_t apr = s->apr[i][cpu] | s->nsapr[i][cpu];
+ if (!apr) {
+ continue;
+ }
+ return (i * 32 + ctz32(apr)) << (GIC_MIN_BPR + 1);
+ }
+ return 0x100;
+}
+
+static void gic_drop_prio(GICState *s, int cpu, int group)
+{
+ /* Drop the priority of the currently active interrupt in the
+ * specified group.
+ *
+ * Note that we can guarantee (because of the requirement to nest
+ * GICC_IAR reads [which activate an interrupt and raise priority]
+ * with GICC_EOIR writes [which drop the priority for the interrupt])
+ * that the interrupt we're being called for is the highest priority
+ * active interrupt, meaning that it has the lowest set bit in the
+ * APR registers.
+ *
+ * If the guest does not honour the ordering constraints then the
+ * behaviour of the GIC is UNPREDICTABLE, which for us means that
+ * the values of the APR registers might become incorrect and the
+ * running priority will be wrong, so interrupts that should preempt
+ * might not do so, and interrupts that should not preempt might do so.
+ */
+ int i;
+
+ for (i = 0; i < GIC_NR_APRS; i++) {
+ uint32_t *papr = group ? &s->nsapr[i][cpu] : &s->apr[i][cpu];
+ if (!*papr) {
+ continue;
+ }
+ /* Clear lowest set bit */
+ *papr &= *papr - 1;
+ break;
+ }
+
+ s->running_priority[cpu] = gic_get_prio_from_apr_bits(s, cpu);
}
uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs)
@@ -239,7 +321,7 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs)
* 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);;
+ 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);
@@ -250,7 +332,6 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs)
DPRINTF("ACK, pending interrupt (%d) has insufficient priority\n", irq);
return 1023;
}
- s->last_active[irq][cpu] = s->running_irq[cpu];
if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
/* Clear pending flags for both level and edge triggered interrupts.
@@ -281,7 +362,8 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs)
}
}
- gic_set_running_irq(s, cpu, irq);
+ gic_activate_irq(s, cpu, irq);
+ gic_update(s);
DPRINTF("ACK %d\n", irq);
return ret;
}
@@ -411,8 +493,9 @@ static uint8_t gic_get_running_priority(GICState *s, int cpu, MemTxAttrs attrs)
void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
{
- int update = 0;
int cm = 1 << cpu;
+ int group;
+
DPRINTF("EOI %d\n", irq);
if (irq >= s->num_irq) {
/* This handles two cases:
@@ -425,8 +508,9 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
*/
return;
}
- if (s->running_irq[cpu] == 1023)
+ if (s->running_priority[cpu] == 0x100) {
return; /* No active IRQ. */
+ }
if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
/* Mark level triggered interrupts as pending if they are still
@@ -435,11 +519,12 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
&& GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
DPRINTF("Set %d pending mask %x\n", irq, cm);
GIC_SET_PENDING(irq, cm);
- update = 1;
}
}
- if (s->security_extn && !attrs.secure && !GIC_TEST_GROUP(irq, cm)) {
+ group = gic_has_groups(s) && GIC_TEST_GROUP(irq, cm);
+
+ if (s->security_extn && !attrs.secure && !group) {
DPRINTF("Non-secure EOI for Group0 interrupt %d ignored\n", irq);
return;
}
@@ -449,23 +534,9 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
* 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];
- while (s->last_active[tmp][cpu] != 1023) {
- if (s->last_active[tmp][cpu] == irq) {
- s->last_active[tmp][cpu] = s->last_active[irq][cpu];
- break;
- }
- tmp = s->last_active[tmp][cpu];
- }
- if (update) {
- gic_update(s);
- }
- } else {
- /* Complete the current running IRQ. */
- gic_set_running_irq(s, cpu, s->last_active[s->running_irq[cpu]][cpu]);
- }
+ gic_drop_prio(s, cpu, group);
+ GIC_CLEAR_ACTIVE(irq, cm);
+ gic_update(s);
}
static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs)
@@ -494,7 +565,7 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs)
if (offset == 4)
/* Interrupt Controller Type Register */
return ((s->num_irq / 32) - 1)
- | ((NUM_CPU(s) - 1) << 5)
+ | ((s->num_cpu - 1) << 5)
| (s->security_extn << 10);
if (offset < 0x08)
return 0;
@@ -922,11 +993,67 @@ static MemTxResult gic_dist_write(void *opaque, hwaddr offset, uint64_t data,
}
}
-static const MemoryRegionOps gic_dist_ops = {
- .read_with_attrs = gic_dist_read,
- .write_with_attrs = gic_dist_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
+static inline uint32_t gic_apr_ns_view(GICState *s, int cpu, int regno)
+{
+ /* Return the Nonsecure view of GICC_APR<regno>. This is the
+ * second half of GICC_NSAPR.
+ */
+ switch (GIC_MIN_BPR) {
+ case 0:
+ if (regno < 2) {
+ return s->nsapr[regno + 2][cpu];
+ }
+ break;
+ case 1:
+ if (regno == 0) {
+ return s->nsapr[regno + 1][cpu];
+ }
+ break;
+ case 2:
+ if (regno == 0) {
+ return extract32(s->nsapr[0][cpu], 16, 16);
+ }
+ break;
+ case 3:
+ if (regno == 0) {
+ return extract32(s->nsapr[0][cpu], 8, 8);
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ return 0;
+}
+
+static inline void gic_apr_write_ns_view(GICState *s, int cpu, int regno,
+ uint32_t value)
+{
+ /* Write the Nonsecure view of GICC_APR<regno>. */
+ switch (GIC_MIN_BPR) {
+ case 0:
+ if (regno < 2) {
+ s->nsapr[regno + 2][cpu] = value;
+ }
+ break;
+ case 1:
+ if (regno == 0) {
+ s->nsapr[regno + 1][cpu] = value;
+ }
+ break;
+ case 2:
+ if (regno == 0) {
+ s->nsapr[0][cpu] = deposit32(s->nsapr[0][cpu], 16, 16, value);
+ }
+ break;
+ case 3:
+ if (regno == 0) {
+ s->nsapr[0][cpu] = deposit32(s->nsapr[0][cpu], 8, 8, value);
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset,
uint64_t *data, MemTxAttrs attrs)
@@ -968,8 +1095,31 @@ static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset,
}
break;
case 0xd0: case 0xd4: case 0xd8: case 0xdc:
- *data = s->apr[(offset - 0xd0) / 4][cpu];
+ {
+ int regno = (offset - 0xd0) / 4;
+
+ if (regno >= GIC_NR_APRS || s->revision != 2) {
+ *data = 0;
+ } else if (s->security_extn && !attrs.secure) {
+ /* NS view of GICC_APR<n> is the top half of GIC_NSAPR<n> */
+ *data = gic_apr_ns_view(s, regno, cpu);
+ } else {
+ *data = s->apr[regno][cpu];
+ }
+ break;
+ }
+ case 0xe0: case 0xe4: case 0xe8: case 0xec:
+ {
+ int regno = (offset - 0xe0) / 4;
+
+ if (regno >= GIC_NR_APRS || s->revision != 2 || !gic_has_groups(s) ||
+ (s->security_extn && !attrs.secure)) {
+ *data = 0;
+ } else {
+ *data = s->nsapr[regno][cpu];
+ }
break;
+ }
default:
qemu_log_mask(LOG_GUEST_ERROR,
"gic_cpu_read: Bad offset %x\n", (int)offset);
@@ -1007,8 +1157,33 @@ static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset,
}
break;
case 0xd0: case 0xd4: case 0xd8: case 0xdc:
- qemu_log_mask(LOG_UNIMP, "Writing APR not implemented\n");
+ {
+ int regno = (offset - 0xd0) / 4;
+
+ if (regno >= GIC_NR_APRS || s->revision != 2) {
+ return MEMTX_OK;
+ }
+ if (s->security_extn && !attrs.secure) {
+ /* NS view of GICC_APR<n> is the top half of GIC_NSAPR<n> */
+ gic_apr_write_ns_view(s, regno, cpu, value);
+ } else {
+ s->apr[regno][cpu] = value;
+ }
+ break;
+ }
+ case 0xe0: case 0xe4: case 0xe8: case 0xec:
+ {
+ int regno = (offset - 0xe0) / 4;
+
+ if (regno >= GIC_NR_APRS || s->revision != 2) {
+ return MEMTX_OK;
+ }
+ if (!gic_has_groups(s) || (s->security_extn && !attrs.secure)) {
+ return MEMTX_OK;
+ }
+ s->nsapr[regno][cpu] = value;
break;
+ }
default:
qemu_log_mask(LOG_GUEST_ERROR,
"gic_cpu_write: Bad offset %x\n", (int)offset);
@@ -1056,10 +1231,17 @@ static MemTxResult gic_do_cpu_write(void *opaque, hwaddr addr,
return gic_cpu_write(s, id, addr, value, attrs);
}
-static const MemoryRegionOps gic_thiscpu_ops = {
- .read_with_attrs = gic_thiscpu_read,
- .write_with_attrs = gic_thiscpu_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
+static const MemoryRegionOps gic_ops[2] = {
+ {
+ .read_with_attrs = gic_dist_read,
+ .write_with_attrs = gic_dist_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ },
+ {
+ .read_with_attrs = gic_thiscpu_read,
+ .write_with_attrs = gic_thiscpu_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ }
};
static const MemoryRegionOps gic_cpu_ops = {
@@ -1068,31 +1250,10 @@ static const MemoryRegionOps gic_cpu_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
+/* This function is used by nvic model */
void gic_init_irqs_and_distributor(GICState *s)
{
- SysBusDevice *sbd = SYS_BUS_DEVICE(s);
- int i;
-
- i = s->num_irq - GIC_INTERNAL;
- /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU.
- * GPIO array layout is thus:
- * [0..N-1] SPIs
- * [N..N+31] PPIs for CPU 0
- * [N+32..N+63] PPIs for CPU 1
- * ...
- */
- if (s->revision != REV_NVIC) {
- i += (GIC_INTERNAL * s->num_cpu);
- }
- qdev_init_gpio_in(DEVICE(s), gic_set_irq, i);
- 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);
+ gic_init_irqs_and_mmio(s, gic_set_irq, gic_ops);
}
static void arm_gic_realize(DeviceState *dev, Error **errp)
@@ -1110,28 +1271,22 @@ static void arm_gic_realize(DeviceState *dev, Error **errp)
return;
}
- gic_init_irqs_and_distributor(s);
+ /* This creates distributor and main CPU interface (s->cpuiomem[0]) */
+ gic_init_irqs_and_mmio(s, gic_set_irq, gic_ops);
- /* Memory regions for the CPU interfaces (NVIC doesn't have these):
- * a region for "CPU interface for this core", then a region for
- * "CPU interface for core 0", "for core 1", ...
+ /* Extra core-specific regions for the CPU interfaces. This is
+ * necessary for "franken-GIC" implementations, for example on
+ * Exynos 4.
* NB that the memory region size of 0x100 applies for the 11MPCore
* and also cores following the GIC v1 spec (ie A9).
* GIC v2 defines a larger memory region (0x1000) so this will need
* to be extended when we implement A15.
*/
- memory_region_init_io(&s->cpuiomem[0], OBJECT(s), &gic_thiscpu_ops, s,
- "gic_cpu", 0x100);
- for (i = 0; i < NUM_CPU(s); i++) {
+ for (i = 0; i < s->num_cpu; i++) {
s->backref[i] = s;
memory_region_init_io(&s->cpuiomem[i+1], OBJECT(s), &gic_cpu_ops,
&s->backref[i], "gic_cpu", 0x100);
- }
- /* Distributor */
- sysbus_init_mmio(sbd, &s->iomem);
- /* cpu interfaces (one for "current cpu" plus one per cpu) */
- for (i = 0; i <= NUM_CPU(s); i++) {
- sysbus_init_mmio(sbd, &s->cpuiomem[i]);
+ sysbus_init_mmio(sbd, &s->cpuiomem[i+1]);
}
}
diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c
index a64d0714e..9c82b9729 100644
--- a/hw/intc/arm_gic_common.c
+++ b/hw/intc/arm_gic_common.c
@@ -19,6 +19,7 @@
*/
#include "gic_internal.h"
+#include "hw/arm/linux-boot-if.h"
static void gic_pre_save(void *opaque)
{
@@ -59,8 +60,8 @@ static const VMStateDescription vmstate_gic_irq_state = {
static const VMStateDescription vmstate_gic = {
.name = "arm_gic",
- .version_id = 10,
- .minimum_version_id = 10,
+ .version_id = 12,
+ .minimum_version_id = 12,
.pre_save = gic_pre_save,
.post_load = gic_post_load,
.fields = (VMStateField[]) {
@@ -71,19 +72,59 @@ static const VMStateDescription vmstate_gic = {
VMSTATE_UINT8_ARRAY(irq_target, GICState, GIC_MAXIRQ),
VMSTATE_UINT8_2DARRAY(priority1, GICState, GIC_INTERNAL, GIC_NCPU),
VMSTATE_UINT8_ARRAY(priority2, GICState, GIC_MAXIRQ - GIC_INTERNAL),
- VMSTATE_UINT16_2DARRAY(last_active, GICState, GIC_MAXIRQ, GIC_NCPU),
VMSTATE_UINT8_2DARRAY(sgi_pending, GICState, GIC_NR_SGIS, GIC_NCPU),
VMSTATE_UINT16_ARRAY(priority_mask, GICState, GIC_NCPU),
- VMSTATE_UINT16_ARRAY(running_irq, GICState, GIC_NCPU),
VMSTATE_UINT16_ARRAY(running_priority, GICState, GIC_NCPU),
VMSTATE_UINT16_ARRAY(current_pending, GICState, GIC_NCPU),
VMSTATE_UINT8_ARRAY(bpr, GICState, GIC_NCPU),
VMSTATE_UINT8_ARRAY(abpr, GICState, GIC_NCPU),
VMSTATE_UINT32_2DARRAY(apr, GICState, GIC_NR_APRS, GIC_NCPU),
+ VMSTATE_UINT32_2DARRAY(nsapr, GICState, GIC_NR_APRS, GIC_NCPU),
VMSTATE_END_OF_LIST()
}
};
+void gic_init_irqs_and_mmio(GICState *s, qemu_irq_handler handler,
+ const MemoryRegionOps *ops)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(s);
+ int i = s->num_irq - GIC_INTERNAL;
+
+ /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU.
+ * GPIO array layout is thus:
+ * [0..N-1] SPIs
+ * [N..N+31] PPIs for CPU 0
+ * [N+32..N+63] PPIs for CPU 1
+ * ...
+ */
+ if (s->revision != REV_NVIC) {
+ i += (GIC_INTERNAL * s->num_cpu);
+ }
+ qdev_init_gpio_in(DEVICE(s), handler, i);
+
+ 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]);
+ }
+
+ /* Distributor */
+ memory_region_init_io(&s->iomem, OBJECT(s), ops, s, "gic_dist", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+
+ if (s->revision != REV_NVIC) {
+ /* This is the main CPU interface "for this core". It is always
+ * present because it is required by both software emulation and KVM.
+ * NVIC is not handled here because its CPU interface is different,
+ * neither it can use KVM.
+ */
+ memory_region_init_io(&s->cpuiomem[0], OBJECT(s), ops ? &ops[1] : NULL,
+ s, "gic_cpu", s->revision == 2 ? 0x1000 : 0x100);
+ sysbus_init_mmio(sbd, &s->cpuiomem[0]);
+ }
+}
+
static void arm_gic_common_realize(DeviceState *dev, Error **errp)
{
GICState *s = ARM_GIC_COMMON(dev);
@@ -124,21 +165,35 @@ static void arm_gic_common_reset(DeviceState *dev)
{
GICState *s = ARM_GIC_COMMON(dev);
int i, j;
+ int resetprio;
+
+ /* If we're resetting a TZ-aware GIC as if secure firmware
+ * had set it up ready to start a kernel in non-secure,
+ * we need to set interrupt priorities to a "zero for the
+ * NS view" value. This is particularly critical for the
+ * priority_mask[] values, because if they are zero then NS
+ * code cannot ever rewrite the priority to anything else.
+ */
+ if (s->security_extn && s->irq_reset_nonsecure) {
+ resetprio = 0x80;
+ } else {
+ resetprio = 0;
+ }
+
memset(s->irq_state, 0, GIC_MAXIRQ * sizeof(gic_irq_state));
for (i = 0 ; i < s->num_cpu; i++) {
if (s->revision == REV_11MPCORE) {
s->priority_mask[i] = 0xf0;
} else {
- s->priority_mask[i] = 0;
+ s->priority_mask[i] = resetprio;
}
s->current_pending[i] = 1023;
- s->running_irq[i] = 1023;
s->running_priority[i] = 0x100;
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;
+ s->priority1[j][i] = resetprio;
}
for (j = 0; j < GIC_NR_SGIS; j++) {
s->sgi_pending[j][i] = 0;
@@ -150,7 +205,7 @@ static void arm_gic_common_reset(DeviceState *dev)
}
for (i = 0; i < ARRAY_SIZE(s->priority2); i++) {
- s->priority2[i] = 0;
+ s->priority2[i] = resetprio;
}
for (i = 0; i < GIC_MAXIRQ; i++) {
@@ -161,9 +216,32 @@ static void arm_gic_common_reset(DeviceState *dev)
s->irq_target[i] = 0;
}
}
+ if (s->security_extn && s->irq_reset_nonsecure) {
+ for (i = 0; i < GIC_MAXIRQ; i++) {
+ GIC_SET_GROUP(i, ALL_CPU_MASK);
+ }
+ }
+
s->ctlr = 0;
}
+static void arm_gic_common_linux_init(ARMLinuxBootIf *obj,
+ bool secure_boot)
+{
+ GICState *s = ARM_GIC_COMMON(obj);
+
+ if (s->security_extn && !secure_boot) {
+ /* We're directly booting a kernel into NonSecure. If this GIC
+ * implements the security extensions then we must configure it
+ * to have all the interrupts be NonSecure (this is a job that
+ * is done by the Secure boot firmware in real hardware, and in
+ * this mode QEMU is acting as a minimalist firmware-and-bootloader
+ * equivalent).
+ */
+ s->irq_reset_nonsecure = true;
+ }
+}
+
static Property arm_gic_common_properties[] = {
DEFINE_PROP_UINT32("num-cpu", GICState, num_cpu, 1),
DEFINE_PROP_UINT32("num-irq", GICState, num_irq, 32),
@@ -180,11 +258,13 @@ static Property arm_gic_common_properties[] = {
static void arm_gic_common_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
+ ARMLinuxBootIfClass *albifc = ARM_LINUX_BOOT_IF_CLASS(klass);
dc->reset = arm_gic_common_reset;
dc->realize = arm_gic_common_realize;
dc->props = arm_gic_common_properties;
dc->vmsd = &vmstate_gic;
+ albifc->arm_linux_init = arm_gic_common_linux_init;
}
static const TypeInfo arm_gic_common_type = {
@@ -194,6 +274,10 @@ static const TypeInfo arm_gic_common_type = {
.class_size = sizeof(ARMGICCommonClass),
.class_init = arm_gic_common_class_init,
.abstract = true,
+ .interfaces = (InterfaceInfo []) {
+ { TYPE_ARM_LINUX_BOOT_IF },
+ { },
+ },
};
static void register_types(void)
diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c
index f56bff1af..0ceebbf87 100644
--- a/hw/intc/arm_gic_kvm.c
+++ b/hw/intc/arm_gic_kvm.c
@@ -20,9 +20,11 @@
*/
#include "hw/sysbus.h"
+#include "migration/migration.h"
#include "sysemu/kvm.h"
#include "kvm_arm.h"
#include "gic_internal.h"
+#include "vgic_common.h"
//#define DEBUG_GIC_KVM
@@ -52,7 +54,7 @@ typedef struct KVMARMGICClass {
void (*parent_reset)(DeviceState *dev);
} KVMARMGICClass;
-static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
+void kvm_arm_gic_set_irq(uint32_t num_irq, int irq, int level)
{
/* Meaning of the 'irq' parameter:
* [0..N-1] : external interrupts
@@ -63,10 +65,9 @@ static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
* has separate fields in the irq number for type,
* CPU number and interrupt number.
*/
- GICState *s = (GICState *)opaque;
int kvm_irq, irqtype, cpu;
- if (irq < (s->num_irq - GIC_INTERNAL)) {
+ if (irq < (num_irq - GIC_INTERNAL)) {
/* External interrupt. The kernel numbers these like the GIC
* hardware, with external interrupt IDs starting after the
* internal ones.
@@ -77,7 +78,7 @@ static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
} else {
/* Internal interrupt: decode into (cpu, interrupt id) */
irqtype = KVM_ARM_IRQ_TYPE_PPI;
- irq -= (s->num_irq - GIC_INTERNAL);
+ irq -= (num_irq - GIC_INTERNAL);
cpu = irq / GIC_INTERNAL;
irq %= GIC_INTERNAL;
}
@@ -87,69 +88,36 @@ static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
kvm_set_irq(kvm_state, kvm_irq, !!level);
}
-static bool kvm_arm_gic_can_save_restore(GICState *s)
-{
- return s->dev_fd >= 0;
-}
-
-static bool kvm_gic_supports_attr(GICState *s, int group, int attrnum)
+static void kvm_arm_gicv2_set_irq(void *opaque, int irq, int level)
{
- struct kvm_device_attr attr = {
- .group = group,
- .attr = attrnum,
- .flags = 0,
- };
-
- if (s->dev_fd == -1) {
- return false;
- }
+ GICState *s = (GICState *)opaque;
- return kvm_device_ioctl(s->dev_fd, KVM_HAS_DEVICE_ATTR, &attr) == 0;
+ kvm_arm_gic_set_irq(s->num_irq, irq, level);
}
-static void kvm_gic_access(GICState *s, int group, int offset,
- int cpu, uint32_t *val, bool write)
+static bool kvm_arm_gic_can_save_restore(GICState *s)
{
- struct kvm_device_attr attr;
- int type;
- int err;
-
- cpu = cpu & 0xff;
-
- attr.flags = 0;
- attr.group = group;
- attr.attr = (((uint64_t)cpu << KVM_DEV_ARM_VGIC_CPUID_SHIFT) &
- KVM_DEV_ARM_VGIC_CPUID_MASK) |
- (((uint64_t)offset << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) &
- KVM_DEV_ARM_VGIC_OFFSET_MASK);
- attr.addr = (uintptr_t)val;
-
- if (write) {
- type = KVM_SET_DEVICE_ATTR;
- } else {
- type = KVM_GET_DEVICE_ATTR;
- }
-
- err = kvm_device_ioctl(s->dev_fd, type, &attr);
- if (err < 0) {
- fprintf(stderr, "KVM_{SET/GET}_DEVICE_ATTR failed: %s\n",
- strerror(-err));
- abort();
- }
+ return s->dev_fd >= 0;
}
+#define KVM_VGIC_ATTR(offset, cpu) \
+ ((((uint64_t)(cpu) << KVM_DEV_ARM_VGIC_CPUID_SHIFT) & \
+ KVM_DEV_ARM_VGIC_CPUID_MASK) | \
+ (((uint64_t)(offset) << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) & \
+ KVM_DEV_ARM_VGIC_OFFSET_MASK))
+
static void kvm_gicd_access(GICState *s, int offset, int cpu,
uint32_t *val, bool write)
{
- kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
- offset, cpu, val, write);
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
+ KVM_VGIC_ATTR(offset, cpu), val, write);
}
static void kvm_gicc_access(GICState *s, int offset, int cpu,
uint32_t *val, bool write)
{
- kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_CPU_REGS,
- offset, cpu, val, write);
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CPU_REGS,
+ KVM_VGIC_ATTR(offset, cpu), val, write);
}
#define for_each_irq_reg(_ctr, _max_irq, _field_width) \
@@ -340,11 +308,6 @@ static void kvm_arm_gic_put(GICState *s)
int num_cpu;
int num_irq;
- if (!kvm_arm_gic_can_save_restore(s)) {
- DPRINTF("Cannot put kernel gic state, no kernel interface");
- return;
- }
-
/* Note: We do the restore in a slightly different order than the save
* (where the order doesn't matter and is simply ordered according to the
* register offset values */
@@ -444,11 +407,6 @@ static void kvm_arm_gic_get(GICState *s)
int i;
int cpu;
- if (!kvm_arm_gic_can_save_restore(s)) {
- DPRINTF("Cannot get kernel gic state, no kernel interface");
- return;
- }
-
/*****************************************************************
* Distributor State
*/
@@ -536,14 +494,16 @@ static void kvm_arm_gic_reset(DeviceState *dev)
KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s);
kgc->parent_reset(dev);
- kvm_arm_gic_put(s);
+
+ if (kvm_arm_gic_can_save_restore(s)) {
+ kvm_arm_gic_put(s);
+ }
}
static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
{
int i;
GICState *s = KVM_ARM_GIC(dev);
- SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s);
Error *local_err = NULL;
int ret;
@@ -560,58 +520,37 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
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:
- * [0..N-1] SPIs
- * [N..N+31] PPIs for CPU 0
- * [N+32..N+63] PPIs for CPU 1
- * ...
- */
- i += (GIC_INTERNAL * s->num_cpu);
- qdev_init_gpio_in(dev, kvm_arm_gic_set_irq, i);
+ gic_init_irqs_and_mmio(s, kvm_arm_gicv2_set_irq, NULL);
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;
ret = kvm_create_device(kvm_state, KVM_DEV_TYPE_ARM_VGIC_V2, false);
if (ret >= 0) {
s->dev_fd = ret;
+
+ /* Newstyle API is used, we may have attributes */
+ if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)) {
+ uint32_t numirqs = s->num_irq;
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0,
+ &numirqs, true);
+ }
+ /* Tell the kernel to complete VGIC initialization now */
+ if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
+ KVM_DEV_ARM_VGIC_CTRL_INIT)) {
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
+ KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true);
+ }
} else if (ret != -ENODEV && ret != -ENOTSUP) {
error_setg_errno(errp, -ret, "error creating in-kernel VGIC");
return;
}
- if (kvm_gic_supports_attr(s, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)) {
- uint32_t numirqs = s->num_irq;
- kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, 0, &numirqs, 1);
- }
-
- /* Tell the kernel to complete VGIC initialization now */
- if (kvm_gic_supports_attr(s, KVM_DEV_ARM_VGIC_GRP_CTRL,
- KVM_DEV_ARM_VGIC_CTRL_INIT)) {
- kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_CTRL,
- KVM_DEV_ARM_VGIC_CTRL_INIT, 0, 0, 1);
- }
-
/* Distributor */
- memory_region_init_reservation(&s->iomem, OBJECT(s),
- "kvm-gic_dist", 0x1000);
- sysbus_init_mmio(sbd, &s->iomem);
kvm_arm_register_device(&s->iomem,
(KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
| KVM_VGIC_V2_ADDR_TYPE_DIST,
@@ -622,15 +561,18 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
* provide the "interface for core #N" memory regions, because
* cores with a VGIC don't have those.
*/
- memory_region_init_reservation(&s->cpuiomem[0], OBJECT(s),
- "kvm-gic_cpu", 0x1000);
- sysbus_init_mmio(sbd, &s->cpuiomem[0]);
kvm_arm_register_device(&s->cpuiomem[0],
(KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
| KVM_VGIC_V2_ADDR_TYPE_CPU,
KVM_DEV_ARM_VGIC_GRP_ADDR,
KVM_VGIC_V2_ADDR_TYPE_CPU,
s->dev_fd);
+
+ if (!kvm_arm_gic_can_save_restore(s)) {
+ error_setg(&s->migration_blocker, "This operating system kernel does "
+ "not support vGICv2 migration");
+ migrate_add_blocker(s->migration_blocker);
+ }
}
static void kvm_arm_gic_class_init(ObjectClass *klass, void *data)
diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c
new file mode 100644
index 000000000..032ece216
--- /dev/null
+++ b/hw/intc/arm_gicv3_common.c
@@ -0,0 +1,140 @@
+/*
+ * ARM GICv3 support - common bits of emulated and KVM kernel model
+ *
+ * Copyright (c) 2012 Linaro Limited
+ * Copyright (c) 2015 Huawei.
+ * Written by Peter Maydell
+ * Extended to 64 cores by Shlomo Pongratz
+ *
+ * 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/intc/arm_gicv3_common.h"
+
+static void gicv3_pre_save(void *opaque)
+{
+ GICv3State *s = (GICv3State *)opaque;
+ ARMGICv3CommonClass *c = ARM_GICV3_COMMON_GET_CLASS(s);
+
+ if (c->pre_save) {
+ c->pre_save(s);
+ }
+}
+
+static int gicv3_post_load(void *opaque, int version_id)
+{
+ GICv3State *s = (GICv3State *)opaque;
+ ARMGICv3CommonClass *c = ARM_GICV3_COMMON_GET_CLASS(s);
+
+ if (c->post_load) {
+ c->post_load(s);
+ }
+ return 0;
+}
+
+static const VMStateDescription vmstate_gicv3 = {
+ .name = "arm_gicv3",
+ .unmigratable = 1,
+ .pre_save = gicv3_pre_save,
+ .post_load = gicv3_post_load,
+};
+
+void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler,
+ const MemoryRegionOps *ops)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(s);
+ int i;
+
+ /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU.
+ * GPIO array layout is thus:
+ * [0..N-1] spi
+ * [N..N+31] PPIs for CPU 0
+ * [N+32..N+63] PPIs for CPU 1
+ * ...
+ */
+ i = s->num_irq - GIC_INTERNAL + GIC_INTERNAL * s->num_cpu;
+ qdev_init_gpio_in(DEVICE(s), handler, i);
+
+ s->parent_irq = g_malloc(s->num_cpu * sizeof(qemu_irq));
+ s->parent_fiq = g_malloc(s->num_cpu * sizeof(qemu_irq));
+
+ 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]);
+ }
+
+ memory_region_init_io(&s->iomem_dist, OBJECT(s), ops, s,
+ "gicv3_dist", 0x10000);
+ memory_region_init_io(&s->iomem_redist, OBJECT(s), ops ? &ops[1] : NULL, s,
+ "gicv3_redist", 0x20000 * s->num_cpu);
+
+ sysbus_init_mmio(sbd, &s->iomem_dist);
+ sysbus_init_mmio(sbd, &s->iomem_redist);
+}
+
+static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
+{
+ GICv3State *s = ARM_GICV3_COMMON(dev);
+
+ /* revision property is actually reserved and currently used only in order
+ * to keep the interface compatible with GICv2 code, avoiding extra
+ * conditions. However, in future it could be used, for example, if we
+ * implement GICv4.
+ */
+ if (s->revision != 3) {
+ error_setg(errp, "unsupported GIC revision %d", s->revision);
+ return;
+ }
+}
+
+static void arm_gicv3_common_reset(DeviceState *dev)
+{
+ /* TODO */
+}
+
+static Property arm_gicv3_common_properties[] = {
+ DEFINE_PROP_UINT32("num-cpu", GICv3State, num_cpu, 1),
+ DEFINE_PROP_UINT32("num-irq", GICv3State, num_irq, 32),
+ DEFINE_PROP_UINT32("revision", GICv3State, revision, 3),
+ DEFINE_PROP_BOOL("has-security-extensions", GICv3State, security_extn, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void arm_gicv3_common_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = arm_gicv3_common_reset;
+ dc->realize = arm_gicv3_common_realize;
+ dc->props = arm_gicv3_common_properties;
+ dc->vmsd = &vmstate_gicv3;
+}
+
+static const TypeInfo arm_gicv3_common_type = {
+ .name = TYPE_ARM_GICV3_COMMON,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(GICv3State),
+ .class_size = sizeof(ARMGICv3CommonClass),
+ .class_init = arm_gicv3_common_class_init,
+ .abstract = true,
+};
+
+static void register_types(void)
+{
+ type_register_static(&arm_gicv3_common_type);
+}
+
+type_init(register_types)
diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c
new file mode 100644
index 000000000..b48f78f13
--- /dev/null
+++ b/hw/intc/arm_gicv3_kvm.c
@@ -0,0 +1,149 @@
+/*
+ * ARM Generic Interrupt Controller using KVM in-kernel support
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ * Written by Pavel Fedin
+ * Based on vGICv2 code by Peter Maydell
+ *
+ * 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/intc/arm_gicv3_common.h"
+#include "hw/sysbus.h"
+#include "sysemu/kvm.h"
+#include "kvm_arm.h"
+#include "vgic_common.h"
+
+#ifdef DEBUG_GICV3_KVM
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stderr, "kvm_gicv3: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+ do { } while (0)
+#endif
+
+#define TYPE_KVM_ARM_GICV3 "kvm-arm-gicv3"
+#define KVM_ARM_GICV3(obj) \
+ OBJECT_CHECK(GICv3State, (obj), TYPE_KVM_ARM_GICV3)
+#define KVM_ARM_GICV3_CLASS(klass) \
+ OBJECT_CLASS_CHECK(KVMARMGICv3Class, (klass), TYPE_KVM_ARM_GICV3)
+#define KVM_ARM_GICV3_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(KVMARMGICv3Class, (obj), TYPE_KVM_ARM_GICV3)
+
+typedef struct KVMARMGICv3Class {
+ ARMGICv3CommonClass parent_class;
+ DeviceRealize parent_realize;
+ void (*parent_reset)(DeviceState *dev);
+} KVMARMGICv3Class;
+
+static void kvm_arm_gicv3_set_irq(void *opaque, int irq, int level)
+{
+ GICv3State *s = (GICv3State *)opaque;
+
+ kvm_arm_gic_set_irq(s->num_irq, irq, level);
+}
+
+static void kvm_arm_gicv3_put(GICv3State *s)
+{
+ /* TODO */
+ DPRINTF("Cannot put kernel gic state, no kernel interface\n");
+}
+
+static void kvm_arm_gicv3_get(GICv3State *s)
+{
+ /* TODO */
+ DPRINTF("Cannot get kernel gic state, no kernel interface\n");
+}
+
+static void kvm_arm_gicv3_reset(DeviceState *dev)
+{
+ GICv3State *s = ARM_GICV3_COMMON(dev);
+ KVMARMGICv3Class *kgc = KVM_ARM_GICV3_GET_CLASS(s);
+
+ DPRINTF("Reset\n");
+
+ kgc->parent_reset(dev);
+ kvm_arm_gicv3_put(s);
+}
+
+static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
+{
+ GICv3State *s = KVM_ARM_GICV3(dev);
+ KVMARMGICv3Class *kgc = KVM_ARM_GICV3_GET_CLASS(s);
+ Error *local_err = NULL;
+
+ DPRINTF("kvm_arm_gicv3_realize\n");
+
+ kgc->parent_realize(dev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (s->security_extn) {
+ error_setg(errp, "the in-kernel VGICv3 does not implement the "
+ "security extensions");
+ return;
+ }
+
+ gicv3_init_irqs_and_mmio(s, kvm_arm_gicv3_set_irq, NULL);
+
+ /* Try to create the device via the device control API */
+ s->dev_fd = kvm_create_device(kvm_state, KVM_DEV_TYPE_ARM_VGIC_V3, false);
+ if (s->dev_fd < 0) {
+ error_setg_errno(errp, -s->dev_fd, "error creating in-kernel VGIC");
+ return;
+ }
+
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
+ 0, &s->num_irq, true);
+
+ /* Tell the kernel to complete VGIC initialization now */
+ kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
+ KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true);
+
+ kvm_arm_register_device(&s->iomem_dist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR,
+ KVM_VGIC_V3_ADDR_TYPE_DIST, s->dev_fd);
+ kvm_arm_register_device(&s->iomem_redist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR,
+ KVM_VGIC_V3_ADDR_TYPE_REDIST, s->dev_fd);
+}
+
+static void kvm_arm_gicv3_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ARMGICv3CommonClass *agcc = ARM_GICV3_COMMON_CLASS(klass);
+ KVMARMGICv3Class *kgc = KVM_ARM_GICV3_CLASS(klass);
+
+ agcc->pre_save = kvm_arm_gicv3_get;
+ agcc->post_load = kvm_arm_gicv3_put;
+ kgc->parent_realize = dc->realize;
+ kgc->parent_reset = dc->reset;
+ dc->realize = kvm_arm_gicv3_realize;
+ dc->reset = kvm_arm_gicv3_reset;
+}
+
+static const TypeInfo kvm_arm_gicv3_info = {
+ .name = TYPE_KVM_ARM_GICV3,
+ .parent = TYPE_ARM_GICV3_COMMON,
+ .instance_size = sizeof(GICv3State),
+ .class_init = kvm_arm_gicv3_class_init,
+ .class_size = sizeof(KVMARMGICv3Class),
+};
+
+static void kvm_arm_gicv3_register_types(void)
+{
+ type_register_static(&kvm_arm_gicv3_info);
+}
+
+type_init(kvm_arm_gicv3_register_types)
diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c
index e13b729e1..6fc167e23 100644
--- a/hw/intc/armv7m_nvic.c
+++ b/hw/intc/armv7m_nvic.c
@@ -28,6 +28,7 @@ typedef struct {
MemoryRegion gic_iomem_alias;
MemoryRegion container;
uint32_t num_irq;
+ qemu_irq sysresetreq;
} nvic_state;
#define TYPE_NVIC "armv7m_nvic"
@@ -185,26 +186,25 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset)
return cpu->midr;
case 0xd04: /* Interrupt Control State. */
/* VECTACTIVE */
- val = s->gic.running_irq[0];
+ cpu = ARM_CPU(current_cpu);
+ val = cpu->env.v7m.exception;
if (val == 1023) {
val = 0;
} else if (val >= 32) {
val -= 16;
}
- /* RETTOBASE */
- if (s->gic.running_irq[0] == 1023
- || s->gic.last_active[s->gic.running_irq[0]][0] == 1023) {
- val |= (1 << 11);
- }
/* VECTPENDING */
if (s->gic.current_pending[0] != 1023)
val |= (s->gic.current_pending[0] << 12);
- /* ISRPENDING */
+ /* ISRPENDING and RETTOBASE */
for (irq = 32; irq < s->num_irq; irq++) {
if (s->gic.irq_state[irq].pending) {
val |= (1 << 22);
break;
}
+ if (irq != cpu->env.v7m.exception && s->gic.irq_state[irq].active) {
+ val |= (1 << 11);
+ }
}
/* PENDSTSET */
if (s->gic.irq_state[ARMV7M_EXCP_SYSTICK].pending)
@@ -349,10 +349,13 @@ static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value)
break;
case 0xd0c: /* Application Interrupt/Reset Control. */
if ((value >> 16) == 0x05fa) {
+ if (value & 4) {
+ qemu_irq_pulse(s->sysresetreq);
+ }
if (value & 2) {
qemu_log_mask(LOG_UNIMP, "VECTCLRACTIVE unimplemented\n");
}
- if (value & 5) {
+ if (value & 1) {
qemu_log_mask(LOG_UNIMP, "AIRCR system reset unimplemented\n");
}
if (value & 0x700) {
@@ -536,11 +539,14 @@ static void armv7m_nvic_instance_init(Object *obj)
* value in the GICState struct.
*/
GICState *s = ARM_GIC_COMMON(obj);
+ DeviceState *dev = DEVICE(obj);
+ nvic_state *nvic = NVIC(obj);
/* The ARM v7m may have anything from 0 to 496 external interrupt
* IRQ lines. We default to 64. Other boards may differ and should
* set the num-irq property appropriately.
*/
s->num_irq = 64;
+ qdev_init_gpio_out_named(dev, &nvic->sysresetreq, "SYSRESETREQ", 1);
}
static void armv7m_nvic_class_init(ObjectClass *klass, void *data)
diff --git a/hw/intc/imx_avic.c b/hw/intc/imx_avic.c
index e48f66c8f..e0535ffc5 100644
--- a/hw/intc/imx_avic.c
+++ b/hw/intc/imx_avic.c
@@ -7,6 +7,7 @@
* Copyright (c) 2008 OKL
* Copyright (c) 2011 NICTA Pty Ltd
* Originally written by Hans Jiang
+ * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
*
* This code is licensed under the GPL version 2 or later. See
* the COPYING file in the top-level directory.
@@ -14,67 +15,22 @@
* TODO: implement vectors.
*/
-#include "hw/hw.h"
-#include "hw/sysbus.h"
-#include "qemu/host-utils.h"
+#include "hw/intc/imx_avic.h"
-#define DEBUG_INT 1
-#undef DEBUG_INT /* comment out for debugging */
-
-#ifdef DEBUG_INT
-#define DPRINTF(fmt, args...) \
-do { printf("imx_avic: " fmt , ##args); } while (0)
-#else
-#define DPRINTF(fmt, args...) do {} while (0)
+#ifndef DEBUG_IMX_AVIC
+#define DEBUG_IMX_AVIC 0
#endif
-/*
- * Define to 1 for messages about attempts to
- * access unimplemented registers or similar.
- */
-#define DEBUG_IMPLEMENTATION 1
-#if DEBUG_IMPLEMENTATION
-# define IPRINTF(fmt, args...) \
- do { fprintf(stderr, "imx_avic: " fmt, ##args); } while (0)
-#else
-# define IPRINTF(fmt, args...) do {} while (0)
-#endif
-
-#define IMX_AVIC_NUM_IRQS 64
-
-/* Interrupt Control Bits */
-#define ABFLAG (1<<25)
-#define ABFEN (1<<24)
-#define NIDIS (1<<22) /* Normal Interrupt disable */
-#define FIDIS (1<<21) /* Fast interrupt disable */
-#define NIAD (1<<20) /* Normal Interrupt Arbiter Rise ARM level */
-#define FIAD (1<<19) /* Fast Interrupt Arbiter Rise ARM level */
-#define NM (1<<18) /* Normal interrupt mode */
-
-
-#define PRIO_PER_WORD (sizeof(uint32_t) * 8 / 4)
-#define PRIO_WORDS (IMX_AVIC_NUM_IRQS/PRIO_PER_WORD)
-
-#define TYPE_IMX_AVIC "imx_avic"
-#define IMX_AVIC(obj) \
- OBJECT_CHECK(IMXAVICState, (obj), TYPE_IMX_AVIC)
-
-typedef struct IMXAVICState {
- SysBusDevice parent_obj;
-
- MemoryRegion iomem;
- uint64_t pending;
- uint64_t enabled;
- uint64_t is_fiq;
- uint32_t intcntl;
- uint32_t intmask;
- qemu_irq irq;
- qemu_irq fiq;
- uint32_t prio[PRIO_WORDS]; /* Priorities are 4-bits each */
-} IMXAVICState;
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX_AVIC) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_AVIC, \
+ __func__, ##args); \
+ } \
+ } while (0)
static const VMStateDescription vmstate_imx_avic = {
- .name = "imx-avic",
+ .name = TYPE_IMX_AVIC,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
@@ -88,8 +44,6 @@ static const VMStateDescription vmstate_imx_avic = {
},
};
-
-
static inline int imx_avic_prio(IMXAVICState *s, int irq)
{
uint32_t word = irq / PRIO_PER_WORD;
@@ -151,8 +105,8 @@ static uint64_t imx_avic_read(void *opaque,
{
IMXAVICState *s = (IMXAVICState *)opaque;
+ DPRINTF("read(offset = 0x%" HWADDR_PRIx ")\n", offset);
- DPRINTF("read(offset = 0x%x)\n", offset >> 2);
switch (offset >> 2) {
case 0: /* INTCNTL */
return s->intcntl;
@@ -249,7 +203,8 @@ static uint64_t imx_avic_read(void *opaque,
return 0x4;
default:
- IPRINTF("imx_avic_read: Bad offset 0x%x\n", (int)offset);
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_AVIC, __func__, offset);
return 0;
}
}
@@ -261,13 +216,13 @@ static void imx_avic_write(void *opaque, hwaddr offset,
/* Vector Registers not yet supported */
if (offset >= 0x100 && offset <= 0x2fc) {
- IPRINTF("imx_avic_write to vector register %d ignored\n",
- (unsigned int)((offset - 0x100) >> 2));
+ qemu_log_mask(LOG_UNIMP, "[%s]%s: vector %d ignored\n",
+ TYPE_IMX_AVIC, __func__, (int)((offset - 0x100) >> 2));
return;
}
- DPRINTF("imx_avic_write(0x%x) = %x\n",
- (unsigned int)offset>>2, (unsigned int)val);
+ DPRINTF("(0x%" HWADDR_PRIx ") = 0x%x\n", offset, (unsigned int)val);
+
switch (offset >> 2) {
case 0: /* Interrupt Control Register, INTCNTL */
s->intcntl = val & (ABFEN | NIDIS | FIDIS | NIAD | FIAD | NM);
@@ -341,7 +296,8 @@ static void imx_avic_write(void *opaque, hwaddr offset,
return;
default:
- IPRINTF("imx_avic_write: Bad offset %x\n", (int)offset);
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_AVIC, __func__, offset);
}
imx_avic_update(s);
}
@@ -370,7 +326,7 @@ static int imx_avic_init(SysBusDevice *sbd)
IMXAVICState *s = IMX_AVIC(dev);
memory_region_init_io(&s->iomem, OBJECT(s), &imx_avic_ops, s,
- "imx_avic", 0x1000);
+ TYPE_IMX_AVIC, 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
qdev_init_gpio_in(dev, imx_avic_set_irq, IMX_AVIC_NUM_IRQS);
diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c
index b52793238..de2dd4ba8 100644
--- a/hw/intc/ioapic.c
+++ b/hw/intc/ioapic.c
@@ -20,6 +20,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "monitor/monitor.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/i386/ioapic.h"
@@ -98,7 +99,9 @@ static void ioapic_set_irq(void *opaque, int vector, int level)
/* level triggered */
if (level) {
s->irr |= mask;
- ioapic_service(s);
+ if (!(entry & IOAPIC_LVT_REMOTE_IRR)) {
+ ioapic_service(s);
+ }
} else {
s->irr &= ~mask;
}
@@ -137,6 +140,17 @@ void ioapic_eoi_broadcast(int vector)
}
}
+void ioapic_dump_state(Monitor *mon, const QDict *qdict)
+{
+ int i;
+
+ for (i = 0; i < MAX_IOAPICS; i++) {
+ if (ioapics[i] != 0) {
+ ioapic_print_redtbl(mon, ioapics[i]);
+ }
+ }
+}
+
static uint64_t
ioapic_mem_read(void *opaque, hwaddr addr, unsigned int size)
{
@@ -154,15 +168,13 @@ ioapic_mem_read(void *opaque, hwaddr addr, unsigned int size)
}
switch (s->ioregsel) {
case IOAPIC_REG_ID:
+ case IOAPIC_REG_ARB:
val = s->id << IOAPIC_ID_SHIFT;
break;
case IOAPIC_REG_VER:
val = IOAPIC_VERSION |
((IOAPIC_NUM_PINS - 1) << IOAPIC_VER_ENTRIES_SHIFT);
break;
- case IOAPIC_REG_ARB:
- val = 0;
- break;
default:
index = (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1;
if (index >= 0 && index < IOAPIC_NUM_PINS) {
diff --git a/hw/intc/ioapic_common.c b/hw/intc/ioapic_common.c
index 8b7d11806..65f6877d7 100644
--- a/hw/intc/ioapic_common.c
+++ b/hw/intc/ioapic_common.c
@@ -19,6 +19,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "monitor/monitor.h"
#include "hw/i386/ioapic.h"
#include "hw/i386/ioapic_internal.h"
#include "hw/sysbus.h"
@@ -31,6 +32,60 @@
*/
int ioapic_no;
+static void ioapic_irr_dump(Monitor *mon, const char *name, uint32_t bitmap)
+{
+ int i;
+
+ monitor_printf(mon, "%-10s ", name);
+ if (bitmap == 0) {
+ monitor_printf(mon, "(none)\n");
+ return;
+ }
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ if (bitmap & (1 << i)) {
+ monitor_printf(mon, "%-2u ", i);
+ }
+ }
+ monitor_printf(mon, "\n");
+}
+
+void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s)
+{
+ static const char *delm_str[] = {
+ "fixed", "lowest", "SMI", "...", "NMI", "INIT", "...", "extINT"};
+ uint32_t remote_irr = 0;
+ int i;
+
+ monitor_printf(mon, "ioapic id=0x%02x sel=0x%02x", s->id, s->ioregsel);
+ if (s->ioregsel) {
+ monitor_printf(mon, " (redir[%u])\n",
+ (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1);
+ } else {
+ monitor_printf(mon, "\n");
+ }
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ uint64_t entry = s->ioredtbl[i];
+ uint32_t delm = (uint32_t)((entry & IOAPIC_LVT_DELIV_MODE) >>
+ IOAPIC_LVT_DELIV_MODE_SHIFT);
+ monitor_printf(mon, "pin %-2u 0x%016"PRIx64" dest=%"PRIx64
+ " vec=%-3"PRIu64" %s %-5s %-6s %-6s %s\n",
+ i, entry,
+ (entry >> IOAPIC_LVT_DEST_SHIFT) &
+ (entry & IOAPIC_LVT_DEST_MODE ? 0xff : 0xf),
+ entry & IOAPIC_VECTOR_MASK,
+ entry & IOAPIC_LVT_POLARITY ? "active-lo" : "active-hi",
+ entry & IOAPIC_LVT_TRIGGER_MODE ? "level" : "edge",
+ entry & IOAPIC_LVT_MASKED ? "masked" : "",
+ delm_str[delm],
+ entry & IOAPIC_LVT_DEST_MODE ? "logical" : "physical");
+
+ remote_irr |= entry & IOAPIC_LVT_TRIGGER_MODE ?
+ (entry & IOAPIC_LVT_REMOTE_IRR ? (1 << i) : 0) : 0;
+ }
+ ioapic_irr_dump(mon, "IRR", s->irr);
+ ioapic_irr_dump(mon, "Remote IRR", remote_irr);
+}
+
void ioapic_reset_common(DeviceState *dev)
{
IOAPICCommonState *s = IOAPIC_COMMON(dev);
diff --git a/hw/intc/openpic.c b/hw/intc/openpic.c
index 14ab0e31b..bfcf15535 100644
--- a/hw/intc/openpic.c
+++ b/hw/intc/openpic.c
@@ -1643,6 +1643,7 @@ static void openpic_class_init(ObjectClass *oc, void *data)
dc->props = openpic_properties;
dc->reset = openpic_reset;
dc->vmsd = &vmstate_openpic;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
static const TypeInfo openpic_info = {
diff --git a/hw/intc/openpic_kvm.c b/hw/intc/openpic_kvm.c
index f7cac585a..649f476ac 100644
--- a/hw/intc/openpic_kvm.c
+++ b/hw/intc/openpic_kvm.c
@@ -275,6 +275,7 @@ static void kvm_openpic_class_init(ObjectClass *oc, void *data)
dc->realize = kvm_openpic_realize;
dc->props = kvm_openpic_properties;
dc->reset = kvm_openpic_reset;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
static const TypeInfo kvm_openpic_info = {
diff --git a/hw/intc/s390_flic_kvm.c b/hw/intc/s390_flic_kvm.c
index b471e7a41..48714f96a 100644
--- a/hw/intc/s390_flic_kvm.c
+++ b/hw/intc/s390_flic_kvm.c
@@ -228,6 +228,8 @@ static int kvm_s390_add_adapter_routes(S390FLICState *fs,
routes->gsi[i] = ret;
routes->adapter.ind_offset++;
}
+ kvm_irqchip_commit_routes(kvm_state);
+
/* Restore passed-in structure to original state. */
routes->adapter.ind_offset = ind_offset;
return 0;
diff --git a/hw/intc/vgic_common.h b/hw/intc/vgic_common.h
new file mode 100644
index 000000000..80d919eb9
--- /dev/null
+++ b/hw/intc/vgic_common.h
@@ -0,0 +1,35 @@
+/*
+ * ARM KVM vGIC utility functions
+ *
+ * Copyright (c) 2015 Samsung Electronics
+ * Written by Pavel Fedin
+ *
+ * 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_ARM_VGIC_COMMON_H
+#define QEMU_ARM_VGIC_COMMON_H
+
+/**
+ * kvm_arm_gic_set_irq - Send an IRQ to the in-kernel vGIC
+ * @num_irq: Total number of IRQs configured for the GIC instance
+ * @irq: qemu internal IRQ line number:
+ * [0..N-1] : external interrupts
+ * [N..N+31] : PPI (internal) interrupts for CPU 0
+ * [N+32..N+63] : PPI (internal interrupts for CPU 1
+ * @level: level of the IRQ line.
+ */
+void kvm_arm_gic_set_irq(uint32_t num_irq, int irq, int level);
+
+#endif
diff --git a/hw/intc/xics.c b/hw/intc/xics.c
index 924b1ae3c..9ff579641 100644
--- a/hw/intc/xics.c
+++ b/hw/intc/xics.c
@@ -739,7 +739,7 @@ int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi)
}
/*
- * Allocate block of consequtive IRQs, returns a number of the first.
+ * Allocate block of consecutive IRQs, and return the number of the first IRQ in the block.
* If align==true, aligns the first IRQ number to num.
*/
int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align)
@@ -848,7 +848,7 @@ static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t xirr = icp_accept(ss);
args[0] = xirr;
- args[1] = cpu_get_real_ticks();
+ args[1] = cpu_get_host_ticks();
return H_SUCCESS;
}
diff --git a/hw/isa/i82378.c b/hw/isa/i82378.c
index fcf97d86a..d4c830684 100644
--- a/hw/isa/i82378.c
+++ b/hw/isa/i82378.c
@@ -100,7 +100,6 @@ static void i82378_realize(PCIDevice *pci, Error **errp)
/* 2 82C37 (dma) */
isa = isa_create_simple(isabus, "i82374");
- qdev_connect_gpio_out(DEVICE(isa), 0, s->out[1]);
/* timer */
isa_create_simple(isabus, "mc146818rtc");
@@ -111,7 +110,7 @@ static void i82378_init(Object *obj)
DeviceState *dev = DEVICE(obj);
I82378State *s = I82378(obj);
- qdev_init_gpio_out(dev, s->out, 2);
+ qdev_init_gpio_out(dev, s->out, 1);
qdev_init_gpio_in(dev, i82378_request_pic_irq, 16);
}
diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index 360699f6f..1ffc80362 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -394,7 +394,7 @@ static void ich9_apm_ctrl_changed(uint32_t val, void *arg)
/* SMI_EN = PMBASE + 30. SMI control and enable register */
if (lpc->pm.smi_en & ICH9_PMIO_SMI_EN_APMC_EN) {
- cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI);
+ cpu_interrupt(current_cpu, CPU_INTERRUPT_SMI);
}
}
diff --git a/hw/lm32/lm32_boards.c b/hw/lm32/lm32_boards.c
index 70f48d3b1..eb553a174 100644
--- a/hw/lm32/lm32_boards.c
+++ b/hw/lm32/lm32_boards.c
@@ -142,7 +142,7 @@ static void lm32_evr_init(MachineState *machine)
int kernel_size;
kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL,
- 1, ELF_MACHINE, 0);
+ 1, EM_LATTICEMICO32, 0);
reset_info->bootstrap_pc = entry;
if (kernel_size < 0) {
@@ -244,7 +244,7 @@ static void lm32_uclinux_init(MachineState *machine)
int kernel_size;
kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL,
- 1, ELF_MACHINE, 0);
+ 1, EM_LATTICEMICO32, 0);
reset_info->bootstrap_pc = entry;
if (kernel_size < 0) {
@@ -292,24 +292,40 @@ static void lm32_uclinux_init(MachineState *machine)
qemu_register_reset(main_cpu_reset, reset_info);
}
-static QEMUMachine lm32_evr_machine = {
- .name = "lm32-evr",
- .desc = "LatticeMico32 EVR32 eval system",
- .init = lm32_evr_init,
- .is_default = 1,
+static void lm32_evr_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "LatticeMico32 EVR32 eval system";
+ mc->init = lm32_evr_init;
+ mc->is_default = 1;
+}
+
+static const TypeInfo lm32_evr_type = {
+ .name = MACHINE_TYPE_NAME("lm32-evr"),
+ .parent = TYPE_MACHINE,
+ .class_init = lm32_evr_class_init,
};
-static QEMUMachine lm32_uclinux_machine = {
- .name = "lm32-uclinux",
- .desc = "lm32 platform for uClinux and u-boot by Theobroma Systems",
- .init = lm32_uclinux_init,
- .is_default = 0,
+static void lm32_uclinux_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "lm32 platform for uClinux and u-boot by Theobroma Systems";
+ mc->init = lm32_uclinux_init;
+ mc->is_default = 0;
+}
+
+static const TypeInfo lm32_uclinux_type = {
+ .name = MACHINE_TYPE_NAME("lm32-uclinux"),
+ .parent = TYPE_MACHINE,
+ .class_init = lm32_uclinux_class_init,
};
static void lm32_machine_init(void)
{
- qemu_register_machine(&lm32_uclinux_machine);
- qemu_register_machine(&lm32_evr_machine);
+ type_register_static(&lm32_evr_type);
+ type_register_static(&lm32_uclinux_type);
}
-machine_init(lm32_machine_init);
+machine_init(lm32_machine_init)
diff --git a/hw/lm32/milkymist-hw.h b/hw/lm32/milkymist-hw.h
index 8d20cac1d..c8dfb4d2d 100644
--- a/hw/lm32/milkymist-hw.h
+++ b/hw/lm32/milkymist-hw.h
@@ -88,7 +88,8 @@ static inline DeviceState *milkymist_pfpu_create(hwaddr base,
#ifdef CONFIG_OPENGL
#include <X11/Xlib.h>
-#include <GL/glx.h>
+#include <epoxy/gl.h>
+#include <epoxy/glx.h>
static const int glx_fbconfig_attr[] = {
GLX_GREEN_SIZE, 5,
GLX_GREEN_SIZE, 6,
diff --git a/hw/lm32/milkymist.c b/hw/lm32/milkymist.c
index e755f5b24..13976b348 100644
--- a/hw/lm32/milkymist.c
+++ b/hw/lm32/milkymist.c
@@ -176,7 +176,7 @@ milkymist_init(MachineState *machine)
/* Boots a kernel elf binary. */
kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL,
- 1, ELF_MACHINE, 0);
+ 1, EM_LATTICEMICO32, 0);
reset_info->bootstrap_pc = entry;
if (kernel_size < 0) {
@@ -209,16 +209,11 @@ milkymist_init(MachineState *machine)
qemu_register_reset(main_cpu_reset, reset_info);
}
-static QEMUMachine milkymist_machine = {
- .name = "milkymist",
- .desc = "Milkymist One",
- .init = milkymist_init,
- .is_default = 0,
-};
-
-static void milkymist_machine_init(void)
+static void milkymist_machine_init(MachineClass *mc)
{
- qemu_register_machine(&milkymist_machine);
+ mc->desc = "Milkymist One";
+ mc->init = milkymist_init;
+ mc->is_default = 0;
}
-machine_init(milkymist_machine_init);
+DEFINE_MACHINE("milkymist", milkymist_machine_init)
diff --git a/hw/m68k/an5206.c b/hw/m68k/an5206.c
index f63ab2b94..c1dea17f3 100644
--- a/hw/m68k/an5206.c
+++ b/hw/m68k/an5206.c
@@ -54,7 +54,7 @@ static void an5206_init(MachineState *machine)
memory_region_add_subregion(address_space_mem, 0, ram);
/* Internal SRAM. */
- memory_region_init_ram(sram, NULL, "an5206.sram", 512, &error_abort);
+ memory_region_init_ram(sram, NULL, "an5206.sram", 512, &error_fatal);
vmstate_register_ram_global(sram);
memory_region_add_subregion(address_space_mem, AN5206_RAMBAR_ADDR, sram);
@@ -70,7 +70,7 @@ static void an5206_init(MachineState *machine)
}
kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
- NULL, NULL, 1, ELF_MACHINE, 0);
+ NULL, NULL, 1, EM_68K, 0);
entry = elf_entry;
if (kernel_size < 0) {
kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL,
@@ -89,15 +89,10 @@ static void an5206_init(MachineState *machine)
env->pc = entry;
}
-static QEMUMachine an5206_machine = {
- .name = "an5206",
- .desc = "Arnewsh 5206",
- .init = an5206_init,
-};
-
-static void an5206_machine_init(void)
+static void an5206_machine_init(MachineClass *mc)
{
- qemu_register_machine(&an5206_machine);
+ mc->desc = "Arnewsh 5206";
+ mc->init = an5206_init;
}
-machine_init(an5206_machine_init);
+DEFINE_MACHINE("an5206", an5206_machine_init)
diff --git a/hw/m68k/dummy_m68k.c b/hw/m68k/dummy_m68k.c
index 5b77d930e..8b3b77597 100644
--- a/hw/m68k/dummy_m68k.c
+++ b/hw/m68k/dummy_m68k.c
@@ -49,7 +49,7 @@ static void dummy_m68k_init(MachineState *machine)
/* Load kernel. */
if (kernel_filename) {
kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
- NULL, NULL, 1, ELF_MACHINE, 0);
+ NULL, NULL, 1, EM_68K, 0);
entry = elf_entry;
if (kernel_size < 0) {
kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL,
@@ -72,15 +72,10 @@ static void dummy_m68k_init(MachineState *machine)
env->pc = entry;
}
-static QEMUMachine dummy_m68k_machine = {
- .name = "dummy",
- .desc = "Dummy board",
- .init = dummy_m68k_init,
-};
-
-static void dummy_m68k_machine_init(void)
+static void dummy_m68k_machine_init(MachineClass *mc)
{
- qemu_register_machine(&dummy_m68k_machine);
+ mc->desc = "Dummy board";
+ mc->init = dummy_m68k_init;
}
-machine_init(dummy_m68k_machine_init);
+DEFINE_MACHINE("dummy", dummy_m68k_machine_init)
diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c
index 326a42d27..ddeccc506 100644
--- a/hw/m68k/mcf5208.c
+++ b/hw/m68k/mcf5208.c
@@ -222,7 +222,7 @@ static void mcf5208evb_init(MachineState *machine)
memory_region_add_subregion(address_space_mem, 0x40000000, ram);
/* Internal SRAM. */
- memory_region_init_ram(sram, NULL, "mcf5208.sram", 16384, &error_abort);
+ memory_region_init_ram(sram, NULL, "mcf5208.sram", 16384, &error_fatal);
vmstate_register_ram_global(sram);
memory_region_add_subregion(address_space_mem, 0x80000000, sram);
@@ -275,7 +275,7 @@ static void mcf5208evb_init(MachineState *machine)
}
kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
- NULL, NULL, 1, ELF_MACHINE, 0);
+ NULL, NULL, 1, EM_68K, 0);
entry = elf_entry;
if (kernel_size < 0) {
kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL,
@@ -294,16 +294,11 @@ static void mcf5208evb_init(MachineState *machine)
env->pc = entry;
}
-static QEMUMachine mcf5208evb_machine = {
- .name = "mcf5208evb",
- .desc = "MCF5206EVB",
- .init = mcf5208evb_init,
- .is_default = 1,
-};
-
-static void mcf5208evb_machine_init(void)
+static void mcf5208evb_machine_init(MachineClass *mc)
{
- qemu_register_machine(&mcf5208evb_machine);
+ mc->desc = "MCF5206EVB";
+ mc->init = mcf5208evb_init;
+ mc->is_default = 1;
}
-machine_init(mcf5208evb_machine_init);
+DEFINE_MACHINE("mcf5208evb", mcf5208evb_machine_init)
diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c
index bb04862de..d5cdab270 100644
--- a/hw/mem/pc-dimm.c
+++ b/hw/mem/pc-dimm.c
@@ -25,6 +25,7 @@
#include "sysemu/numa.h"
#include "sysemu/kvm.h"
#include "trace.h"
+#include "hw/virtio/vhost.h"
typedef struct pc_dimms_capacity {
uint64_t size;
@@ -95,6 +96,12 @@ void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms,
goto out;
}
+ if (!vhost_has_free_slot()) {
+ error_setg(&local_err, "a used vhost backend 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);
@@ -172,7 +179,7 @@ int qmp_pc_dimm_device_list(Object *obj, void *opaque)
NULL);
di->memdev = object_get_canonical_path(OBJECT(dimm->hostmem));
- info->dimm = di;
+ info->u.dimm = di;
elem->value = info;
elem->next = NULL;
**prev = elem;
@@ -196,9 +203,9 @@ ram_addr_t get_current_ram_size(void)
MemoryDeviceInfo *value = info->value;
if (value) {
- switch (value->kind) {
+ switch (value->type) {
case MEMORY_DEVICE_INFO_KIND_DIMM:
- size += value->dimm->size;
+ size += value->u.dimm->size;
break;
default:
break;
@@ -414,10 +421,11 @@ static void pc_dimm_realize(DeviceState *dev, Error **errp)
error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property is not set");
return;
}
- if ((nb_numa_nodes > 0) && (dimm->node >= nb_numa_nodes)) {
+ if (((nb_numa_nodes > 0) && (dimm->node >= nb_numa_nodes)) ||
+ (!nb_numa_nodes && dimm->node)) {
error_setg(errp, "'DIMM property " PC_DIMM_NODE_PROP " has value %"
PRIu32 "' which exceeds the number of numa nodes: %d",
- dimm->node, nb_numa_nodes);
+ dimm->node, nb_numa_nodes ? nb_numa_nodes : 1);
return;
}
}
diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c
index 3e8820f36..d7eaa1f02 100644
--- a/hw/microblaze/boot.c
+++ b/hw/microblaze/boot.c
@@ -141,12 +141,12 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base,
/* Boots a kernel elf binary. */
kernel_size = load_elf(kernel_filename, NULL, NULL,
&entry, &low, &high,
- big_endian, ELF_MACHINE, 0);
+ big_endian, EM_MICROBLAZE, 0);
base32 = entry;
if (base32 == 0xc0000000) {
kernel_size = load_elf(kernel_filename, translate_kernel_address,
NULL, &entry, NULL, NULL,
- big_endian, ELF_MACHINE, 0);
+ big_endian, EM_MICROBLAZE, 0);
}
/* Always boot into physical ram. */
boot_info.bootstrap_pc = (uint32_t)entry;
diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c
index ed84a37e6..462060f09 100644
--- a/hw/microblaze/petalogix_ml605_mmu.c
+++ b/hw/microblaze/petalogix_ml605_mmu.c
@@ -92,12 +92,12 @@ petalogix_ml605_init(MachineState *machine)
/* Attach emulated BRAM through the LMB. */
memory_region_init_ram(phys_lmb_bram, NULL, "petalogix_ml605.lmb_bram",
- LMB_BRAM_SIZE, &error_abort);
+ LMB_BRAM_SIZE, &error_fatal);
vmstate_register_ram_global(phys_lmb_bram);
memory_region_add_subregion(address_space_mem, 0x00000000, phys_lmb_bram);
memory_region_init_ram(phys_ram, NULL, "petalogix_ml605.ram", ram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(phys_ram);
memory_region_add_subregion(address_space_mem, MEMORY_BASEADDR, phys_ram);
@@ -206,16 +206,11 @@ petalogix_ml605_init(MachineState *machine)
}
-static QEMUMachine petalogix_ml605_machine = {
- .name = "petalogix-ml605",
- .desc = "PetaLogix linux refdesign for xilinx ml605 little endian",
- .init = petalogix_ml605_init,
- .is_default = 0,
-};
-
-static void petalogix_ml605_machine_init(void)
+static void petalogix_ml605_machine_init(MachineClass *mc)
{
- qemu_register_machine(&petalogix_ml605_machine);
+ mc->desc = "PetaLogix linux refdesign for xilinx ml605 little endian";
+ mc->init = petalogix_ml605_init;
+ mc->is_default = 0;
}
-machine_init(petalogix_ml605_machine_init);
+DEFINE_MACHINE("petalogix-ml605", petalogix_ml605_machine_init)
diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c
index 0c2140c3f..33811561b 100644
--- a/hw/microblaze/petalogix_s3adsp1800_mmu.c
+++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c
@@ -71,12 +71,12 @@ petalogix_s3adsp1800_init(MachineState *machine)
/* Attach emulated BRAM through the LMB. */
memory_region_init_ram(phys_lmb_bram, NULL,
"petalogix_s3adsp1800.lmb_bram", LMB_BRAM_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(phys_lmb_bram);
memory_region_add_subregion(sysmem, 0x00000000, phys_lmb_bram);
memory_region_init_ram(phys_ram, NULL, "petalogix_s3adsp1800.ram",
- ram_size, &error_abort);
+ ram_size, &error_fatal);
vmstate_register_ram_global(phys_ram);
memory_region_add_subregion(sysmem, ddr_base, phys_ram);
@@ -124,16 +124,11 @@ petalogix_s3adsp1800_init(MachineState *machine)
NULL);
}
-static QEMUMachine petalogix_s3adsp1800_machine = {
- .name = "petalogix-s3adsp1800",
- .desc = "PetaLogix linux refdesign for xilinx Spartan 3ADSP1800",
- .init = petalogix_s3adsp1800_init,
- .is_default = 1,
-};
-
-static void petalogix_s3adsp1800_machine_init(void)
+static void petalogix_s3adsp1800_machine_init(MachineClass *mc)
{
- qemu_register_machine(&petalogix_s3adsp1800_machine);
+ mc->desc = "PetaLogix linux refdesign for xilinx Spartan 3ADSP1800";
+ mc->init = petalogix_s3adsp1800_init;
+ mc->is_default = 1;
}
-machine_init(petalogix_s3adsp1800_machine_init);
+DEFINE_MACHINE("petalogix-s3adsp1800", petalogix_s3adsp1800_machine_init)
diff --git a/hw/mips/cputimer.c b/hw/mips/cputimer.c
index 577c9aeab..f046588ad 100644
--- a/hw/mips/cputimer.c
+++ b/hw/mips/cputimer.c
@@ -25,18 +25,26 @@
#include "qemu/timer.h"
#include "sysemu/kvm.h"
-#define TIMER_FREQ 100 * 1000 * 1000
+#define TIMER_PERIOD 10 /* 10 ns period for 100 Mhz frequency */
/* XXX: do not use a global */
uint32_t cpu_mips_get_random (CPUMIPSState *env)
{
- static uint32_t lfsr = 1;
+ static uint32_t seed = 1;
static uint32_t prev_idx = 0;
uint32_t idx;
+ uint32_t nb_rand_tlb = env->tlb->nb_tlb - env->CP0_Wired;
+
+ if (nb_rand_tlb == 1) {
+ return env->tlb->nb_tlb - 1;
+ }
+
/* Don't return same value twice, so get another value */
do {
- lfsr = (lfsr >> 1) ^ (-(lfsr & 1u) & 0xd0000001u);
- idx = lfsr % (env->tlb->nb_tlb - env->CP0_Wired) + env->CP0_Wired;
+ /* Use a simple algorithm of Linear Congruential Generator
+ * from ISO/IEC 9899 standard. */
+ seed = 1103515245 * seed + 12345;
+ idx = (seed >> 16) % nb_rand_tlb + env->CP0_Wired;
} while (idx == prev_idx);
prev_idx = idx;
return idx;
@@ -49,9 +57,8 @@ static void cpu_mips_timer_update(CPUMIPSState *env)
uint32_t wait;
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- wait = env->CP0_Compare - env->CP0_Count -
- (uint32_t)muldiv64(now, TIMER_FREQ, get_ticks_per_sec());
- next = now + muldiv64(wait, get_ticks_per_sec(), TIMER_FREQ);
+ wait = env->CP0_Compare - env->CP0_Count - (uint32_t)(now / TIMER_PERIOD);
+ next = now + (uint64_t)wait * TIMER_PERIOD;
timer_mod(env->timer, next);
}
@@ -79,8 +86,7 @@ uint32_t cpu_mips_get_count (CPUMIPSState *env)
cpu_mips_timer_expire(env);
}
- return env->CP0_Count +
- (uint32_t)muldiv64(now, TIMER_FREQ, get_ticks_per_sec());
+ return env->CP0_Count + (uint32_t)(now / TIMER_PERIOD);
}
}
@@ -95,9 +101,8 @@ void cpu_mips_store_count (CPUMIPSState *env, uint32_t count)
env->CP0_Count = count;
else {
/* Store new count register */
- env->CP0_Count =
- count - (uint32_t)muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
- TIMER_FREQ, get_ticks_per_sec());
+ env->CP0_Count = count -
+ (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD);
/* Update timer timer */
cpu_mips_timer_update(env);
}
@@ -121,8 +126,8 @@ void cpu_mips_start_count(CPUMIPSState *env)
void cpu_mips_stop_count(CPUMIPSState *env)
{
/* Store the current value */
- env->CP0_Count += (uint32_t)muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
- TIMER_FREQ, get_ticks_per_sec());
+ env->CP0_Count += (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) /
+ TIMER_PERIOD);
}
static void mips_timer_cb (void *opaque)
diff --git a/hw/mips/gt64xxx_pci.c b/hw/mips/gt64xxx_pci.c
index 10fcca33f..f76a9fd36 100644
--- a/hw/mips/gt64xxx_pci.c
+++ b/hw/mips/gt64xxx_pci.c
@@ -275,7 +275,8 @@ static void check_reserved_space (hwaddr *start,
static void gt64120_isd_mapping(GT64120State *s)
{
- hwaddr start = s->regs[GT_ISD] << 21;
+ /* Bits 14:0 of ISD map to bits 35:21 of the start address. */
+ hwaddr start = ((hwaddr)s->regs[GT_ISD] << 21) & 0xFFFE00000ull;
hwaddr length = 0x1000;
if (s->ISD_length) {
diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c
index dea941ad8..5988a88c0 100644
--- a/hw/mips/mips_fulong2e.c
+++ b/hw/mips/mips_fulong2e.c
@@ -116,7 +116,7 @@ static int64_t load_kernel (CPUMIPSState *env)
if (load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL,
(uint64_t *)&kernel_entry, (uint64_t *)&kernel_low,
- (uint64_t *)&kernel_high, 0, ELF_MACHINE, 1) < 0) {
+ (uint64_t *)&kernel_high, 0, EM_MIPS, 1) < 0) {
fprintf(stderr, "qemu: could not load kernel '%s'\n",
loaderparams.kernel_filename);
exit(1);
@@ -251,15 +251,6 @@ static void network_init (PCIBus *pci_bus)
}
}
-static void cpu_request_exit(void *opaque, int irq, int level)
-{
- CPUState *cpu = current_cpu;
-
- if (cpu && level) {
- cpu_exit(cpu);
- }
-}
-
static void mips_fulong2e_init(MachineState *machine)
{
ram_addr_t ram_size = machine->ram_size;
@@ -274,7 +265,6 @@ static void mips_fulong2e_init(MachineState *machine)
long bios_size;
int64_t kernel_entry;
qemu_irq *i8259;
- qemu_irq *cpu_exit_irq;
PCIBus *pci_bus;
ISABus *isa_bus;
I2CBus *smbus;
@@ -304,7 +294,7 @@ static void mips_fulong2e_init(MachineState *machine)
/* allocate RAM */
memory_region_allocate_system_memory(ram, NULL, "fulong2e.ram", ram_size);
memory_region_init_ram(bios, NULL, "fulong2e.bios", bios_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(bios);
memory_region_set_readonly(bios, true);
@@ -375,8 +365,7 @@ static void mips_fulong2e_init(MachineState *machine)
/* init other devices */
pit = pit_init(isa_bus, 0x40, 0, NULL);
- cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
- DMA_init(0, cpu_exit_irq);
+ DMA_init(0);
/* Super I/O */
isa_create_simple(isa_bus, "i8042");
@@ -392,15 +381,10 @@ static void mips_fulong2e_init(MachineState *machine)
network_init(pci_bus);
}
-static QEMUMachine mips_fulong2e_machine = {
- .name = "fulong2e",
- .desc = "Fulong 2e mini pc",
- .init = mips_fulong2e_init,
-};
-
-static void mips_fulong2e_machine_init(void)
+static void mips_fulong2e_machine_init(MachineClass *mc)
{
- qemu_register_machine(&mips_fulong2e_machine);
+ mc->desc = "Fulong 2e mini pc";
+ mc->init = mips_fulong2e_init;
}
-machine_init(mips_fulong2e_machine_init);
+DEFINE_MACHINE("fulong2e", mips_fulong2e_machine_init)
diff --git a/hw/mips/mips_jazz.c b/hw/mips/mips_jazz.c
index 9d60633ef..1ab885bb3 100644
--- a/hw/mips/mips_jazz.c
+++ b/hw/mips/mips_jazz.c
@@ -104,15 +104,6 @@ static const MemoryRegionOps dma_dummy_ops = {
#define MAGNUM_BIOS_SIZE_MAX 0x7e000
#define MAGNUM_BIOS_SIZE (BIOS_SIZE < MAGNUM_BIOS_SIZE_MAX ? BIOS_SIZE : MAGNUM_BIOS_SIZE_MAX)
-static void cpu_request_exit(void *opaque, int irq, int level)
-{
- CPUState *cpu = current_cpu;
-
- if (cpu && level) {
- cpu_exit(cpu);
- }
-}
-
static CPUUnassignedAccess real_do_unassigned_access;
static void mips_jazz_do_unassigned_access(CPUState *cpu, hwaddr addr,
bool is_write, bool is_exec,
@@ -150,7 +141,6 @@ static void mips_jazz_init(MachineState *machine,
ISADevice *pit;
DriveInfo *fds[MAX_FD];
qemu_irq esp_reset, dma_enable;
- qemu_irq *cpu_exit_irq;
MemoryRegion *ram = g_new(MemoryRegion, 1);
MemoryRegion *bios = g_new(MemoryRegion, 1);
MemoryRegion *bios2 = g_new(MemoryRegion, 1);
@@ -184,7 +174,7 @@ static void mips_jazz_init(MachineState *machine,
memory_region_add_subregion(address_space, 0, ram);
memory_region_init_ram(bios, NULL, "mips_jazz.bios", MAGNUM_BIOS_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(bios);
memory_region_set_readonly(bios, true);
memory_region_init_alias(bios2, NULL, "mips_jazz.bios", bios,
@@ -234,8 +224,7 @@ static void mips_jazz_init(MachineState *machine,
/* ISA devices */
i8259 = i8259_init(isa_bus, env->irq[4]);
isa_bus_irqs(isa_bus, i8259);
- cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
- DMA_init(0, cpu_exit_irq);
+ DMA_init(0);
pit = pit_init(isa_bus, 0x40, 0, NULL);
pcspk_init(isa_bus, pit);
@@ -252,7 +241,7 @@ static void mips_jazz_init(MachineState *machine,
/* Simple ROM, so user doesn't have to provide one */
MemoryRegion *rom_mr = g_new(MemoryRegion, 1);
memory_region_init_ram(rom_mr, NULL, "g364fb.rom", 0x80000,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(rom_mr);
memory_region_set_readonly(rom_mr, true);
uint8_t *rom = memory_region_get_ram_ptr(rom_mr);
@@ -360,24 +349,40 @@ void mips_pica61_init(MachineState *machine)
mips_jazz_init(machine, JAZZ_PICA61);
}
-static QEMUMachine mips_magnum_machine = {
- .name = "magnum",
- .desc = "MIPS Magnum",
- .init = mips_magnum_init,
- .block_default_type = IF_SCSI,
+static void mips_magnum_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "MIPS Magnum";
+ mc->init = mips_magnum_init;
+ mc->block_default_type = IF_SCSI;
+}
+
+static const TypeInfo mips_magnum_type = {
+ .name = MACHINE_TYPE_NAME("magnum"),
+ .parent = TYPE_MACHINE,
+ .class_init = mips_magnum_class_init,
};
-static QEMUMachine mips_pica61_machine = {
- .name = "pica61",
- .desc = "Acer Pica 61",
- .init = mips_pica61_init,
- .block_default_type = IF_SCSI,
+static void mips_pica61_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Acer Pica 61";
+ mc->init = mips_pica61_init;
+ mc->block_default_type = IF_SCSI;
+}
+
+static const TypeInfo mips_pica61_type = {
+ .name = MACHINE_TYPE_NAME("pica61"),
+ .parent = TYPE_MACHINE,
+ .class_init = mips_pica61_class_init,
};
static void mips_jazz_machine_init(void)
{
- qemu_register_machine(&mips_magnum_machine);
- qemu_register_machine(&mips_pica61_machine);
+ type_register_static(&mips_magnum_type);
+ type_register_static(&mips_pica61_type);
}
-machine_init(mips_jazz_machine_init);
+machine_init(mips_jazz_machine_init)
diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c
index 3082e7534..91c36baa5 100644
--- a/hw/mips/mips_malta.c
+++ b/hw/mips/mips_malta.c
@@ -795,7 +795,7 @@ static int64_t load_kernel (void)
if (load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL,
(uint64_t *)&kernel_entry, NULL, (uint64_t *)&kernel_high,
- big_endian, ELF_MACHINE, 1) < 0) {
+ big_endian, EM_MIPS, 1) < 0) {
fprintf(stderr, "qemu: could not load kernel '%s'\n",
loaderparams.kernel_filename);
exit(1);
@@ -901,16 +901,7 @@ static void main_cpu_reset(void *opaque)
if (kvm_enabled()) {
/* Start running from the bootloader we wrote to end of RAM */
- env->active_tc.PC = 0x40000000 + loaderparams.ram_size;
- }
-}
-
-static void cpu_request_exit(void *opaque, int irq, int level)
-{
- CPUState *cpu = current_cpu;
-
- if (cpu && level) {
- cpu_exit(cpu);
+ env->active_tc.PC = 0x40000000 + loaderparams.ram_low_size;
}
}
@@ -939,7 +930,6 @@ void mips_malta_init(MachineState *machine)
MIPSCPU *cpu;
CPUMIPSState *env;
qemu_irq *isa_irq;
- qemu_irq *cpu_exit_irq;
int piix4_devfn;
I2CBus *smbus;
int i;
@@ -1130,7 +1120,7 @@ void mips_malta_init(MachineState *machine)
* regions are not executable.
*/
memory_region_init_ram(bios_copy, NULL, "bios.1fc", BIOS_SIZE,
- &error_abort);
+ &error_fatal);
if (!rom_copy(memory_region_get_ram_ptr(bios_copy),
FLASH_ADDRESS, BIOS_SIZE)) {
memcpy(memory_region_get_ram_ptr(bios_copy),
@@ -1175,8 +1165,7 @@ void mips_malta_init(MachineState *machine)
smbus_eeprom_init(smbus, 8, smbus_eeprom_buf, smbus_eeprom_size);
g_free(smbus_eeprom_buf);
pit = pit_init(isa_bus, 0x40, 0, NULL);
- cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
- DMA_init(0, cpu_exit_irq);
+ DMA_init(0);
/* Super I/O */
isa_create_simple(isa_bus, "i8042");
@@ -1216,23 +1205,19 @@ static const TypeInfo mips_malta_device = {
.class_init = mips_malta_class_init,
};
-static QEMUMachine mips_malta_machine = {
- .name = "malta",
- .desc = "MIPS Malta Core LV",
- .init = mips_malta_init,
- .max_cpus = 16,
- .is_default = 1,
-};
-
-static void mips_malta_register_types(void)
+static void mips_malta_machine_init(MachineClass *mc)
{
- type_register_static(&mips_malta_device);
+ mc->desc = "MIPS Malta Core LV";
+ mc->init = mips_malta_init;
+ mc->max_cpus = 16;
+ mc->is_default = 1;
}
-static void mips_malta_machine_init(void)
+DEFINE_MACHINE("malta", mips_malta_machine_init)
+
+static void mips_malta_register_types(void)
{
- qemu_register_machine(&mips_malta_machine);
+ type_register_static(&mips_malta_device);
}
type_init(mips_malta_register_types)
-machine_init(mips_malta_machine_init);
diff --git a/hw/mips/mips_mipssim.c b/hw/mips/mips_mipssim.c
index 61f74a631..23b35bea2 100644
--- a/hw/mips/mips_mipssim.c
+++ b/hw/mips/mips_mipssim.c
@@ -69,7 +69,7 @@ static int64_t load_kernel(void)
kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys,
NULL, (uint64_t *)&entry, NULL,
(uint64_t *)&kernel_high, big_endian,
- ELF_MACHINE, 1);
+ EM_MIPS, 1);
if (kernel_size >= 0) {
if ((entry & ~0x7fffffffULL) == 0x80000000)
entry = (int32_t)entry;
@@ -174,7 +174,7 @@ mips_mipssim_init(MachineState *machine)
memory_region_allocate_system_memory(ram, NULL, "mips_mipssim.ram",
ram_size);
memory_region_init_ram(bios, NULL, "mips_mipssim.bios", BIOS_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(bios);
memory_region_set_readonly(bios, true);
@@ -231,15 +231,10 @@ mips_mipssim_init(MachineState *machine)
mipsnet_init(0x4200, env->irq[2], &nd_table[0]);
}
-static QEMUMachine mips_mipssim_machine = {
- .name = "mipssim",
- .desc = "MIPS MIPSsim platform",
- .init = mips_mipssim_init,
-};
-
-static void mips_mipssim_machine_init(void)
+static void mips_mipssim_machine_init(MachineClass *mc)
{
- qemu_register_machine(&mips_mipssim_machine);
+ mc->desc = "MIPS MIPSsim platform";
+ mc->init = mips_mipssim_init;
}
-machine_init(mips_mipssim_machine_init);
+DEFINE_MACHINE("mipssim", mips_mipssim_machine_init)
diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c
index f4dcacd86..af10da1c5 100644
--- a/hw/mips/mips_r4k.c
+++ b/hw/mips/mips_r4k.c
@@ -87,7 +87,7 @@ static int64_t load_kernel(void)
kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys,
NULL, (uint64_t *)&entry, NULL,
(uint64_t *)&kernel_high, big_endian,
- ELF_MACHINE, 1);
+ EM_MIPS, 1);
if (kernel_size >= 0) {
if ((entry & ~0x7fffffffULL) == 0x80000000)
entry = (int32_t)entry;
@@ -233,7 +233,7 @@ void mips_r4k_init(MachineState *machine)
if ((bios_size > 0) && (bios_size <= BIOS_SIZE)) {
bios = g_new(MemoryRegion, 1);
memory_region_init_ram(bios, NULL, "mips_r4k.bios", BIOS_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(bios);
memory_region_set_readonly(bios, true);
memory_region_add_subregion(get_system_memory(), 0x1fc00000, bios);
@@ -252,9 +252,7 @@ void mips_r4k_init(MachineState *machine)
fprintf(stderr, "qemu: Warning, could not load MIPS bios '%s'\n",
bios_name);
}
- if (filename) {
- g_free(filename);
- }
+ g_free(filename);
if (kernel_filename) {
loaderparams.ram_size = ram_size;
@@ -300,15 +298,10 @@ void mips_r4k_init(MachineState *machine)
isa_create_simple(isa_bus, "i8042");
}
-static QEMUMachine mips_machine = {
- .name = "mips",
- .desc = "mips r4k platform",
- .init = mips_r4k_init,
-};
-
-static void mips_machine_init(void)
+static void mips_machine_init(MachineClass *mc)
{
- qemu_register_machine(&mips_machine);
+ mc->desc = "mips r4k platform";
+ mc->init = mips_r4k_init;
}
-machine_init(mips_machine_init);
+DEFINE_MACHINE("mips", mips_machine_init)
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 4aa76ffec..aeb6b7da7 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -36,6 +36,7 @@ obj-$(CONFIG_OMAP) += omap_sdrc.o
obj-$(CONFIG_OMAP) += omap_tap.o
obj-$(CONFIG_SLAVIO) += slavio_misc.o
obj-$(CONFIG_ZYNQ) += zynq_slcr.o
+obj-$(CONFIG_ZYNQ) += zynq-xadc.o
obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o
obj-$(CONFIG_PVPANIC) += pvpanic.o
diff --git a/hw/misc/imx_ccm.c b/hw/misc/imx_ccm.c
index 092028863..4cc2bbc0e 100644
--- a/hw/misc/imx_ccm.c
+++ b/hw/misc/imx_ccm.c
@@ -2,6 +2,7 @@
* IMX31 Clock Control Module
*
* Copyright (C) 2012 NICTA
+ * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
@@ -10,51 +11,27 @@
* the CCM.
*/
-#include "hw/hw.h"
-#include "hw/sysbus.h"
-#include "sysemu/sysemu.h"
-#include "hw/arm/imx.h"
+#include "hw/misc/imx_ccm.h"
#define CKIH_FREQ 26000000 /* 26MHz crystal input */
#define CKIL_FREQ 32768 /* nominal 32khz clock */
+#ifndef DEBUG_IMX_CCM
+#define DEBUG_IMX_CCM 0
+#endif
-//#define DEBUG_CCM 1
-#ifdef DEBUG_CCM
#define DPRINTF(fmt, args...) \
-do { printf("imx_ccm: " fmt , ##args); } while (0)
-#else
-#define DPRINTF(fmt, args...) do {} while (0)
-#endif
+ do { \
+ if (DEBUG_IMX_CCM) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_CCM, \
+ __func__, ##args); \
+ } \
+ } while (0)
static int imx_ccm_post_load(void *opaque, int version_id);
-#define TYPE_IMX_CCM "imx_ccm"
-#define IMX_CCM(obj) OBJECT_CHECK(IMXCCMState, (obj), TYPE_IMX_CCM)
-
-typedef struct IMXCCMState {
- SysBusDevice parent_obj;
-
- MemoryRegion iomem;
-
- uint32_t ccmr;
- uint32_t pdr0;
- uint32_t pdr1;
- uint32_t mpctl;
- uint32_t spctl;
- uint32_t cgr[3];
- uint32_t pmcr0;
- uint32_t pmcr1;
-
- /* Frequencies precalculated on register changes */
- uint32_t pll_refclk_freq;
- uint32_t mcu_clk_freq;
- uint32_t hsp_clk_freq;
- uint32_t ipg_clk_freq;
-} IMXCCMState;
-
static const VMStateDescription vmstate_imx_ccm = {
- .name = "imx-ccm",
+ .name = TYPE_IMX_CCM,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
@@ -72,44 +49,6 @@ static const VMStateDescription vmstate_imx_ccm = {
.post_load = imx_ccm_post_load,
};
-/* CCMR */
-#define CCMR_FPME (1<<0)
-#define CCMR_MPE (1<<3)
-#define CCMR_MDS (1<<7)
-#define CCMR_FPMF (1<<26)
-#define CCMR_PRCS (3<<1)
-
-/* PDR0 */
-#define PDR0_MCU_PODF_SHIFT (0)
-#define PDR0_MCU_PODF_MASK (0x7)
-#define PDR0_MAX_PODF_SHIFT (3)
-#define PDR0_MAX_PODF_MASK (0x7)
-#define PDR0_IPG_PODF_SHIFT (6)
-#define PDR0_IPG_PODF_MASK (0x3)
-#define PDR0_NFC_PODF_SHIFT (8)
-#define PDR0_NFC_PODF_MASK (0x7)
-#define PDR0_HSP_PODF_SHIFT (11)
-#define PDR0_HSP_PODF_MASK (0x7)
-#define PDR0_PER_PODF_SHIFT (16)
-#define PDR0_PER_PODF_MASK (0x1f)
-#define PDR0_CSI_PODF_SHIFT (23)
-#define PDR0_CSI_PODF_MASK (0x1ff)
-
-#define EXTRACT(value, name) (((value) >> PDR0_##name##_PODF_SHIFT) \
- & PDR0_##name##_PODF_MASK)
-#define INSERT(value, name) (((value) & PDR0_##name##_PODF_MASK) << \
- PDR0_##name##_PODF_SHIFT)
-/* PLL control registers */
-#define PD(v) (((v) >> 26) & 0xf)
-#define MFD(v) (((v) >> 16) & 0x3ff)
-#define MFI(v) (((v) >> 10) & 0xf);
-#define MFN(v) ((v) & 0x3ff)
-
-#define PLL_PD(x) (((x) & 0xf) << 26)
-#define PLL_MFD(x) (((x) & 0x3ff) << 16)
-#define PLL_MFI(x) (((x) & 0xf) << 10)
-#define PLL_MFN(x) (((x) & 0x3ff) << 0)
-
uint32_t imx_clock_frequency(DeviceState *dev, IMXClk clock)
{
IMXCCMState *s = IMX_CCM(dev);
@@ -174,7 +113,7 @@ static void update_clocks(IMXCCMState *s)
s->hsp_clk_freq = s->mcu_clk_freq / (1 + EXTRACT(s->pdr0, HSP));
s->ipg_clk_freq = s->hsp_clk_freq / (1 + EXTRACT(s->pdr0, IPG));
- DPRINTF("Clocks: mcu %uMHz, HSP %uMHz, IPG %uHz\n",
+ DPRINTF("mcu %uMHz, HSP %uMHz, IPG %uHz\n",
s->mcu_clk_freq / 1000000,
s->hsp_clk_freq / 1000000,
s->ipg_clk_freq);
@@ -200,7 +139,8 @@ static uint64_t imx_ccm_read(void *opaque, hwaddr offset,
{
IMXCCMState *s = (IMXCCMState *)opaque;
- DPRINTF("read(offset=%x)", offset >> 2);
+ DPRINTF("(offset=0x%" HWADDR_PRIx ")\n", offset);
+
switch (offset >> 2) {
case 0: /* CCMR */
DPRINTF(" ccmr = 0x%x\n", s->ccmr);
@@ -231,9 +171,11 @@ static uint64_t imx_ccm_read(void *opaque, hwaddr offset,
case 23:
DPRINTF(" pcmr0 = 0x%x\n", s->pmcr0);
return s->pmcr0;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_CCM, __func__, offset);
+ return 0;
}
- DPRINTF(" return 0\n");
- return 0;
}
static void imx_ccm_write(void *opaque, hwaddr offset,
@@ -241,8 +183,9 @@ static void imx_ccm_write(void *opaque, hwaddr offset,
{
IMXCCMState *s = (IMXCCMState *)opaque;
- DPRINTF("write(offset=%x, value = %x)\n",
- offset >> 2, (unsigned int)value);
+ DPRINTF("(offset=0x%" HWADDR_PRIx ", value = 0x%x)\n",
+ offset, (unsigned int)value);
+
switch (offset >> 2) {
case 0:
s->ccmr = CCMR_FPMF | (value & 0x3b6fdfff);
@@ -270,6 +213,8 @@ static void imx_ccm_write(void *opaque, hwaddr offset,
return;
default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_CCM, __func__, offset);
return;
}
update_clocks(s);
@@ -286,7 +231,7 @@ static int imx_ccm_init(SysBusDevice *dev)
IMXCCMState *s = IMX_CCM(dev);
memory_region_init_io(&s->iomem, OBJECT(dev), &imx_ccm_ops, s,
- "imx_ccm", 0x1000);
+ TYPE_IMX_CCM, 0x1000);
sysbus_init_mmio(dev, &s->iomem);
return 0;
diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c
index cc76989a3..dcfc8cc5f 100644
--- a/hw/misc/ivshmem.c
+++ b/hw/misc/ivshmem.c
@@ -19,6 +19,7 @@
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/pci/pci.h"
+#include "hw/pci/msi.h"
#include "hw/pci/msix.h"
#include "sysemu/kvm.h"
#include "migration/migration.h"
@@ -26,6 +27,10 @@
#include "qemu/event_notifier.h"
#include "qemu/fifo8.h"
#include "sysemu/char.h"
+#include "sysemu/hostmem.h"
+#include "qapi/visitor.h"
+
+#include "hw/misc/ivshmem.h"
#include <sys/mman.h>
#include <sys/types.h>
@@ -34,6 +39,7 @@
#define PCI_VENDOR_ID_IVSHMEM PCI_VENDOR_ID_REDHAT_QUMRANET
#define PCI_DEVICE_ID_IVSHMEM 0x1110
+#define IVSHMEM_MAX_PEERS G_MAXUINT16
#define IVSHMEM_IOEVENTFD 0
#define IVSHMEM_MSI 1
@@ -59,19 +65,19 @@ typedef struct Peer {
EventNotifier *eventfds;
} Peer;
-typedef struct EventfdEntry {
+typedef struct MSIVector {
PCIDevice *pdev;
- int vector;
-} EventfdEntry;
+ int virq;
+} MSIVector;
typedef struct IVShmemState {
/*< private >*/
PCIDevice parent_obj;
/*< public >*/
+ HostMemoryBackend *hostmem;
uint32_t intrmask;
uint32_t intrstatus;
- uint32_t doorbell;
CharDriverState **eventfd_chr;
CharDriverState *server_chr;
@@ -85,18 +91,15 @@ typedef struct IVShmemState {
MemoryRegion bar;
MemoryRegion ivshmem;
uint64_t ivshmem_size; /* size of shared memory region */
- uint32_t ivshmem_attr;
uint32_t ivshmem_64bit;
- int shm_fd; /* shared memory file descriptor */
Peer *peers;
- int nb_peers; /* how many guests we have space for */
- int max_peer; /* maximum numbered peer */
+ int nb_peers; /* how many peers we have space for */
int vm_id;
uint32_t vectors;
uint32_t features;
- EventfdEntry *eventfd_table;
+ MSIVector *msi_vectors;
Error *migration_blocker;
@@ -119,12 +122,8 @@ static inline uint32_t ivshmem_has_feature(IVShmemState *ivs,
return (ivs->features & (1 << feature));
}
-static inline bool is_power_of_two(uint64_t x) {
- return (x & (x - 1)) == 0;
-}
-
/* accessing registers - based on rtl8139 */
-static void ivshmem_update_irq(IVShmemState *s, int val)
+static void ivshmem_update_irq(IVShmemState *s)
{
PCIDevice *d = PCI_DEVICE(s);
int isr;
@@ -145,7 +144,7 @@ static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val)
s->intrmask = val;
- ivshmem_update_irq(s, val);
+ ivshmem_update_irq(s);
}
static uint32_t ivshmem_IntrMask_read(IVShmemState *s)
@@ -163,7 +162,7 @@ static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val)
s->intrstatus = val;
- ivshmem_update_irq(s, val);
+ ivshmem_update_irq(s);
}
static uint32_t ivshmem_IntrStatus_read(IVShmemState *s)
@@ -173,7 +172,7 @@ static uint32_t ivshmem_IntrStatus_read(IVShmemState *s)
/* reading ISR clears all interrupts */
s->intrstatus = 0;
- ivshmem_update_irq(s, 0);
+ ivshmem_update_irq(s);
return ret;
}
@@ -201,7 +200,7 @@ static void ivshmem_io_write(void *opaque, hwaddr addr,
case DOORBELL:
/* check that dest VM ID is reasonable */
- if (dest > s->max_peer) {
+ if (dest >= s->nb_peers) {
IVSHMEM_DPRINTF("Invalid destination VM ID (%d)\n", dest);
break;
}
@@ -210,10 +209,13 @@ static void ivshmem_io_write(void *opaque, hwaddr addr,
if (vector < s->peers[dest].nb_eventfds) {
IVSHMEM_DPRINTF("Notifying VM %d on vector %d\n", dest, vector);
event_notifier_set(&s->peers[dest].eventfds[vector]);
+ } else {
+ IVSHMEM_DPRINTF("Invalid destination vector %d on VM %d\n",
+ vector, dest);
}
break;
default:
- IVSHMEM_DPRINTF("Invalid VM Doorbell VM %d\n", dest);
+ IVSHMEM_DPRINTF("Unhandled write " TARGET_FMT_plx "\n", addr);
}
}
@@ -236,7 +238,7 @@ static uint64_t ivshmem_io_read(void *opaque, hwaddr addr,
case IVPOSITION:
/* return my VM ID if the memory is mapped */
- if (s->shm_fd > 0) {
+ if (memory_region_is_mapped(&s->ivshmem)) {
ret = s->vm_id;
} else {
ret = -1;
@@ -265,14 +267,14 @@ static void ivshmem_receive(void *opaque, const uint8_t *buf, int size)
{
IVShmemState *s = opaque;
- ivshmem_IntrStatus_write(s, *buf);
+ IVSHMEM_DPRINTF("ivshmem_receive 0x%02x size: %d\n", *buf, size);
- IVSHMEM_DPRINTF("ivshmem_receive 0x%02x\n", *buf);
+ ivshmem_IntrStatus_write(s, *buf);
}
static int ivshmem_can_receive(void * opaque)
{
- return 8;
+ return sizeof(int64_t);
}
static void ivshmem_event(void *opaque, int event)
@@ -282,36 +284,94 @@ static void ivshmem_event(void *opaque, int event)
static void fake_irqfd(void *opaque, const uint8_t *buf, int size) {
- EventfdEntry *entry = opaque;
+ MSIVector *entry = opaque;
PCIDevice *pdev = entry->pdev;
+ IVShmemState *s = IVSHMEM(pdev);
+ int vector = entry - s->msi_vectors;
+
+ IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, vector);
+ msix_notify(pdev, vector);
+}
+
+static int ivshmem_vector_unmask(PCIDevice *dev, unsigned vector,
+ MSIMessage msg)
+{
+ IVShmemState *s = IVSHMEM(dev);
+ EventNotifier *n = &s->peers[s->vm_id].eventfds[vector];
+ MSIVector *v = &s->msi_vectors[vector];
+ int ret;
+
+ IVSHMEM_DPRINTF("vector unmask %p %d\n", dev, vector);
+
+ ret = kvm_irqchip_update_msi_route(kvm_state, v->virq, msg, dev);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL, v->virq);
+}
- IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, entry->vector);
- msix_notify(pdev, entry->vector);
+static void ivshmem_vector_mask(PCIDevice *dev, unsigned vector)
+{
+ IVShmemState *s = IVSHMEM(dev);
+ EventNotifier *n = &s->peers[s->vm_id].eventfds[vector];
+ int ret;
+
+ IVSHMEM_DPRINTF("vector mask %p %d\n", dev, vector);
+
+ ret = kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n,
+ s->msi_vectors[vector].virq);
+ if (ret != 0) {
+ error_report("remove_irqfd_notifier_gsi failed");
+ }
+}
+
+static void ivshmem_vector_poll(PCIDevice *dev,
+ unsigned int vector_start,
+ unsigned int vector_end)
+{
+ IVShmemState *s = IVSHMEM(dev);
+ unsigned int vector;
+
+ IVSHMEM_DPRINTF("vector poll %p %d-%d\n", dev, vector_start, vector_end);
+
+ vector_end = MIN(vector_end, s->vectors);
+
+ for (vector = vector_start; vector < vector_end; vector++) {
+ EventNotifier *notifier = &s->peers[s->vm_id].eventfds[vector];
+
+ if (!msix_is_masked(dev, vector)) {
+ continue;
+ }
+
+ if (event_notifier_test_and_clear(notifier)) {
+ msix_set_pending(dev, vector);
+ }
+ }
}
-static CharDriverState* create_eventfd_chr_device(void * opaque, EventNotifier *n,
+static CharDriverState* create_eventfd_chr_device(IVShmemState *s,
+ EventNotifier *n,
int vector)
{
/* create a event character device based on the passed eventfd */
- IVShmemState *s = opaque;
- CharDriverState * chr;
int eventfd = event_notifier_get_fd(n);
+ CharDriverState *chr;
chr = qemu_chr_open_eventfd(eventfd);
if (chr == NULL) {
- error_report("creating eventfd for eventfd %d failed", eventfd);
- exit(1);
+ error_report("creating chardriver for eventfd %d failed", eventfd);
+ return NULL;
}
qemu_chr_fe_claim_no_fail(chr);
/* if MSI is supported we need multiple interrupts */
if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
- s->eventfd_table[vector].pdev = PCI_DEVICE(s);
- s->eventfd_table[vector].vector = vector;
+ s->msi_vectors[vector].pdev = PCI_DEVICE(s);
qemu_chr_add_handlers(chr, ivshmem_can_receive, fake_irqfd,
- ivshmem_event, &s->eventfd_table[vector]);
+ ivshmem_event, &s->msi_vectors[vector]);
} else {
qemu_chr_add_handlers(chr, ivshmem_can_receive, ivshmem_receive,
ivshmem_event, s);
@@ -321,22 +381,23 @@ static CharDriverState* create_eventfd_chr_device(void * opaque, EventNotifier *
}
-static int check_shm_size(IVShmemState *s, int fd) {
+static int check_shm_size(IVShmemState *s, int fd, Error **errp)
+{
/* check that the guest isn't going to try and map more memory than the
* the object has allocated return -1 to indicate error */
struct stat buf;
if (fstat(fd, &buf) < 0) {
- error_report("exiting: fstat on fd %d failed: %s",
- fd, strerror(errno));
+ error_setg(errp, "exiting: fstat on fd %d failed: %s",
+ fd, strerror(errno));
return -1;
}
if (s->ivshmem_size > buf.st_size) {
- error_report("Requested memory size greater"
- " than shared object size (%" PRIu64 " > %" PRIu64")",
- s->ivshmem_size, (uint64_t)buf.st_size);
+ error_setg(errp, "Requested memory size greater"
+ " than shared object size (%" PRIu64 " > %" PRIu64")",
+ s->ivshmem_size, (uint64_t)buf.st_size);
return -1;
} else {
return 0;
@@ -345,13 +406,16 @@ static int check_shm_size(IVShmemState *s, int fd) {
/* create the shared memory BAR when we are not using the server, so we can
* create the BAR and map the memory immediately */
-static void create_shared_memory_BAR(IVShmemState *s, int fd) {
-
+static int create_shared_memory_BAR(IVShmemState *s, int fd, uint8_t attr,
+ Error **errp)
+{
void * ptr;
- s->shm_fd = fd;
-
ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if (ptr == MAP_FAILED) {
+ error_setg_errno(errp, errno, "Failed to mmap shared memory");
+ return -1;
+ }
memory_region_init_ram_ptr(&s->ivshmem, OBJECT(s), "ivshmem.bar2",
s->ivshmem_size, ptr);
@@ -359,7 +423,9 @@ static void create_shared_memory_BAR(IVShmemState *s, int fd) {
memory_region_add_subregion(&s->bar, 0, &s->ivshmem);
/* region for shared memory */
- pci_register_bar(PCI_DEVICE(s), 2, s->ivshmem_attr, &s->bar);
+ pci_register_bar(PCI_DEVICE(s), 2, attr, &s->bar);
+
+ return 0;
}
static void ivshmem_add_eventfd(IVShmemState *s, int posn, int i)
@@ -382,25 +448,26 @@ static void ivshmem_del_eventfd(IVShmemState *s, int posn, int i)
&s->peers[posn].eventfds[i]);
}
-static void close_guest_eventfds(IVShmemState *s, int posn)
+static void close_peer_eventfds(IVShmemState *s, int posn)
{
- int i, guest_curr_max;
+ int i, n;
if (!ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
return;
}
if (posn < 0 || posn >= s->nb_peers) {
+ error_report("invalid peer %d", posn);
return;
}
- guest_curr_max = s->peers[posn].nb_eventfds;
+ n = s->peers[posn].nb_eventfds;
memory_region_transaction_begin();
- for (i = 0; i < guest_curr_max; i++) {
+ for (i = 0; i < n; i++) {
ivshmem_del_eventfd(s, posn, i);
}
memory_region_transaction_commit();
- for (i = 0; i < guest_curr_max; i++) {
+ for (i = 0; i < n; i++) {
event_notifier_cleanup(&s->peers[posn].eventfds[i]);
}
@@ -409,125 +476,207 @@ static void close_guest_eventfds(IVShmemState *s, int posn)
}
/* this function increase the dynamic storage need to store data about other
- * guests */
-static int increase_dynamic_storage(IVShmemState *s, int new_min_size)
+ * peers */
+static int resize_peers(IVShmemState *s, int new_min_size)
{
- int j, old_nb_alloc;
+ int j, old_size;
- /* check for integer overflow */
- if (new_min_size >= INT_MAX / sizeof(Peer) - 1 || new_min_size <= 0) {
+ /* limit number of max peers */
+ if (new_min_size <= 0 || new_min_size > IVSHMEM_MAX_PEERS) {
return -1;
}
-
- old_nb_alloc = s->nb_peers;
-
- if (new_min_size >= s->nb_peers) {
- /* +1 because #new_min_size is used as last array index */
- s->nb_peers = new_min_size + 1;
- } else {
+ if (new_min_size <= s->nb_peers) {
return 0;
}
- IVSHMEM_DPRINTF("bumping storage to %d guests\n", s->nb_peers);
+ old_size = s->nb_peers;
+ s->nb_peers = new_min_size;
+
+ IVSHMEM_DPRINTF("bumping storage to %d peers\n", s->nb_peers);
+
s->peers = g_realloc(s->peers, s->nb_peers * sizeof(Peer));
- /* zero out new pointers */
- for (j = old_nb_alloc; j < s->nb_peers; j++) {
- s->peers[j].eventfds = NULL;
+ for (j = old_size; j < s->nb_peers; j++) {
+ s->peers[j].eventfds = g_new0(EventNotifier, s->vectors);
s->peers[j].nb_eventfds = 0;
}
return 0;
}
-static void ivshmem_read(void *opaque, const uint8_t *buf, int size)
+static bool fifo_update_and_get(IVShmemState *s, const uint8_t *buf, int size,
+ void *data, size_t len)
{
- IVShmemState *s = opaque;
- int incoming_fd, tmp_fd;
- int guest_max_eventfd;
- long incoming_posn;
+ const uint8_t *p;
+ uint32_t num;
- if (fifo8_is_empty(&s->incoming_fifo) && size == sizeof(incoming_posn)) {
- memcpy(&incoming_posn, buf, size);
- } else {
- const uint8_t *p;
- uint32_t num;
+ assert(len <= sizeof(int64_t)); /* limitation of the fifo */
+ if (fifo8_is_empty(&s->incoming_fifo) && size == len) {
+ memcpy(data, buf, size);
+ return true;
+ }
+
+ IVSHMEM_DPRINTF("short read of %d bytes\n", size);
+
+ num = MIN(size, sizeof(int64_t) - fifo8_num_used(&s->incoming_fifo));
+ fifo8_push_all(&s->incoming_fifo, buf, num);
+
+ if (fifo8_num_used(&s->incoming_fifo) < len) {
+ assert(num == 0);
+ return false;
+ }
+
+ size -= num;
+ buf += num;
+ p = fifo8_pop_buf(&s->incoming_fifo, len, &num);
+ assert(num == len);
+
+ memcpy(data, p, len);
+
+ if (size > 0) {
+ fifo8_push_all(&s->incoming_fifo, buf, size);
+ }
- IVSHMEM_DPRINTF("short read of %d bytes\n", size);
- num = MAX(size, sizeof(long) - fifo8_num_used(&s->incoming_fifo));
- fifo8_push_all(&s->incoming_fifo, buf, num);
- if (fifo8_num_used(&s->incoming_fifo) < sizeof(incoming_posn)) {
+ return true;
+}
+
+static bool fifo_update_and_get_i64(IVShmemState *s,
+ const uint8_t *buf, int size, int64_t *i64)
+{
+ if (fifo_update_and_get(s, buf, size, i64, sizeof(*i64))) {
+ *i64 = GINT64_FROM_LE(*i64);
+ return true;
+ }
+
+ return false;
+}
+
+static int ivshmem_add_kvm_msi_virq(IVShmemState *s, int vector)
+{
+ PCIDevice *pdev = PCI_DEVICE(s);
+ MSIMessage msg = msix_get_message(pdev, vector);
+ int ret;
+
+ IVSHMEM_DPRINTF("ivshmem_add_kvm_msi_virq vector:%d\n", vector);
+
+ if (s->msi_vectors[vector].pdev != NULL) {
+ return 0;
+ }
+
+ ret = kvm_irqchip_add_msi_route(kvm_state, msg, pdev);
+ if (ret < 0) {
+ error_report("ivshmem: kvm_irqchip_add_msi_route failed");
+ return -1;
+ }
+
+ s->msi_vectors[vector].virq = ret;
+ s->msi_vectors[vector].pdev = pdev;
+
+ return 0;
+}
+
+static void setup_interrupt(IVShmemState *s, int vector)
+{
+ EventNotifier *n = &s->peers[s->vm_id].eventfds[vector];
+ bool with_irqfd = kvm_msi_via_irqfd_enabled() &&
+ ivshmem_has_feature(s, IVSHMEM_MSI);
+ PCIDevice *pdev = PCI_DEVICE(s);
+
+ IVSHMEM_DPRINTF("setting up interrupt for vector: %d\n", vector);
+
+ if (!with_irqfd) {
+ IVSHMEM_DPRINTF("with eventfd");
+ s->eventfd_chr[vector] = create_eventfd_chr_device(s, n, vector);
+ } else if (msix_enabled(pdev)) {
+ IVSHMEM_DPRINTF("with irqfd");
+ if (ivshmem_add_kvm_msi_virq(s, vector) < 0) {
return;
}
- size -= num;
- buf += num;
- p = fifo8_pop_buf(&s->incoming_fifo, sizeof(incoming_posn), &num);
- g_assert(num == sizeof(incoming_posn));
- memcpy(&incoming_posn, p, sizeof(incoming_posn));
- if (size > 0) {
- fifo8_push_all(&s->incoming_fifo, buf, size);
+
+ if (!msix_is_masked(pdev, vector)) {
+ kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, NULL,
+ s->msi_vectors[vector].virq);
}
+ } else {
+ /* it will be delayed until msix is enabled, in write_config */
+ IVSHMEM_DPRINTF("with irqfd, delayed until msix enabled");
+ }
+}
+
+static void ivshmem_read(void *opaque, const uint8_t *buf, int size)
+{
+ IVShmemState *s = opaque;
+ int incoming_fd;
+ int new_eventfd;
+ int64_t incoming_posn;
+ Error *err = NULL;
+ Peer *peer;
+
+ if (!fifo_update_and_get_i64(s, buf, size, &incoming_posn)) {
+ return;
}
if (incoming_posn < -1) {
- IVSHMEM_DPRINTF("invalid incoming_posn %ld\n", incoming_posn);
+ IVSHMEM_DPRINTF("invalid incoming_posn %" PRId64 "\n", incoming_posn);
return;
}
/* pick off s->server_chr->msgfd and store it, posn should accompany msg */
- tmp_fd = qemu_chr_fe_get_msgfd(s->server_chr);
- IVSHMEM_DPRINTF("posn is %ld, fd is %d\n", incoming_posn, tmp_fd);
+ incoming_fd = qemu_chr_fe_get_msgfd(s->server_chr);
+ IVSHMEM_DPRINTF("posn is %" PRId64 ", fd is %d\n",
+ incoming_posn, incoming_fd);
- /* make sure we have enough space for this guest */
+ /* make sure we have enough space for this peer */
if (incoming_posn >= s->nb_peers) {
- if (increase_dynamic_storage(s, incoming_posn) < 0) {
- error_report("increase_dynamic_storage() failed");
- if (tmp_fd != -1) {
- close(tmp_fd);
+ if (resize_peers(s, incoming_posn + 1) < 0) {
+ error_report("failed to resize peers array");
+ if (incoming_fd != -1) {
+ close(incoming_fd);
}
return;
}
}
- if (tmp_fd == -1) {
+ peer = &s->peers[incoming_posn];
+
+ if (incoming_fd == -1) {
/* if posn is positive and unseen before then this is our posn*/
- if ((incoming_posn >= 0) &&
- (s->peers[incoming_posn].eventfds == NULL)) {
+ if (incoming_posn >= 0 && s->vm_id == -1) {
/* receive our posn */
s->vm_id = incoming_posn;
- return;
} else {
- /* otherwise an fd == -1 means an existing guest has gone away */
- IVSHMEM_DPRINTF("posn %ld has gone away\n", incoming_posn);
- close_guest_eventfds(s, incoming_posn);
- return;
+ /* otherwise an fd == -1 means an existing peer has gone away */
+ IVSHMEM_DPRINTF("posn %" PRId64 " has gone away\n", incoming_posn);
+ close_peer_eventfds(s, incoming_posn);
}
- }
-
- /* because of the implementation of get_msgfd, we need a dup */
- incoming_fd = dup(tmp_fd);
-
- if (incoming_fd == -1) {
- error_report("could not allocate file descriptor %s", strerror(errno));
- close(tmp_fd);
return;
}
/* if the position is -1, then it's shared memory region fd */
if (incoming_posn == -1) {
-
void * map_ptr;
- s->max_peer = 0;
+ if (memory_region_is_mapped(&s->ivshmem)) {
+ error_report("shm already initialized");
+ close(incoming_fd);
+ return;
+ }
- if (check_shm_size(s, incoming_fd) == -1) {
- exit(1);
+ if (check_shm_size(s, incoming_fd, &err) == -1) {
+ error_report_err(err);
+ close(incoming_fd);
+ return;
}
/* mmap the region and map into the BAR2 */
map_ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED,
incoming_fd, 0);
+ if (map_ptr == MAP_FAILED) {
+ error_report("Failed to mmap shared memory %s", strerror(errno));
+ close(incoming_fd);
+ return;
+ }
memory_region_init_ram_ptr(&s->ivshmem, OBJECT(s),
"ivshmem.bar2", s->ivshmem_size, map_ptr);
vmstate_register_ram(&s->ivshmem, DEVICE(s));
@@ -537,44 +686,59 @@ static void ivshmem_read(void *opaque, const uint8_t *buf, int size)
memory_region_add_subregion(&s->bar, 0, &s->ivshmem);
- /* only store the fd if it is successfully mapped */
- s->shm_fd = incoming_fd;
+ close(incoming_fd);
+ return;
+ }
+ /* each peer has an associated array of eventfds, and we keep
+ * track of how many eventfds received so far */
+ /* get a new eventfd: */
+ if (peer->nb_eventfds >= s->vectors) {
+ error_report("Too many eventfd received, device has %d vectors",
+ s->vectors);
+ close(incoming_fd);
return;
}
- /* each guest has an array of eventfds, and we keep track of how many
- * guests for each VM */
- guest_max_eventfd = s->peers[incoming_posn].nb_eventfds;
+ new_eventfd = peer->nb_eventfds++;
- if (guest_max_eventfd == 0) {
- /* one eventfd per MSI vector */
- s->peers[incoming_posn].eventfds = g_new(EventNotifier, s->vectors);
+ /* this is an eventfd for a particular peer VM */
+ IVSHMEM_DPRINTF("eventfds[%" PRId64 "][%d] = %d\n", incoming_posn,
+ new_eventfd, incoming_fd);
+ event_notifier_init_fd(&peer->eventfds[new_eventfd], incoming_fd);
+ fcntl_setfl(incoming_fd, O_NONBLOCK); /* msix/irqfd poll non block */
+
+ if (incoming_posn == s->vm_id) {
+ setup_interrupt(s, new_eventfd);
}
- /* this is an eventfd for a particular guest VM */
- IVSHMEM_DPRINTF("eventfds[%ld][%d] = %d\n", incoming_posn,
- guest_max_eventfd, incoming_fd);
- event_notifier_init_fd(&s->peers[incoming_posn].eventfds[guest_max_eventfd],
- incoming_fd);
+ if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
+ ivshmem_add_eventfd(s, incoming_posn, new_eventfd);
+ }
+}
- /* increment count for particular guest */
- s->peers[incoming_posn].nb_eventfds++;
+static void ivshmem_check_version(void *opaque, const uint8_t * buf, int size)
+{
+ IVShmemState *s = opaque;
+ int tmp;
+ int64_t version;
- /* keep track of the maximum VM ID */
- if (incoming_posn > s->max_peer) {
- s->max_peer = incoming_posn;
+ if (!fifo_update_and_get_i64(s, buf, size, &version)) {
+ return;
}
- if (incoming_posn == s->vm_id) {
- s->eventfd_chr[guest_max_eventfd] = create_eventfd_chr_device(s,
- &s->peers[s->vm_id].eventfds[guest_max_eventfd],
- guest_max_eventfd);
+ tmp = qemu_chr_fe_get_msgfd(s->server_chr);
+ if (tmp != -1 || version != IVSHMEM_PROTOCOL_VERSION) {
+ fprintf(stderr, "incompatible version, you are connecting to a ivshmem-"
+ "server using a different protocol please check your setup\n");
+ qemu_chr_delete(s->server_chr);
+ s->server_chr = NULL;
+ return;
}
- if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
- ivshmem_add_eventfd(s, incoming_posn, guest_max_eventfd);
- }
+ IVSHMEM_DPRINTF("version check ok, switch to real chardev handler\n");
+ qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, ivshmem_read,
+ ivshmem_event, s);
}
/* Select the MSI-X vectors used by device.
@@ -585,6 +749,7 @@ static void ivshmem_use_msix(IVShmemState * s)
PCIDevice *d = PCI_DEVICE(s);
int i;
+ IVSHMEM_DPRINTF("%s, msix present: %d\n", __func__, msix_present(d));
if (!msix_present(d)) {
return;
}
@@ -599,128 +764,127 @@ static void ivshmem_reset(DeviceState *d)
IVShmemState *s = IVSHMEM(d);
s->intrstatus = 0;
+ s->intrmask = 0;
ivshmem_use_msix(s);
}
-static uint64_t ivshmem_get_size(IVShmemState * s) {
-
- uint64_t value;
- char *ptr;
-
- value = strtoull(s->sizearg, &ptr, 10);
- switch (*ptr) {
- case 0: case 'M': case 'm':
- value <<= 20;
- break;
- case 'G': case 'g':
- value <<= 30;
- break;
- default:
- error_report("invalid ram size: %s", s->sizearg);
- exit(1);
- }
-
- /* BARs must be a power of 2 */
- if (!is_power_of_two(value)) {
- error_report("size must be power of 2");
- exit(1);
- }
-
- return value;
-}
-
-static void ivshmem_setup_msi(IVShmemState * s)
+static int ivshmem_setup_msi(IVShmemState * s)
{
if (msix_init_exclusive_bar(PCI_DEVICE(s), s->vectors, 1)) {
- IVSHMEM_DPRINTF("msix initialization failed\n");
- exit(1);
+ return -1;
}
IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors);
/* allocate QEMU char devices for receiving interrupts */
- s->eventfd_table = g_malloc0(s->vectors * sizeof(EventfdEntry));
+ s->msi_vectors = g_malloc0(s->vectors * sizeof(MSIVector));
ivshmem_use_msix(s);
+ return 0;
}
-static void ivshmem_save(QEMUFile* f, void *opaque)
+static void ivshmem_enable_irqfd(IVShmemState *s)
{
- IVShmemState *proxy = opaque;
- PCIDevice *pci_dev = PCI_DEVICE(proxy);
-
- IVSHMEM_DPRINTF("ivshmem_save\n");
- pci_device_save(pci_dev, f);
+ PCIDevice *pdev = PCI_DEVICE(s);
+ int i;
- if (ivshmem_has_feature(proxy, IVSHMEM_MSI)) {
- msix_save(pci_dev, f);
- } else {
- qemu_put_be32(f, proxy->intrstatus);
- qemu_put_be32(f, proxy->intrmask);
+ for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) {
+ ivshmem_add_kvm_msi_virq(s, i);
}
+ if (msix_set_vector_notifiers(pdev,
+ ivshmem_vector_unmask,
+ ivshmem_vector_mask,
+ ivshmem_vector_poll)) {
+ error_report("ivshmem: msix_set_vector_notifiers failed");
+ }
}
-static int ivshmem_load(QEMUFile* f, void *opaque, int version_id)
+static void ivshmem_remove_kvm_msi_virq(IVShmemState *s, int vector)
{
- IVSHMEM_DPRINTF("ivshmem_load\n");
+ IVSHMEM_DPRINTF("ivshmem_remove_kvm_msi_virq vector:%d\n", vector);
- IVShmemState *proxy = opaque;
- PCIDevice *pci_dev = PCI_DEVICE(proxy);
- int ret;
-
- if (version_id > 0) {
- return -EINVAL;
+ if (s->msi_vectors[vector].pdev == NULL) {
+ return;
}
- if (proxy->role_val == IVSHMEM_PEER) {
- error_report("'peer' devices are not migratable");
- return -EINVAL;
- }
+ /* it was cleaned when masked in the frontend. */
+ kvm_irqchip_release_virq(kvm_state, s->msi_vectors[vector].virq);
- ret = pci_device_load(pci_dev, f);
- if (ret) {
- return ret;
- }
+ s->msi_vectors[vector].pdev = NULL;
+}
- if (ivshmem_has_feature(proxy, IVSHMEM_MSI)) {
- msix_load(pci_dev, f);
- ivshmem_use_msix(proxy);
- } else {
- proxy->intrstatus = qemu_get_be32(f);
- proxy->intrmask = qemu_get_be32(f);
+static void ivshmem_disable_irqfd(IVShmemState *s)
+{
+ PCIDevice *pdev = PCI_DEVICE(s);
+ int i;
+
+ for (i = 0; i < s->peers[s->vm_id].nb_eventfds; i++) {
+ ivshmem_remove_kvm_msi_virq(s, i);
}
- return 0;
+ msix_unset_vector_notifiers(pdev);
}
-static void ivshmem_write_config(PCIDevice *pci_dev, uint32_t address,
- uint32_t val, int len)
+static void ivshmem_write_config(PCIDevice *pdev, uint32_t address,
+ uint32_t val, int len)
{
- pci_default_write_config(pci_dev, address, val, len);
+ IVShmemState *s = IVSHMEM(pdev);
+ int is_enabled, was_enabled = msix_enabled(pdev);
+
+ pci_default_write_config(pdev, address, val, len);
+ is_enabled = msix_enabled(pdev);
+
+ if (kvm_msi_via_irqfd_enabled() && s->vm_id != -1) {
+ if (!was_enabled && is_enabled) {
+ ivshmem_enable_irqfd(s);
+ } else if (was_enabled && !is_enabled) {
+ ivshmem_disable_irqfd(s);
+ }
+ }
}
-static int pci_ivshmem_init(PCIDevice *dev)
+static void pci_ivshmem_realize(PCIDevice *dev, Error **errp)
{
IVShmemState *s = IVSHMEM(dev);
uint8_t *pci_conf;
+ uint8_t attr = PCI_BASE_ADDRESS_SPACE_MEMORY |
+ PCI_BASE_ADDRESS_MEM_PREFETCH;
- if (s->sizearg == NULL)
- s->ivshmem_size = 4 << 20; /* 4 MB default */
- else {
- s->ivshmem_size = ivshmem_get_size(s);
+ if (!!s->server_chr + !!s->shmobj + !!s->hostmem != 1) {
+ error_setg(errp,
+ "You must specify either 'shm', 'chardev' or 'x-memdev'");
+ return;
}
- fifo8_create(&s->incoming_fifo, sizeof(long));
+ if (s->hostmem) {
+ MemoryRegion *mr;
- register_savevm(DEVICE(dev), "ivshmem", 0, 0, ivshmem_save, ivshmem_load,
- dev);
+ if (s->sizearg) {
+ g_warning("size argument ignored with hostmem");
+ }
+
+ mr = host_memory_backend_get_memory(s->hostmem, errp);
+ s->ivshmem_size = memory_region_size(mr);
+ } else if (s->sizearg == NULL) {
+ s->ivshmem_size = 4 << 20; /* 4 MB default */
+ } else {
+ char *end;
+ int64_t size = qemu_strtosz(s->sizearg, &end);
+ if (size < 0 || *end != '\0' || !is_power_of_2(size)) {
+ error_setg(errp, "Invalid size %s", s->sizearg);
+ return;
+ }
+ s->ivshmem_size = size;
+ }
+
+ fifo8_create(&s->incoming_fifo, sizeof(int64_t));
/* IRQFD requires MSI */
if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) &&
!ivshmem_has_feature(s, IVSHMEM_MSI)) {
- error_report("ioeventfd/irqfd requires MSI");
- exit(1);
+ error_setg(errp, "ioeventfd/irqfd requires MSI");
+ return;
}
/* check that role is reasonable */
@@ -730,8 +894,8 @@ static int pci_ivshmem_init(PCIDevice *dev)
} else if (strncmp(s->role, "master", 7) == 0) {
s->role_val = IVSHMEM_MASTER;
} else {
- error_report("'role' must be 'peer' or 'master'");
- exit(1);
+ error_setg(errp, "'role' must be 'peer' or 'master'");
+ return;
}
} else {
s->role_val = IVSHMEM_MASTER; /* default */
@@ -748,8 +912,6 @@ static int pci_ivshmem_init(PCIDevice *dev)
pci_config_set_interrupt_pin(pci_conf, 1);
- s->shm_fd = 0;
-
memory_region_init_io(&s->ivshmem_mmio, OBJECT(s), &ivshmem_mmio_ops, s,
"ivshmem-mmio", IVSHMEM_REG_BAR_SIZE);
@@ -758,51 +920,52 @@ static int pci_ivshmem_init(PCIDevice *dev)
&s->ivshmem_mmio);
memory_region_init(&s->bar, OBJECT(s), "ivshmem-bar2-container", s->ivshmem_size);
- s->ivshmem_attr = PCI_BASE_ADDRESS_SPACE_MEMORY |
- PCI_BASE_ADDRESS_MEM_PREFETCH;
if (s->ivshmem_64bit) {
- s->ivshmem_attr |= PCI_BASE_ADDRESS_MEM_TYPE_64;
+ attr |= PCI_BASE_ADDRESS_MEM_TYPE_64;
}
- if ((s->server_chr != NULL) &&
- (strncmp(s->server_chr->filename, "unix:", 5) == 0)) {
- /* if we get a UNIX socket as the parameter we will talk
- * to the ivshmem server to receive the memory region */
+ if (s->hostmem != NULL) {
+ MemoryRegion *mr;
+
+ IVSHMEM_DPRINTF("using hostmem\n");
- if (s->shmobj != NULL) {
- error_report("WARNING: do not specify both 'chardev' "
- "and 'shm' with ivshmem");
+ mr = host_memory_backend_get_memory(MEMORY_BACKEND(s->hostmem), errp);
+ vmstate_register_ram(mr, DEVICE(s));
+ memory_region_add_subregion(&s->bar, 0, mr);
+ pci_register_bar(PCI_DEVICE(s), 2, attr, &s->bar);
+ } else if (s->server_chr != NULL) {
+ /* FIXME do not rely on what chr drivers put into filename */
+ if (strncmp(s->server_chr->filename, "unix:", 5)) {
+ error_setg(errp, "chardev is not a unix client socket");
+ return;
}
+ /* if we get a UNIX socket as the parameter we will talk
+ * to the ivshmem server to receive the memory region */
+
IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n",
s->server_chr->filename);
- if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
- ivshmem_setup_msi(s);
+ if (ivshmem_has_feature(s, IVSHMEM_MSI) &&
+ ivshmem_setup_msi(s)) {
+ error_setg(errp, "msix initialization failed");
+ return;
}
- /* we allocate enough space for 16 guests and grow as needed */
- s->nb_peers = 16;
+ /* we allocate enough space for 16 peers and grow as needed */
+ resize_peers(s, 16);
s->vm_id = -1;
- /* allocate/initialize space for interrupt handling */
- s->peers = g_malloc0(s->nb_peers * sizeof(Peer));
-
- pci_register_bar(dev, 2, s->ivshmem_attr, &s->bar);
+ pci_register_bar(dev, 2, attr, &s->bar);
s->eventfd_chr = g_malloc0(s->vectors * sizeof(CharDriverState *));
- qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, ivshmem_read,
- ivshmem_event, s);
+ qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive,
+ ivshmem_check_version, ivshmem_event, s);
} else {
/* just map the file immediately, we're not using a server */
int fd;
- if (s->shmobj == NULL) {
- error_report("Must specify 'chardev' or 'shm' to ivshmem");
- exit(1);
- }
-
IVSHMEM_DPRINTF("using shm_open (shm object = %s)\n", s->shmobj);
/* try opening with O_EXCL and if it succeeds zero the memory
@@ -816,39 +979,155 @@ static int pci_ivshmem_init(PCIDevice *dev)
} else if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR,
S_IRWXU|S_IRWXG|S_IRWXO)) < 0) {
- error_report("could not open shared file");
- exit(1);
+ error_setg(errp, "could not open shared file");
+ return;
+ }
+
+ if (check_shm_size(s, fd, errp) == -1) {
+ return;
+ }
+
+ create_shared_memory_BAR(s, fd, attr, errp);
+ close(fd);
+ }
+}
+
+static void pci_ivshmem_exit(PCIDevice *dev)
+{
+ IVShmemState *s = IVSHMEM(dev);
+ int i;
+
+ fifo8_destroy(&s->incoming_fifo);
+
+ if (s->migration_blocker) {
+ migrate_del_blocker(s->migration_blocker);
+ error_free(s->migration_blocker);
+ }
+
+ if (memory_region_is_mapped(&s->ivshmem)) {
+ if (!s->hostmem) {
+ void *addr = memory_region_get_ram_ptr(&s->ivshmem);
+
+ if (munmap(addr, s->ivshmem_size) == -1) {
+ error_report("Failed to munmap shared memory %s",
+ strerror(errno));
+ }
+ }
+ vmstate_unregister_ram(&s->ivshmem, DEVICE(dev));
+ memory_region_del_subregion(&s->bar, &s->ivshmem);
+ }
+
+ if (s->eventfd_chr) {
+ for (i = 0; i < s->vectors; i++) {
+ if (s->eventfd_chr[i]) {
+ qemu_chr_free(s->eventfd_chr[i]);
+ }
}
+ g_free(s->eventfd_chr);
+ }
- if (check_shm_size(s, fd) == -1) {
- exit(1);
+ if (s->peers) {
+ for (i = 0; i < s->nb_peers; i++) {
+ close_peer_eventfds(s, i);
}
+ g_free(s->peers);
+ }
+
+ if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ msix_uninit_exclusive_bar(dev);
+ }
+
+ g_free(s->msi_vectors);
+}
- create_shared_memory_BAR(s, fd);
+static bool test_msix(void *opaque, int version_id)
+{
+ IVShmemState *s = opaque;
+
+ return ivshmem_has_feature(s, IVSHMEM_MSI);
+}
+static bool test_no_msix(void *opaque, int version_id)
+{
+ return !test_msix(opaque, version_id);
+}
+
+static int ivshmem_pre_load(void *opaque)
+{
+ IVShmemState *s = opaque;
+
+ if (s->role_val == IVSHMEM_PEER) {
+ error_report("'peer' devices are not migratable");
+ return -EINVAL;
}
- dev->config_write = ivshmem_write_config;
+ return 0;
+}
+
+static int ivshmem_post_load(void *opaque, int version_id)
+{
+ IVShmemState *s = opaque;
+
+ if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ ivshmem_use_msix(s);
+ }
return 0;
}
-static void pci_ivshmem_uninit(PCIDevice *dev)
+static int ivshmem_load_old(QEMUFile *f, void *opaque, int version_id)
{
- IVShmemState *s = IVSHMEM(dev);
+ IVShmemState *s = opaque;
+ PCIDevice *pdev = PCI_DEVICE(s);
+ int ret;
- if (s->migration_blocker) {
- migrate_del_blocker(s->migration_blocker);
- error_free(s->migration_blocker);
+ IVSHMEM_DPRINTF("ivshmem_load_old\n");
+
+ if (version_id != 0) {
+ return -EINVAL;
}
- memory_region_del_subregion(&s->bar, &s->ivshmem);
- vmstate_unregister_ram(&s->ivshmem, DEVICE(dev));
- unregister_savevm(DEVICE(dev), "ivshmem", s);
- fifo8_destroy(&s->incoming_fifo);
+ if (s->role_val == IVSHMEM_PEER) {
+ error_report("'peer' devices are not migratable");
+ return -EINVAL;
+ }
+
+ ret = pci_device_load(pdev, f);
+ if (ret) {
+ return ret;
+ }
+
+ if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ msix_load(pdev, f);
+ ivshmem_use_msix(s);
+ } else {
+ s->intrstatus = qemu_get_be32(f);
+ s->intrmask = qemu_get_be32(f);
+ }
+
+ return 0;
}
+static const VMStateDescription ivshmem_vmsd = {
+ .name = "ivshmem",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .pre_load = ivshmem_pre_load,
+ .post_load = ivshmem_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj, IVShmemState),
+
+ VMSTATE_MSIX_TEST(parent_obj, IVShmemState, test_msix),
+ VMSTATE_UINT32_TEST(intrstatus, IVShmemState, test_no_msix),
+ VMSTATE_UINT32_TEST(intrmask, IVShmemState, test_no_msix),
+
+ VMSTATE_END_OF_LIST()
+ },
+ .load_state_old = ivshmem_load_old,
+ .minimum_version_id_old = 0
+};
+
static Property ivshmem_properties[] = {
DEFINE_PROP_CHR("chardev", IVShmemState, server_chr),
DEFINE_PROP_STRING("size", IVShmemState, sizearg),
@@ -866,20 +1145,50 @@ static void ivshmem_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- k->init = pci_ivshmem_init;
- k->exit = pci_ivshmem_uninit;
+ k->realize = pci_ivshmem_realize;
+ k->exit = pci_ivshmem_exit;
+ k->config_write = ivshmem_write_config;
k->vendor_id = PCI_VENDOR_ID_IVSHMEM;
k->device_id = PCI_DEVICE_ID_IVSHMEM;
k->class_id = PCI_CLASS_MEMORY_RAM;
dc->reset = ivshmem_reset;
dc->props = ivshmem_properties;
+ dc->vmsd = &ivshmem_vmsd;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->desc = "Inter-VM shared memory";
+}
+
+static void ivshmem_check_memdev_is_busy(Object *obj, const char *name,
+ Object *val, Error **errp)
+{
+ MemoryRegion *mr;
+
+ mr = host_memory_backend_get_memory(MEMORY_BACKEND(val), errp);
+ if (memory_region_is_mapped(mr)) {
+ char *path = object_get_canonical_path_component(val);
+ error_setg(errp, "can't use already busy memdev: %s", path);
+ g_free(path);
+ } else {
+ qdev_prop_allow_set_link_before_realize(obj, name, val, errp);
+ }
+}
+
+static void ivshmem_init(Object *obj)
+{
+ IVShmemState *s = IVSHMEM(obj);
+
+ object_property_add_link(obj, "x-memdev", TYPE_MEMORY_BACKEND,
+ (Object **)&s->hostmem,
+ ivshmem_check_memdev_is_busy,
+ OBJ_PROP_LINK_UNREF_ON_RELEASE,
+ &error_abort);
}
static const TypeInfo ivshmem_info = {
.name = TYPE_IVSHMEM,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(IVShmemState),
+ .instance_init = ivshmem_init,
.class_init = ivshmem_class_init,
};
diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c
index 5d7043e99..9db4c6415 100644
--- a/hw/misc/macio/cuda.c
+++ b/hw/misc/macio/cuda.c
@@ -57,6 +57,8 @@
#define IER_SET 0x80 /* set bits in IER */
#define IER_CLR 0 /* clear bits in IER */
#define SR_INT 0x04 /* Shift register full/empty */
+#define SR_DATA_INT 0x08
+#define SR_CLOCK_INT 0x10
#define T1_INT 0x40 /* Timer 1 interrupt */
#define T2_INT 0x20 /* Timer 2 interrupt */
@@ -108,6 +110,24 @@
/* CUDA returns time_t's offset from Jan 1, 1904, not 1970 */
#define RTC_OFFSET 2082844800
+/* CUDA registers */
+#define CUDA_REG_B 0x00
+#define CUDA_REG_A 0x01
+#define CUDA_REG_DIRB 0x02
+#define CUDA_REG_DIRA 0x03
+#define CUDA_REG_T1CL 0x04
+#define CUDA_REG_T1CH 0x05
+#define CUDA_REG_T1LL 0x06
+#define CUDA_REG_T1LH 0x07
+#define CUDA_REG_T2CL 0x08
+#define CUDA_REG_T2CH 0x09
+#define CUDA_REG_SR 0x0a
+#define CUDA_REG_ACR 0x0b
+#define CUDA_REG_PCR 0x0c
+#define CUDA_REG_IFR 0x0d
+#define CUDA_REG_IER 0x0e
+#define CUDA_REG_ANH 0x0f
+
static void cuda_update(CUDAState *s);
static void cuda_receive_packet_from_host(CUDAState *s,
const uint8_t *data, int len);
@@ -116,47 +136,48 @@ static void cuda_timer_update(CUDAState *s, CUDATimer *ti,
static void cuda_update_irq(CUDAState *s)
{
- if (s->ifr & s->ier & (SR_INT | T1_INT)) {
+ if (s->ifr & s->ier & (SR_INT | T1_INT | T2_INT)) {
qemu_irq_raise(s->irq);
} else {
qemu_irq_lower(s->irq);
}
}
-static uint64_t get_tb(uint64_t freq)
+static uint64_t get_tb(uint64_t time, uint64_t freq)
{
- return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
- freq, get_ticks_per_sec());
+ return muldiv64(time, freq, get_ticks_per_sec());
}
-static unsigned int get_counter(CUDATimer *s)
+static unsigned int get_counter(CUDATimer *ti)
{
int64_t d;
unsigned int counter;
uint64_t tb_diff;
+ uint64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
/* Reverse of the tb calculation algorithm that Mac OS X uses on bootup. */
- tb_diff = get_tb(s->frequency) - s->load_time;
- d = (tb_diff * 0xBF401675E5DULL) / (s->frequency << 24);
+ tb_diff = get_tb(current_time, ti->frequency) - ti->load_time;
+ d = (tb_diff * 0xBF401675E5DULL) / (ti->frequency << 24);
- if (s->index == 0) {
+ if (ti->index == 0) {
/* the timer goes down from latch to -1 (period of latch + 2) */
- if (d <= (s->counter_value + 1)) {
- counter = (s->counter_value - d) & 0xffff;
+ if (d <= (ti->counter_value + 1)) {
+ counter = (ti->counter_value - d) & 0xffff;
} else {
- counter = (d - (s->counter_value + 1)) % (s->latch + 2);
- counter = (s->latch - counter) & 0xffff;
+ counter = (d - (ti->counter_value + 1)) % (ti->latch + 2);
+ counter = (ti->latch - counter) & 0xffff;
}
} else {
- counter = (s->counter_value - d) & 0xffff;
+ counter = (ti->counter_value - d) & 0xffff;
}
return counter;
}
static void set_counter(CUDAState *s, CUDATimer *ti, unsigned int val)
{
- CUDA_DPRINTF("T%d.counter=%d\n", 1 + (ti->timer == NULL), val);
- ti->load_time = get_tb(s->frequency);
+ CUDA_DPRINTF("T%d.counter=%d\n", 1 + ti->index, val);
+ ti->load_time = get_tb(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
+ s->frequency);
ti->counter_value = val;
cuda_timer_update(s, ti, ti->load_time);
}
@@ -199,7 +220,7 @@ static void cuda_timer_update(CUDAState *s, CUDATimer *ti,
{
if (!ti->timer)
return;
- if ((s->acr & T1MODE) != T1MODE_CONT) {
+ if (ti->index == 0 && (s->acr & T1MODE) != T1MODE_CONT) {
timer_del(ti->timer);
} else {
ti->next_irq_time = get_next_irq_time(ti, current_time);
@@ -217,6 +238,41 @@ static void cuda_timer1(void *opaque)
cuda_update_irq(s);
}
+static void cuda_timer2(void *opaque)
+{
+ CUDAState *s = opaque;
+ CUDATimer *ti = &s->timers[1];
+
+ cuda_timer_update(s, ti, ti->next_irq_time);
+ s->ifr |= T2_INT;
+ cuda_update_irq(s);
+}
+
+static void cuda_set_sr_int(void *opaque)
+{
+ CUDAState *s = opaque;
+
+ CUDA_DPRINTF("CUDA: %s:%d\n", __func__, __LINE__);
+ s->ifr |= SR_INT;
+ cuda_update_irq(s);
+}
+
+static void cuda_delay_set_sr_int(CUDAState *s)
+{
+ int64_t expire;
+
+ if (s->dirb == 0xff) {
+ /* Not in Mac OS, fire the IRQ directly */
+ cuda_set_sr_int(s);
+ return;
+ }
+
+ CUDA_DPRINTF("CUDA: %s:%d\n", __func__, __LINE__);
+
+ expire = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 300 * SCALE_US;
+ timer_mod(s->sr_delay_timer, expire);
+}
+
static uint32_t cuda_readb(void *opaque, hwaddr addr)
{
CUDAState *s = opaque;
@@ -224,66 +280,68 @@ static uint32_t cuda_readb(void *opaque, hwaddr addr)
addr = (addr >> 9) & 0xf;
switch(addr) {
- case 0:
+ case CUDA_REG_B:
val = s->b;
break;
- case 1:
+ case CUDA_REG_A:
val = s->a;
break;
- case 2:
+ case CUDA_REG_DIRB:
val = s->dirb;
break;
- case 3:
+ case CUDA_REG_DIRA:
val = s->dira;
break;
- case 4:
+ case CUDA_REG_T1CL:
val = get_counter(&s->timers[0]) & 0xff;
s->ifr &= ~T1_INT;
cuda_update_irq(s);
break;
- case 5:
+ case CUDA_REG_T1CH:
val = get_counter(&s->timers[0]) >> 8;
cuda_update_irq(s);
break;
- case 6:
+ case CUDA_REG_T1LL:
val = s->timers[0].latch & 0xff;
break;
- case 7:
+ case CUDA_REG_T1LH:
/* XXX: check this */
val = (s->timers[0].latch >> 8) & 0xff;
break;
- case 8:
+ case CUDA_REG_T2CL:
val = get_counter(&s->timers[1]) & 0xff;
s->ifr &= ~T2_INT;
+ cuda_update_irq(s);
break;
- case 9:
+ case CUDA_REG_T2CH:
val = get_counter(&s->timers[1]) >> 8;
break;
- case 10:
+ case CUDA_REG_SR:
val = s->sr;
- s->ifr &= ~SR_INT;
+ s->ifr &= ~(SR_INT | SR_CLOCK_INT | SR_DATA_INT);
cuda_update_irq(s);
break;
- case 11:
+ case CUDA_REG_ACR:
val = s->acr;
break;
- case 12:
+ case CUDA_REG_PCR:
val = s->pcr;
break;
- case 13:
+ case CUDA_REG_IFR:
val = s->ifr;
- if (s->ifr & s->ier)
+ if (s->ifr & s->ier) {
val |= 0x80;
+ }
break;
- case 14:
+ case CUDA_REG_IER:
val = s->ier | 0x80;
break;
default:
- case 15:
+ case CUDA_REG_ANH:
val = s->anh;
break;
}
- if (addr != 13 || val != 0) {
+ if (addr != CUDA_REG_IFR || val != 0) {
CUDA_DPRINTF("read: reg=0x%x val=%02x\n", (int)addr, val);
}
@@ -298,61 +356,65 @@ static void cuda_writeb(void *opaque, hwaddr addr, uint32_t val)
CUDA_DPRINTF("write: reg=0x%x val=%02x\n", (int)addr, val);
switch(addr) {
- case 0:
+ case CUDA_REG_B:
s->b = val;
cuda_update(s);
break;
- case 1:
+ case CUDA_REG_A:
s->a = val;
break;
- case 2:
+ case CUDA_REG_DIRB:
s->dirb = val;
break;
- case 3:
+ case CUDA_REG_DIRA:
s->dira = val;
break;
- case 4:
+ case CUDA_REG_T1CL:
s->timers[0].latch = (s->timers[0].latch & 0xff00) | val;
cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
break;
- case 5:
+ case CUDA_REG_T1CH:
s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8);
s->ifr &= ~T1_INT;
set_counter(s, &s->timers[0], s->timers[0].latch);
break;
- case 6:
+ case CUDA_REG_T1LL:
s->timers[0].latch = (s->timers[0].latch & 0xff00) | val;
cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
break;
- case 7:
+ case CUDA_REG_T1LH:
s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8);
s->ifr &= ~T1_INT;
cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
break;
- case 8:
- s->timers[1].latch = val;
- set_counter(s, &s->timers[1], val);
+ case CUDA_REG_T2CL:
+ s->timers[1].latch = (s->timers[1].latch & 0xff00) | val;
break;
- case 9:
- set_counter(s, &s->timers[1], (val << 8) | s->timers[1].latch);
+ case CUDA_REG_T2CH:
+ /* To ensure T2 generates an interrupt on zero crossing with the
+ common timer code, write the value directly from the latch to
+ the counter */
+ s->timers[1].latch = (s->timers[1].latch & 0xff) | (val << 8);
+ s->ifr &= ~T2_INT;
+ set_counter(s, &s->timers[1], s->timers[1].latch);
break;
- case 10:
+ case CUDA_REG_SR:
s->sr = val;
break;
- case 11:
+ case CUDA_REG_ACR:
s->acr = val;
cuda_timer_update(s, &s->timers[0], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
cuda_update(s);
break;
- case 12:
+ case CUDA_REG_PCR:
s->pcr = val;
break;
- case 13:
+ case CUDA_REG_IFR:
/* reset bits */
s->ifr &= ~val;
cuda_update_irq(s);
break;
- case 14:
+ case CUDA_REG_IER:
if (val & IER_SET) {
/* set bits */
s->ier |= val & 0x7f;
@@ -363,7 +425,7 @@ static void cuda_writeb(void *opaque, hwaddr addr, uint32_t val)
cuda_update_irq(s);
break;
default:
- case 15:
+ case CUDA_REG_ANH:
s->anh = val;
break;
}
@@ -384,8 +446,7 @@ static void cuda_update(CUDAState *s)
if (s->data_out_index < sizeof(s->data_out)) {
CUDA_DPRINTF("send: %02x\n", s->sr);
s->data_out[s->data_out_index++] = s->sr;
- s->ifr |= SR_INT;
- cuda_update_irq(s);
+ cuda_delay_set_sr_int(s);
}
}
} else {
@@ -398,8 +459,7 @@ static void cuda_update(CUDAState *s)
if (s->data_in_index >= s->data_in_size) {
s->b = (s->b | TREQ);
}
- s->ifr |= SR_INT;
- cuda_update_irq(s);
+ cuda_delay_set_sr_int(s);
}
}
}
@@ -411,15 +471,13 @@ static void cuda_update(CUDAState *s)
s->b = (s->b | TREQ);
else
s->b = (s->b & ~TREQ);
- s->ifr |= SR_INT;
- cuda_update_irq(s);
+ cuda_delay_set_sr_int(s);
} else {
if (!(s->last_b & TIP)) {
/* handle end of host to cuda transfer */
packet_received = (s->data_out_index > 0);
/* always an IRQ at the end of transfer */
- s->ifr |= SR_INT;
- cuda_update_irq(s);
+ cuda_delay_set_sr_int(s);
}
/* signal if there is data to read */
if (s->data_in_index < s->data_in_size) {
@@ -456,8 +514,7 @@ static void cuda_send_packet_to_host(CUDAState *s,
s->data_in_size = len;
s->data_in_index = 0;
cuda_update(s);
- s->ifr |= SR_INT;
- cuda_update_irq(s);
+ cuda_delay_set_sr_int(s);
}
static void cuda_adb_poll(void *opaque)
@@ -480,7 +537,7 @@ static void cuda_adb_poll(void *opaque)
static void cuda_receive_packet(CUDAState *s,
const uint8_t *data, int len)
{
- uint8_t obuf[16];
+ uint8_t obuf[16] = { CUDA_PACKET, 0, data[0] };
int autopoll;
uint32_t ti;
@@ -497,23 +554,18 @@ static void cuda_receive_packet(CUDAState *s,
timer_del(s->adb_poll_timer);
}
}
- obuf[0] = CUDA_PACKET;
- obuf[1] = data[1];
- cuda_send_packet_to_host(s, obuf, 2);
+ cuda_send_packet_to_host(s, obuf, 3);
+ break;
+ case CUDA_GET_6805_ADDR:
+ cuda_send_packet_to_host(s, obuf, 3);
break;
case CUDA_SET_TIME:
ti = (((uint32_t)data[1]) << 24) + (((uint32_t)data[2]) << 16) + (((uint32_t)data[3]) << 8) + data[4];
s->tick_offset = ti - (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / get_ticks_per_sec());
- obuf[0] = CUDA_PACKET;
- obuf[1] = 0;
- obuf[2] = 0;
cuda_send_packet_to_host(s, obuf, 3);
break;
case CUDA_GET_TIME:
ti = s->tick_offset + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / get_ticks_per_sec());
- obuf[0] = CUDA_PACKET;
- obuf[1] = 0;
- obuf[2] = 0;
obuf[3] = ti >> 24;
obuf[4] = ti >> 16;
obuf[5] = ti >> 8;
@@ -524,22 +576,34 @@ static void cuda_receive_packet(CUDAState *s,
case CUDA_SET_DEVICE_LIST:
case CUDA_SET_AUTO_RATE:
case CUDA_SET_POWER_MESSAGES:
- obuf[0] = CUDA_PACKET;
- obuf[1] = 0;
- cuda_send_packet_to_host(s, obuf, 2);
+ cuda_send_packet_to_host(s, obuf, 3);
break;
case CUDA_POWERDOWN:
- obuf[0] = CUDA_PACKET;
- obuf[1] = 0;
- cuda_send_packet_to_host(s, obuf, 2);
+ cuda_send_packet_to_host(s, obuf, 3);
qemu_system_shutdown_request();
break;
case CUDA_RESET_SYSTEM:
- obuf[0] = CUDA_PACKET;
- obuf[1] = 0;
- cuda_send_packet_to_host(s, obuf, 2);
+ cuda_send_packet_to_host(s, obuf, 3);
qemu_system_reset_request();
break;
+ case CUDA_COMBINED_FORMAT_IIC:
+ obuf[0] = ERROR_PACKET;
+ obuf[1] = 0x5;
+ obuf[2] = CUDA_PACKET;
+ obuf[3] = data[0];
+ cuda_send_packet_to_host(s, obuf, 4);
+ break;
+ case CUDA_GET_SET_IIC:
+ if (len == 4) {
+ cuda_send_packet_to_host(s, obuf, 3);
+ } else {
+ obuf[0] = ERROR_PACKET;
+ obuf[1] = 0x2;
+ obuf[2] = CUDA_PACKET;
+ obuf[3] = data[0];
+ cuda_send_packet_to_host(s, obuf, 4);
+ }
+ break;
default:
break;
}
@@ -560,19 +624,21 @@ static void cuda_receive_packet_from_host(CUDAState *s,
switch(data[0]) {
case ADB_PACKET:
{
- uint8_t obuf[ADB_MAX_OUT_LEN + 2];
+ uint8_t obuf[ADB_MAX_OUT_LEN + 3];
int olen;
olen = adb_request(&s->adb_bus, obuf + 2, data + 1, len - 1);
if (olen > 0) {
obuf[0] = ADB_PACKET;
obuf[1] = 0x00;
+ cuda_send_packet_to_host(s, obuf, olen + 2);
} else {
/* error */
obuf[0] = ADB_PACKET;
obuf[1] = -olen;
+ obuf[2] = data[1];
olen = 0;
+ cuda_send_packet_to_host(s, obuf, olen + 3);
}
- cuda_send_packet_to_host(s, obuf, olen + 2);
}
break;
case CUDA_PACKET:
@@ -671,7 +737,7 @@ static void cuda_reset(DeviceState *dev)
s->b = 0;
s->a = 0;
- s->dirb = 0;
+ s->dirb = 0xff;
s->dira = 0;
s->sr = 0;
s->acr = 0;
@@ -688,8 +754,9 @@ static void cuda_reset(DeviceState *dev)
s->timers[0].latch = 0xffff;
set_counter(s, &s->timers[0], 0xffff);
- s->timers[1].latch = 0;
- set_counter(s, &s->timers[1], 0xffff);
+ s->timers[1].latch = 0xffff;
+
+ s->sr_delay_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_set_sr_int, s);
}
static void cuda_realizefn(DeviceState *dev, Error **errp)
@@ -699,7 +766,8 @@ static void cuda_realizefn(DeviceState *dev, Error **errp)
s->timers[0].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_timer1, s);
s->timers[0].frequency = s->frequency;
- s->timers[1].frequency = s->frequency;
+ s->timers[1].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_timer2, s);
+ s->timers[1].frequency = (SCALE_US * 6000) / 4700;
qemu_get_timedate(&tm, 0);
s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET;
@@ -738,6 +806,7 @@ static void cuda_class_init(ObjectClass *oc, void *data)
dc->reset = cuda_reset;
dc->vmsd = &vmstate_cuda;
dc->props = cuda_properties;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
static const TypeInfo cuda_type_info = {
diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c
index 779683cc0..5ee8f022a 100644
--- a/hw/misc/macio/mac_dbdma.c
+++ b/hw/misc/macio/mac_dbdma.c
@@ -557,7 +557,6 @@ void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq,
DBDMA_DPRINTF("DBDMA_register_channel 0x%x\n", nchan);
ch->irq = irq;
- ch->channel = nchan;
ch->rw = rw;
ch->flush = flush;
ch->io.opaque = opaque;
@@ -753,6 +752,7 @@ void* DBDMA_init (MemoryRegion **dbdma_mem)
for (i = 0; i < DBDMA_CHANNELS; i++) {
DBDMA_io *io = &s->channels[i].io;
qemu_iovec_init(&io->iov, 1);
+ s->channels[i].channel = i;
}
memory_region_init_io(&s->mem, NULL, &dbdma_ops, s, "dbdma", 0x1000);
diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c
index c661f86c2..adb990e56 100644
--- a/hw/misc/macio/macio.c
+++ b/hw/misc/macio/macio.c
@@ -393,6 +393,7 @@ static void macio_class_init(ObjectClass *klass, void *data)
k->vendor_id = PCI_VENDOR_ID_APPLE;
k->class_id = PCI_CLASS_OTHERS << 8;
dc->props = macio_properties;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
static const TypeInfo macio_oldworld_type_info = {
diff --git a/hw/misc/omap_clk.c b/hw/misc/omap_clk.c
index 80a3c50e1..73d4f8bec 100644
--- a/hw/misc/omap_clk.c
+++ b/hw/misc/omap_clk.c
@@ -1239,7 +1239,7 @@ void omap_clk_init(struct omap_mpu_state_s *mpu)
for (i = onchip_clks, count = 0; *i; i ++)
if ((*i)->flags & flag)
count ++;
- mpu->clks = (struct clk *) g_malloc0(sizeof(struct clk) * (count + 1));
+ mpu->clks = g_new0(struct clk, count + 1);
for (i = onchip_clks, j = mpu->clks; *i; i ++)
if ((*i)->flags & flag) {
memcpy(j, *i, sizeof(struct clk));
diff --git a/hw/misc/omap_gpmc.c b/hw/misc/omap_gpmc.c
index 74fc91c8e..8960f1bf1 100644
--- a/hw/misc/omap_gpmc.c
+++ b/hw/misc/omap_gpmc.c
@@ -826,8 +826,7 @@ struct omap_gpmc_s *omap_gpmc_init(struct omap_mpu_state_s *mpu,
qemu_irq irq, qemu_irq drq)
{
int cs;
- struct omap_gpmc_s *s = (struct omap_gpmc_s *)
- g_malloc0(sizeof(struct omap_gpmc_s));
+ struct omap_gpmc_s *s = g_new0(struct omap_gpmc_s, 1);
memory_region_init_io(&s->iomem, NULL, &omap_gpmc_ops, s, "omap-gpmc", 0x1000);
memory_region_add_subregion(get_system_memory(), base, &s->iomem);
diff --git a/hw/misc/omap_sdrc.c b/hw/misc/omap_sdrc.c
index 3de0c0e9d..bca25307b 100644
--- a/hw/misc/omap_sdrc.c
+++ b/hw/misc/omap_sdrc.c
@@ -157,8 +157,7 @@ static const MemoryRegionOps omap_sdrc_ops = {
struct omap_sdrc_s *omap_sdrc_init(MemoryRegion *sysmem,
hwaddr base)
{
- struct omap_sdrc_s *s = (struct omap_sdrc_s *)
- g_malloc0(sizeof(struct omap_sdrc_s));
+ struct omap_sdrc_s *s = g_new0(struct omap_sdrc_s, 1);
omap_sdrc_reset(s);
diff --git a/hw/misc/pvpanic.c b/hw/misc/pvpanic.c
index 994f8af8e..370948872 100644
--- a/hw/misc/pvpanic.c
+++ b/hw/misc/pvpanic.c
@@ -41,8 +41,7 @@ static void handle_event(int event)
}
if (event & PVPANIC_PANICKED) {
- qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, &error_abort);
- vm_stop(RUN_STATE_GUEST_PANICKED);
+ qemu_system_guest_panicked();
return;
}
}
diff --git a/hw/misc/zynq-xadc.c b/hw/misc/zynq-xadc.c
new file mode 100644
index 000000000..1a3259545
--- /dev/null
+++ b/hw/misc/zynq-xadc.c
@@ -0,0 +1,302 @@
+/*
+ * ADC registers for Xilinx Zynq Platform
+ *
+ * Copyright (c) 2015 Guenter Roeck
+ * Based on hw/misc/zynq_slcr.c, written by Michal Simek
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/hw.h"
+#include "hw/misc/zynq-xadc.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+
+enum {
+ CFG = 0x000 / 4,
+ INT_STS,
+ INT_MASK,
+ MSTS,
+ CMDFIFO,
+ RDFIFO,
+ MCTL,
+};
+
+#define CFG_ENABLE BIT(31)
+#define CFG_CFIFOTH_SHIFT 20
+#define CFG_CFIFOTH_LENGTH 4
+#define CFG_DFIFOTH_SHIFT 16
+#define CFG_DFIFOTH_LENGTH 4
+#define CFG_WEDGE BIT(13)
+#define CFG_REDGE BIT(12)
+#define CFG_TCKRATE_SHIFT 8
+#define CFG_TCKRATE_LENGTH 2
+
+#define CFG_TCKRATE_DIV(x) (0x1 << (x - 1))
+
+#define CFG_IGAP_SHIFT 0
+#define CFG_IGAP_LENGTH 5
+
+#define INT_CFIFO_LTH BIT(9)
+#define INT_DFIFO_GTH BIT(8)
+#define INT_OT BIT(7)
+#define INT_ALM_SHIFT 0
+#define INT_ALM_LENGTH 7
+#define INT_ALM_MASK (((1 << INT_ALM_LENGTH) - 1) << INT_ALM_SHIFT)
+
+#define INT_ALL (INT_CFIFO_LTH | INT_DFIFO_GTH | INT_OT | INT_ALM_MASK)
+
+#define MSTS_CFIFO_LVL_SHIFT 16
+#define MSTS_CFIFO_LVL_LENGTH 4
+#define MSTS_DFIFO_LVL_SHIFT 12
+#define MSTS_DFIFO_LVL_LENGTH 4
+#define MSTS_CFIFOF BIT(11)
+#define MSTS_CFIFOE BIT(10)
+#define MSTS_DFIFOF BIT(9)
+#define MSTS_DFIFOE BIT(8)
+#define MSTS_OT BIT(7)
+#define MSTS_ALM_SHIFT 0
+#define MSTS_ALM_LENGTH 7
+
+#define MCTL_RESET BIT(4)
+
+#define CMD_NOP 0x00
+#define CMD_READ 0x01
+#define CMD_WRITE 0x02
+
+static void zynq_xadc_update_ints(ZynqXADCState *s)
+{
+
+ /* We are fast, commands are actioned instantly so the CFIFO is always
+ * empty (and below threshold).
+ */
+ s->regs[INT_STS] |= INT_CFIFO_LTH;
+
+ if (s->xadc_dfifo_entries >
+ extract32(s->regs[CFG], CFG_DFIFOTH_SHIFT, CFG_DFIFOTH_LENGTH)) {
+ s->regs[INT_STS] |= INT_DFIFO_GTH;
+ }
+
+ qemu_set_irq(s->qemu_irq, !!(s->regs[INT_STS] & ~s->regs[INT_MASK]));
+}
+
+static void zynq_xadc_reset(DeviceState *d)
+{
+ ZynqXADCState *s = ZYNQ_XADC(d);
+
+ s->regs[CFG] = 0x14 << CFG_IGAP_SHIFT |
+ CFG_TCKRATE_DIV(4) << CFG_TCKRATE_SHIFT | CFG_REDGE;
+ s->regs[INT_STS] = INT_CFIFO_LTH;
+ s->regs[INT_MASK] = 0xffffffff;
+ s->regs[CMDFIFO] = 0;
+ s->regs[RDFIFO] = 0;
+ s->regs[MCTL] = MCTL_RESET;
+
+ memset(s->xadc_regs, 0, sizeof(s->xadc_regs));
+ memset(s->xadc_dfifo, 0, sizeof(s->xadc_dfifo));
+ s->xadc_dfifo_entries = 0;
+
+ zynq_xadc_update_ints(s);
+}
+
+static uint16_t xadc_pop_dfifo(ZynqXADCState *s)
+{
+ uint16_t rv = s->xadc_dfifo[0];
+ int i;
+
+ if (s->xadc_dfifo_entries > 0) {
+ s->xadc_dfifo_entries--;
+ }
+ for (i = 0; i < s->xadc_dfifo_entries; i++) {
+ s->xadc_dfifo[i] = s->xadc_dfifo[i + 1];
+ }
+ s->xadc_dfifo[s->xadc_dfifo_entries] = 0;
+ zynq_xadc_update_ints(s);
+ return rv;
+}
+
+static void xadc_push_dfifo(ZynqXADCState *s, uint16_t regval)
+{
+ if (s->xadc_dfifo_entries < ZYNQ_XADC_FIFO_DEPTH) {
+ s->xadc_dfifo[s->xadc_dfifo_entries++] = s->xadc_read_reg_previous;
+ }
+ s->xadc_read_reg_previous = regval;
+ zynq_xadc_update_ints(s);
+}
+
+static bool zynq_xadc_check_offset(hwaddr offset, bool rnw)
+{
+ switch (offset) {
+ case CFG:
+ case INT_MASK:
+ case INT_STS:
+ case MCTL:
+ return true;
+ case RDFIFO:
+ case MSTS:
+ return rnw; /* read only */
+ case CMDFIFO:
+ return !rnw; /* write only */
+ default:
+ return false;
+ }
+}
+
+static uint64_t zynq_xadc_read(void *opaque, hwaddr offset, unsigned size)
+{
+ ZynqXADCState *s = opaque;
+ int reg = offset / 4;
+ uint32_t rv = 0;
+
+ if (!zynq_xadc_check_offset(reg, true)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "zynq_xadc: Invalid read access to "
+ "addr %" HWADDR_PRIx "\n", offset);
+ return 0;
+ }
+
+ switch (reg) {
+ case CFG:
+ case INT_MASK:
+ case INT_STS:
+ case MCTL:
+ rv = s->regs[reg];
+ break;
+ case MSTS:
+ rv = MSTS_CFIFOE;
+ rv |= s->xadc_dfifo_entries << MSTS_DFIFO_LVL_SHIFT;
+ if (!s->xadc_dfifo_entries) {
+ rv |= MSTS_DFIFOE;
+ } else if (s->xadc_dfifo_entries == ZYNQ_XADC_FIFO_DEPTH) {
+ rv |= MSTS_DFIFOF;
+ }
+ break;
+ case RDFIFO:
+ rv = xadc_pop_dfifo(s);
+ break;
+ }
+ return rv;
+}
+
+static void zynq_xadc_write(void *opaque, hwaddr offset, uint64_t val,
+ unsigned size)
+{
+ ZynqXADCState *s = (ZynqXADCState *)opaque;
+ int reg = offset / 4;
+ int xadc_reg;
+ int xadc_cmd;
+ int xadc_data;
+
+ if (!zynq_xadc_check_offset(reg, false)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "zynq_xadc: Invalid write access "
+ "to addr %" HWADDR_PRIx "\n", offset);
+ return;
+ }
+
+ switch (reg) {
+ case CFG:
+ s->regs[CFG] = val;
+ break;
+ case INT_STS:
+ s->regs[INT_STS] &= ~val;
+ break;
+ case INT_MASK:
+ s->regs[INT_MASK] = val & INT_ALL;
+ break;
+ case CMDFIFO:
+ xadc_cmd = extract32(val, 26, 4);
+ xadc_reg = extract32(val, 16, 10);
+ xadc_data = extract32(val, 0, 16);
+
+ if (s->regs[MCTL] & MCTL_RESET) {
+ qemu_log_mask(LOG_GUEST_ERROR, "zynq_xadc: Sending command "
+ "while comm channel held in reset: %" PRIx32 "\n",
+ (uint32_t) val);
+ break;
+ }
+
+ if (xadc_reg > ZYNQ_XADC_NUM_ADC_REGS && xadc_cmd != CMD_NOP) {
+ qemu_log_mask(LOG_GUEST_ERROR, "read/write op to invalid xadc "
+ "reg 0x%x\n", xadc_reg);
+ break;
+ }
+
+ switch (xadc_cmd) {
+ case CMD_READ:
+ xadc_push_dfifo(s, s->xadc_regs[xadc_reg]);
+ break;
+ case CMD_WRITE:
+ s->xadc_regs[xadc_reg] = xadc_data;
+ /* fallthrough */
+ case CMD_NOP:
+ xadc_push_dfifo(s, 0);
+ break;
+ }
+ break;
+ case MCTL:
+ s->regs[MCTL] = val & 0x00fffeff;
+ break;
+ }
+ zynq_xadc_update_ints(s);
+}
+
+static const MemoryRegionOps xadc_ops = {
+ .read = zynq_xadc_read,
+ .write = zynq_xadc_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void zynq_xadc_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ ZynqXADCState *s = ZYNQ_XADC(obj);
+
+ memory_region_init_io(&s->iomem, obj, &xadc_ops, s, "zynq-xadc",
+ ZYNQ_XADC_MMIO_SIZE);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->qemu_irq);
+}
+
+static const VMStateDescription vmstate_zynq_xadc = {
+ .name = "zynq-xadc",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, ZynqXADCState, ZYNQ_XADC_NUM_IO_REGS),
+ VMSTATE_UINT16_ARRAY(xadc_regs, ZynqXADCState,
+ ZYNQ_XADC_NUM_ADC_REGS),
+ VMSTATE_UINT16_ARRAY(xadc_dfifo, ZynqXADCState,
+ ZYNQ_XADC_FIFO_DEPTH),
+ VMSTATE_UINT16(xadc_read_reg_previous, ZynqXADCState),
+ VMSTATE_UINT16(xadc_dfifo_entries, ZynqXADCState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void zynq_xadc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &vmstate_zynq_xadc;
+ dc->reset = zynq_xadc_reset;
+}
+
+static const TypeInfo zynq_xadc_info = {
+ .class_init = zynq_xadc_class_init,
+ .name = TYPE_ZYNQ_XADC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(ZynqXADCState),
+ .instance_init = zynq_xadc_init,
+};
+
+static void zynq_xadc_register_types(void)
+{
+ type_register_static(&zynq_xadc_info);
+}
+
+type_init(zynq_xadc_register_types)
diff --git a/hw/moxie/moxiesim.c b/hw/moxie/moxiesim.c
index 80bcc5b4f..ada3d5882 100644
--- a/hw/moxie/moxiesim.c
+++ b/hw/moxie/moxiesim.c
@@ -34,6 +34,7 @@
#include "hw/loader.h"
#include "hw/char/serial.h"
#include "exec/address-spaces.h"
+#include "elf.h"
#define PHYS_MEM_BASE 0x80000000
@@ -52,8 +53,7 @@ static void load_kernel(MoxieCPU *cpu, LoaderParams *loader_params)
ram_addr_t initrd_offset;
kernel_size = load_elf(loader_params->kernel_filename, NULL, NULL,
- &entry, &kernel_low, &kernel_high, 1,
- ELF_MACHINE, 0);
+ &entry, &kernel_low, &kernel_high, 1, EM_MOXIE, 0);
if (kernel_size <= 0) {
fprintf(stderr, "qemu: could not load kernel '%s'\n",
@@ -123,11 +123,11 @@ static void moxiesim_init(MachineState *machine)
qemu_register_reset(main_cpu_reset, cpu);
/* Allocate RAM. */
- memory_region_init_ram(ram, NULL, "moxiesim.ram", ram_size, &error_abort);
+ memory_region_init_ram(ram, NULL, "moxiesim.ram", ram_size, &error_fatal);
vmstate_register_ram_global(ram);
memory_region_add_subregion(address_space_mem, ram_base, ram);
- memory_region_init_ram(rom, NULL, "moxie.rom", 128*0x1000, &error_abort);
+ memory_region_init_ram(rom, NULL, "moxie.rom", 128*0x1000, &error_fatal);
vmstate_register_ram_global(rom);
memory_region_add_subregion(get_system_memory(), 0x1000, rom);
@@ -146,16 +146,11 @@ static void moxiesim_init(MachineState *machine)
}
}
-static QEMUMachine moxiesim_machine = {
- .name = "moxiesim",
- .desc = "Moxie simulator platform",
- .init = moxiesim_init,
- .is_default = 1,
-};
-
-static void moxie_machine_init(void)
+static void moxiesim_machine_init(MachineClass *mc)
{
- qemu_register_machine(&moxiesim_machine);
+ mc->desc = "Moxie simulator platform";
+ mc->init = moxiesim_init;
+ mc->is_default = 1;
}
-machine_init(moxie_machine_init)
+DEFINE_MACHINE("moxiesim", moxiesim_machine_init)
diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
index 98801739e..64d044923 100644
--- a/hw/net/Makefile.objs
+++ b/hw/net/Makefile.objs
@@ -19,6 +19,7 @@ common-obj-$(CONFIG_XGMAC) += xgmac.o
common-obj-$(CONFIG_MIPSNET) += mipsnet.o
common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o
common-obj-$(CONFIG_ALLWINNER_EMAC) += allwinner_emac.o
+common-obj-$(CONFIG_IMX_FEC) += imx_fec.o
common-obj-$(CONFIG_CADENCE) += cadence_gem.o
common-obj-$(CONFIG_STELLARIS_ENET) += stellaris_enet.o
diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c
index 494a346cf..3639fc17f 100644
--- a/hw/net/cadence_gem.c
+++ b/hw/net/cadence_gem.c
@@ -951,7 +951,7 @@ static void gem_phy_reset(CadenceGEMState *s)
s->phy_regs[PHY_REG_1000BTSTAT] = 0x7C00;
s->phy_regs[PHY_REG_EXTSTAT] = 0x3000;
s->phy_regs[PHY_REG_PHYSPCFC_CTL] = 0x0078;
- s->phy_regs[PHY_REG_PHYSPCFC_ST] = 0xBC00;
+ s->phy_regs[PHY_REG_PHYSPCFC_ST] = 0x7C00;
s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL] = 0x0C60;
s->phy_regs[PHY_REG_LED] = 0x4100;
s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL2] = 0x000A;
@@ -964,6 +964,7 @@ static void gem_reset(DeviceState *d)
{
int i;
CadenceGEMState *s = CADENCE_GEM(d);
+ const uint8_t *a;
DB_PRINT("\n");
@@ -982,6 +983,11 @@ static void gem_reset(DeviceState *d)
s->regs[GEM_DESCONF5] = 0x002f2145;
s->regs[GEM_DESCONF6] = 0x00000200;
+ /* Set MAC address */
+ a = &s->conf.macaddr.a[0];
+ s->regs[GEM_SPADDR1LO] = a[0] | (a[1] << 8) | (a[2] << 16) | (a[3] << 24);
+ s->regs[GEM_SPADDR1HI] = a[4] | (a[5] << 8);
+
for (i = 0; i < 4; i++) {
s->sar_active[i] = false;
}
diff --git a/hw/net/e1000.c b/hw/net/e1000.c
index 09c9e9d53..34d082354 100644
--- a/hw/net/e1000.c
+++ b/hw/net/e1000.c
@@ -37,24 +37,26 @@
#include "e1000_regs.h"
+static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
#define E1000_DEBUG
#ifdef E1000_DEBUG
enum {
- DEBUG_GENERAL, DEBUG_IO, DEBUG_MMIO, DEBUG_INTERRUPT,
- DEBUG_RX, DEBUG_TX, DEBUG_MDIC, DEBUG_EEPROM,
- DEBUG_UNKNOWN, DEBUG_TXSUM, DEBUG_TXERR, DEBUG_RXERR,
+ DEBUG_GENERAL, DEBUG_IO, DEBUG_MMIO, DEBUG_INTERRUPT,
+ DEBUG_RX, DEBUG_TX, DEBUG_MDIC, DEBUG_EEPROM,
+ DEBUG_UNKNOWN, DEBUG_TXSUM, DEBUG_TXERR, DEBUG_RXERR,
DEBUG_RXFILTER, DEBUG_PHY, DEBUG_NOTYET,
};
-#define DBGBIT(x) (1<<DEBUG_##x)
+#define DBGBIT(x) (1<<DEBUG_##x)
static int debugflags = DBGBIT(TXERR) | DBGBIT(GENERAL);
-#define DBGOUT(what, fmt, ...) do { \
+#define DBGOUT(what, fmt, ...) do { \
if (debugflags & DBGBIT(what)) \
fprintf(stderr, "e1000: " fmt, ## __VA_ARGS__); \
} while (0)
#else
-#define DBGOUT(what, fmt, ...) do {} while (0)
+#define DBGOUT(what, fmt, ...) do {} while (0)
#endif
#define IOPORT_SIZE 0x40
@@ -118,7 +120,7 @@ typedef struct E1000State_st {
} tx;
struct {
- uint32_t val_in; // shifted in from guest driver
+ uint32_t val_in; /* shifted in from guest driver */
uint16_t bitnum_in;
uint16_t bitnum_out;
uint16_t reading;
@@ -135,11 +137,15 @@ typedef struct E1000State_st {
/* Compatibility flags for migration to/from qemu 1.3.0 and older */
#define E1000_FLAG_AUTONEG_BIT 0
#define E1000_FLAG_MIT_BIT 1
+#define E1000_FLAG_MAC_BIT 2
#define E1000_FLAG_AUTONEG (1 << E1000_FLAG_AUTONEG_BIT)
#define E1000_FLAG_MIT (1 << E1000_FLAG_MIT_BIT)
+#define E1000_FLAG_MAC (1 << E1000_FLAG_MAC_BIT)
uint32_t compat_flags;
} E1000State;
+#define chkflag(x) (s->compat_flags & E1000_FLAG_##x)
+
typedef struct E1000BaseClass {
PCIDeviceClass parent_class;
uint16_t phy_id2;
@@ -155,20 +161,36 @@ typedef struct E1000BaseClass {
#define E1000_DEVICE_GET_CLASS(obj) \
OBJECT_GET_CLASS(E1000BaseClass, (obj), TYPE_E1000_BASE)
-#define defreg(x) x = (E1000_##x>>2)
+#define defreg(x) x = (E1000_##x>>2)
enum {
- defreg(CTRL), defreg(EECD), defreg(EERD), defreg(GPRC),
- defreg(GPTC), defreg(ICR), defreg(ICS), defreg(IMC),
- defreg(IMS), defreg(LEDCTL), defreg(MANC), defreg(MDIC),
- defreg(MPC), defreg(PBA), defreg(RCTL), defreg(RDBAH),
- defreg(RDBAL), defreg(RDH), defreg(RDLEN), defreg(RDT),
- defreg(STATUS), defreg(SWSM), defreg(TCTL), defreg(TDBAH),
- defreg(TDBAL), defreg(TDH), defreg(TDLEN), defreg(TDT),
- defreg(TORH), defreg(TORL), defreg(TOTH), defreg(TOTL),
- defreg(TPR), defreg(TPT), defreg(TXDCTL), defreg(WUFC),
- defreg(RA), defreg(MTA), defreg(CRCERRS),defreg(VFTA),
- defreg(VET), defreg(RDTR), defreg(RADV), defreg(TADV),
- defreg(ITR),
+ defreg(CTRL), defreg(EECD), defreg(EERD), defreg(GPRC),
+ defreg(GPTC), defreg(ICR), defreg(ICS), defreg(IMC),
+ defreg(IMS), defreg(LEDCTL), defreg(MANC), defreg(MDIC),
+ defreg(MPC), defreg(PBA), defreg(RCTL), defreg(RDBAH),
+ defreg(RDBAL), defreg(RDH), defreg(RDLEN), defreg(RDT),
+ defreg(STATUS), defreg(SWSM), defreg(TCTL), defreg(TDBAH),
+ defreg(TDBAL), defreg(TDH), defreg(TDLEN), defreg(TDT),
+ defreg(TORH), defreg(TORL), defreg(TOTH), defreg(TOTL),
+ defreg(TPR), defreg(TPT), defreg(TXDCTL), defreg(WUFC),
+ defreg(RA), defreg(MTA), defreg(CRCERRS), defreg(VFTA),
+ defreg(VET), defreg(RDTR), defreg(RADV), defreg(TADV),
+ defreg(ITR), defreg(FCRUC), defreg(TDFH), defreg(TDFT),
+ defreg(TDFHS), defreg(TDFTS), defreg(TDFPC), defreg(RDFH),
+ defreg(RDFT), defreg(RDFHS), defreg(RDFTS), defreg(RDFPC),
+ defreg(IPAV), defreg(WUC), defreg(WUS), defreg(AIT),
+ defreg(IP6AT), defreg(IP4AT), defreg(FFLT), defreg(FFMT),
+ defreg(FFVT), defreg(WUPM), defreg(PBM), defreg(SCC),
+ defreg(ECOL), defreg(MCC), defreg(LATECOL), defreg(COLC),
+ defreg(DC), defreg(TNCRS), defreg(SEC), defreg(CEXTERR),
+ defreg(RLEC), defreg(XONRXC), defreg(XONTXC), defreg(XOFFRXC),
+ defreg(XOFFTXC), defreg(RFC), defreg(RJC), defreg(RNBC),
+ defreg(TSCTFC), defreg(MGTPRC), defreg(MGTPDC), defreg(MGTPTC),
+ defreg(RUC), defreg(ROC), defreg(GORCL), defreg(GORCH),
+ defreg(GOTCL), defreg(GOTCH), defreg(BPRC), defreg(MPRC),
+ defreg(TSCTC), defreg(PRC64), defreg(PRC127), defreg(PRC255),
+ defreg(PRC511), defreg(PRC1023), defreg(PRC1522), defreg(PTC64),
+ defreg(PTC127), defreg(PTC255), defreg(PTC511), defreg(PTC1023),
+ defreg(PTC1522), defreg(MPTC), defreg(BPTC)
};
static void
@@ -193,8 +215,7 @@ e1000_link_up(E1000State *s)
static bool
have_autoneg(E1000State *s)
{
- return (s->compat_flags & E1000_FLAG_AUTONEG) &&
- (s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN);
+ return chkflag(AUTONEG) && (s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN);
}
static void
@@ -226,18 +247,18 @@ enum { NPHYWRITEOPS = ARRAY_SIZE(phyreg_writeops) };
enum { PHY_R = 1, PHY_W = 2, PHY_RW = PHY_R | PHY_W };
static const char phy_regcap[0x20] = {
- [PHY_STATUS] = PHY_R, [M88E1000_EXT_PHY_SPEC_CTRL] = PHY_RW,
- [PHY_ID1] = PHY_R, [M88E1000_PHY_SPEC_CTRL] = PHY_RW,
- [PHY_CTRL] = PHY_RW, [PHY_1000T_CTRL] = PHY_RW,
- [PHY_LP_ABILITY] = PHY_R, [PHY_1000T_STATUS] = PHY_R,
- [PHY_AUTONEG_ADV] = PHY_RW, [M88E1000_RX_ERR_CNTR] = PHY_R,
- [PHY_ID2] = PHY_R, [M88E1000_PHY_SPEC_STATUS] = PHY_R,
+ [PHY_STATUS] = PHY_R, [M88E1000_EXT_PHY_SPEC_CTRL] = PHY_RW,
+ [PHY_ID1] = PHY_R, [M88E1000_PHY_SPEC_CTRL] = PHY_RW,
+ [PHY_CTRL] = PHY_RW, [PHY_1000T_CTRL] = PHY_RW,
+ [PHY_LP_ABILITY] = PHY_R, [PHY_1000T_STATUS] = PHY_R,
+ [PHY_AUTONEG_ADV] = PHY_RW, [M88E1000_RX_ERR_CNTR] = PHY_R,
+ [PHY_ID2] = PHY_R, [M88E1000_PHY_SPEC_STATUS] = PHY_R,
[PHY_AUTONEG_EXP] = PHY_R,
};
/* PHY_ID2 documented in 8254x_GBe_SDM.pdf, pp. 250 */
static const uint16_t phy_reg_init[] = {
- [PHY_CTRL] = MII_CR_SPEED_SELECT_MSB |
+ [PHY_CTRL] = MII_CR_SPEED_SELECT_MSB |
MII_CR_FULL_DUPLEX |
MII_CR_AUTO_NEG_EN,
@@ -264,15 +285,15 @@ static const uint16_t phy_reg_init[] = {
};
static const uint32_t mac_reg_init[] = {
- [PBA] = 0x00100030,
- [LEDCTL] = 0x602,
- [CTRL] = E1000_CTRL_SWDPIN2 | E1000_CTRL_SWDPIN0 |
+ [PBA] = 0x00100030,
+ [LEDCTL] = 0x602,
+ [CTRL] = E1000_CTRL_SWDPIN2 | E1000_CTRL_SWDPIN0 |
E1000_CTRL_SPD_1000 | E1000_CTRL_SLU,
- [STATUS] = 0x80000000 | E1000_STATUS_GIO_MASTER_ENABLE |
+ [STATUS] = 0x80000000 | E1000_STATUS_GIO_MASTER_ENABLE |
E1000_STATUS_ASDV | E1000_STATUS_MTXCKOK |
E1000_STATUS_SPEED_1000 | E1000_STATUS_FD |
E1000_STATUS_LU,
- [MANC] = E1000_MANC_EN_MNG2HOST | E1000_MANC_RCV_TCO_EN |
+ [MANC] = E1000_MANC_EN_MNG2HOST | E1000_MANC_RCV_TCO_EN |
E1000_MANC_ARP_EN | E1000_MANC_0298_EN |
E1000_MANC_RMCP_EN,
};
@@ -319,7 +340,7 @@ set_interrupt_cause(E1000State *s, int index, uint32_t val)
if (s->mit_timer_on) {
return;
}
- if (s->compat_flags & E1000_FLAG_MIT) {
+ if (chkflag(MIT)) {
/* Compute the next mitigation delay according to pending
* interrupts and the current values of RADV (provided
* RDTR!=0), TADV and ITR.
@@ -426,6 +447,11 @@ static void e1000_reset(void *opaque)
e1000_link_down(d);
}
+ /* Throttle interrupts to prevent guest (e.g Win 2012) from
+ * reinjecting interrupts endlessly. TODO: fix non ITR case.
+ */
+ d->mac_reg[ITR] = 250;
+
/* Some guests expect pre-initialized RAH/RAL (AddrValid flag + MACaddr) */
d->mac_reg[RA] = 0;
d->mac_reg[RA + 1] = E1000_RAH_AV;
@@ -510,17 +536,19 @@ set_eecd(E1000State *s, int index, uint32_t val)
s->eecd_state.old_eecd = val & (E1000_EECD_SK | E1000_EECD_CS |
E1000_EECD_DI|E1000_EECD_FWE_MASK|E1000_EECD_REQ);
- if (!(E1000_EECD_CS & val)) // CS inactive; nothing to do
- return;
- if (E1000_EECD_CS & (val ^ oldval)) { // CS rise edge; reset state
- s->eecd_state.val_in = 0;
- s->eecd_state.bitnum_in = 0;
- s->eecd_state.bitnum_out = 0;
- s->eecd_state.reading = 0;
+ if (!(E1000_EECD_CS & val)) { /* CS inactive; nothing to do */
+ return;
}
- if (!(E1000_EECD_SK & (val ^ oldval))) // no clock edge
+ if (E1000_EECD_CS & (val ^ oldval)) { /* CS rise edge; reset state */
+ s->eecd_state.val_in = 0;
+ s->eecd_state.bitnum_in = 0;
+ s->eecd_state.bitnum_out = 0;
+ s->eecd_state.reading = 0;
+ }
+ if (!(E1000_EECD_SK & (val ^ oldval))) { /* no clock edge */
return;
- if (!(E1000_EECD_SK & val)) { // falling edge
+ }
+ if (!(E1000_EECD_SK & val)) { /* falling edge */
s->eecd_state.bitnum_out++;
return;
}
@@ -565,6 +593,56 @@ putsum(uint8_t *data, uint32_t n, uint32_t sloc, uint32_t css, uint32_t cse)
}
}
+static inline void
+inc_reg_if_not_full(E1000State *s, int index)
+{
+ if (s->mac_reg[index] != 0xffffffff) {
+ s->mac_reg[index]++;
+ }
+}
+
+static inline void
+inc_tx_bcast_or_mcast_count(E1000State *s, const unsigned char *arr)
+{
+ if (!memcmp(arr, bcast, sizeof bcast)) {
+ inc_reg_if_not_full(s, BPTC);
+ } else if (arr[0] & 1) {
+ inc_reg_if_not_full(s, MPTC);
+ }
+}
+
+static void
+grow_8reg_if_not_full(E1000State *s, int index, int size)
+{
+ uint64_t sum = s->mac_reg[index] | (uint64_t)s->mac_reg[index+1] << 32;
+
+ if (sum + size < sum) {
+ sum = ~0ULL;
+ } else {
+ sum += size;
+ }
+ s->mac_reg[index] = sum;
+ s->mac_reg[index+1] = sum >> 32;
+}
+
+static void
+increase_size_stats(E1000State *s, const int *size_regs, int size)
+{
+ if (size > 1023) {
+ inc_reg_if_not_full(s, size_regs[5]);
+ } else if (size > 511) {
+ inc_reg_if_not_full(s, size_regs[4]);
+ } else if (size > 255) {
+ inc_reg_if_not_full(s, size_regs[3]);
+ } else if (size > 127) {
+ inc_reg_if_not_full(s, size_regs[2]);
+ } else if (size > 64) {
+ inc_reg_if_not_full(s, size_regs[1]);
+ } else if (size == 64) {
+ inc_reg_if_not_full(s, size_regs[0]);
+ }
+}
+
static inline int
vlan_enabled(E1000State *s)
{
@@ -602,40 +680,49 @@ fcs_len(E1000State *s)
static void
e1000_send_packet(E1000State *s, const uint8_t *buf, int size)
{
+ static const int PTCregs[6] = { PTC64, PTC127, PTC255, PTC511,
+ PTC1023, PTC1522 };
+
NetClientState *nc = qemu_get_queue(s->nic);
if (s->phy_reg[PHY_CTRL] & MII_CR_LOOPBACK) {
nc->info->receive(nc, buf, size);
} else {
qemu_send_packet(nc, buf, size);
}
+ inc_tx_bcast_or_mcast_count(s, buf);
+ increase_size_stats(s, PTCregs, size);
}
static void
xmit_seg(E1000State *s)
{
uint16_t len, *sp;
- unsigned int frames = s->tx.tso_frames, css, sofar, n;
+ unsigned int frames = s->tx.tso_frames, css, sofar;
struct e1000_tx *tp = &s->tx;
if (tp->tse && tp->cptse) {
css = tp->ipcss;
DBGOUT(TXSUM, "frames %d size %d ipcss %d\n",
frames, tp->size, css);
- if (tp->ip) { // IPv4
+ if (tp->ip) { /* IPv4 */
stw_be_p(tp->data+css+2, tp->size - css);
stw_be_p(tp->data+css+4,
- be16_to_cpup((uint16_t *)(tp->data+css+4))+frames);
- } else // IPv6
+ be16_to_cpup((uint16_t *)(tp->data+css+4))+frames);
+ } else { /* IPv6 */
stw_be_p(tp->data+css+4, tp->size - css);
+ }
css = tp->tucss;
len = tp->size - css;
DBGOUT(TXSUM, "tcp %d tucss %d len %d\n", tp->tcp, css, len);
if (tp->tcp) {
sofar = frames * tp->mss;
stl_be_p(tp->data+css+4, ldl_be_p(tp->data+css+4)+sofar); /* seq */
- if (tp->paylen - sofar > tp->mss)
- tp->data[css + 13] &= ~9; // PSH, FIN
- } else // UDP
+ if (tp->paylen - sofar > tp->mss) {
+ tp->data[css + 13] &= ~9; /* PSH, FIN */
+ } else if (frames) {
+ inc_reg_if_not_full(s, TSCTC);
+ }
+ } else /* UDP */
stw_be_p(tp->data+css+4, len);
if (tp->sum_needed & E1000_TXD_POPTS_TXSM) {
unsigned int phsum;
@@ -657,13 +744,15 @@ xmit_seg(E1000State *s)
memmove(tp->data, tp->data + 4, 8);
memcpy(tp->data + 8, tp->vlan_header, 4);
e1000_send_packet(s, tp->vlan, tp->size + 4);
- } else
+ } else {
e1000_send_packet(s, tp->data, tp->size);
- s->mac_reg[TPT]++;
- s->mac_reg[GPTC]++;
- n = s->mac_reg[TOTL];
- if ((s->mac_reg[TOTL] += s->tx.size) < n)
- s->mac_reg[TOTH]++;
+ }
+
+ inc_reg_if_not_full(s, TPT);
+ grow_8reg_if_not_full(s, TOTL, s->tx.size);
+ s->mac_reg[GPTC] = s->mac_reg[TPT];
+ s->mac_reg[GOTCL] = s->mac_reg[TOTL];
+ s->mac_reg[GOTCH] = s->mac_reg[TOTH];
}
static void
@@ -679,7 +768,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp)
struct e1000_tx *tp = &s->tx;
s->mit_ide |= (txd_lower & E1000_TXD_CMD_IDE);
- if (dtype == E1000_TXD_CMD_DEXT) { // context descriptor
+ if (dtype == E1000_TXD_CMD_DEXT) { /* context descriptor */
op = le32_to_cpu(xp->cmd_and_length);
tp->ipcss = xp->lower_setup.ip_fields.ipcss;
tp->ipcso = xp->lower_setup.ip_fields.ipcso;
@@ -694,7 +783,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp)
tp->tcp = (op & E1000_TXD_CMD_TCP) ? 1 : 0;
tp->tse = (op & E1000_TXD_CMD_TSE) ? 1 : 0;
tp->tso_frames = 0;
- if (tp->tucso == 0) { // this is probably wrong
+ if (tp->tucso == 0) { /* this is probably wrong */
DBGOUT(TXSUM, "TCP/UDP: cso 0!\n");
tp->tucso = tp->tucss + (tp->tcp ? 16 : 6);
}
@@ -718,7 +807,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp)
stw_be_p(tp->vlan_header + 2,
le16_to_cpu(dp->upper.fields.special));
}
-
+
addr = le64_to_cpu(dp->buffer_addr);
if (tp->tse && tp->cptse) {
msh = tp->hdr_len + tp->mss;
@@ -819,7 +908,8 @@ start_xmit(E1000State *s)
* bogus values to TDT/TDLEN.
* there's nothing too intelligent we could do about this.
*/
- if (s->mac_reg[TDH] == tdh_start) {
+ if (s->mac_reg[TDH] == tdh_start ||
+ tdh_start >= s->mac_reg[TDLEN] / sizeof(desc)) {
DBGOUT(TXERR, "TDH wraparound @%x, TDT %x, TDLEN %x\n",
tdh_start, s->mac_reg[TDT], s->mac_reg[TDLEN]);
break;
@@ -831,9 +921,9 @@ start_xmit(E1000State *s)
static int
receive_filter(E1000State *s, const uint8_t *buf, int size)
{
- static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
static const int mta_shift[] = {4, 3, 2, 0};
uint32_t f, rctl = s->mac_reg[RCTL], ra[2], *rp;
+ int isbcast = !memcmp(buf, bcast, sizeof bcast), ismcast = (buf[0] & 1);
if (is_vlan_packet(s, buf) && vlan_rx_filter_enabled(s)) {
uint16_t vid = be16_to_cpup((uint16_t *)(buf + 14));
@@ -843,14 +933,19 @@ receive_filter(E1000State *s, const uint8_t *buf, int size)
return 0;
}
- if (rctl & E1000_RCTL_UPE) // promiscuous
+ if (!isbcast && !ismcast && (rctl & E1000_RCTL_UPE)) { /* promiscuous ucast */
return 1;
+ }
- if ((buf[0] & 1) && (rctl & E1000_RCTL_MPE)) // promiscuous mcast
+ if (ismcast && (rctl & E1000_RCTL_MPE)) { /* promiscuous mcast */
+ inc_reg_if_not_full(s, MPRC);
return 1;
+ }
- if ((rctl & E1000_RCTL_BAM) && !memcmp(buf, bcast, sizeof bcast))
+ if (isbcast && (rctl & E1000_RCTL_BAM)) { /* broadcast enabled */
+ inc_reg_if_not_full(s, BPRC);
return 1;
+ }
for (rp = s->mac_reg + RA; rp < s->mac_reg + RA + 32; rp += 2) {
if (!(rp[1] & E1000_RAH_AV))
@@ -870,8 +965,10 @@ receive_filter(E1000State *s, const uint8_t *buf, int size)
f = mta_shift[(rctl >> E1000_RCTL_MO_SHIFT) & 3];
f = (((buf[5] << 8) | buf[4]) >> f) & 0xfff;
- if (s->mac_reg[MTA + (f >> 5)] & (1 << (f & 0x1f)))
+ if (s->mac_reg[MTA + (f >> 5)] & (1 << (f & 0x1f))) {
+ inc_reg_if_not_full(s, MPRC);
return 1;
+ }
DBGOUT(RXFILTER,
"dropping, inexact filter mismatch: %02x:%02x:%02x:%02x:%02x:%02x MO %d MTA[%d] %x\n",
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5],
@@ -960,6 +1057,8 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt)
size_t desc_offset;
size_t desc_size;
size_t total_size;
+ static const int PRCregs[6] = { PRC64, PRC127, PRC255, PRC511,
+ PRC1023, PRC1522 };
if (!(s->mac_reg[STATUS] & E1000_STATUS_LU)) {
return -1;
@@ -973,6 +1072,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt)
if (size < sizeof(min_buf)) {
iov_to_buf(iov, iovcnt, 0, min_buf, size);
memset(&min_buf[size], 0, sizeof(min_buf) - size);
+ inc_reg_if_not_full(s, RUC);
min_iov.iov_base = filter_buf = min_buf;
min_iov.iov_len = size = sizeof(min_buf);
iovcnt = 1;
@@ -988,6 +1088,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt)
(size > MAXIMUM_ETHERNET_VLAN_SIZE
&& !(s->mac_reg[RCTL] & E1000_RCTL_LPE)))
&& !(s->mac_reg[RCTL] & E1000_RCTL_SBP)) {
+ inc_reg_if_not_full(s, ROC);
return size;
}
@@ -1065,7 +1166,8 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt)
if (++s->mac_reg[RDH] * sizeof(desc) >= s->mac_reg[RDLEN])
s->mac_reg[RDH] = 0;
/* see comment in start_xmit; same here */
- if (s->mac_reg[RDH] == rdh_start) {
+ if (s->mac_reg[RDH] == rdh_start ||
+ rdh_start >= s->mac_reg[RDLEN] / sizeof(desc)) {
DBGOUT(RXERR, "RDH wraparound @%x, RDT %x, RDLEN %x\n",
rdh_start, s->mac_reg[RDT], s->mac_reg[RDLEN]);
set_ics(s, 0, E1000_ICS_RXO);
@@ -1073,16 +1175,17 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt)
}
} while (desc_offset < total_size);
- s->mac_reg[GPRC]++;
- s->mac_reg[TPR]++;
+ increase_size_stats(s, PRCregs, total_size);
+ inc_reg_if_not_full(s, TPR);
+ s->mac_reg[GPRC] = s->mac_reg[TPR];
/* TOR - Total Octets Received:
* This register includes bytes received in a packet from the <Destination
* Address> field through the <CRC> field, inclusively.
+ * Always include FCS length (4) in size.
*/
- n = s->mac_reg[TORL] + size + /* Always include FCS length. */ 4;
- if (n < s->mac_reg[TORL])
- s->mac_reg[TORH]++;
- s->mac_reg[TORL] = n;
+ grow_8reg_if_not_full(s, TORL, size+4);
+ s->mac_reg[GORCL] = s->mac_reg[TORL];
+ s->mac_reg[GORCH] = s->mac_reg[TORH];
n = E1000_ICS_RXT0;
if ((rdt = s->mac_reg[RDT]) < s->mac_reg[RDH])
@@ -1114,6 +1217,30 @@ mac_readreg(E1000State *s, int index)
}
static uint32_t
+mac_low4_read(E1000State *s, int index)
+{
+ return s->mac_reg[index] & 0xf;
+}
+
+static uint32_t
+mac_low11_read(E1000State *s, int index)
+{
+ return s->mac_reg[index] & 0x7ff;
+}
+
+static uint32_t
+mac_low13_read(E1000State *s, int index)
+{
+ return s->mac_reg[index] & 0x1fff;
+}
+
+static uint32_t
+mac_low16_read(E1000State *s, int index)
+{
+ return s->mac_reg[index] & 0xffff;
+}
+
+static uint32_t
mac_icr_read(E1000State *s, int index)
{
uint32_t ret = s->mac_reg[ICR];
@@ -1206,46 +1333,144 @@ set_ims(E1000State *s, int index, uint32_t val)
set_ics(s, 0, 0);
}
-#define getreg(x) [x] = mac_readreg
+#define getreg(x) [x] = mac_readreg
static uint32_t (*macreg_readops[])(E1000State *, int) = {
- getreg(PBA), getreg(RCTL), getreg(TDH), getreg(TXDCTL),
- getreg(WUFC), getreg(TDT), getreg(CTRL), getreg(LEDCTL),
- getreg(MANC), getreg(MDIC), getreg(SWSM), getreg(STATUS),
- getreg(TORL), getreg(TOTL), getreg(IMS), getreg(TCTL),
- getreg(RDH), getreg(RDT), getreg(VET), getreg(ICS),
- getreg(TDBAL), getreg(TDBAH), getreg(RDBAH), getreg(RDBAL),
- getreg(TDLEN), getreg(RDLEN), getreg(RDTR), getreg(RADV),
- getreg(TADV), getreg(ITR),
-
- [TOTH] = mac_read_clr8, [TORH] = mac_read_clr8, [GPRC] = mac_read_clr4,
- [GPTC] = mac_read_clr4, [TPR] = mac_read_clr4, [TPT] = mac_read_clr4,
- [ICR] = mac_icr_read, [EECD] = get_eecd, [EERD] = flash_eerd_read,
- [CRCERRS ... MPC] = &mac_readreg,
- [RA ... RA+31] = &mac_readreg,
- [MTA ... MTA+127] = &mac_readreg,
+ getreg(PBA), getreg(RCTL), getreg(TDH), getreg(TXDCTL),
+ getreg(WUFC), getreg(TDT), getreg(CTRL), getreg(LEDCTL),
+ getreg(MANC), getreg(MDIC), getreg(SWSM), getreg(STATUS),
+ getreg(TORL), getreg(TOTL), getreg(IMS), getreg(TCTL),
+ getreg(RDH), getreg(RDT), getreg(VET), getreg(ICS),
+ getreg(TDBAL), getreg(TDBAH), getreg(RDBAH), getreg(RDBAL),
+ getreg(TDLEN), getreg(RDLEN), getreg(RDTR), getreg(RADV),
+ getreg(TADV), getreg(ITR), getreg(FCRUC), getreg(IPAV),
+ getreg(WUC), getreg(WUS), getreg(SCC), getreg(ECOL),
+ getreg(MCC), getreg(LATECOL), getreg(COLC), getreg(DC),
+ getreg(TNCRS), getreg(SEC), getreg(CEXTERR), getreg(RLEC),
+ getreg(XONRXC), getreg(XONTXC), getreg(XOFFRXC), getreg(XOFFTXC),
+ getreg(RFC), getreg(RJC), getreg(RNBC), getreg(TSCTFC),
+ getreg(MGTPRC), getreg(MGTPDC), getreg(MGTPTC), getreg(GORCL),
+ getreg(GOTCL),
+
+ [TOTH] = mac_read_clr8, [TORH] = mac_read_clr8,
+ [GOTCH] = mac_read_clr8, [GORCH] = mac_read_clr8,
+ [PRC64] = mac_read_clr4, [PRC127] = mac_read_clr4,
+ [PRC255] = mac_read_clr4, [PRC511] = mac_read_clr4,
+ [PRC1023] = mac_read_clr4, [PRC1522] = mac_read_clr4,
+ [PTC64] = mac_read_clr4, [PTC127] = mac_read_clr4,
+ [PTC255] = mac_read_clr4, [PTC511] = mac_read_clr4,
+ [PTC1023] = mac_read_clr4, [PTC1522] = mac_read_clr4,
+ [GPRC] = mac_read_clr4, [GPTC] = mac_read_clr4,
+ [TPT] = mac_read_clr4, [TPR] = mac_read_clr4,
+ [RUC] = mac_read_clr4, [ROC] = mac_read_clr4,
+ [BPRC] = mac_read_clr4, [MPRC] = mac_read_clr4,
+ [TSCTC] = mac_read_clr4, [BPTC] = mac_read_clr4,
+ [MPTC] = mac_read_clr4,
+ [ICR] = mac_icr_read, [EECD] = get_eecd,
+ [EERD] = flash_eerd_read,
+ [RDFH] = mac_low13_read, [RDFT] = mac_low13_read,
+ [RDFHS] = mac_low13_read, [RDFTS] = mac_low13_read,
+ [RDFPC] = mac_low13_read,
+ [TDFH] = mac_low11_read, [TDFT] = mac_low11_read,
+ [TDFHS] = mac_low13_read, [TDFTS] = mac_low13_read,
+ [TDFPC] = mac_low13_read,
+ [AIT] = mac_low16_read,
+
+ [CRCERRS ... MPC] = &mac_readreg,
+ [IP6AT ... IP6AT+3] = &mac_readreg, [IP4AT ... IP4AT+6] = &mac_readreg,
+ [FFLT ... FFLT+6] = &mac_low11_read,
+ [RA ... RA+31] = &mac_readreg,
+ [WUPM ... WUPM+31] = &mac_readreg,
+ [MTA ... MTA+127] = &mac_readreg,
[VFTA ... VFTA+127] = &mac_readreg,
+ [FFMT ... FFMT+254] = &mac_low4_read,
+ [FFVT ... FFVT+254] = &mac_readreg,
+ [PBM ... PBM+16383] = &mac_readreg,
};
enum { NREADOPS = ARRAY_SIZE(macreg_readops) };
-#define putreg(x) [x] = mac_writereg
+#define putreg(x) [x] = mac_writereg
static void (*macreg_writeops[])(E1000State *, int, uint32_t) = {
- putreg(PBA), putreg(EERD), putreg(SWSM), putreg(WUFC),
- putreg(TDBAL), putreg(TDBAH), putreg(TXDCTL), putreg(RDBAH),
- putreg(RDBAL), putreg(LEDCTL), putreg(VET),
- [TDLEN] = set_dlen, [RDLEN] = set_dlen, [TCTL] = set_tctl,
- [TDT] = set_tctl, [MDIC] = set_mdic, [ICS] = set_ics,
- [TDH] = set_16bit, [RDH] = set_16bit, [RDT] = set_rdt,
- [IMC] = set_imc, [IMS] = set_ims, [ICR] = set_icr,
- [EECD] = set_eecd, [RCTL] = set_rx_control, [CTRL] = set_ctrl,
- [RDTR] = set_16bit, [RADV] = set_16bit, [TADV] = set_16bit,
- [ITR] = set_16bit,
- [RA ... RA+31] = &mac_writereg,
- [MTA ... MTA+127] = &mac_writereg,
+ putreg(PBA), putreg(EERD), putreg(SWSM), putreg(WUFC),
+ putreg(TDBAL), putreg(TDBAH), putreg(TXDCTL), putreg(RDBAH),
+ putreg(RDBAL), putreg(LEDCTL), putreg(VET), putreg(FCRUC),
+ putreg(TDFH), putreg(TDFT), putreg(TDFHS), putreg(TDFTS),
+ putreg(TDFPC), putreg(RDFH), putreg(RDFT), putreg(RDFHS),
+ putreg(RDFTS), putreg(RDFPC), putreg(IPAV), putreg(WUC),
+ putreg(WUS), putreg(AIT),
+
+ [TDLEN] = set_dlen, [RDLEN] = set_dlen, [TCTL] = set_tctl,
+ [TDT] = set_tctl, [MDIC] = set_mdic, [ICS] = set_ics,
+ [TDH] = set_16bit, [RDH] = set_16bit, [RDT] = set_rdt,
+ [IMC] = set_imc, [IMS] = set_ims, [ICR] = set_icr,
+ [EECD] = set_eecd, [RCTL] = set_rx_control, [CTRL] = set_ctrl,
+ [RDTR] = set_16bit, [RADV] = set_16bit, [TADV] = set_16bit,
+ [ITR] = set_16bit,
+
+ [IP6AT ... IP6AT+3] = &mac_writereg, [IP4AT ... IP4AT+6] = &mac_writereg,
+ [FFLT ... FFLT+6] = &mac_writereg,
+ [RA ... RA+31] = &mac_writereg,
+ [WUPM ... WUPM+31] = &mac_writereg,
+ [MTA ... MTA+127] = &mac_writereg,
[VFTA ... VFTA+127] = &mac_writereg,
+ [FFMT ... FFMT+254] = &mac_writereg, [FFVT ... FFVT+254] = &mac_writereg,
+ [PBM ... PBM+16383] = &mac_writereg,
};
enum { NWRITEOPS = ARRAY_SIZE(macreg_writeops) };
+enum { MAC_ACCESS_PARTIAL = 1, MAC_ACCESS_FLAG_NEEDED = 2 };
+
+#define markflag(x) ((E1000_FLAG_##x << 2) | MAC_ACCESS_FLAG_NEEDED)
+/* In the array below the meaning of the bits is: [f|f|f|f|f|f|n|p]
+ * f - flag bits (up to 6 possible flags)
+ * n - flag needed
+ * p - partially implenented */
+static const uint8_t mac_reg_access[0x8000] = {
+ [RDTR] = markflag(MIT), [TADV] = markflag(MIT),
+ [RADV] = markflag(MIT), [ITR] = markflag(MIT),
+
+ [IPAV] = markflag(MAC), [WUC] = markflag(MAC),
+ [IP6AT] = markflag(MAC), [IP4AT] = markflag(MAC),
+ [FFVT] = markflag(MAC), [WUPM] = markflag(MAC),
+ [ECOL] = markflag(MAC), [MCC] = markflag(MAC),
+ [DC] = markflag(MAC), [TNCRS] = markflag(MAC),
+ [RLEC] = markflag(MAC), [XONRXC] = markflag(MAC),
+ [XOFFTXC] = markflag(MAC), [RFC] = markflag(MAC),
+ [TSCTFC] = markflag(MAC), [MGTPRC] = markflag(MAC),
+ [WUS] = markflag(MAC), [AIT] = markflag(MAC),
+ [FFLT] = markflag(MAC), [FFMT] = markflag(MAC),
+ [SCC] = markflag(MAC), [FCRUC] = markflag(MAC),
+ [LATECOL] = markflag(MAC), [COLC] = markflag(MAC),
+ [SEC] = markflag(MAC), [CEXTERR] = markflag(MAC),
+ [XONTXC] = markflag(MAC), [XOFFRXC] = markflag(MAC),
+ [RJC] = markflag(MAC), [RNBC] = markflag(MAC),
+ [MGTPDC] = markflag(MAC), [MGTPTC] = markflag(MAC),
+ [RUC] = markflag(MAC), [ROC] = markflag(MAC),
+ [GORCL] = markflag(MAC), [GORCH] = markflag(MAC),
+ [GOTCL] = markflag(MAC), [GOTCH] = markflag(MAC),
+ [BPRC] = markflag(MAC), [MPRC] = markflag(MAC),
+ [TSCTC] = markflag(MAC), [PRC64] = markflag(MAC),
+ [PRC127] = markflag(MAC), [PRC255] = markflag(MAC),
+ [PRC511] = markflag(MAC), [PRC1023] = markflag(MAC),
+ [PRC1522] = markflag(MAC), [PTC64] = markflag(MAC),
+ [PTC127] = markflag(MAC), [PTC255] = markflag(MAC),
+ [PTC511] = markflag(MAC), [PTC1023] = markflag(MAC),
+ [PTC1522] = markflag(MAC), [MPTC] = markflag(MAC),
+ [BPTC] = markflag(MAC),
+
+ [TDFH] = markflag(MAC) | MAC_ACCESS_PARTIAL,
+ [TDFT] = markflag(MAC) | MAC_ACCESS_PARTIAL,
+ [TDFHS] = markflag(MAC) | MAC_ACCESS_PARTIAL,
+ [TDFTS] = markflag(MAC) | MAC_ACCESS_PARTIAL,
+ [TDFPC] = markflag(MAC) | MAC_ACCESS_PARTIAL,
+ [RDFH] = markflag(MAC) | MAC_ACCESS_PARTIAL,
+ [RDFT] = markflag(MAC) | MAC_ACCESS_PARTIAL,
+ [RDFHS] = markflag(MAC) | MAC_ACCESS_PARTIAL,
+ [RDFTS] = markflag(MAC) | MAC_ACCESS_PARTIAL,
+ [RDFPC] = markflag(MAC) | MAC_ACCESS_PARTIAL,
+ [PBM] = markflag(MAC) | MAC_ACCESS_PARTIAL,
+};
+
static void
e1000_mmio_write(void *opaque, hwaddr addr, uint64_t val,
unsigned size)
@@ -1254,9 +1479,20 @@ e1000_mmio_write(void *opaque, hwaddr addr, uint64_t val,
unsigned int index = (addr & 0x1ffff) >> 2;
if (index < NWRITEOPS && macreg_writeops[index]) {
- macreg_writeops[index](s, index, val);
+ if (!(mac_reg_access[index] & MAC_ACCESS_FLAG_NEEDED)
+ || (s->compat_flags & (mac_reg_access[index] >> 2))) {
+ if (mac_reg_access[index] & MAC_ACCESS_PARTIAL) {
+ DBGOUT(GENERAL, "Writing to register at offset: 0x%08x. "
+ "It is not fully implemented.\n", index<<2);
+ }
+ macreg_writeops[index](s, index, val);
+ } else { /* "flag needed" bit is set, but the flag is not active */
+ DBGOUT(MMIO, "MMIO write attempt to disabled reg. addr=0x%08x\n",
+ index<<2);
+ }
} else if (index < NREADOPS && macreg_readops[index]) {
- DBGOUT(MMIO, "e1000_mmio_writel RO %x: 0x%04"PRIx64"\n", index<<2, val);
+ DBGOUT(MMIO, "e1000_mmio_writel RO %x: 0x%04"PRIx64"\n",
+ index<<2, val);
} else {
DBGOUT(UNKNOWN, "MMIO unknown write addr=0x%08x,val=0x%08"PRIx64"\n",
index<<2, val);
@@ -1269,11 +1505,21 @@ e1000_mmio_read(void *opaque, hwaddr addr, unsigned size)
E1000State *s = opaque;
unsigned int index = (addr & 0x1ffff) >> 2;
- if (index < NREADOPS && macreg_readops[index])
- {
- return macreg_readops[index](s, index);
+ if (index < NREADOPS && macreg_readops[index]) {
+ if (!(mac_reg_access[index] & MAC_ACCESS_FLAG_NEEDED)
+ || (s->compat_flags & (mac_reg_access[index] >> 2))) {
+ if (mac_reg_access[index] & MAC_ACCESS_PARTIAL) {
+ DBGOUT(GENERAL, "Reading register at offset: 0x%08x. "
+ "It is not fully implemented.\n", index<<2);
+ }
+ return macreg_readops[index](s, index);
+ } else { /* "flag needed" bit is set, but the flag is not active */
+ DBGOUT(MMIO, "MMIO read attempt of disabled reg. addr=0x%08x\n",
+ index<<2);
+ }
+ } else {
+ DBGOUT(UNKNOWN, "MMIO unknown read addr=0x%08x\n", index<<2);
}
- DBGOUT(UNKNOWN, "MMIO unknown read addr=0x%08x\n", index<<2);
return 0;
}
@@ -1340,7 +1586,7 @@ static int e1000_post_load(void *opaque, int version_id)
E1000State *s = opaque;
NetClientState *nc = qemu_get_queue(s->nic);
- if (!(s->compat_flags & E1000_FLAG_MIT)) {
+ if (!chkflag(MIT)) {
s->mac_reg[ITR] = s->mac_reg[RDTR] = s->mac_reg[RADV] =
s->mac_reg[TADV] = 0;
s->mit_irq_level = false;
@@ -1367,7 +1613,14 @@ static bool e1000_mit_state_needed(void *opaque)
{
E1000State *s = opaque;
- return s->compat_flags & E1000_FLAG_MIT;
+ return chkflag(MIT);
+}
+
+static bool e1000_full_mac_needed(void *opaque)
+{
+ E1000State *s = opaque;
+
+ return chkflag(MAC);
}
static const VMStateDescription vmstate_e1000_mit_state = {
@@ -1385,6 +1638,17 @@ static const VMStateDescription vmstate_e1000_mit_state = {
}
};
+static const VMStateDescription vmstate_e1000_full_mac_state = {
+ .name = "e1000/full_mac_state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = e1000_full_mac_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(mac_reg, E1000State, 0x8000),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_e1000 = {
.name = "e1000",
.version_id = 2,
@@ -1464,6 +1728,7 @@ static const VMStateDescription vmstate_e1000 = {
},
.subsections = (const VMStateDescription*[]) {
&vmstate_e1000_mit_state,
+ &vmstate_e1000_full_mac_state,
NULL
}
};
@@ -1596,6 +1861,8 @@ static Property e1000_properties[] = {
compat_flags, E1000_FLAG_AUTONEG_BIT, true),
DEFINE_PROP_BIT("mitigation", E1000State,
compat_flags, E1000_FLAG_MIT_BIT, true),
+ DEFINE_PROP_BIT("extra_mac_registers", E1000State,
+ compat_flags, E1000_FLAG_MAC_BIT, true),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1647,7 +1914,7 @@ static const TypeInfo e1000_base_info = {
static const E1000Info e1000_devices[] = {
{
- .name = "e1000-82540em",
+ .name = "e1000",
.device_id = E1000_DEV_ID_82540EM,
.revision = 0x03,
.phy_id2 = E1000_PHY_ID2_8254xx_DEFAULT,
@@ -1666,11 +1933,6 @@ static const E1000Info e1000_devices[] = {
},
};
-static const TypeInfo e1000_default_info = {
- .name = "e1000",
- .parent = "e1000-82540em",
-};
-
static void e1000_register_types(void)
{
int i;
@@ -1688,7 +1950,6 @@ static void e1000_register_types(void)
type_register(&type_info);
}
- type_register_static(&e1000_default_info);
}
type_init(e1000_register_types)
diff --git a/hw/net/e1000_regs.h b/hw/net/e1000_regs.h
index 60b96aaf1..1c40244ab 100644
--- a/hw/net/e1000_regs.h
+++ b/hw/net/e1000_regs.h
@@ -158,7 +158,8 @@
#define E1000_PHY_CTRL 0x00F10 /* PHY Control Register in CSR */
#define FEXTNVM_SW_CONFIG 0x0001
#define E1000_PBA 0x01000 /* Packet Buffer Allocation - RW */
-#define E1000_PBS 0x01008 /* Packet Buffer Size */
+#define E1000_PBM 0x10000 /* Packet Buffer Memory - RW */
+#define E1000_PBS 0x01008 /* Packet Buffer Size - RW */
#define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */
#define E1000_FLASH_UPDATES 1000
#define E1000_EEARBC 0x01024 /* EEPROM Auto Read Bus Control */
@@ -191,6 +192,11 @@
#define E1000_RAID 0x02C08 /* Receive Ack Interrupt Delay - RW */
#define E1000_TXDMAC 0x03000 /* TX DMA Control - RW */
#define E1000_KABGTXD 0x03004 /* AFE Band Gap Transmit Ref Data */
+#define E1000_RDFH 0x02410 /* Receive Data FIFO Head Register - RW */
+#define E1000_RDFT 0x02418 /* Receive Data FIFO Tail Register - RW */
+#define E1000_RDFHS 0x02420 /* Receive Data FIFO Head Saved Register - RW */
+#define E1000_RDFTS 0x02428 /* Receive Data FIFO Tail Saved Register - RW */
+#define E1000_RDFPC 0x02430 /* Receive Data FIFO Packet Count - RW */
#define E1000_TDFH 0x03410 /* TX Data FIFO Head - RW */
#define E1000_TDFT 0x03418 /* TX Data FIFO Tail - RW */
#define E1000_TDFHS 0x03420 /* TX Data FIFO Head Saved - RW */
diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c
index 60333b7fc..685a4781b 100644
--- a/hw/net/eepro100.c
+++ b/hw/net/eepro100.c
@@ -774,6 +774,11 @@ static void tx_command(EEPRO100State *s)
#if 0
uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, tbd_address + 6);
#endif
+ if (tx_buffer_size == 0) {
+ /* Prevent an endless loop. */
+ logout("loop in %s:%u\n", __FILE__, __LINE__);
+ break;
+ }
tbd_address += 8;
TRACE(RXTX, logout
("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n",
@@ -855,6 +860,10 @@ static void set_multicast_list(EEPRO100State *s)
static void action_command(EEPRO100State *s)
{
+ /* The loop below won't stop if it gets special handcrafted data.
+ Therefore we limit the number of iterations. */
+ unsigned max_loop_count = 16;
+
for (;;) {
bool bit_el;
bool bit_s;
@@ -870,6 +879,13 @@ static void action_command(EEPRO100State *s)
#if 0
bool bit_sf = ((s->tx.command & COMMAND_SF) != 0);
#endif
+
+ if (max_loop_count-- == 0) {
+ /* Prevent an endless loop. */
+ logout("loop in %s:%u\n", __FILE__, __LINE__);
+ break;
+ }
+
s->cu_offset = s->tx.link;
TRACE(OTHER,
logout("val=(cu start), status=0x%04x, command=0x%04x, link=0x%08x\n",
diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c
index 0f5cf4477..04bb41da6 100644
--- a/hw/net/fsl_etsec/etsec.c
+++ b/hw/net/fsl_etsec/etsec.c
@@ -353,7 +353,7 @@ static ssize_t etsec_receive(NetClientState *nc,
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
+ /* The packet will be queued, let's flush it when buffer is available
* again. */
etsec->need_flush = true;
}
diff --git a/hw/net/fsl_etsec/rings.c b/hw/net/fsl_etsec/rings.c
index 68e7b6d16..0a5c6cfe3 100644
--- a/hw/net/fsl_etsec/rings.c
+++ b/hw/net/fsl_etsec/rings.c
@@ -464,9 +464,7 @@ static void rx_init_frame(eTSEC *etsec, const uint8_t *buf, size_t size)
etsec->rx_fcb_size = 0;
}
- if (etsec->rx_buffer != NULL) {
- g_free(etsec->rx_buffer);
- }
+ g_free(etsec->rx_buffer);
/* Do not copy the frame for now */
etsec->rx_buffer = (uint8_t *)buf;
diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c
new file mode 100644
index 000000000..c50bf7ff3
--- /dev/null
+++ b/hw/net/imx_fec.c
@@ -0,0 +1,709 @@
+/*
+ * i.MX Fast Ethernet Controller emulation.
+ *
+ * Copyright (c) 2013 Jean-Christophe Dubois. <jcd@tribudubois.net>
+ *
+ * Based on Coldfire Fast Ethernet Controller emulation.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * 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/net/imx_fec.h"
+#include "sysemu/dma.h"
+
+/* For crc32 */
+#include <zlib.h>
+
+#ifndef DEBUG_IMX_FEC
+#define DEBUG_IMX_FEC 0
+#endif
+
+#define FEC_PRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX_FEC) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_FEC, \
+ __func__, ##args); \
+ } \
+ } while (0)
+
+#ifndef DEBUG_IMX_PHY
+#define DEBUG_IMX_PHY 0
+#endif
+
+#define PHY_PRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX_PHY) { \
+ fprintf(stderr, "[%s.phy]%s: " fmt , TYPE_IMX_FEC, \
+ __func__, ##args); \
+ } \
+ } while (0)
+
+static const VMStateDescription vmstate_imx_fec = {
+ .name = TYPE_IMX_FEC,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(irq_state, IMXFECState),
+ VMSTATE_UINT32(eir, IMXFECState),
+ VMSTATE_UINT32(eimr, IMXFECState),
+ VMSTATE_UINT32(rx_enabled, IMXFECState),
+ VMSTATE_UINT32(rx_descriptor, IMXFECState),
+ VMSTATE_UINT32(tx_descriptor, IMXFECState),
+ VMSTATE_UINT32(ecr, IMXFECState),
+ VMSTATE_UINT32(mmfr, IMXFECState),
+ VMSTATE_UINT32(mscr, IMXFECState),
+ VMSTATE_UINT32(mibc, IMXFECState),
+ VMSTATE_UINT32(rcr, IMXFECState),
+ VMSTATE_UINT32(tcr, IMXFECState),
+ VMSTATE_UINT32(tfwr, IMXFECState),
+ VMSTATE_UINT32(frsr, IMXFECState),
+ VMSTATE_UINT32(erdsr, IMXFECState),
+ VMSTATE_UINT32(etdsr, IMXFECState),
+ VMSTATE_UINT32(emrbr, IMXFECState),
+ VMSTATE_UINT32(miigsk_cfgr, IMXFECState),
+ VMSTATE_UINT32(miigsk_enr, IMXFECState),
+
+ VMSTATE_UINT32(phy_status, IMXFECState),
+ VMSTATE_UINT32(phy_control, IMXFECState),
+ VMSTATE_UINT32(phy_advertise, IMXFECState),
+ VMSTATE_UINT32(phy_int, IMXFECState),
+ VMSTATE_UINT32(phy_int_mask, IMXFECState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+#define PHY_INT_ENERGYON (1 << 7)
+#define PHY_INT_AUTONEG_COMPLETE (1 << 6)
+#define PHY_INT_FAULT (1 << 5)
+#define PHY_INT_DOWN (1 << 4)
+#define PHY_INT_AUTONEG_LP (1 << 3)
+#define PHY_INT_PARFAULT (1 << 2)
+#define PHY_INT_AUTONEG_PAGE (1 << 1)
+
+static void imx_fec_update(IMXFECState *s);
+
+/*
+ * The MII phy could raise a GPIO to the processor which in turn
+ * could be handled as an interrpt by the OS.
+ * For now we don't handle any GPIO/interrupt line, so the OS will
+ * have to poll for the PHY status.
+ */
+static void phy_update_irq(IMXFECState *s)
+{
+ imx_fec_update(s);
+}
+
+static void phy_update_link(IMXFECState *s)
+{
+ /* Autonegotiation status mirrors link status. */
+ if (qemu_get_queue(s->nic)->link_down) {
+ PHY_PRINTF("link is down\n");
+ s->phy_status &= ~0x0024;
+ s->phy_int |= PHY_INT_DOWN;
+ } else {
+ PHY_PRINTF("link is up\n");
+ s->phy_status |= 0x0024;
+ s->phy_int |= PHY_INT_ENERGYON;
+ s->phy_int |= PHY_INT_AUTONEG_COMPLETE;
+ }
+ phy_update_irq(s);
+}
+
+static void imx_fec_set_link(NetClientState *nc)
+{
+ phy_update_link(IMX_FEC(qemu_get_nic_opaque(nc)));
+}
+
+static void phy_reset(IMXFECState *s)
+{
+ s->phy_status = 0x7809;
+ s->phy_control = 0x3000;
+ s->phy_advertise = 0x01e1;
+ s->phy_int_mask = 0;
+ s->phy_int = 0;
+ phy_update_link(s);
+}
+
+static uint32_t do_phy_read(IMXFECState *s, int reg)
+{
+ uint32_t val;
+
+ if (reg > 31) {
+ /* we only advertise one phy */
+ return 0;
+ }
+
+ switch (reg) {
+ case 0: /* Basic Control */
+ val = s->phy_control;
+ break;
+ case 1: /* Basic Status */
+ val = s->phy_status;
+ break;
+ case 2: /* ID1 */
+ val = 0x0007;
+ break;
+ case 3: /* ID2 */
+ val = 0xc0d1;
+ break;
+ case 4: /* Auto-neg advertisement */
+ val = s->phy_advertise;
+ break;
+ case 5: /* Auto-neg Link Partner Ability */
+ val = 0x0f71;
+ break;
+ case 6: /* Auto-neg Expansion */
+ val = 1;
+ break;
+ case 29: /* Interrupt source. */
+ val = s->phy_int;
+ s->phy_int = 0;
+ phy_update_irq(s);
+ break;
+ case 30: /* Interrupt mask */
+ val = s->phy_int_mask;
+ break;
+ case 17:
+ case 18:
+ case 27:
+ case 31:
+ qemu_log_mask(LOG_UNIMP, "[%s.phy]%s: reg %d not implemented\n",
+ TYPE_IMX_FEC, __func__, reg);
+ val = 0;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n",
+ TYPE_IMX_FEC, __func__, reg);
+ val = 0;
+ break;
+ }
+
+ PHY_PRINTF("read 0x%04x @ %d\n", val, reg);
+
+ return val;
+}
+
+static void do_phy_write(IMXFECState *s, int reg, uint32_t val)
+{
+ PHY_PRINTF("write 0x%04x @ %d\n", val, reg);
+
+ if (reg > 31) {
+ /* we only advertise one phy */
+ return;
+ }
+
+ switch (reg) {
+ case 0: /* Basic Control */
+ if (val & 0x8000) {
+ phy_reset(s);
+ } else {
+ s->phy_control = val & 0x7980;
+ /* Complete autonegotiation immediately. */
+ if (val & 0x1000) {
+ s->phy_status |= 0x0020;
+ }
+ }
+ break;
+ case 4: /* Auto-neg advertisement */
+ s->phy_advertise = (val & 0x2d7f) | 0x80;
+ break;
+ case 30: /* Interrupt mask */
+ s->phy_int_mask = val & 0xff;
+ phy_update_irq(s);
+ break;
+ case 17:
+ case 18:
+ case 27:
+ case 31:
+ qemu_log_mask(LOG_UNIMP, "[%s.phy)%s: reg %d not implemented\n",
+ TYPE_IMX_FEC, __func__, reg);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s.phy]%s: Bad address at offset %d\n",
+ TYPE_IMX_FEC, __func__, reg);
+ break;
+ }
+}
+
+static void imx_fec_read_bd(IMXFECBufDesc *bd, dma_addr_t addr)
+{
+ dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd));
+}
+
+static void imx_fec_write_bd(IMXFECBufDesc *bd, dma_addr_t addr)
+{
+ dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd));
+}
+
+static void imx_fec_update(IMXFECState *s)
+{
+ uint32_t active;
+ uint32_t changed;
+
+ active = s->eir & s->eimr;
+ changed = active ^ s->irq_state;
+ if (changed) {
+ qemu_set_irq(s->irq, active);
+ }
+ s->irq_state = active;
+}
+
+static void imx_fec_do_tx(IMXFECState *s)
+{
+ int frame_size = 0;
+ uint8_t frame[FEC_MAX_FRAME_SIZE];
+ uint8_t *ptr = frame;
+ uint32_t addr = s->tx_descriptor;
+
+ while (1) {
+ IMXFECBufDesc bd;
+ int len;
+
+ imx_fec_read_bd(&bd, addr);
+ FEC_PRINTF("tx_bd %x flags %04x len %d data %08x\n",
+ addr, bd.flags, bd.length, bd.data);
+ if ((bd.flags & FEC_BD_R) == 0) {
+ /* Run out of descriptors to transmit. */
+ break;
+ }
+ len = bd.length;
+ if (frame_size + len > FEC_MAX_FRAME_SIZE) {
+ len = FEC_MAX_FRAME_SIZE - frame_size;
+ s->eir |= FEC_INT_BABT;
+ }
+ dma_memory_read(&address_space_memory, bd.data, ptr, len);
+ ptr += len;
+ frame_size += len;
+ if (bd.flags & FEC_BD_L) {
+ /* Last buffer in frame. */
+ qemu_send_packet(qemu_get_queue(s->nic), frame, len);
+ ptr = frame;
+ frame_size = 0;
+ s->eir |= FEC_INT_TXF;
+ }
+ s->eir |= FEC_INT_TXB;
+ bd.flags &= ~FEC_BD_R;
+ /* Write back the modified descriptor. */
+ imx_fec_write_bd(&bd, addr);
+ /* Advance to the next descriptor. */
+ if ((bd.flags & FEC_BD_W) != 0) {
+ addr = s->etdsr;
+ } else {
+ addr += 8;
+ }
+ }
+
+ s->tx_descriptor = addr;
+
+ imx_fec_update(s);
+}
+
+static void imx_fec_enable_rx(IMXFECState *s)
+{
+ IMXFECBufDesc bd;
+ uint32_t tmp;
+
+ imx_fec_read_bd(&bd, s->rx_descriptor);
+
+ tmp = ((bd.flags & FEC_BD_E) != 0);
+
+ if (!tmp) {
+ FEC_PRINTF("RX buffer full\n");
+ } else if (!s->rx_enabled) {
+ qemu_flush_queued_packets(qemu_get_queue(s->nic));
+ }
+
+ s->rx_enabled = tmp;
+}
+
+static void imx_fec_reset(DeviceState *d)
+{
+ IMXFECState *s = IMX_FEC(d);
+
+ /* Reset the FEC */
+ s->eir = 0;
+ s->eimr = 0;
+ s->rx_enabled = 0;
+ s->ecr = 0;
+ s->mscr = 0;
+ s->mibc = 0xc0000000;
+ s->rcr = 0x05ee0001;
+ s->tcr = 0;
+ s->tfwr = 0;
+ s->frsr = 0x500;
+ s->miigsk_cfgr = 0;
+ s->miigsk_enr = 0x6;
+
+ /* We also reset the PHY */
+ phy_reset(s);
+}
+
+static uint64_t imx_fec_read(void *opaque, hwaddr addr, unsigned size)
+{
+ IMXFECState *s = IMX_FEC(opaque);
+
+ FEC_PRINTF("reading from @ 0x%" HWADDR_PRIx "\n", addr);
+
+ switch (addr & 0x3ff) {
+ case 0x004:
+ return s->eir;
+ case 0x008:
+ return s->eimr;
+ 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 0x044:
+ return s->mscr;
+ case 0x064:
+ return s->mibc; /* MIBC */
+ case 0x084:
+ return s->rcr;
+ case 0x0c4:
+ return s->tcr;
+ case 0x0e4: /* PALR */
+ return (s->conf.macaddr.a[0] << 24)
+ | (s->conf.macaddr.a[1] << 16)
+ | (s->conf.macaddr.a[2] << 8)
+ | s->conf.macaddr.a[3];
+ break;
+ case 0x0e8: /* PAUR */
+ return (s->conf.macaddr.a[4] << 24)
+ | (s->conf.macaddr.a[5] << 16)
+ | 0x8808;
+ case 0x0ec:
+ return 0x10000; /* OPD */
+ case 0x118:
+ return 0;
+ case 0x11c:
+ return 0;
+ case 0x120:
+ return 0;
+ case 0x124:
+ return 0;
+ case 0x144:
+ return s->tfwr;
+ case 0x14c:
+ return 0x600;
+ case 0x150:
+ return s->frsr;
+ case 0x180:
+ return s->erdsr;
+ case 0x184:
+ return s->etdsr;
+ case 0x188:
+ return s->emrbr;
+ case 0x300:
+ return s->miigsk_cfgr;
+ case 0x308:
+ return s->miigsk_enr;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_FEC, __func__, addr);
+ return 0;
+ }
+}
+
+static void imx_fec_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ IMXFECState *s = IMX_FEC(opaque);
+
+ FEC_PRINTF("writing 0x%08x @ 0x%" HWADDR_PRIx "\n", (int)value, addr);
+
+ switch (addr & 0x3ff) {
+ case 0x004: /* EIR */
+ s->eir &= ~value;
+ break;
+ case 0x008: /* EIMR */
+ s->eimr = value;
+ break;
+ case 0x010: /* RDAR */
+ if ((s->ecr & FEC_EN) && !s->rx_enabled) {
+ imx_fec_enable_rx(s);
+ }
+ break;
+ case 0x014: /* TDAR */
+ if (s->ecr & FEC_EN) {
+ imx_fec_do_tx(s);
+ }
+ break;
+ case 0x024: /* ECR */
+ s->ecr = value;
+ if (value & FEC_RESET) {
+ imx_fec_reset(DEVICE(s));
+ }
+ if ((s->ecr & FEC_EN) == 0) {
+ s->rx_enabled = 0;
+ }
+ break;
+ case 0x040: /* MMFR */
+ /* store the value */
+ s->mmfr = value;
+ if (extract32(value, 28, 1)) {
+ do_phy_write(s, extract32(value, 18, 9), extract32(value, 0, 16));
+ } else {
+ s->mmfr = do_phy_read(s, extract32(value, 18, 9));
+ }
+ /* raise the interrupt as the PHY operation is done */
+ s->eir |= FEC_INT_MII;
+ break;
+ case 0x044: /* MSCR */
+ s->mscr = value & 0xfe;
+ break;
+ case 0x064: /* MIBC */
+ /* TODO: Implement MIB. */
+ s->mibc = (value & 0x80000000) ? 0xc0000000 : 0;
+ break;
+ case 0x084: /* RCR */
+ s->rcr = value & 0x07ff003f;
+ /* TODO: Implement LOOP mode. */
+ break;
+ case 0x0c4: /* TCR */
+ /* We transmit immediately, so raise GRA immediately. */
+ s->tcr = value;
+ if (value & 1) {
+ s->eir |= FEC_INT_GRA;
+ }
+ break;
+ case 0x0e4: /* PALR */
+ s->conf.macaddr.a[0] = value >> 24;
+ s->conf.macaddr.a[1] = value >> 16;
+ s->conf.macaddr.a[2] = value >> 8;
+ s->conf.macaddr.a[3] = value;
+ break;
+ case 0x0e8: /* PAUR */
+ s->conf.macaddr.a[4] = value >> 24;
+ s->conf.macaddr.a[5] = value >> 16;
+ break;
+ case 0x0ec: /* OPDR */
+ break;
+ case 0x118: /* IAUR */
+ case 0x11c: /* IALR */
+ case 0x120: /* GAUR */
+ case 0x124: /* GALR */
+ /* TODO: implement MAC hash filtering. */
+ break;
+ case 0x144: /* TFWR */
+ s->tfwr = value & 3;
+ break;
+ case 0x14c: /* FRBR */
+ /* FRBR writes ignored. */
+ break;
+ case 0x150: /* FRSR */
+ s->frsr = (value & 0x3fc) | 0x400;
+ break;
+ case 0x180: /* ERDSR */
+ s->erdsr = value & ~3;
+ s->rx_descriptor = s->erdsr;
+ break;
+ case 0x184: /* ETDSR */
+ s->etdsr = value & ~3;
+ s->tx_descriptor = s->etdsr;
+ break;
+ case 0x188: /* EMRBR */
+ s->emrbr = value & 0x7f0;
+ break;
+ case 0x300: /* MIIGSK_CFGR */
+ s->miigsk_cfgr = value & 0x53;
+ break;
+ case 0x308: /* MIIGSK_ENR */
+ s->miigsk_enr = (value & 0x2) ? 0x6 : 0;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_FEC, __func__, addr);
+ break;
+ }
+
+ imx_fec_update(s);
+}
+
+static int imx_fec_can_receive(NetClientState *nc)
+{
+ IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
+
+ return s->rx_enabled;
+}
+
+static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf,
+ size_t len)
+{
+ IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
+ IMXFECBufDesc bd;
+ uint32_t flags = 0;
+ uint32_t addr;
+ uint32_t crc;
+ uint32_t buf_addr;
+ uint8_t *crc_ptr;
+ unsigned int buf_len;
+ size_t size = len;
+
+ FEC_PRINTF("len %d\n", (int)size);
+
+ if (!s->rx_enabled) {
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Unexpected packet\n",
+ TYPE_IMX_FEC, __func__);
+ return 0;
+ }
+
+ /* 4 bytes for the CRC. */
+ size += 4;
+ crc = cpu_to_be32(crc32(~0, buf, size));
+ crc_ptr = (uint8_t *) &crc;
+
+ /* Huge frames are truncted. */
+ if (size > FEC_MAX_FRAME_SIZE) {
+ size = FEC_MAX_FRAME_SIZE;
+ flags |= FEC_BD_TR | FEC_BD_LG;
+ }
+
+ /* Frames larger than the user limit just set error flags. */
+ if (size > (s->rcr >> 16)) {
+ flags |= FEC_BD_LG;
+ }
+
+ addr = s->rx_descriptor;
+ while (size > 0) {
+ imx_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.
+ */
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Lost end of frame\n",
+ TYPE_IMX_FEC, __func__);
+ break;
+ }
+ buf_len = (size <= s->emrbr) ? size : s->emrbr;
+ bd.length = buf_len;
+ size -= buf_len;
+
+ FEC_PRINTF("rx_bd 0x%x length %d\n", addr, bd.length);
+
+ /* The last 4 bytes are the CRC. */
+ if (size < 4) {
+ buf_len += size - 4;
+ }
+ buf_addr = bd.data;
+ dma_memory_write(&address_space_memory, buf_addr, buf, buf_len);
+ buf += buf_len;
+ if (size < 4) {
+ dma_memory_write(&address_space_memory, buf_addr + buf_len,
+ crc_ptr, 4 - size);
+ crc_ptr += 4 - size;
+ }
+ bd.flags &= ~FEC_BD_E;
+ if (size == 0) {
+ /* Last buffer in frame. */
+ bd.flags |= flags | FEC_BD_L;
+ FEC_PRINTF("rx frame flags %04x\n", bd.flags);
+ s->eir |= FEC_INT_RXF;
+ } else {
+ s->eir |= FEC_INT_RXB;
+ }
+ imx_fec_write_bd(&bd, addr);
+ /* Advance to the next descriptor. */
+ if ((bd.flags & FEC_BD_W) != 0) {
+ addr = s->erdsr;
+ } else {
+ addr += 8;
+ }
+ }
+ s->rx_descriptor = addr;
+ imx_fec_enable_rx(s);
+ imx_fec_update(s);
+ return len;
+}
+
+static const MemoryRegionOps imx_fec_ops = {
+ .read = imx_fec_read,
+ .write = imx_fec_write,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void imx_fec_cleanup(NetClientState *nc)
+{
+ IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
+
+ s->nic = NULL;
+}
+
+static NetClientInfo net_imx_fec_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .size = sizeof(NICState),
+ .can_receive = imx_fec_can_receive,
+ .receive = imx_fec_receive,
+ .cleanup = imx_fec_cleanup,
+ .link_status_changed = imx_fec_set_link,
+};
+
+
+static void imx_fec_realize(DeviceState *dev, Error **errp)
+{
+ IMXFECState *s = IMX_FEC(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+ memory_region_init_io(&s->iomem, OBJECT(dev), &imx_fec_ops, s,
+ TYPE_IMX_FEC, 0x400);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->irq);
+ qemu_macaddr_default_if_unset(&s->conf.macaddr);
+
+ s->conf.peers.ncs[0] = nd_table[0].netdev;
+
+ s->nic = qemu_new_nic(&net_imx_fec_info, &s->conf,
+ object_get_typename(OBJECT(dev)), DEVICE(dev)->id,
+ s);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+}
+
+static Property imx_fec_properties[] = {
+ DEFINE_NIC_PROPERTIES(IMXFECState, conf),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void imx_fec_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &vmstate_imx_fec;
+ dc->reset = imx_fec_reset;
+ dc->props = imx_fec_properties;
+ dc->realize = imx_fec_realize;
+}
+
+static const TypeInfo imx_fec_info = {
+ .name = TYPE_IMX_FEC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(IMXFECState),
+ .class_init = imx_fec_class_init,
+};
+
+static void imx_fec_register_types(void)
+{
+ type_register_static(&imx_fec_info);
+}
+
+type_init(imx_fec_register_types)
diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c
index 4f0e840f0..1734b5276 100644
--- a/hw/net/lan9118.c
+++ b/hw/net/lan9118.c
@@ -56,6 +56,8 @@ do { fprintf(stderr, "lan9118: error: " fmt , ## __VA_ARGS__);} while (0)
#define CSR_E2P_CMD 0xb0
#define CSR_E2P_DATA 0xb4
+#define E2P_CMD_MAC_ADDR_LOADED 0x100
+
/* IRQ_CFG */
#define IRQ_INT 0x00001000
#define IRQ_EN 0x00000100
@@ -352,14 +354,14 @@ static void lan9118_reload_eeprom(lan9118_state *s)
{
int i;
if (s->eeprom[0] != 0xa5) {
- s->e2p_cmd &= ~0x10;
+ s->e2p_cmd &= ~E2P_CMD_MAC_ADDR_LOADED;
DPRINTF("MACADDR load failed\n");
return;
}
for (i = 0; i < 6; i++) {
s->conf.macaddr.a[i] = s->eeprom[i + 1];
}
- s->e2p_cmd |= 0x10;
+ s->e2p_cmd |= E2P_CMD_MAC_ADDR_LOADED;
DPRINTF("MACADDR loaded from eeprom\n");
lan9118_mac_changed(s);
}
@@ -902,7 +904,8 @@ static void do_mac_write(lan9118_state *s, int reg, uint32_t val)
*/
break;
default:
- hw_error("lan9118: Unimplemented MAC register write: %d = 0x%x\n",
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "lan9118: Unimplemented MAC register write: %d = 0x%x\n",
s->mac_cmd & 0xf, val);
}
}
@@ -930,14 +933,16 @@ static uint32_t do_mac_read(lan9118_state *s, int reg)
case MAC_FLOW:
return s->mac_flow;
default:
- hw_error("lan9118: Unimplemented MAC register read: %d\n",
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "lan9118: Unimplemented MAC register read: %d\n",
s->mac_cmd & 0xf);
+ return 0;
}
}
static void lan9118_eeprom_cmd(lan9118_state *s, int cmd, int addr)
{
- s->e2p_cmd = (s->e2p_cmd & 0x10) | (cmd << 28) | addr;
+ s->e2p_cmd = (s->e2p_cmd & E2P_CMD_MAC_ADDR_LOADED) | (cmd << 28) | addr;
switch (cmd) {
case 0:
s->e2p_data = s->eeprom[addr];
@@ -1128,7 +1133,8 @@ static void lan9118_writel(void *opaque, hwaddr offset,
break;
default:
- hw_error("lan9118_write: Bad reg 0x%x = %x\n", (int)offset, (int)val);
+ qemu_log_mask(LOG_GUEST_ERROR, "lan9118_write: Bad reg 0x%x = %x\n",
+ (int)offset, (int)val);
break;
}
lan9118_update(s);
@@ -1246,7 +1252,7 @@ static uint64_t lan9118_readl(void *opaque, hwaddr offset,
case CSR_E2P_DATA:
return s->e2p_data;
}
- hw_error("lan9118_read: Bad reg 0x%x\n", (int)offset);
+ qemu_log_mask(LOG_GUEST_ERROR, "lan9118_read: Bad reg 0x%x\n", (int)offset);
return 0;
}
diff --git a/hw/net/milkymist-minimac2.c b/hw/net/milkymist-minimac2.c
index 5d1cf0851..6302b8bfa 100644
--- a/hw/net/milkymist-minimac2.c
+++ b/hw/net/milkymist-minimac2.c
@@ -463,7 +463,7 @@ static int milkymist_minimac2_init(SysBusDevice *sbd)
/* register buffers memory */
memory_region_init_ram(&s->buffers, OBJECT(dev), "milkymist-minimac2.buffers",
- buffers_size, &error_abort);
+ buffers_size, &error_fatal);
vmstate_register_ram_global(&s->buffers);
s->rx0_buf = memory_region_get_ram_ptr(&s->buffers);
s->rx1_buf = s->rx0_buf + MINIMAC2_BUFFER_SIZE;
diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c
index 17e7199f7..18b064463 100644
--- a/hw/net/ne2000-isa.c
+++ b/hw/net/ne2000-isa.c
@@ -44,7 +44,6 @@ typedef struct ISANE2000State {
static NetClientInfo net_ne2000_isa_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = ne2000_can_receive,
.receive = ne2000_receive,
};
diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c
index 2bdb4c927..995115e09 100644
--- a/hw/net/ne2000.c
+++ b/hw/net/ne2000.c
@@ -154,6 +154,10 @@ static int ne2000_buffer_full(NE2000State *s)
{
int avail, index, boundary;
+ if (s->stop <= s->start) {
+ return 1;
+ }
+
index = s->curpag << 8;
boundary = s->boundary << 8;
if (index < boundary)
@@ -165,15 +169,6 @@ static int ne2000_buffer_full(NE2000State *s)
return 0;
}
-int ne2000_can_receive(NetClientState *nc)
-{
- NE2000State *s = qemu_get_nic_opaque(nc);
-
- if (s->cmd & E8390_STOP)
- return 1;
- return !ne2000_buffer_full(s);
-}
-
#define MIN_BUF_SIZE 60
ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
@@ -476,8 +471,9 @@ static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr,
uint32_t val)
{
addr &= ~1; /* XXX: check exact behaviour if not even */
- if (addr < 32 ||
- (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+ if (addr < 32
+ || (addr >= NE2000_PMEM_START
+ && addr + sizeof(uint32_t) <= NE2000_MEM_SIZE)) {
stl_le_p(s->mem + addr, val);
}
}
@@ -506,8 +502,9 @@ static inline uint32_t ne2000_mem_readw(NE2000State *s, uint32_t addr)
static inline uint32_t ne2000_mem_readl(NE2000State *s, uint32_t addr)
{
addr &= ~1; /* XXX: check exact behaviour if not even */
- if (addr < 32 ||
- (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+ if (addr < 32
+ || (addr >= NE2000_PMEM_START
+ && addr + sizeof(uint32_t) <= NE2000_MEM_SIZE)) {
return ldl_le_p(s->mem + addr);
} else {
return 0xffffffff;
@@ -716,7 +713,6 @@ void ne2000_setup_io(NE2000State *s, DeviceState *dev, unsigned size)
static NetClientInfo net_ne2000_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = ne2000_can_receive,
.receive = ne2000_receive,
};
diff --git a/hw/net/ne2000.h b/hw/net/ne2000.h
index e500306aa..d022b28fc 100644
--- a/hw/net/ne2000.h
+++ b/hw/net/ne2000.h
@@ -34,7 +34,6 @@ typedef struct NE2000State {
void ne2000_setup_io(NE2000State *s, DeviceState *dev, unsigned size);
extern const VMStateDescription vmstate_ne2000;
void ne2000_reset(NE2000State *s);
-int ne2000_can_receive(NetClientState *nc);
ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_);
#endif
diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c
index 34373767d..1f4a3dbe4 100644
--- a/hw/net/pcnet.c
+++ b/hw/net/pcnet.c
@@ -670,8 +670,7 @@ static inline hwaddr pcnet_rdra_addr(PCNetState *s, int idx)
static inline int64_t pcnet_get_next_poll_time(PCNetState *s, int64_t current_time)
{
int64_t next_time = current_time +
- muldiv64(65536 - (CSR_SPND(s) ? 0 : CSR_POLL(s)),
- get_ticks_per_sec(), 33000000L);
+ (65536 - (CSR_SPND(s) ? 0 : CSR_POLL(s))) * 30;
if (next_time <= current_time)
next_time = current_time + 1;
return next_time;
@@ -1065,6 +1064,12 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
int pktcount = 0;
if (!s->looptest) {
+ if (size > 4092) {
+#ifdef PCNET_DEBUG_RMD
+ fprintf(stderr, "pcnet: truncates rx packet.\n");
+#endif
+ size = 4092;
+ }
memcpy(src, buf, size);
/* no need to compute the CRC */
src[size] = 0;
@@ -1085,7 +1090,7 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
uint32_t fcs = ~0;
uint8_t *p = src;
- while (p != &src[size-4])
+ while (p != &src[size])
CRC(fcs, *p++);
crc_err = (*(uint32_t *)p != htonl(fcs));
}
@@ -1234,8 +1239,10 @@ 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)) {
+ * Note: this is not what real hw does.
+ * Last four bytes of s->buffer are used to store CRC FCS code.
+ */
+ if (s->xmit_pos + bcnt > sizeof(s->buffer) - 4) {
s->xmit_pos = -1;
goto txdone;
}
diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c
index 47d080fd3..2e77e5086 100644
--- a/hw/net/rocker/rocker.c
+++ b/hw/net/rocker/rocker.c
@@ -101,8 +101,7 @@ RockerSwitch *qmp_query_rocker(const char *name, Error **errp)
r = rocker_find(name);
if (!r) {
- error_set(errp, ERROR_CLASS_GENERIC_ERROR,
- "rocker %s not found", name);
+ error_setg(errp, "rocker %s not found", name);
return NULL;
}
@@ -122,8 +121,7 @@ RockerPortList *qmp_query_rocker_ports(const char *name, Error **errp)
r = rocker_find(name);
if (!r) {
- error_set(errp, ERROR_CLASS_GENERIC_ERROR,
- "rocker %s not found", name);
+ error_setg(errp, "rocker %s not found", name);
return NULL;
}
@@ -234,6 +232,9 @@ static int tx_consume(Rocker *r, DescInfo *info)
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]);
+ if (iovcnt >= ROCKER_TX_FRAGS_MAX) {
+ goto err_too_many_frags;
+ }
iov[iovcnt].iov_len = frag_len;
iov[iovcnt].iov_base = g_malloc(frag_len);
if (!iov[iovcnt].iov_base) {
@@ -246,10 +247,7 @@ static int tx_consume(Rocker *r, DescInfo *info)
err = -ROCKER_ENXIO;
goto err_bad_io;
}
-
- if (++iovcnt > ROCKER_TX_FRAGS_MAX) {
- goto err_too_many_frags;
- }
+ iovcnt++;
}
if (iovcnt) {
@@ -265,9 +263,7 @@ 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);
- }
+ g_free(iov[i].iov_base);
}
return err;
@@ -1364,7 +1360,7 @@ static int pci_rocker_init(PCIDevice *dev)
r->fp_ports = ROCKER_FP_PORTS_MAX;
}
- r->rings = g_malloc(sizeof(DescRing *) * rocker_pci_ring_count(r));
+ r->rings = g_new(DescRing *, rocker_pci_ring_count(r));
if (!r->rings) {
goto err_rings_alloc;
}
diff --git a/hw/net/rocker/rocker_desc.c b/hw/net/rocker/rocker_desc.c
index 9d896fe47..5e697b19e 100644
--- a/hw/net/rocker/rocker_desc.c
+++ b/hw/net/rocker/rocker_desc.c
@@ -136,15 +136,13 @@ bool desc_ring_set_size(DescRing *ring, uint32_t size)
}
for (i = 0; i < ring->size; i++) {
- if (ring->info[i].buf) {
- g_free(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));
+ ring->info = g_renew(DescInfo, ring->info, size);
if (!ring->info) {
return false;
}
@@ -347,7 +345,7 @@ DescRing *desc_ring_alloc(Rocker *r, int index)
{
DescRing *ring;
- ring = g_malloc0(sizeof(DescRing));
+ ring = g_new0(DescRing, 1);
if (!ring) {
return NULL;
}
@@ -360,9 +358,7 @@ DescRing *desc_ring_alloc(Rocker *r, int index)
void desc_ring_free(DescRing *ring)
{
- if (ring->info) {
- g_free(ring->info);
- }
+ g_free(ring->info);
g_free(ring);
}
diff --git a/hw/net/rocker/rocker_fp.c b/hw/net/rocker/rocker_fp.c
index c693ae508..5906396b9 100644
--- a/hw/net/rocker/rocker_fp.c
+++ b/hw/net/rocker/rocker_fp.c
@@ -218,7 +218,7 @@ FpPort *fp_port_alloc(Rocker *r, char *sw_name,
MACAddr *start_mac, unsigned int index,
NICPeers *peers)
{
- FpPort *port = g_malloc0(sizeof(FpPort));
+ FpPort *port = g_new0(FpPort, 1);
if (!port) {
return NULL;
diff --git a/hw/net/rocker/rocker_of_dpa.c b/hw/net/rocker/rocker_of_dpa.c
index 874fb01d6..3cf1d61fe 100644
--- a/hw/net/rocker/rocker_of_dpa.c
+++ b/hw/net/rocker/rocker_of_dpa.c
@@ -367,7 +367,7 @@ 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));
+ flow = g_new0(OfDpaFlow, 1);
if (!flow) {
return NULL;
}
@@ -811,7 +811,7 @@ static int of_dpa_group_get_stats(OfDpa *of_dpa, uint32_t id)
static OfDpaGroup *of_dpa_group_alloc(uint32_t id)
{
- OfDpaGroup *group = g_malloc0(sizeof(OfDpaGroup));
+ OfDpaGroup *group = g_new0(OfDpaGroup, 1);
if (!group) {
return NULL;
@@ -2039,15 +2039,14 @@ static int of_dpa_cmd_add_l2_flood(OfDpa *of_dpa, OfDpaGroup *group,
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 *));
+ tlvs = g_new0(RockerTlv *, group->l2_flood.group_count + 1);
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));
+ g_new0(uint32_t, group->l2_flood.group_count);
if (!group->l2_flood.group_ids) {
err = -ROCKER_ENOMEM;
goto err_out;
@@ -2463,15 +2462,13 @@ RockerOfDpaFlowList *qmp_query_rocker_of_dpa_flows(const char *name,
r = rocker_find(name);
if (!r) {
- error_set(errp, ERROR_CLASS_GENERIC_ERROR,
- "rocker %s not found", name);
+ error_setg(errp, "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);
+ error_setg(errp, "rocker %s doesn't have OF-DPA world", name);
return NULL;
}
@@ -2598,15 +2595,13 @@ RockerOfDpaGroupList *qmp_query_rocker_of_dpa_groups(const char *name,
r = rocker_find(name);
if (!r) {
- error_set(errp, ERROR_CLASS_GENERIC_ERROR,
- "rocker %s not found", name);
+ error_setg(errp, "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);
+ error_setg(errp, "rocker %s doesn't have OF-DPA world", name);
return NULL;
}
diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c
index cb51613eb..68e43f3d4 100644
--- a/hw/net/rtl8139.c
+++ b/hw/net/rtl8139.c
@@ -43,7 +43,7 @@
* Added rx/tx buffer reset when enabling rx/tx operation
*
* 2010-Feb-04 Frediano Ziglio: Rewrote timer support using QEMU timer only
- * when strictly needed (required for for
+ * when strictly needed (required for
* Darwin)
* 2011-Mar-22 Benjamin Poirier: Implemented VLAN offloading
*/
@@ -56,6 +56,7 @@
#include "sysemu/dma.h"
#include "qemu/timer.h"
#include "net/net.h"
+#include "net/eth.h"
#include "hw/loader.h"
#include "sysemu/sysemu.h"
#include "qemu/iov.h"
@@ -63,7 +64,7 @@
/* debug RTL8139 card */
//#define DEBUG_RTL8139 1
-#define PCI_FREQUENCY 33000000L
+#define PCI_PERIOD 30 /* 30 ns period = 33.333333 Mhz frequency */
#define SET_MASKED(input, mask, curr) \
( ( (input) & ~(mask) ) | ( (curr) & (mask) ) )
@@ -72,11 +73,8 @@
#define MOD2(input, size) \
( ( input ) & ( size - 1 ) )
-#define ETHER_ADDR_LEN 6
#define ETHER_TYPE_LEN 2
-#define ETH_HLEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN)
-#define ETH_P_IP 0x0800 /* Internet Protocol packet */
-#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */
+#define ETH_HLEN (ETH_ALEN * 2 + ETHER_TYPE_LEN)
#define ETH_MTU 1500
#define VLAN_TCI_LEN 2
@@ -1016,8 +1014,8 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t
/* write VLAN info to descriptor variables. */
if (s->CpCmd & CPlusRxVLAN && be16_to_cpup((uint16_t *)
- &buf[ETHER_ADDR_LEN * 2]) == ETH_P_8021Q) {
- dot1q_buf = &buf[ETHER_ADDR_LEN * 2];
+ &buf[ETH_ALEN * 2]) == ETH_P_VLAN) {
+ dot1q_buf = &buf[ETH_ALEN * 2];
size -= VLAN_HLEN;
/* if too small buffer, use the tailroom added duing expansion */
if (size < MIN_BUF_SIZE) {
@@ -1058,10 +1056,10 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t
/* receive/copy to target memory */
if (dot1q_buf) {
- pci_dma_write(d, rx_addr, buf, 2 * ETHER_ADDR_LEN);
- pci_dma_write(d, rx_addr + 2 * ETHER_ADDR_LEN,
- buf + 2 * ETHER_ADDR_LEN + VLAN_HLEN,
- size - 2 * ETHER_ADDR_LEN);
+ pci_dma_write(d, rx_addr, buf, 2 * ETH_ALEN);
+ pci_dma_write(d, rx_addr + 2 * ETH_ALEN,
+ buf + 2 * ETH_ALEN + VLAN_HLEN,
+ size - 2 * ETH_ALEN);
} else {
pci_dma_write(d, rx_addr, buf, size);
}
@@ -1785,12 +1783,12 @@ static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size,
return;
}
- if (dot1q_buf && size >= ETHER_ADDR_LEN * 2) {
+ if (dot1q_buf && size >= ETH_ALEN * 2) {
iov = (struct iovec[3]) {
- { .iov_base = buf, .iov_len = ETHER_ADDR_LEN * 2 },
+ { .iov_base = buf, .iov_len = ETH_ALEN * 2 },
{ .iov_base = (void *) dot1q_buf, .iov_len = VLAN_HLEN },
- { .iov_base = buf + ETHER_ADDR_LEN * 2,
- .iov_len = size - ETHER_ADDR_LEN * 2 },
+ { .iov_base = buf + ETH_ALEN * 2,
+ .iov_len = size - ETH_ALEN * 2 },
};
memcpy(vlan_iov, iov, sizeof(vlan_iov));
@@ -1870,64 +1868,12 @@ static int rtl8139_transmit_one(RTL8139State *s, int descriptor)
}
/* structures and macros for task offloading */
-typedef struct ip_header
-{
- uint8_t ip_ver_len; /* version and header length */
- uint8_t ip_tos; /* type of service */
- uint16_t ip_len; /* total length */
- uint16_t ip_id; /* identification */
- uint16_t ip_off; /* fragment offset field */
- uint8_t ip_ttl; /* time to live */
- uint8_t ip_p; /* protocol */
- uint16_t ip_sum; /* checksum */
- uint32_t ip_src,ip_dst; /* source and dest address */
-} ip_header;
-
-#define IP_HEADER_VERSION_4 4
-#define IP_HEADER_VERSION(ip) ((ip->ip_ver_len >> 4)&0xf)
-#define IP_HEADER_LENGTH(ip) (((ip->ip_ver_len)&0xf) << 2)
-
-typedef struct tcp_header
-{
- uint16_t th_sport; /* source port */
- uint16_t th_dport; /* destination port */
- uint32_t th_seq; /* sequence number */
- uint32_t th_ack; /* acknowledgement number */
- uint16_t th_offset_flags; /* data offset, reserved 6 bits, TCP protocol flags */
- uint16_t th_win; /* window */
- uint16_t th_sum; /* checksum */
- uint16_t th_urp; /* urgent pointer */
-} tcp_header;
-
-typedef struct udp_header
-{
- uint16_t uh_sport; /* source port */
- uint16_t uh_dport; /* destination port */
- uint16_t uh_ulen; /* udp length */
- uint16_t uh_sum; /* udp checksum */
-} udp_header;
-
-typedef struct ip_pseudo_header
-{
- uint32_t ip_src;
- uint32_t ip_dst;
- uint8_t zeros;
- uint8_t ip_proto;
- uint16_t ip_payload;
-} ip_pseudo_header;
-
-#define IP_PROTO_TCP 6
-#define IP_PROTO_UDP 17
-
#define TCP_HEADER_DATA_OFFSET(tcp) (((be16_to_cpu(tcp->th_offset_flags) >> 12)&0xf) << 2)
#define TCP_FLAGS_ONLY(flags) ((flags)&0x3f)
#define TCP_HEADER_FLAGS(tcp) TCP_FLAGS_ONLY(be16_to_cpu(tcp->th_offset_flags))
#define TCP_HEADER_CLEAR_FLAGS(tcp, off) ((tcp)->th_offset_flags &= cpu_to_be16(~TCP_FLAGS_ONLY(off)))
-#define TCP_FLAG_FIN 0x01
-#define TCP_FLAG_PUSH 0x08
-
/* produces ones' complement sum of data */
static uint16_t ones_complement_sum(uint8_t *data, size_t len)
{
@@ -2136,7 +2082,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
bswap16(txdw1 & CP_TX_VLAN_TAG_MASK));
dot1q_buffer = (uint16_t *) dot1q_buffer_space;
- dot1q_buffer[0] = cpu_to_be16(ETH_P_8021Q);
+ dot1q_buffer[0] = cpu_to_be16(ETH_P_VLAN);
/* BE + le_to_cpu() + ~cpu_to_le()~ = BE */
dot1q_buffer[1] = cpu_to_le16(txdw1 & CP_TX_VLAN_TAG_MASK);
} else {
@@ -2153,12 +2099,12 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
DPRINTF("+++ C+ mode offloaded task checksum\n");
/* Large enough for Ethernet and IP headers? */
- if (saved_size < ETH_HLEN + sizeof(ip_header)) {
+ if (saved_size < ETH_HLEN + sizeof(struct ip_header)) {
goto skip_offload;
}
/* ip packet header */
- ip_header *ip = NULL;
+ struct ip_header *ip = NULL;
int hlen = 0;
uint8_t ip_protocol = 0;
uint16_t ip_data_len = 0;
@@ -2174,11 +2120,15 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
DPRINTF("+++ C+ mode has IP packet\n");
- /* not aligned */
+ /* Note on memory alignment: eth_payload_data is 16-bit aligned
+ * since saved_buffer is allocated with g_malloc() and ETH_HLEN is
+ * even. 32-bit accesses must use ldl/stl wrappers to avoid
+ * unaligned accesses.
+ */
eth_payload_data = saved_buffer + ETH_HLEN;
eth_payload_len = saved_size - ETH_HLEN;
- ip = (ip_header*)eth_payload_data;
+ ip = (struct ip_header*)eth_payload_data;
if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) {
DPRINTF("+++ C+ mode packet has bad IP version %d "
@@ -2187,8 +2137,8 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
goto skip_offload;
}
- hlen = IP_HEADER_LENGTH(ip);
- if (hlen < sizeof(ip_header) || hlen > eth_payload_len) {
+ hlen = IP_HDR_GET_LEN(ip);
+ if (hlen < sizeof(struct ip_header) || hlen > eth_payload_len) {
goto skip_offload;
}
@@ -2271,7 +2221,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
}
DPRINTF("+++ C+ mode TSO TCP seqno %08x\n",
- be32_to_cpu(p_tcp_hdr->th_seq));
+ ldl_be_p(&p_tcp_hdr->th_seq));
/* add 4 TCP pseudoheader fields */
/* copy IP source and destination fields */
@@ -2289,7 +2239,7 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
/* keep PUSH and FIN flags only for the last frame */
if (!is_last_frame)
{
- TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TCP_FLAG_PUSH|TCP_FLAG_FIN);
+ TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TH_PUSH | TH_FIN);
}
/* recalculate TCP checksum */
@@ -2327,7 +2277,8 @@ static int rtl8139_cplus_transmit_one(RTL8139State *s)
0, (uint8_t *) dot1q_buffer);
/* add transferred count to TCP sequence number */
- p_tcp_hdr->th_seq = cpu_to_be32(chunk_size + be32_to_cpu(p_tcp_hdr->th_seq));
+ stl_be_p(&p_tcp_hdr->th_seq,
+ chunk_size + ldl_be_p(&p_tcp_hdr->th_seq));
++send_count;
}
@@ -2883,8 +2834,7 @@ static void rtl8139_io_writew(void *opaque, uint8_t addr, uint32_t val)
static void rtl8139_set_next_tctr_time(RTL8139State *s)
{
- const uint64_t ns_per_period =
- muldiv64(0x100000000LL, get_ticks_per_sec(), PCI_FREQUENCY);
+ const uint64_t ns_per_period = (uint64_t)PCI_PERIOD << 32;
DPRINTF("entered rtl8139_set_next_tctr_time\n");
@@ -2902,7 +2852,7 @@ static void rtl8139_set_next_tctr_time(RTL8139State *s)
if (!s->TimerInt) {
timer_del(s->timer);
} else {
- uint64_t delta = muldiv64(s->TimerInt, get_ticks_per_sec(), PCI_FREQUENCY);
+ uint64_t delta = (uint64_t)s->TimerInt * PCI_PERIOD;
if (s->TCTR_base + delta <= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) {
delta += ns_per_period;
}
@@ -3176,8 +3126,8 @@ static uint32_t rtl8139_io_readl(void *opaque, uint8_t addr)
break;
case Timer:
- ret = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->TCTR_base,
- PCI_FREQUENCY, get_ticks_per_sec());
+ ret = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->TCTR_base) /
+ PCI_PERIOD;
DPRINTF("TCTR Timer read val=0x%08x\n", ret);
break;
@@ -3271,8 +3221,7 @@ static void rtl8139_pre_save(void *opaque)
int64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
/* for migration to older versions */
- s->TCTR = muldiv64(current_time - s->TCTR_base, PCI_FREQUENCY,
- get_ticks_per_sec());
+ s->TCTR = (current_time - s->TCTR_base) / PCI_PERIOD;
s->rtl8139_mmio_io_addr_dummy = 0;
}
@@ -3440,10 +3389,8 @@ static void pci_rtl8139_uninit(PCIDevice *dev)
{
RTL8139State *s = RTL8139(dev);
- if (s->cplus_txbuffer) {
- g_free(s->cplus_txbuffer);
- s->cplus_txbuffer = NULL;
- }
+ g_free(s->cplus_txbuffer);
+ s->cplus_txbuffer = NULL;
timer_del(s->timer);
timer_free(s->timer);
qemu_del_nic(s->nic);
diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c
index 74e06e6c7..c19cdd14d 100644
--- a/hw/net/smc91c111.c
+++ b/hw/net/smc91c111.c
@@ -124,6 +124,25 @@ static void smc91c111_update(smc91c111_state *s)
qemu_set_irq(s->irq, level);
}
+static int smc91c111_can_receive(smc91c111_state *s)
+{
+ if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST)) {
+ return 1;
+ }
+ if (s->allocated == (1 << NUM_PACKETS) - 1 ||
+ s->rx_fifo_len == NUM_PACKETS) {
+ return 0;
+ }
+ return 1;
+}
+
+static inline void smc91c111_flush_queued_packets(smc91c111_state *s)
+{
+ if (smc91c111_can_receive(s)) {
+ qemu_flush_queued_packets(qemu_get_queue(s->nic));
+ }
+}
+
/* Try to allocate a packet. Returns 0x80 on failure. */
static int smc91c111_allocate_packet(smc91c111_state *s)
{
@@ -164,6 +183,7 @@ static void smc91c111_pop_rx_fifo(smc91c111_state *s)
} else {
s->int_level &= ~INT_RCV;
}
+ smc91c111_flush_queued_packets(s);
smc91c111_update(s);
}
@@ -185,7 +205,7 @@ static void smc91c111_release_packet(smc91c111_state *s, int packet)
s->allocated &= ~(1 << packet);
if (s->tx_alloc == 0x80)
smc91c111_tx_alloc(s);
- qemu_flush_queued_packets(qemu_get_queue(s->nic));
+ smc91c111_flush_queued_packets(s);
}
/* Flush the TX FIFO. */
@@ -311,6 +331,7 @@ static void smc91c111_writeb(void *opaque, hwaddr offset,
if (s->rcr & RCR_SOFT_RST) {
smc91c111_reset(DEVICE(s));
}
+ smc91c111_flush_queued_packets(s);
return;
case 10: case 11: /* RPCR */
/* Ignored */
@@ -636,15 +657,11 @@ static uint32_t smc91c111_readl(void *opaque, hwaddr offset)
return val;
}
-static int smc91c111_can_receive(NetClientState *nc)
+static int smc91c111_can_receive_nc(NetClientState *nc)
{
smc91c111_state *s = qemu_get_nic_opaque(nc);
- if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
- return 1;
- if (s->allocated == (1 << NUM_PACKETS) - 1)
- return 0;
- return 1;
+ return smc91c111_can_receive(s);
}
static ssize_t smc91c111_receive(NetClientState *nc, const uint8_t *buf, size_t size)
@@ -739,7 +756,7 @@ static const MemoryRegionOps smc91c111_mem_ops = {
static NetClientInfo net_smc91c111_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = smc91c111_can_receive,
+ .can_receive = smc91c111_can_receive_nc,
.receive = smc91c111_receive,
};
diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
index 1d76b94c8..10e233a7f 100644
--- a/hw/net/vhost_net.c
+++ b/hw/net/vhost_net.c
@@ -77,13 +77,9 @@ static const int user_feature_bits[] = {
VIRTIO_NET_F_HOST_ECN,
VIRTIO_NET_F_HOST_UFO,
VIRTIO_NET_F_MRG_RXBUF,
- VIRTIO_NET_F_STATUS,
- VIRTIO_NET_F_CTRL_VQ,
- VIRTIO_NET_F_CTRL_RX,
- VIRTIO_NET_F_CTRL_VLAN,
- VIRTIO_NET_F_CTRL_RX_EXTRA,
- VIRTIO_NET_F_CTRL_MAC_ADDR,
- VIRTIO_NET_F_CTRL_GUEST_OFFLOADS,
+
+ /* This bit implies RARP isn't sent by QEMU out of band */
+ VIRTIO_NET_F_GUEST_ANNOUNCE,
VIRTIO_NET_F_MQ,
@@ -122,6 +118,11 @@ void vhost_net_ack_features(struct vhost_net *net, uint64_t features)
vhost_ack_features(&net->dev, vhost_net_get_feature_bits(net), features);
}
+uint64_t vhost_net_get_max_queues(VHostNetState *net)
+{
+ return net->dev.max_queues;
+}
+
static int vhost_net_get_fd(NetClientState *backend)
{
switch (backend->info->type) {
@@ -143,6 +144,11 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options)
fprintf(stderr, "vhost-net requires net backend to be setup\n");
goto fail;
}
+ net->nc = options->net_backend;
+
+ net->dev.max_queues = 1;
+ net->dev.nvqs = 2;
+ net->dev.vqs = net->vqs;
if (backend_kernel) {
r = vhost_net_get_fd(options->net_backend);
@@ -152,14 +158,15 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options)
net->dev.backend_features = qemu_has_vnet_hdr(options->net_backend)
? 0 : (1ULL << VHOST_NET_F_VIRTIO_NET_HDR);
net->backend = r;
+ net->dev.protocol_features = 0;
} else {
net->dev.backend_features = 0;
+ net->dev.protocol_features = 0;
net->backend = -1;
- }
- net->nc = options->net_backend;
- net->dev.nvqs = 2;
- net->dev.vqs = net->vqs;
+ /* vhost-user needs vq_index to initiate a specific queue pair */
+ net->dev.vq_index = net->nc->queue_index * net->dev.nvqs;
+ }
r = vhost_dev_init(&net->dev, options->opaque,
options->backend_type);
@@ -241,8 +248,7 @@ static int vhost_net_start_one(struct vhost_net *net,
file.fd = net->backend;
for (file.index = 0; file.index < net->dev.nvqs; ++file.index) {
const VhostOps *vhost_ops = net->dev.vhost_ops;
- r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND,
- &file);
+ r = vhost_ops->vhost_net_set_backend(&net->dev, &file);
if (r < 0) {
r = -errno;
goto fail;
@@ -255,8 +261,7 @@ fail:
if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) {
while (file.index-- > 0) {
const VhostOps *vhost_ops = net->dev.vhost_ops;
- int r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND,
- &file);
+ int r = vhost_ops->vhost_net_set_backend(&net->dev, &file);
assert(r >= 0);
}
}
@@ -278,15 +283,7 @@ static void vhost_net_stop_one(struct vhost_net *net,
if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) {
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_NET_SET_BACKEND,
- &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);
+ int r = vhost_ops->vhost_net_set_backend(&net->dev, &file);
assert(r >= 0);
}
}
@@ -303,21 +300,19 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs,
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev)));
VirtioBusState *vbus = VIRTIO_BUS(qbus);
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus);
- int r, e, i;
+ int r, e, i, j;
if (!k->set_guest_notifiers) {
error_report("binding does not support guest notifiers");
- r = -ENOSYS;
- goto err;
- }
-
- r = vhost_net_set_vnet_endian(dev, ncs[0].peer, true);
- if (r < 0) {
- goto err;
+ return -ENOSYS;
}
- for (i = 0; i < total_queues; i++) {
- vhost_net_set_vq_index(get_vhost_net(ncs[i].peer), i * 2);
+ for (j = 0; j < total_queues; j++) {
+ r = vhost_net_set_vnet_endian(dev, ncs[j].peer, true);
+ if (r < 0) {
+ goto err_endian;
+ }
+ vhost_net_set_vq_index(get_vhost_net(ncs[j].peer), j * 2);
}
r = k->set_guest_notifiers(qbus->parent, total_queues * 2, true);
@@ -346,8 +341,9 @@ err_start:
fflush(stderr);
}
err_endian:
- vhost_net_set_vnet_endian(dev, ncs[0].peer, false);
-err:
+ while (--j >= 0) {
+ vhost_net_set_vnet_endian(dev, ncs[j].peer, false);
+ }
return r;
}
@@ -379,6 +375,18 @@ void vhost_net_cleanup(struct vhost_net *net)
g_free(net);
}
+int vhost_net_notify_migration_done(struct vhost_net *net, char* mac_addr)
+{
+ const VhostOps *vhost_ops = net->dev.vhost_ops;
+ int r = -1;
+
+ if (vhost_ops->vhost_migration_done) {
+ r = vhost_ops->vhost_migration_done(&net->dev, mac_addr);
+ }
+
+ return r;
+}
+
bool vhost_net_virtqueue_pending(VHostNetState *net, int idx)
{
return vhost_virtqueue_pending(&net->dev, idx);
@@ -411,7 +419,25 @@ VHostNetState *get_vhost_net(NetClientState *nc)
return vhost_net;
}
+
+int vhost_set_vring_enable(NetClientState *nc, int enable)
+{
+ VHostNetState *net = get_vhost_net(nc);
+ const VhostOps *vhost_ops = net->dev.vhost_ops;
+
+ if (vhost_ops->vhost_set_vring_enable) {
+ return vhost_ops->vhost_set_vring_enable(&net->dev, enable);
+ }
+
+ return 0;
+}
+
#else
+uint64_t vhost_net_get_max_queues(VHostNetState *net)
+{
+ return 1;
+}
+
struct vhost_net *vhost_net_init(VhostNetOptions *options)
{
error_report("vhost-net support is not compiled in");
@@ -452,8 +478,18 @@ void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev,
{
}
+int vhost_net_notify_migration_done(struct vhost_net *net, char* mac_addr)
+{
+ return -1;
+}
+
VHostNetState *get_vhost_net(NetClientState *nc)
{
return 0;
}
+
+int vhost_set_vring_enable(NetClientState *nc, int enable)
+{
+ return 0;
+}
#endif
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 4aa93fb93..a877614e3 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -406,6 +406,10 @@ static int peer_attach(VirtIONet *n, int index)
return 0;
}
+ if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) {
+ vhost_set_vring_enable(nc->peer, 1);
+ }
+
if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
return 0;
}
@@ -421,6 +425,10 @@ static int peer_detach(VirtIONet *n, int index)
return 0;
}
+ if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) {
+ vhost_set_vring_enable(nc->peer, 0);
+ }
+
if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
return 0;
}
@@ -1118,7 +1126,7 @@ static void virtio_net_tx_complete(NetClientState *nc, ssize_t len)
virtqueue_push(q->tx_vq, &q->async_tx.elem, 0);
virtio_notify(vdev, q->tx_vq);
- q->async_tx.elem.out_num = q->async_tx.len = 0;
+ q->async_tx.elem.out_num = 0;
virtio_queue_set_notification(q->tx_vq, 1);
virtio_net_flush_tx(q);
@@ -1142,7 +1150,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
}
while (virtqueue_pop(q->tx_vq, &elem)) {
- ssize_t ret, len;
+ ssize_t ret;
unsigned int out_num = elem.out_num;
struct iovec *out_sg = &elem.out_sg[0];
struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1];
@@ -1190,18 +1198,14 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
out_sg = sg;
}
- len = n->guest_hdr_len;
-
ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index),
out_sg, out_num, virtio_net_tx_complete);
if (ret == 0) {
virtio_queue_set_notification(q->tx_vq, 0);
q->async_tx.elem = elem;
- q->async_tx.len = len;
return -EBUSY;
}
- len += ret;
drop:
virtqueue_push(q->tx_vq, &elem, 0);
virtio_notify(vdev, q->tx_vq);
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
index 2504425cf..2b4aad718 100644
--- a/hw/net/vmxnet3.c
+++ b/hw/net/vmxnet3.c
@@ -729,9 +729,7 @@ static void vmxnet3_process_tx_queue(VMXNET3State *s, int qidx)
}
if (txd.eop) {
- if (!s->skip_current_tx_pkt) {
- vmxnet_tx_pkt_parse(s->tx_pkt);
-
+ if (!s->skip_current_tx_pkt && vmxnet_tx_pkt_parse(s->tx_pkt)) {
if (s->needs_vlan) {
vmxnet_tx_pkt_setup_vlan_header(s->tx_pkt, s->tci);
}
@@ -927,9 +925,9 @@ static void vmxnet3_rx_need_csum_calculate(struct VmxnetRxPkt *pkt,
/* 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, "
+ VMW_PKPRN("packet len:%lu < csum_start(%d) + csum_offset(%d) + 2, "
"cannot calculate checksum",
- len, vhdr->csum_start, vhdr->csum_offset);
+ pkt_len, vhdr->csum_start, vhdr->csum_offset);
return;
}
@@ -1165,9 +1163,13 @@ vmxnet3_io_bar0_write(void *opaque, hwaddr addr,
static uint64_t
vmxnet3_io_bar0_read(void *opaque, hwaddr addr, unsigned size)
{
+ VMXNET3State *s = opaque;
+
if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_IMR,
VMXNET3_MAX_INTRS, VMXNET3_REG_ALIGN)) {
- g_assert_not_reached();
+ int l = VMW_MULTIREG_IDX_BY_ADDR(addr, VMXNET3_REG_IMR,
+ VMXNET3_REG_ALIGN);
+ return s->interrupt_states[l].is_masked;
}
VMW_CBPRN("BAR0 unknown read [%" PRIx64 "], size %d", addr, size);
@@ -1192,8 +1194,13 @@ static void vmxnet3_reset_mac(VMXNET3State *s)
static void vmxnet3_deactivate_device(VMXNET3State *s)
{
- VMW_CBPRN("Deactivating vmxnet3...");
- s->device_active = false;
+ if (s->device_active) {
+ VMW_CBPRN("Deactivating vmxnet3...");
+ vmxnet_tx_pkt_reset(s->tx_pkt);
+ vmxnet_tx_pkt_uninit(s->tx_pkt);
+ vmxnet_rx_pkt_uninit(s->rx_pkt);
+ s->device_active = false;
+ }
}
static void vmxnet3_reset(VMXNET3State *s)
@@ -1202,7 +1209,6 @@ static void vmxnet3_reset(VMXNET3State *s)
vmxnet3_deactivate_device(s);
vmxnet3_reset_interrupt_states(s);
- vmxnet_tx_pkt_reset(s->tx_pkt);
s->drv_shmem = 0;
s->tx_sop = true;
s->skip_current_tx_pkt = false;
@@ -1287,6 +1293,10 @@ static uint32_t vmxnet3_get_interrupt_config(VMXNET3State *s)
static void vmxnet3_fill_stats(VMXNET3State *s)
{
int i;
+
+ if (!s->device_active)
+ return;
+
for (i = 0; i < s->txq_num; i++) {
cpu_physical_memory_write(s->txq_descr[i].tx_stats_pa,
&s->txq_descr[i].txq_stats,
@@ -1425,6 +1435,12 @@ static void vmxnet3_activate_device(VMXNET3State *s)
return;
}
+ /* Verify if device is active */
+ if (s->device_active) {
+ VMW_CFPRN("Vmxnet3 device is active");
+ return;
+ }
+
vmxnet3_adjust_by_guest_type(s);
vmxnet3_update_features(s);
vmxnet3_update_pm_state(s);
@@ -1621,7 +1637,7 @@ static void vmxnet3_handle_command(VMXNET3State *s, uint64_t cmd)
break;
case VMXNET3_CMD_QUIESCE_DEV:
- VMW_CBPRN("Set: VMXNET3_CMD_QUIESCE_DEV - pause the device");
+ VMW_CBPRN("Set: VMXNET3_CMD_QUIESCE_DEV - deactivate the device");
vmxnet3_deactivate_device(s);
break;
@@ -1629,6 +1645,11 @@ static void vmxnet3_handle_command(VMXNET3State *s, uint64_t cmd)
VMW_CBPRN("Set: VMXNET3_CMD_GET_CONF_INTR - interrupt configuration");
break;
+ case VMXNET3_CMD_GET_ADAPTIVE_RING_INFO:
+ VMW_CBPRN("Set: VMXNET3_CMD_GET_ADAPTIVE_RING_INFO - "
+ "adaptive ring info flags");
+ break;
+
default:
VMW_CBPRN("Received unknown command: %" PRIx64, cmd);
break;
@@ -1668,6 +1689,10 @@ static uint64_t vmxnet3_get_command_status(VMXNET3State *s)
ret = vmxnet3_get_interrupt_config(s);
break;
+ case VMXNET3_CMD_GET_ADAPTIVE_RING_INFO:
+ ret = VMXNET3_DISABLE_ADAPTIVE_RING;
+ break;
+
default:
VMW_WRPRN("Received request for unknown command: %x", s->last_command);
ret = -1;
@@ -1726,7 +1751,7 @@ vmxnet3_io_bar1_write(void *opaque,
* shared address only after we get the high part
*/
if (val == 0) {
- s->device_active = false;
+ vmxnet3_deactivate_device(s);
}
s->temp_shared_guest_driver_memory = val;
s->drv_shmem = 0;
@@ -2000,16 +2025,13 @@ static bool vmxnet3_peer_has_vnet_hdr(VMXNET3State *s)
return true;
}
- VMW_WRPRN("Peer has no virtio extension. Task offloads will be emulated.");
return false;
}
static void vmxnet3_net_uninit(VMXNET3State *s)
{
g_free(s->mcast_list);
- vmxnet_tx_pkt_reset(s->tx_pkt);
- vmxnet_tx_pkt_uninit(s->tx_pkt);
- vmxnet_rx_pkt_uninit(s->rx_pkt);
+ vmxnet3_deactivate_device(s);
qemu_del_nic(s->nic);
}
diff --git a/hw/net/vmxnet3.h b/hw/net/vmxnet3.h
index f987d7126..f7006afe9 100644
--- a/hw/net/vmxnet3.h
+++ b/hw/net/vmxnet3.h
@@ -198,9 +198,13 @@ enum {
VMXNET3_CMD_GET_DID_LO, /* 0xF00D0005 */
VMXNET3_CMD_GET_DID_HI, /* 0xF00D0006 */
VMXNET3_CMD_GET_DEV_EXTRA_INFO, /* 0xF00D0007 */
- VMXNET3_CMD_GET_CONF_INTR /* 0xF00D0008 */
+ VMXNET3_CMD_GET_CONF_INTR, /* 0xF00D0008 */
+ VMXNET3_CMD_GET_ADAPTIVE_RING_INFO /* 0xF00D0009 */
};
+/* Adaptive Ring Info Flags */
+#define VMXNET3_DISABLE_ADAPTIVE_RING 1
+
/*
* Little Endian layout of bitfields -
* Byte 0 : 7.....len.....0
diff --git a/hw/net/vmxnet_tx_pkt.c b/hw/net/vmxnet_tx_pkt.c
index f7344c4cb..eb88ddf25 100644
--- a/hw/net/vmxnet_tx_pkt.c
+++ b/hw/net/vmxnet_tx_pkt.c
@@ -142,11 +142,24 @@ static bool vmxnet_tx_pkt_parse_headers(struct VmxnetTxPkt *pkt)
bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, 0, l2_hdr->iov_base,
ETH_MAX_L2_HDR_LEN);
- if (bytes_read < ETH_MAX_L2_HDR_LEN) {
+ if (bytes_read < sizeof(struct eth_header)) {
+ l2_hdr->iov_len = 0;
+ return false;
+ }
+
+ l2_hdr->iov_len = sizeof(struct eth_header);
+ switch (be16_to_cpu(PKT_GET_ETH_HDR(l2_hdr->iov_base)->h_proto)) {
+ case ETH_P_VLAN:
+ l2_hdr->iov_len += sizeof(struct vlan_header);
+ break;
+ case ETH_P_DVLAN:
+ l2_hdr->iov_len += 2 * sizeof(struct vlan_header);
+ break;
+ }
+
+ if (bytes_read < l2_hdr->iov_len) {
l2_hdr->iov_len = 0;
return false;
- } else {
- l2_hdr->iov_len = eth_get_l2_hdr_length(l2_hdr->iov_base);
}
l3_proto = eth_get_l3_proto(l2_hdr->iov_base, l2_hdr->iov_len);
diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c
index d7cbfc103..0da16b44f 100644
--- a/hw/net/xen_nic.c
+++ b/hw/net/xen_nic.c
@@ -24,7 +24,6 @@
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
-#include <signal.h>
#include <inttypes.h>
#include <fcntl.h>
#include <errno.h>
diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c
index 88481b78c..5a03b5d35 100644
--- a/hw/nvram/fw_cfg.c
+++ b/hw/nvram/fw_cfg.c
@@ -23,6 +23,7 @@
*/
#include "hw/hw.h"
#include "sysemu/sysemu.h"
+#include "sysemu/dma.h"
#include "hw/isa/isa.h"
#include "hw/nvram/fw_cfg.h"
#include "hw/sysbus.h"
@@ -30,7 +31,7 @@
#include "qemu/error-report.h"
#include "qemu/config-file.h"
-#define FW_CFG_SIZE 2
+#define FW_CFG_CTL_SIZE 2
#define FW_CFG_NAME "fw_cfg"
#define FW_CFG_PATH "/machine/" FW_CFG_NAME
@@ -42,6 +43,18 @@
#define FW_CFG_IO(obj) OBJECT_CHECK(FWCfgIoState, (obj), TYPE_FW_CFG_IO)
#define FW_CFG_MEM(obj) OBJECT_CHECK(FWCfgMemState, (obj), TYPE_FW_CFG_MEM)
+/* FW_CFG_VERSION bits */
+#define FW_CFG_VERSION 0x01
+#define FW_CFG_VERSION_DMA 0x02
+
+/* FW_CFG_DMA_CONTROL bits */
+#define FW_CFG_DMA_CTL_ERROR 0x01
+#define FW_CFG_DMA_CTL_READ 0x02
+#define FW_CFG_DMA_CTL_SKIP 0x04
+#define FW_CFG_DMA_CTL_SELECT 0x08
+
+#define FW_CFG_DMA_SIGNATURE 0x51454d5520434647ULL /* "QEMU CFG" */
+
typedef struct FWCfgEntry {
uint32_t len;
uint8_t *data;
@@ -59,6 +72,11 @@ struct FWCfgState {
uint16_t cur_entry;
uint32_t cur_offset;
Notifier machine_ready;
+
+ bool dma_enabled;
+ dma_addr_t dma_addr;
+ AddressSpace *dma_as;
+ MemoryRegion dma_iomem;
};
struct FWCfgIoState {
@@ -67,7 +85,7 @@ struct FWCfgIoState {
/*< public >*/
MemoryRegion comb_iomem;
- uint32_t iobase;
+ uint32_t iobase, dma_iobase;
};
struct FWCfgMemState {
@@ -187,9 +205,7 @@ static void fw_cfg_bootsplash(FWCfgState *s)
g_free(filename);
return;
}
- if (boot_splash_filedata != NULL) {
- g_free(boot_splash_filedata);
- }
+ g_free(boot_splash_filedata);
boot_splash_filedata = (uint8_t *)file_data;
boot_splash_filedata_size = file_size;
@@ -254,7 +270,8 @@ static int fw_cfg_select(FWCfgState *s, uint16_t key)
static uint8_t fw_cfg_read(FWCfgState *s)
{
int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
- FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
+ FWCfgEntry *e = (s->cur_entry == FW_CFG_INVALID) ? NULL :
+ &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
uint8_t ret;
if (s->cur_entry == FW_CFG_INVALID || !e->data || s->cur_offset >= e->len)
@@ -294,6 +311,130 @@ static void fw_cfg_data_mem_write(void *opaque, hwaddr addr,
} while (i);
}
+static void fw_cfg_dma_transfer(FWCfgState *s)
+{
+ dma_addr_t len;
+ FWCfgDmaAccess dma;
+ int arch;
+ FWCfgEntry *e;
+ int read;
+ dma_addr_t dma_addr;
+
+ /* Reset the address before the next access */
+ dma_addr = s->dma_addr;
+ s->dma_addr = 0;
+
+ if (dma_memory_read(s->dma_as, dma_addr, &dma, sizeof(dma))) {
+ stl_be_dma(s->dma_as, dma_addr + offsetof(FWCfgDmaAccess, control),
+ FW_CFG_DMA_CTL_ERROR);
+ return;
+ }
+
+ dma.address = be64_to_cpu(dma.address);
+ dma.length = be32_to_cpu(dma.length);
+ dma.control = be32_to_cpu(dma.control);
+
+ if (dma.control & FW_CFG_DMA_CTL_SELECT) {
+ fw_cfg_select(s, dma.control >> 16);
+ }
+
+ arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
+ e = (s->cur_entry == FW_CFG_INVALID) ? NULL :
+ &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
+
+ if (dma.control & FW_CFG_DMA_CTL_READ) {
+ read = 1;
+ } else if (dma.control & FW_CFG_DMA_CTL_SKIP) {
+ read = 0;
+ } else {
+ dma.length = 0;
+ }
+
+ dma.control = 0;
+
+ while (dma.length > 0 && !(dma.control & FW_CFG_DMA_CTL_ERROR)) {
+ if (s->cur_entry == FW_CFG_INVALID || !e->data ||
+ s->cur_offset >= e->len) {
+ len = dma.length;
+
+ /* If the access is not a read access, it will be a skip access,
+ * tested before.
+ */
+ if (read) {
+ if (dma_memory_set(s->dma_as, dma.address, 0, len)) {
+ dma.control |= FW_CFG_DMA_CTL_ERROR;
+ }
+ }
+
+ } else {
+ if (dma.length <= (e->len - s->cur_offset)) {
+ len = dma.length;
+ } else {
+ len = (e->len - s->cur_offset);
+ }
+
+ if (e->read_callback) {
+ e->read_callback(e->callback_opaque, s->cur_offset);
+ }
+
+ /* If the access is not a read access, it will be a skip access,
+ * tested before.
+ */
+ if (read) {
+ if (dma_memory_write(s->dma_as, dma.address,
+ &e->data[s->cur_offset], len)) {
+ dma.control |= FW_CFG_DMA_CTL_ERROR;
+ }
+ }
+
+ s->cur_offset += len;
+ }
+
+ dma.address += len;
+ dma.length -= len;
+
+ }
+
+ stl_be_dma(s->dma_as, dma_addr + offsetof(FWCfgDmaAccess, control),
+ dma.control);
+
+ trace_fw_cfg_read(s, 0);
+}
+
+static uint64_t fw_cfg_dma_mem_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ /* Return a signature value (and handle various read sizes) */
+ return extract64(FW_CFG_DMA_SIGNATURE, (8 - addr - size) * 8, size * 8);
+}
+
+static void fw_cfg_dma_mem_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ FWCfgState *s = opaque;
+
+ if (size == 4) {
+ if (addr == 0) {
+ /* FWCfgDmaAccess high address */
+ s->dma_addr = value << 32;
+ } else if (addr == 4) {
+ /* FWCfgDmaAccess low address */
+ s->dma_addr |= value;
+ fw_cfg_dma_transfer(s);
+ }
+ } else if (size == 8 && addr == 0) {
+ s->dma_addr = value;
+ fw_cfg_dma_transfer(s);
+ }
+}
+
+static bool fw_cfg_dma_mem_valid(void *opaque, hwaddr addr,
+ unsigned size, bool is_write)
+{
+ return !is_write || ((size == 4 && (addr == 0 || addr == 4)) ||
+ (size == 8 && addr == 0));
+}
+
static bool fw_cfg_data_mem_valid(void *opaque, hwaddr addr,
unsigned size, bool is_write)
{
@@ -361,6 +502,15 @@ static const MemoryRegionOps fw_cfg_comb_mem_ops = {
.valid.accepts = fw_cfg_comb_valid,
};
+static const MemoryRegionOps fw_cfg_dma_mem_ops = {
+ .read = fw_cfg_dma_mem_read,
+ .write = fw_cfg_dma_mem_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+ .valid.accepts = fw_cfg_dma_mem_valid,
+ .valid.max_access_size = 8,
+ .impl.max_access_size = 8,
+};
+
static void fw_cfg_reset(DeviceState *d)
{
FWCfgState *s = FW_CFG(d);
@@ -401,6 +551,22 @@ static bool is_version_1(void *opaque, int version_id)
return version_id == 1;
}
+static bool fw_cfg_dma_enabled(void *opaque)
+{
+ FWCfgState *s = opaque;
+
+ return s->dma_enabled;
+}
+
+static const VMStateDescription vmstate_fw_cfg_dma = {
+ .name = "fw_cfg/dma",
+ .needed = fw_cfg_dma_enabled,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(dma_addr, FWCfgState),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
static const VMStateDescription vmstate_fw_cfg = {
.name = "fw_cfg",
.version_id = 2,
@@ -410,6 +576,10 @@ static const VMStateDescription vmstate_fw_cfg = {
VMSTATE_UINT16_HACK(cur_offset, FWCfgState, is_version_1),
VMSTATE_UINT32_V(cur_offset, FWCfgState, 2),
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_fw_cfg_dma,
+ NULL,
}
};
@@ -595,7 +765,6 @@ static void fw_cfg_init1(DeviceState *dev)
qdev_init_nofail(dev);
fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (char *)"QEMU", 4);
- fw_cfg_add_i32(s, FW_CFG_ID, 1);
fw_cfg_add_bytes(s, FW_CFG_UUID, qemu_uuid, 16);
fw_cfg_add_i16(s, FW_CFG_NOGRAPHIC, (uint16_t)(display_type == DT_NOGRAPHIC));
fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus);
@@ -607,25 +776,57 @@ static void fw_cfg_init1(DeviceState *dev)
qemu_add_machine_init_done_notifier(&s->machine_ready);
}
-FWCfgState *fw_cfg_init_io(uint32_t iobase)
+FWCfgState *fw_cfg_init_io_dma(uint32_t iobase, uint32_t dma_iobase,
+ AddressSpace *dma_as)
{
DeviceState *dev;
+ FWCfgState *s;
+ uint32_t version = FW_CFG_VERSION;
+ bool dma_requested = dma_iobase && dma_as;
dev = qdev_create(NULL, TYPE_FW_CFG_IO);
qdev_prop_set_uint32(dev, "iobase", iobase);
+ qdev_prop_set_uint32(dev, "dma_iobase", dma_iobase);
+ if (!dma_requested) {
+ qdev_prop_set_bit(dev, "dma_enabled", false);
+ }
+
fw_cfg_init1(dev);
+ s = FW_CFG(dev);
+
+ if (s->dma_enabled) {
+ /* 64 bits for the address field */
+ s->dma_as = dma_as;
+ s->dma_addr = 0;
+
+ version |= FW_CFG_VERSION_DMA;
+ }
- return FW_CFG(dev);
+ fw_cfg_add_i32(s, FW_CFG_ID, version);
+
+ return s;
+}
+
+FWCfgState *fw_cfg_init_io(uint32_t iobase)
+{
+ return fw_cfg_init_io_dma(iobase, 0, NULL);
}
-FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, hwaddr data_addr,
- uint32_t data_width)
+FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr,
+ hwaddr data_addr, uint32_t data_width,
+ hwaddr dma_addr, AddressSpace *dma_as)
{
DeviceState *dev;
SysBusDevice *sbd;
+ FWCfgState *s;
+ uint32_t version = FW_CFG_VERSION;
+ bool dma_requested = dma_addr && dma_as;
dev = qdev_create(NULL, TYPE_FW_CFG_MEM);
qdev_prop_set_uint32(dev, "data_width", data_width);
+ if (!dma_requested) {
+ qdev_prop_set_bit(dev, "dma_enabled", false);
+ }
fw_cfg_init1(dev);
@@ -633,13 +834,25 @@ FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, hwaddr data_addr,
sysbus_mmio_map(sbd, 0, ctl_addr);
sysbus_mmio_map(sbd, 1, data_addr);
- return FW_CFG(dev);
+ s = FW_CFG(dev);
+
+ if (s->dma_enabled) {
+ s->dma_as = dma_as;
+ s->dma_addr = 0;
+ sysbus_mmio_map(sbd, 2, dma_addr);
+ version |= FW_CFG_VERSION_DMA;
+ }
+
+ fw_cfg_add_i32(s, FW_CFG_ID, version);
+
+ return s;
}
FWCfgState *fw_cfg_init_mem(hwaddr ctl_addr, hwaddr data_addr)
{
return fw_cfg_init_mem_wide(ctl_addr, data_addr,
- fw_cfg_data_mem_ops.valid.max_access_size);
+ fw_cfg_data_mem_ops.valid.max_access_size,
+ 0, NULL);
}
@@ -666,6 +879,9 @@ static const TypeInfo fw_cfg_info = {
static Property fw_cfg_io_properties[] = {
DEFINE_PROP_UINT32("iobase", FWCfgIoState, iobase, -1),
+ DEFINE_PROP_UINT32("dma_iobase", FWCfgIoState, dma_iobase, -1),
+ DEFINE_PROP_BOOL("dma_enabled", FWCfgIoState, parent_obj.dma_enabled,
+ true),
DEFINE_PROP_END_OF_LIST(),
};
@@ -675,8 +891,15 @@ static void fw_cfg_io_realize(DeviceState *dev, Error **errp)
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
memory_region_init_io(&s->comb_iomem, OBJECT(s), &fw_cfg_comb_mem_ops,
- FW_CFG(s), "fwcfg", FW_CFG_SIZE);
+ FW_CFG(s), "fwcfg", FW_CFG_CTL_SIZE);
sysbus_add_io(sbd, s->iobase, &s->comb_iomem);
+
+ if (FW_CFG(s)->dma_enabled) {
+ memory_region_init_io(&FW_CFG(s)->dma_iomem, OBJECT(s),
+ &fw_cfg_dma_mem_ops, FW_CFG(s), "fwcfg.dma",
+ sizeof(dma_addr_t));
+ sysbus_add_io(sbd, s->dma_iobase, &FW_CFG(s)->dma_iomem);
+ }
}
static void fw_cfg_io_class_init(ObjectClass *klass, void *data)
@@ -697,6 +920,8 @@ static const TypeInfo fw_cfg_io_info = {
static Property fw_cfg_mem_properties[] = {
DEFINE_PROP_UINT32("data_width", FWCfgMemState, data_width, -1),
+ DEFINE_PROP_BOOL("dma_enabled", FWCfgMemState, parent_obj.dma_enabled,
+ true),
DEFINE_PROP_END_OF_LIST(),
};
@@ -707,7 +932,7 @@ static void fw_cfg_mem_realize(DeviceState *dev, Error **errp)
const MemoryRegionOps *data_ops = &fw_cfg_data_mem_ops;
memory_region_init_io(&s->ctl_iomem, OBJECT(s), &fw_cfg_ctl_mem_ops,
- FW_CFG(s), "fwcfg.ctl", FW_CFG_SIZE);
+ FW_CFG(s), "fwcfg.ctl", FW_CFG_CTL_SIZE);
sysbus_init_mmio(sbd, &s->ctl_iomem);
if (s->data_width > data_ops->valid.max_access_size) {
@@ -725,6 +950,13 @@ static void fw_cfg_mem_realize(DeviceState *dev, Error **errp)
memory_region_init_io(&s->data_iomem, OBJECT(s), data_ops, FW_CFG(s),
"fwcfg.data", data_ops->valid.max_access_size);
sysbus_init_mmio(sbd, &s->data_iomem);
+
+ if (FW_CFG(s)->dma_enabled) {
+ memory_region_init_io(&FW_CFG(s)->dma_iomem, OBJECT(s),
+ &fw_cfg_dma_mem_ops, FW_CFG(s), "fwcfg.dma",
+ sizeof(dma_addr_t));
+ sysbus_init_mmio(sbd, &FW_CFG(s)->dma_iomem);
+ }
}
static void fw_cfg_mem_class_init(ObjectClass *klass, void *data)
diff --git a/hw/nvram/mac_nvram.c b/hw/nvram/mac_nvram.c
index d35f8a312..9f165664c 100644
--- a/hw/nvram/mac_nvram.c
+++ b/hw/nvram/mac_nvram.c
@@ -123,6 +123,7 @@ static void macio_nvram_class_init(ObjectClass *oc, void *data)
dc->reset = macio_nvram_reset;
dc->vmsd = &vmstate_macio_nvram;
dc->props = macio_nvram_properties;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
static const TypeInfo macio_nvram_type_info = {
diff --git a/hw/openrisc/cputimer.c b/hw/openrisc/cputimer.c
index 9c5494510..560cb914c 100644
--- a/hw/openrisc/cputimer.c
+++ b/hw/openrisc/cputimer.c
@@ -22,7 +22,7 @@
#include "hw/hw.h"
#include "qemu/timer.h"
-#define TIMER_FREQ (20 * 1000 * 1000) /* 20MHz */
+#define TIMER_PERIOD 50 /* 50 ns period for 20 MHz timer */
/* The time when TTCR changes */
static uint64_t last_clk;
@@ -36,8 +36,7 @@ void cpu_openrisc_count_update(OpenRISCCPU *cpu)
return;
}
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- cpu->env.ttcr += (uint32_t)muldiv64(now - last_clk, TIMER_FREQ,
- get_ticks_per_sec());
+ cpu->env.ttcr += (uint32_t)((now - last_clk) / TIMER_PERIOD);
last_clk = now;
}
@@ -59,7 +58,7 @@ void cpu_openrisc_timer_update(OpenRISCCPU *cpu)
} else {
wait = (cpu->env.ttmr & TTMR_TP) - (cpu->env.ttcr & TTMR_TP);
}
- next = now + muldiv64(wait, get_ticks_per_sec(), TIMER_FREQ);
+ next = now + (uint64_t)wait * TIMER_PERIOD;
timer_mod(cpu->env.timer, next);
}
diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c
index 1da0657dd..be6c9b562 100644
--- a/hw/openrisc/openrisc_sim.c
+++ b/hw/openrisc/openrisc_sim.c
@@ -68,7 +68,7 @@ static void cpu_openrisc_load_kernel(ram_addr_t ram_size,
if (kernel_filename && !qtest_enabled()) {
kernel_size = load_elf(kernel_filename, NULL, NULL,
- &elf_entry, NULL, NULL, 1, ELF_MACHINE, 1);
+ &elf_entry, NULL, NULL, 1, EM_OPENRISC, 1);
entry = elf_entry;
if (kernel_size < 0) {
kernel_size = load_uimage(kernel_filename,
@@ -114,7 +114,7 @@ static void openrisc_sim_init(MachineState *machine)
}
ram = g_malloc(sizeof(*ram));
- memory_region_init_ram(ram, NULL, "openrisc.ram", ram_size, &error_abort);
+ memory_region_init_ram(ram, NULL, "openrisc.ram", ram_size, &error_fatal);
vmstate_register_ram_global(ram);
memory_region_add_subregion(get_system_memory(), 0, ram);
@@ -132,17 +132,12 @@ static void openrisc_sim_init(MachineState *machine)
cpu_openrisc_load_kernel(ram_size, kernel_filename, cpu);
}
-static QEMUMachine openrisc_sim_machine = {
- .name = "or32-sim",
- .desc = "or32 simulation",
- .init = openrisc_sim_init,
- .max_cpus = 1,
- .is_default = 1,
-};
-
-static void openrisc_sim_machine_init(void)
+static void openrisc_sim_machine_init(MachineClass *mc)
{
- qemu_register_machine(&openrisc_sim_machine);
+ mc->desc = "or32 simulation";
+ mc->init = openrisc_sim_init;
+ mc->max_cpus = 1;
+ mc->is_default = 1;
}
-machine_init(openrisc_sim_machine_init);
+DEFINE_MACHINE("or32-sim", openrisc_sim_machine_init)
diff --git a/hw/pci-host/bonito.c b/hw/pci-host/bonito.c
index 3a731fe18..4139a2c46 100644
--- a/hw/pci-host/bonito.c
+++ b/hw/pci-host/bonito.c
@@ -355,6 +355,10 @@ static uint64_t bonito_ldma_readl(void *opaque, hwaddr addr,
uint32_t val;
PCIBonitoState *s = opaque;
+ if (addr >= sizeof(s->bonldma)) {
+ return 0;
+ }
+
val = ((uint32_t *)(&s->bonldma))[addr/sizeof(uint32_t)];
return val;
@@ -365,6 +369,10 @@ static void bonito_ldma_writel(void *opaque, hwaddr addr,
{
PCIBonitoState *s = opaque;
+ if (addr >= sizeof(s->bonldma)) {
+ return;
+ }
+
((uint32_t *)(&s->bonldma))[addr/sizeof(uint32_t)] = val & 0xffffffff;
}
@@ -384,6 +392,10 @@ static uint64_t bonito_cop_readl(void *opaque, hwaddr addr,
uint32_t val;
PCIBonitoState *s = opaque;
+ if (addr >= sizeof(s->boncop)) {
+ return 0;
+ }
+
val = ((uint32_t *)(&s->boncop))[addr/sizeof(uint32_t)];
return val;
@@ -394,6 +406,10 @@ static void bonito_cop_writel(void *opaque, hwaddr addr,
{
PCIBonitoState *s = opaque;
+ if (addr >= sizeof(s->boncop)) {
+ return;
+ }
+
((uint32_t *)(&s->boncop))[addr/sizeof(uint32_t)] = val & 0xffffffff;
}
diff --git a/hw/pci-host/grackle.c b/hw/pci-host/grackle.c
index bfe707a1a..ea31b72e7 100644
--- a/hw/pci-host/grackle.c
+++ b/hw/pci-host/grackle.c
@@ -146,8 +146,10 @@ static const TypeInfo grackle_pci_info = {
static void pci_grackle_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
k->init = pci_grackle_init_device;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
static const TypeInfo grackle_pci_host_info = {
diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c
index ad55f9966..715208b22 100644
--- a/hw/pci-host/piix.c
+++ b/hw/pci-host/piix.c
@@ -34,13 +34,13 @@
#include "sysemu/sysemu.h"
#include "hw/i386/ioapic.h"
#include "qapi/visitor.h"
+#include "qemu/error-report.h"
/*
* I440FX chipset data sheet.
* http://download.intel.com/design/chipsets/datashts/29054901.pdf
*/
-#define TYPE_I440FX_PCI_HOST_BRIDGE "i440FX-pcihost"
#define I440FX_PCI_HOST_BRIDGE(obj) \
OBJECT_CHECK(I440FXState, (obj), TYPE_I440FX_PCI_HOST_BRIDGE)
@@ -95,7 +95,6 @@ typedef struct PIIX3State {
#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)
@@ -117,6 +116,11 @@ struct PCII440FXState {
#define I440FX_PAM_SIZE 7
#define I440FX_SMRAM 0x72
+/* Older coreboot versions (4.0 and older) read a config register that doesn't
+ * exist in real hardware, to get the RAM size from QEMU.
+ */
+#define I440FX_COREBOOT_RAM_SIZE 0x57
+
static void piix3_set_irq(void *opaque, int pirq, int level);
static PCIINTxRoute piix3_route_intx_pin_to_irq(void *opaque, int pci_intx);
static void piix3_write_config_xen(PCIDevice *dev,
@@ -298,9 +302,14 @@ static void i440fx_pcihost_realize(DeviceState *dev, Error **errp)
static void i440fx_realize(PCIDevice *dev, Error **errp)
{
dev->config[I440FX_SMRAM] = 0x02;
+
+ if (object_property_get_bool(qdev_get_machine(), "iommu", NULL)) {
+ error_report("warning: i440fx doesn't support emulated iommu");
+ }
}
-PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
+PCIBus *i440fx_init(const char *host_type, const char *pci_type,
+ PCII440FXState **pi440fx_state,
int *piix3_devfn,
ISABus **isa_bus, qemu_irq *pic,
MemoryRegion *address_space_mem,
@@ -320,7 +329,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
unsigned i;
I440FXState *i440fx;
- dev = qdev_create(NULL, TYPE_I440FX_PCI_HOST_BRIDGE);
+ dev = qdev_create(NULL, host_type);
s = PCI_HOST_BRIDGE(dev);
b = pci_bus_new(dev, NULL, pci_address_space,
address_space_io, 0, TYPE_PCI_BUS);
@@ -328,7 +337,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
object_property_add_child(qdev_get_machine(), "i440fx", OBJECT(dev), NULL);
qdev_init_nofail(dev);
- d = pci_create_simple(b, 0, TYPE_I440FX_PCI_DEVICE);
+ d = pci_create_simple(b, 0, pci_type);
*pi440fx_state = I440FX_PCI_DEVICE(d);
f = *pi440fx_state;
f->system_memory = address_space_mem;
@@ -394,7 +403,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
if (ram_size > 255) {
ram_size = 255;
}
- d->config[0x57] = ram_size;
+ d->config[I440FX_COREBOOT_RAM_SIZE] = ram_size;
i440fx_update_memory_mappings(f);
@@ -735,6 +744,93 @@ static const TypeInfo i440fx_info = {
.class_init = i440fx_class_init,
};
+/* IGD Passthrough Host Bridge. */
+typedef struct {
+ uint8_t offset;
+ uint8_t len;
+} IGDHostInfo;
+
+/* Here we just expose minimal host bridge offset subset. */
+static const IGDHostInfo igd_host_bridge_infos[] = {
+ {0x08, 2}, /* revision id */
+ {0x2c, 2}, /* sybsystem vendor id */
+ {0x2e, 2}, /* sybsystem id */
+ {0x50, 2}, /* SNB: processor graphics control register */
+ {0x52, 2}, /* processor graphics control register */
+ {0xa4, 4}, /* SNB: graphics base of stolen memory */
+ {0xa8, 4}, /* SNB: base of GTT stolen memory */
+};
+
+static int host_pci_config_read(int pos, int len, uint32_t val)
+{
+ char path[PATH_MAX];
+ int config_fd;
+ ssize_t size = sizeof(path);
+ /* Access real host bridge. */
+ int rc = snprintf(path, size, "/sys/bus/pci/devices/%04x:%02x:%02x.%d/%s",
+ 0, 0, 0, 0, "config");
+ int ret = 0;
+
+ if (rc >= size || rc < 0) {
+ return -ENODEV;
+ }
+
+ config_fd = open(path, O_RDWR);
+ if (config_fd < 0) {
+ return -ENODEV;
+ }
+
+ if (lseek(config_fd, pos, SEEK_SET) != pos) {
+ ret = -errno;
+ goto out;
+ }
+ do {
+ rc = read(config_fd, (uint8_t *)&val, len);
+ } while (rc < 0 && (errno == EINTR || errno == EAGAIN));
+ if (rc != len) {
+ ret = -errno;
+ }
+out:
+ close(config_fd);
+ return ret;
+}
+
+static int igd_pt_i440fx_initfn(struct PCIDevice *pci_dev)
+{
+ uint32_t val = 0;
+ int rc, i, num;
+ int pos, len;
+
+ num = ARRAY_SIZE(igd_host_bridge_infos);
+ for (i = 0; i < num; i++) {
+ pos = igd_host_bridge_infos[i].offset;
+ len = igd_host_bridge_infos[i].len;
+ rc = host_pci_config_read(pos, len, val);
+ if (rc) {
+ return -ENODEV;
+ }
+ pci_default_write_config(pci_dev, pos, val, len);
+ }
+
+ return 0;
+}
+
+static void igd_passthrough_i440fx_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->init = igd_pt_i440fx_initfn;
+ dc->desc = "IGD Passthrough Host bridge";
+}
+
+static const TypeInfo igd_passthrough_i440fx_info = {
+ .name = TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE,
+ .parent = TYPE_I440FX_PCI_DEVICE,
+ .instance_size = sizeof(PCII440FXState),
+ .class_init = igd_passthrough_i440fx_class_init,
+};
+
static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge,
PCIBus *rootbus)
{
@@ -776,6 +872,7 @@ static const TypeInfo i440fx_pcihost_info = {
static void i440fx_register_types(void)
{
type_register_static(&i440fx_info);
+ type_register_static(&igd_passthrough_i440fx_info);
type_register_static(&piix3_pci_type_info);
type_register_static(&piix3_info);
type_register_static(&piix3_xen_info);
diff --git a/hw/pci-host/ppce500.c b/hw/pci-host/ppce500.c
index 613ba73c6..50add34e5 100644
--- a/hw/pci-host/ppce500.c
+++ b/hw/pci-host/ppce500.c
@@ -140,7 +140,7 @@ static uint64_t pci_reg_read4(void *opaque, hwaddr addr,
case PPCE500_PCI_OW3:
case PPCE500_PCI_OW4:
idx = (addr >> 5) & 0x7;
- switch (addr & 0xC) {
+ switch (addr & 0x1F) {
case PCI_POTAR:
value = pci->pob[idx].potar;
break;
@@ -162,7 +162,7 @@ static uint64_t pci_reg_read4(void *opaque, hwaddr addr,
case PPCE500_PCI_IW2:
case PPCE500_PCI_IW1:
idx = ((addr >> 5) & 0x3) - 1;
- switch (addr & 0xC) {
+ switch (addr & 0x1F) {
case PCI_PITAR:
value = pci->pib[idx].pitar;
break;
diff --git a/hw/pci-host/prep.c b/hw/pci-host/prep.c
index c63f45d21..da88cb335 100644
--- a/hw/pci-host/prep.c
+++ b/hw/pci-host/prep.c
@@ -302,7 +302,7 @@ static void raven_realize(PCIDevice *d, Error **errp)
d->config[0x34] = 0x00; // capabilities_pointer
memory_region_init_ram(&s->bios, OBJECT(s), "bios", BIOS_SIZE,
- &error_abort);
+ &error_fatal);
memory_region_set_readonly(&s->bios, true);
memory_region_add_subregion(get_system_memory(), (uint32_t)(-BIOS_SIZE),
&s->bios);
@@ -328,9 +328,7 @@ static void raven_realize(PCIDevice *d, Error **errp)
if (bios_size < 0 || bios_size > BIOS_SIZE) {
hw_error("qemu: could not load bios image '%s'\n", s->bios_name);
}
- if (filename) {
- g_free(filename);
- }
+ g_free(filename);
}
}
diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c
index bd7409456..1fb470758 100644
--- a/hw/pci-host/q35.c
+++ b/hw/pci-host/q35.c
@@ -426,31 +426,12 @@ static void mch_reset(DeviceState *qdev)
static AddressSpace *q35_host_dma_iommu(PCIBus *bus, void *opaque, int devfn)
{
IntelIOMMUState *s = opaque;
- VTDAddressSpace **pvtd_as;
- int bus_num = pci_bus_num(bus);
+ VTDAddressSpace *vtd_as;
- assert(0 <= bus_num && bus_num <= VTD_PCI_BUS_MAX);
assert(0 <= devfn && devfn <= VTD_PCI_DEVFN_MAX);
- pvtd_as = s->address_spaces[bus_num];
- if (!pvtd_as) {
- /* No corresponding free() */
- pvtd_as = g_malloc0(sizeof(VTDAddressSpace *) * VTD_PCI_DEVFN_MAX);
- s->address_spaces[bus_num] = pvtd_as;
- }
- if (!pvtd_as[devfn]) {
- pvtd_as[devfn] = g_malloc0(sizeof(VTDAddressSpace));
-
- pvtd_as[devfn]->bus_num = (uint8_t)bus_num;
- pvtd_as[devfn]->devfn = (uint8_t)devfn;
- pvtd_as[devfn]->iommu_state = s;
- pvtd_as[devfn]->context_cache_entry.context_cache_gen = 0;
- memory_region_init_iommu(&pvtd_as[devfn]->iommu, OBJECT(s),
- &s->iommu_ops, "intel_iommu", UINT64_MAX);
- address_space_init(&pvtd_as[devfn]->as,
- &pvtd_as[devfn]->iommu, "intel_iommu");
- }
- return &pvtd_as[devfn]->as;
+ vtd_as = vtd_find_add_as(s, bus, devfn);
+ return &vtd_as->as;
}
static void mch_init_dmar(MCHPCIState *mch)
@@ -525,7 +506,7 @@ static void mch_realize(PCIDevice *d, Error **errp)
PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, PAM_EXPAN_SIZE);
}
/* Intel IOMMU (VT-d) */
- if (machine_iommu(current_machine)) {
+ if (object_property_get_bool(qdev_get_machine(), "iommu", NULL)) {
mch_init_dmar(mch);
}
}
diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c
index f0144eb7b..215b64ffe 100644
--- a/hw/pci-host/uninorth.c
+++ b/hw/pci-host/uninorth.c
@@ -446,8 +446,10 @@ static const TypeInfo unin_internal_pci_host_info = {
static void pci_unin_main_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
sbc->init = pci_unin_main_init_device;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
static const TypeInfo pci_unin_main_info = {
@@ -460,8 +462,10 @@ static const TypeInfo pci_unin_main_info = {
static void pci_u3_agp_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
sbc->init = pci_u3_agp_init_device;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
static const TypeInfo pci_u3_agp_info = {
@@ -474,8 +478,10 @@ static const TypeInfo pci_u3_agp_info = {
static void pci_unin_agp_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
sbc->init = pci_unin_agp_init_device;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
static const TypeInfo pci_unin_agp_info = {
@@ -488,8 +494,10 @@ static const TypeInfo pci_unin_agp_info = {
static void pci_unin_internal_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
sbc->init = pci_unin_internal_init_device;
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
static const TypeInfo pci_unin_internal_info = {
diff --git a/hw/pci/msi.c b/hw/pci/msi.c
index f9c048442..c1dd5318c 100644
--- a/hw/pci/msi.c
+++ b/hw/pci/msi.c
@@ -294,7 +294,7 @@ void msi_send_message(PCIDevice *dev, MSIMessage msg)
{
MemTxAttrs attrs = {};
- attrs.stream_id = (pci_bus_num(dev->bus) << 8) | dev->devfn;
+ attrs.requester_id = pci_requester_id(dev);
address_space_stl_le(&dev->bus_master_as, msg.address, msg.data,
attrs, NULL);
}
diff --git a/hw/pci/msix.c b/hw/pci/msix.c
index 7716bf364..64c93d83d 100644
--- a/hw/pci/msix.c
+++ b/hw/pci/msix.c
@@ -200,8 +200,14 @@ static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr,
return pci_get_long(dev->msix_pba + addr);
}
+static void msix_pba_mmio_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+}
+
static const MemoryRegionOps msix_pba_mmio_ops = {
.read = msix_pba_mmio_read,
+ .write = msix_pba_mmio_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 4,
@@ -314,9 +320,7 @@ int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries,
bar_size = bar_pba_offset + bar_pba_size;
}
- if (bar_size & (bar_size - 1)) {
- bar_size = 1 << qemu_fls(bar_size);
- }
+ bar_size = pow2ceil(bar_size);
name = g_strdup_printf("%s-msix", dev->name);
memory_region_init(&dev->msix_exclusive_bar, OBJECT(dev), name, bar_size);
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index a017614d4..168b9cc56 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -38,6 +38,7 @@
#include "hw/pci/msix.h"
#include "exec/address-spaces.h"
#include "hw/hotplug.h"
+#include "hw/boards.h"
//#define DEBUG_PCI
#ifdef DEBUG_PCI
@@ -846,6 +847,9 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,
PCIConfigWriteFunc *config_write = pc->config_write;
Error *local_err = NULL;
AddressSpace *dma_as;
+ DeviceState *dev = DEVICE(pci_dev);
+
+ pci_dev->bus = bus;
if (devfn < 0) {
for(devfn = bus->devfn_min ; devfn < ARRAY_SIZE(bus->devices);
@@ -863,9 +867,17 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,
PCI_SLOT(devfn), PCI_FUNC(devfn), name,
bus->devices[devfn]->name);
return NULL;
+ } else if (dev->hotplugged &&
+ pci_get_function_0(pci_dev)) {
+ error_setg(errp, "PCI: slot %d function 0 already ocuppied by %s,"
+ " new func %s cannot be exposed to guest.",
+ PCI_SLOT(devfn),
+ bus->devices[PCI_DEVFN(PCI_SLOT(devfn), 0)]->name,
+ name);
+
+ return NULL;
}
- pci_dev->bus = bus;
pci_dev->devfn = devfn;
dma_as = pci_device_iommu_address_space(pci_dev);
@@ -1065,6 +1077,10 @@ static pcibus_t pci_bar_address(PCIDevice *d,
pcibus_t new_addr, last_addr;
int bar = pci_bar(d, reg);
uint16_t cmd = pci_get_word(d->config + PCI_COMMAND);
+ Object *machine = qdev_get_machine();
+ ObjectClass *oc = object_get_class(machine);
+ MachineClass *mc = MACHINE_CLASS(oc);
+ bool allow_0_address = mc->pci_allow_0_address;
if (type & PCI_BASE_ADDRESS_SPACE_IO) {
if (!(cmd & PCI_COMMAND_IO)) {
@@ -1075,7 +1091,8 @@ static pcibus_t pci_bar_address(PCIDevice *d,
/* Check if 32 bit BAR wraps around explicitly.
* TODO: make priorities correct and remove this work around.
*/
- if (last_addr <= new_addr || new_addr == 0 || last_addr >= UINT32_MAX) {
+ if (last_addr <= new_addr || last_addr >= UINT32_MAX ||
+ (!allow_0_address && new_addr == 0)) {
return PCI_BAR_UNMAPPED;
}
return new_addr;
@@ -1099,8 +1116,8 @@ static pcibus_t pci_bar_address(PCIDevice *d,
/* XXX: as we cannot support really dynamic
mappings, we handle specific values as invalid
mappings. */
- if (last_addr <= new_addr || new_addr == 0 ||
- last_addr == PCI_BAR_UNMAPPED) {
+ if (last_addr <= new_addr || last_addr == PCI_BAR_UNMAPPED ||
+ (!allow_0_address && new_addr == 0)) {
return PCI_BAR_UNMAPPED;
}
@@ -1148,16 +1165,16 @@ static void pci_update_mappings(PCIDevice *d)
/* now do the real mapping */
if (r->addr != PCI_BAR_UNMAPPED) {
trace_pci_update_mappings_del(d, pci_bus_num(d->bus),
- PCI_FUNC(d->devfn),
PCI_SLOT(d->devfn),
+ PCI_FUNC(d->devfn),
i, r->addr, r->size);
memory_region_del_subregion(r->address_space, r->memory);
}
r->addr = new_addr;
if (r->addr != PCI_BAR_UNMAPPED) {
trace_pci_update_mappings_add(d, pci_bus_num(d->bus),
- PCI_FUNC(d->devfn),
PCI_SLOT(d->devfn),
+ PCI_FUNC(d->devfn),
i, r->addr, r->size);
memory_region_add_subregion_overlap(r->address_space,
r->addr, r->memory, 1);
@@ -2065,9 +2082,7 @@ static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom,
g_free(path);
return;
}
- if (size & (size - 1)) {
- size = 1 << qemu_fls(size);
- }
+ size = pow2ceil(size);
vmsd = qdev_get_vmsd(DEVICE(pdev));
@@ -2077,7 +2092,7 @@ static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom,
snprintf(name, sizeof(name), "%s.rom", object_get_typename(OBJECT(pdev)));
}
pdev->has_rom = true;
- memory_region_init_ram(&pdev->rom, OBJECT(pdev), name, size, &error_abort);
+ memory_region_init_ram(&pdev->rom, OBJECT(pdev), name, size, &error_fatal);
vmstate_register_ram(&pdev->rom, &pdev->qdev);
ptr = memory_region_get_ram_ptr(&pdev->rom);
load_image(path, ptr);
@@ -2379,17 +2394,14 @@ static void pci_device_class_init(ObjectClass *klass, void *data)
AddressSpace *pci_device_iommu_address_space(PCIDevice *dev)
{
PCIBus *bus = PCI_BUS(dev->bus);
+ PCIBus *iommu_bus = bus;
- if (bus->iommu_fn) {
- return bus->iommu_fn(bus, bus->iommu_opaque, dev->devfn);
+ while(iommu_bus && !iommu_bus->iommu_fn && iommu_bus->parent_dev) {
+ iommu_bus = PCI_BUS(iommu_bus->parent_dev->bus);
}
-
- if (bus->parent_dev) {
- /** We are ignoring the bus master DMA bit of the bridge
- * as it would complicate things such as VFIO for no good reason */
- return pci_device_iommu_address_space(bus->parent_dev);
+ if (iommu_bus && iommu_bus->iommu_fn) {
+ return iommu_bus->iommu_fn(bus, iommu_bus->iommu_opaque, dev->devfn);
}
-
return &address_space_memory;
}
@@ -2453,6 +2465,33 @@ void pci_bus_get_w64_range(PCIBus *bus, Range *range)
pci_for_each_device_under_bus(bus, pci_dev_get_w64, range);
}
+static bool pcie_has_upstream_port(PCIDevice *dev)
+{
+ PCIDevice *parent_dev = pci_bridge_get_device(dev->bus);
+
+ /* Device associated with an upstream port.
+ * As there are several types of these, it's easier to check the
+ * parent device: upstream ports are always connected to
+ * root or downstream ports.
+ */
+ return parent_dev &&
+ pci_is_express(parent_dev) &&
+ parent_dev->exp.exp_cap &&
+ (pcie_cap_get_type(parent_dev) == PCI_EXP_TYPE_ROOT_PORT ||
+ pcie_cap_get_type(parent_dev) == PCI_EXP_TYPE_DOWNSTREAM);
+}
+
+PCIDevice *pci_get_function_0(PCIDevice *pci_dev)
+{
+ if(pcie_has_upstream_port(pci_dev)) {
+ /* With an upstream PCIe port, we only support 1 device at slot 0 */
+ return pci_dev->bus->devices[0];
+ } else {
+ /* Other bus types might support multiple devices at slots 0-31 */
+ return pci_dev->bus->devices[PCI_DEVFN(PCI_SLOT(pci_dev->devfn), 0)];
+ }
+}
+
static const TypeInfo pci_device_type_info = {
.name = TYPE_PCI_DEVICE,
.parent = TYPE_DEVICE,
diff --git a/hw/pci/pci_host.c b/hw/pci/pci_host.c
index 3e26f9256..49f59a5db 100644
--- a/hw/pci/pci_host.c
+++ b/hw/pci/pci_host.c
@@ -20,6 +20,7 @@
#include "hw/pci/pci.h"
#include "hw/pci/pci_host.h"
+#include "hw/pci/pci_bus.h"
#include "trace.h"
/* debug PCI */
@@ -52,6 +53,13 @@ void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr,
uint32_t limit, uint32_t val, uint32_t len)
{
assert(len <= 4);
+ /* non-zero functions are only exposed when function 0 is present,
+ * allowing direct removal of unexposed functions.
+ */
+ if (pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) {
+ return;
+ }
+
trace_pci_cfg_write(pci_dev->name, PCI_SLOT(pci_dev->devfn),
PCI_FUNC(pci_dev->devfn), addr, val);
pci_dev->config_write(pci_dev, addr, val, MIN(len, limit - addr));
@@ -63,6 +71,13 @@ uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr,
uint32_t ret;
assert(len <= 4);
+ /* non-zero functions are only exposed when function 0 is present,
+ * allowing direct removal of unexposed functions.
+ */
+ if (pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) {
+ return ~0x0;
+ }
+
ret = pci_dev->config_read(pci_dev, addr, MIN(len, limit - addr));
trace_pci_cfg_read(pci_dev->name, PCI_SLOT(pci_dev->devfn),
PCI_FUNC(pci_dev->devfn), addr, ret);
diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c
index 6e28985bd..0eab29d53 100644
--- a/hw/pci/pcie.c
+++ b/hw/pci/pcie.c
@@ -249,25 +249,43 @@ void pcie_cap_slot_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
return;
}
- /* TODO: multifunction hot-plug.
- * Right now, only a device of function = 0 is allowed to be
- * hot plugged/unplugged.
+ /* To enable multifunction hot-plug, we just ensure the function
+ * 0 added last. When function 0 is added, we set the sltsta and
+ * inform OS via event notification.
*/
- assert(PCI_FUNC(pci_dev->devfn) == 0);
+ if (pci_get_function_0(pci_dev)) {
+ pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA,
+ PCI_EXP_SLTSTA_PDS);
+ pcie_cap_slot_event(PCI_DEVICE(hotplug_dev),
+ PCI_EXP_HP_EV_PDC | PCI_EXP_HP_EV_ABP);
+ }
+}
- pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA,
- PCI_EXP_SLTSTA_PDS);
- pcie_cap_slot_event(PCI_DEVICE(hotplug_dev),
- PCI_EXP_HP_EV_PDC | PCI_EXP_HP_EV_ABP);
+static void pcie_unplug_device(PCIBus *bus, PCIDevice *dev, void *opaque)
+{
+ object_unparent(OBJECT(dev));
}
void pcie_cap_slot_hot_unplug_request_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
uint8_t *exp_cap;
+ PCIDevice *pci_dev = PCI_DEVICE(dev);
+ PCIBus *bus = pci_dev->bus;
pcie_cap_slot_hotplug_common(PCI_DEVICE(hotplug_dev), dev, &exp_cap, errp);
+ /* In case user cancel the operation of multi-function hot-add,
+ * remove the function that is unexposed to guest individually,
+ * without interaction with guest.
+ */
+ if (pci_dev->devfn &&
+ !bus->devices[0]) {
+ pcie_unplug_device(bus, pci_dev, NULL);
+
+ return;
+ }
+
pcie_cap_slot_push_attention_button(PCI_DEVICE(hotplug_dev));
}
@@ -378,11 +396,6 @@ void pcie_cap_slot_reset(PCIDevice *dev)
hotplug_event_update_event_status(dev);
}
-static void pcie_unplug_device(PCIBus *bus, PCIDevice *dev, void *opaque)
-{
- object_unparent(OBJECT(dev));
-}
-
void pcie_cap_slot_write_config(PCIDevice *dev,
uint32_t addr, uint32_t val, int len)
{
@@ -413,13 +426,13 @@ void pcie_cap_slot_write_config(PCIDevice *dev,
*/
if ((sltsta & PCI_EXP_SLTSTA_PDS) && (val & PCI_EXP_SLTCTL_PCC) &&
((val & PCI_EXP_SLTCTL_PIC_OFF) == PCI_EXP_SLTCTL_PIC_OFF)) {
- PCIBus *sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(dev));
- pci_for_each_device(sec_bus, pci_bus_num(sec_bus),
- pcie_unplug_device, NULL);
+ PCIBus *sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(dev));
+ pci_for_each_device(sec_bus, pci_bus_num(sec_bus),
+ pcie_unplug_device, NULL);
- pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA,
- PCI_EXP_SLTSTA_PDS);
- pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA,
+ pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA,
+ PCI_EXP_SLTSTA_PDS);
+ pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA,
PCI_EXP_SLTSTA_PDC);
}
diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c
index f1847ac21..98d2c183b 100644
--- a/hw/pci/pcie_aer.c
+++ b/hw/pci/pcie_aer.c
@@ -827,10 +827,6 @@ typedef struct PCIEAERErrorName {
*/
static const struct PCIEAERErrorName pcie_aer_error_list[] = {
{
- .name = "TRAIN",
- .val = PCI_ERR_UNC_TRAIN,
- .correctable = false,
- }, {
.name = "DLP",
.val = PCI_ERR_UNC_DLP,
.correctable = false,
@@ -983,7 +979,7 @@ static int do_pcie_aer_inject_error(Monitor *mon,
}
}
err.status = error_status;
- err.source_id = (pci_bus_num(dev->bus) << 8) | dev->devfn;
+ err.source_id = pci_requester_id(dev);
err.flags = 0;
if (correctable) {
diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c
index bfb4d31b6..d34fdf3ec 100644
--- a/hw/pci/shpc.c
+++ b/hw/pci/shpc.c
@@ -1,5 +1,4 @@
#include "qemu-common.h"
-#include <strings.h>
#include <stdint.h>
#include "qemu/range.h"
#include "qemu/error-report.h"
diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index c8ab06e7f..c1ffc7771 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 spapr_drc.o
+obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.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 d300846c3..b3418dbae 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -1017,7 +1017,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
bios_size = load_elf(filename, NULL, NULL, &bios_entry, &loadaddr, NULL,
- 1, ELF_MACHINE, 0);
+ 1, PPC_ELF_MACHINE, 0);
if (bios_size < 0) {
/*
* Hrm. No ELF image? Try a uImage, maybe someone is giving us an
@@ -1048,10 +1048,6 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
boot_info->entry = bios_entry;
boot_info->dt_base = dt_base;
boot_info->dt_size = dt_size;
-
- if (kvm_enabled()) {
- kvmppc_init();
- }
}
static int e500_ccsr_initfn(SysBusDevice *dev)
diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c
index 14b14eaa7..384b6e84d 100644
--- a/hw/ppc/e500plat.c
+++ b/hw/ppc/e500plat.c
@@ -57,17 +57,12 @@ static void e500plat_init(MachineState *machine)
ppce500_init(machine, &params);
}
-static QEMUMachine e500plat_machine = {
- .name = "ppce500",
- .desc = "generic paravirt e500 platform",
- .init = e500plat_init,
- .max_cpus = 32,
- .has_dynamic_sysbus = true,
-};
-
-static void e500plat_machine_init(void)
+static void e500plat_machine_init(MachineClass *mc)
{
- qemu_register_machine(&e500plat_machine);
+ mc->desc = "generic paravirt e500 platform";
+ mc->init = e500plat_init;
+ mc->max_cpus = 32;
+ mc->has_dynamic_sysbus = true;
}
-machine_init(e500plat_machine_init);
+DEFINE_MACHINE("ppce500", e500plat_machine_init)
diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h
index 8bdba30c1..e375ed2b2 100644
--- a/hw/ppc/mac.h
+++ b/hw/ppc/mac.h
@@ -103,6 +103,9 @@ typedef struct CUDAState {
uint8_t last_b;
uint8_t last_acr;
+ /* MacOS 9 is racy and requires a delay upon setting the SR_INT bit */
+ QEMUTimer *sr_delay_timer;
+
int data_in_size;
int data_in_index;
int data_out_index;
diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c
index 77d5c819e..1b9a573ca 100644
--- a/hw/ppc/mac_newworld.c
+++ b/hw/ppc/mac_newworld.c
@@ -207,7 +207,7 @@ static void ppc_core99_init(MachineState *machine)
/* allocate and load BIOS */
memory_region_init_ram(bios, NULL, "ppc_core99.bios", BIOS_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(bios);
if (bios_name == NULL)
@@ -219,7 +219,7 @@ static void ppc_core99_init(MachineState *machine)
/* Load OpenBIOS (ELF) */
if (filename) {
bios_size = load_elf(filename, NULL, NULL, NULL,
- NULL, NULL, 1, ELF_MACHINE, 0);
+ NULL, NULL, 1, PPC_ELF_MACHINE, 0);
g_free(filename);
} else {
@@ -242,7 +242,7 @@ static void ppc_core99_init(MachineState *machine)
kernel_base = KERNEL_LOAD_ADDR;
kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
- NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0);
+ NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, 0);
if (kernel_size < 0)
kernel_size = load_aout(kernel_filename, kernel_base,
ram_size - kernel_base, bswap_needed,
@@ -371,12 +371,13 @@ static void ppc_core99_init(MachineState *machine)
/* 970 gets a U3 bus */
pci_bus = pci_pmac_u3_init(pic, get_system_memory(), get_system_io());
machine_arch = ARCH_MAC99_U3;
- machine->usb |= defaults_enabled() && !machine->usb_disabled;
} else {
pci_bus = pci_pmac_init(pic, get_system_memory(), get_system_io());
machine_arch = ARCH_MAC99;
}
+ machine->usb |= defaults_enabled() && !machine->usb_disabled;
+
/* Timebase Frequency */
if (kvm_enabled()) {
tbfreq = kvmppc_get_tbfreq();
@@ -508,7 +509,6 @@ static void core99_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
- mc->name = "mac99";
mc->desc = "Mac99 based PowerMAC";
mc->init = ppc_core99_init;
mc->max_cpus = MAX_CPUS;
@@ -517,7 +517,7 @@ static void core99_machine_class_init(ObjectClass *oc, void *data)
}
static const TypeInfo core99_machine_info = {
- .name = "mac99-machine",
+ .name = MACHINE_TYPE_NAME("mac99"),
.parent = TYPE_MACHINE,
.class_init = core99_machine_class_init,
};
diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c
index 06fdbaf58..21eaf0e66 100644
--- a/hw/ppc/mac_oldworld.c
+++ b/hw/ppc/mac_oldworld.c
@@ -135,7 +135,7 @@ static void ppc_heathrow_init(MachineState *machine)
/* allocate and load BIOS */
memory_region_init_ram(bios, NULL, "ppc_heathrow.bios", BIOS_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(bios);
if (bios_name == NULL)
@@ -147,7 +147,7 @@ static void ppc_heathrow_init(MachineState *machine)
/* Load OpenBIOS (ELF) */
if (filename) {
bios_size = load_elf(filename, 0, NULL, NULL, NULL, NULL,
- 1, ELF_MACHINE, 0);
+ 1, PPC_ELF_MACHINE, 0);
g_free(filename);
} else {
bios_size = -1;
@@ -168,7 +168,7 @@ static void ppc_heathrow_init(MachineState *machine)
#endif
kernel_base = KERNEL_LOAD_ADDR;
kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
- NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0);
+ NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, 0);
if (kernel_size < 0)
kernel_size = load_aout(kernel_filename, kernel_base,
ram_size - kernel_base, bswap_needed,
@@ -357,21 +357,17 @@ static int heathrow_kvm_type(const char *arg)
return 2;
}
-static QEMUMachine heathrow_machine = {
- .name = "g3beige",
- .desc = "Heathrow based PowerMAC",
- .init = ppc_heathrow_init,
- .max_cpus = MAX_CPUS,
+static void heathrow_machine_init(MachineClass *mc)
+{
+ mc->desc = "Heathrow based PowerMAC";
+ mc->init = ppc_heathrow_init;
+ mc->max_cpus = MAX_CPUS;
#ifndef TARGET_PPC64
- .is_default = 1,
+ mc->is_default = 1;
#endif
- .default_boot_order = "cd", /* TOFIX "cad" when Mac floppy is implemented */
- .kvm_type = heathrow_kvm_type,
-};
-
-static void heathrow_machine_init(void)
-{
- qemu_register_machine(&heathrow_machine);
+ /* TOFIX "cad" when Mac floppy is implemented */
+ mc->default_boot_order = "cd";
+ mc->kvm_type = heathrow_kvm_type;
}
-machine_init(heathrow_machine_init);
+DEFINE_MACHINE("g3beige", heathrow_machine_init)
diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c
index 3a3b141e4..0afbd34d6 100644
--- a/hw/ppc/mpc8544ds.c
+++ b/hw/ppc/mpc8544ds.c
@@ -50,16 +50,11 @@ static void mpc8544ds_init(MachineState *machine)
}
-static QEMUMachine ppce500_machine = {
- .name = "mpc8544ds",
- .desc = "mpc8544ds",
- .init = mpc8544ds_init,
- .max_cpus = 15,
-};
-
-static void ppce500_machine_init(void)
+static void ppce500_machine_init(MachineClass *mc)
{
- qemu_register_machine(&ppce500_machine);
+ mc->desc = "mpc8544ds";
+ mc->init = mpc8544ds_init;
+ mc->max_cpus = 15;
}
-machine_init(ppce500_machine_init);
+DEFINE_MACHINE("mpc8544ds", ppce500_machine_init)
diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
index b77e30357..2c604ef49 100644
--- a/hw/ppc/ppc.c
+++ b/hw/ppc/ppc.c
@@ -834,7 +834,7 @@ static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq)
static void timebase_pre_save(void *opaque)
{
PPCTimebase *tb = opaque;
- uint64_t ticks = cpu_get_real_ticks();
+ uint64_t ticks = cpu_get_host_ticks();
PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu);
if (!first_ppc_cpu->env.tb_env) {
@@ -878,7 +878,7 @@ static int timebase_post_load(void *opaque, int version_id)
NANOSECONDS_PER_SECOND);
guest_tb = tb_remote->guest_timebase + MIN(0, migration_duration_tb);
- tb_off_adj = guest_tb - cpu_get_real_ticks();
+ tb_off_adj = guest_tb - cpu_get_host_ticks();
tb_off = first_ppc_cpu->env.tb_env->tb_offset;
trace_ppc_tb_adjust(tb_off, tb_off_adj, tb_off_adj - tb_off,
diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c
index ec6c4cbaf..31bc18642 100644
--- a/hw/ppc/ppc405_boards.c
+++ b/hw/ppc/ppc405_boards.c
@@ -215,7 +215,8 @@ static void ref405ep_init(MachineState *machine)
33333333, &pic, kernel_filename == NULL ? 0 : 1);
/* allocate SRAM */
sram_size = 512 * 1024;
- memory_region_init_ram(sram, NULL, "ef405ep.sram", sram_size, &error_abort);
+ memory_region_init_ram(sram, NULL, "ef405ep.sram", sram_size,
+ &error_fatal);
vmstate_register_ram_global(sram);
memory_region_add_subregion(sysmem, 0xFFF00000, sram);
/* allocate and load BIOS */
@@ -250,7 +251,7 @@ static void ref405ep_init(MachineState *machine)
#endif
bios = g_new(MemoryRegion, 1);
memory_region_init_ram(bios, NULL, "ef405ep.bios", BIOS_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(bios);
if (bios_name == NULL)
@@ -368,10 +369,18 @@ static void ref405ep_init(MachineState *machine)
#endif
}
-static QEMUMachine ref405ep_machine = {
- .name = "ref405ep",
- .desc = "ref405ep",
- .init = ref405ep_init,
+static void ref405ep_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "ref405ep";
+ mc->init = ref405ep_init;
+}
+
+static const TypeInfo ref405ep_type = {
+ .name = MACHINE_TYPE_NAME("ref405ep"),
+ .parent = TYPE_MACHINE,
+ .class_init = ref405ep_class_init,
};
/*****************************************************************************/
@@ -399,7 +408,7 @@ struct taihu_cpld_t {
uint8_t reg1;
};
-static uint32_t taihu_cpld_readb (void *opaque, hwaddr addr)
+static uint64_t taihu_cpld_read(void *opaque, hwaddr addr, unsigned size)
{
taihu_cpld_t *cpld;
uint32_t ret;
@@ -420,8 +429,8 @@ static uint32_t taihu_cpld_readb (void *opaque, hwaddr addr)
return ret;
}
-static void taihu_cpld_writeb (void *opaque,
- hwaddr addr, uint32_t value)
+static void taihu_cpld_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
{
taihu_cpld_t *cpld;
@@ -438,48 +447,12 @@ static void taihu_cpld_writeb (void *opaque,
}
}
-static uint32_t taihu_cpld_readw (void *opaque, hwaddr addr)
-{
- uint32_t ret;
-
- ret = taihu_cpld_readb(opaque, addr) << 8;
- ret |= taihu_cpld_readb(opaque, addr + 1);
-
- return ret;
-}
-
-static void taihu_cpld_writew (void *opaque,
- hwaddr addr, uint32_t value)
-{
- taihu_cpld_writeb(opaque, addr, (value >> 8) & 0xFF);
- taihu_cpld_writeb(opaque, addr + 1, value & 0xFF);
-}
-
-static uint32_t taihu_cpld_readl (void *opaque, hwaddr addr)
-{
- uint32_t ret;
-
- ret = taihu_cpld_readb(opaque, addr) << 24;
- ret |= taihu_cpld_readb(opaque, addr + 1) << 16;
- ret |= taihu_cpld_readb(opaque, addr + 2) << 8;
- ret |= taihu_cpld_readb(opaque, addr + 3);
-
- return ret;
-}
-
-static void taihu_cpld_writel (void *opaque,
- hwaddr addr, uint32_t value)
-{
- taihu_cpld_writel(opaque, addr, (value >> 24) & 0xFF);
- taihu_cpld_writel(opaque, addr + 1, (value >> 16) & 0xFF);
- taihu_cpld_writel(opaque, addr + 2, (value >> 8) & 0xFF);
- taihu_cpld_writeb(opaque, addr + 3, value & 0xFF);
-}
-
static const MemoryRegionOps taihu_cpld_ops = {
- .old_mmio = {
- .read = { taihu_cpld_readb, taihu_cpld_readw, taihu_cpld_readl, },
- .write = { taihu_cpld_writeb, taihu_cpld_writew, taihu_cpld_writel, },
+ .read = taihu_cpld_read,
+ .write = taihu_cpld_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
},
.endianness = DEVICE_NATIVE_ENDIAN,
};
@@ -579,7 +552,7 @@ static void taihu_405ep_init(MachineState *machine)
bios_name = BIOS_FILENAME;
bios = g_new(MemoryRegion, 1);
memory_region_init_ram(bios, NULL, "taihu_405ep.bios", BIOS_SIZE,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(bios);
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
if (filename) {
@@ -664,16 +637,24 @@ static void taihu_405ep_init(MachineState *machine)
#endif
}
-static QEMUMachine taihu_machine = {
- .name = "taihu",
- .desc = "taihu",
- .init = taihu_405ep_init,
+static void taihu_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "taihu";
+ mc->init = taihu_405ep_init;
+}
+
+static const TypeInfo taihu_type = {
+ .name = MACHINE_TYPE_NAME("taihu"),
+ .parent = TYPE_MACHINE,
+ .class_init = taihu_class_init,
};
static void ppc405_machine_init(void)
{
- qemu_register_machine(&ref405ep_machine);
- qemu_register_machine(&taihu_machine);
+ type_register_static(&ref405ep_type);
+ type_register_static(&taihu_type);
}
-machine_init(ppc405_machine_init);
+machine_init(ppc405_machine_init)
diff --git a/hw/ppc/ppc405_uc.c b/hw/ppc/ppc405_uc.c
index c77434ae0..10f5dda1b 100644
--- a/hw/ppc/ppc405_uc.c
+++ b/hw/ppc/ppc405_uc.c
@@ -975,7 +975,7 @@ static void ppc405_ocm_init(CPUPPCState *env)
ocm = g_malloc0(sizeof(ppc405_ocm_t));
/* XXX: Size is 4096 or 0x04000000 */
memory_region_init_ram(&ocm->isarc_ram, NULL, "ppc405.ocm", 4096,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&ocm->isarc_ram);
memory_region_init_alias(&ocm->dsarc_ram, NULL, "ppc405.dsarc", &ocm->isarc_ram,
0, 4096);
diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c
index 032fa803d..b66c11338 100644
--- a/hw/ppc/ppc440_bamboo.c
+++ b/hw/ppc/ppc440_bamboo.c
@@ -256,7 +256,7 @@ static void bamboo_init(MachineState *machine)
NULL, NULL);
if (success < 0) {
success = load_elf(kernel_filename, NULL, NULL, &elf_entry,
- &elf_lowaddr, NULL, 1, ELF_MACHINE, 0);
+ &elf_lowaddr, NULL, 1, PPC_ELF_MACHINE, 0);
entry = elf_entry;
loadaddr = elf_lowaddr;
}
@@ -288,20 +288,12 @@ static void bamboo_init(MachineState *machine)
exit(1);
}
}
-
- if (kvm_enabled())
- kvmppc_init();
}
-static QEMUMachine bamboo_machine = {
- .name = "bamboo",
- .desc = "bamboo",
- .init = bamboo_init,
-};
-
-static void bamboo_machine_init(void)
+static void bamboo_machine_init(MachineClass *mc)
{
- qemu_register_machine(&bamboo_machine);
+ mc->desc = "bamboo";
+ mc->init = bamboo_init;
}
-machine_init(bamboo_machine_init);
+DEFINE_MACHINE("bamboo", bamboo_machine_init)
diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c
index 45b5f62d6..5ad28f75c 100644
--- a/hw/ppc/prep.c
+++ b/hw/ppc/prep.c
@@ -42,11 +42,9 @@
#include "sysemu/arch_init.h"
#include "sysemu/qtest.h"
#include "exec/address-spaces.h"
+#include "trace.h"
#include "elf.h"
-//#define HARD_DEBUG_PPC_IO
-//#define DEBUG_PPC_IO
-
/* SMP is not enabled, for now */
#define MAX_CPUS 1
@@ -57,26 +55,6 @@
#define KERNEL_LOAD_ADDR 0x01000000
#define INITRD_LOAD_ADDR 0x01800000
-#if defined (HARD_DEBUG_PPC_IO) && !defined (DEBUG_PPC_IO)
-#define DEBUG_PPC_IO
-#endif
-
-#if defined (HARD_DEBUG_PPC_IO)
-#define PPC_IO_DPRINTF(fmt, ...) \
-do { \
- if (qemu_loglevel_mask(CPU_LOG_IOPORT)) { \
- qemu_log("%s: " fmt, __func__ , ## __VA_ARGS__); \
- } else { \
- printf("%s : " fmt, __func__ , ## __VA_ARGS__); \
- } \
-} while (0)
-#elif defined (DEBUG_PPC_IO)
-#define PPC_IO_DPRINTF(fmt, ...) \
-qemu_log_mask(CPU_LOG_IOPORT, fmt, ## __VA_ARGS__)
-#else
-#define PPC_IO_DPRINTF(fmt, ...) do { } while (0)
-#endif
-
/* Constants for devices init */
static const int ide_iobase[2] = { 0x1f0, 0x170 };
static const int ide_iobase2[2] = { 0x3f6, 0x376 };
@@ -199,8 +177,7 @@ static void PREP_io_800_writeb (void *opaque, uint32_t addr, uint32_t val)
{
sysctrl_t *sysctrl = opaque;
- PPC_IO_DPRINTF("0x%08" PRIx32 " => 0x%02" PRIx32 "\n",
- addr - PPC_IO_BASE, val);
+ trace_prep_io_800_writeb(addr - PPC_IO_BASE, val);
switch (addr) {
case 0x0092:
/* Special port 92 */
@@ -327,8 +304,7 @@ static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr)
printf("ERROR: unaffected IO port: %04" PRIx32 " read\n", addr);
break;
}
- PPC_IO_DPRINTF("0x%08" PRIx32 " <= 0x%02" PRIx32 "\n",
- addr - PPC_IO_BASE, retval);
+ trace_prep_io_800_readb(addr - PPC_IO_BASE, retval);
return retval;
}
@@ -336,15 +312,6 @@ static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr)
#define NVRAM_SIZE 0x2000
-static void cpu_request_exit(void *opaque, int irq, int level)
-{
- CPUState *cpu = current_cpu;
-
- if (cpu && level) {
- cpu_exit(cpu);
- }
-}
-
static void ppc_prep_reset(void *opaque)
{
PowerPCCPU *cpu = opaque;
@@ -610,7 +577,7 @@ static void ppc_prep_init(MachineState *machine)
bios_name = BIOS_FILENAME;
}
qdev_prop_set_string(dev, "bios-name", bios_name);
- qdev_prop_set_uint32(dev, "elf-machine", ELF_MACHINE);
+ qdev_prop_set_uint32(dev, "elf-machine", PPC_ELF_MACHINE);
pcihost = PCI_HOST_BRIDGE(dev);
object_property_add_child(qdev_get_machine(), "raven", OBJECT(dev), NULL);
qdev_init_nofail(dev);
@@ -626,8 +593,6 @@ static void ppc_prep_init(MachineState *machine)
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,
- 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));
@@ -698,17 +663,12 @@ static void ppc_prep_init(MachineState *machine)
graphic_width, graphic_height, graphic_depth);
}
-static QEMUMachine prep_machine = {
- .name = "prep",
- .desc = "PowerPC PREP platform",
- .init = ppc_prep_init,
- .max_cpus = MAX_CPUS,
- .default_boot_order = "cad",
-};
-
-static void prep_machine_init(void)
+static void prep_machine_init(MachineClass *mc)
{
- qemu_register_machine(&prep_machine);
+ mc->desc = "PowerPC PREP platform";
+ mc->init = ppc_prep_init;
+ mc->max_cpus = MAX_CPUS;
+ mc->default_boot_order = "cad";
}
-machine_init(prep_machine_init);
+DEFINE_MACHINE("prep", prep_machine_init)
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index a6f19473c..ff1537a47 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -30,9 +30,11 @@
#include "hw/fw-path-provider.h"
#include "elf.h"
#include "net/net.h"
+#include "sysemu/device_tree.h"
#include "sysemu/block-backend.h"
#include "sysemu/cpus.h"
#include "sysemu/kvm.h"
+#include "sysemu/device_tree.h"
#include "kvm_ppc.h"
#include "migration/migration.h"
#include "mmu-hash64.h"
@@ -60,6 +62,7 @@
#include "hw/nmi.h"
#include "hw/compat.h"
+#include "qemu-common.h"
#include <libfdt.h>
@@ -73,7 +76,7 @@
*
* We load our kernel at 4M, leaving space for SLOF initial image
*/
-#define FDT_MAX_SIZE 0x40000
+#define FDT_MAX_SIZE 0x100000
#define RTAS_MAX_SIZE 0x10000
#define RTAS_MAX_ADDR 0x80000000 /* RTAS must stay below that */
#define FW_MAX_SIZE 0x400000
@@ -85,8 +88,6 @@
#define TIMEBASE_FREQ 512000000ULL
-#define MAX_CPUS 255
-
#define PHANDLE_XICP 0x00001111
#define HTAB_SIZE(spapr) (1ULL << ((spapr)->htab_shift))
@@ -124,6 +125,7 @@ static XICSState *xics_system_init(MachineState *machine,
error_report("kernel_irqchip requested but unavailable: %s",
error_get_pretty(err));
}
+ error_free(err);
}
if (!icp) {
@@ -375,6 +377,11 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base,
_FDT((fdt_property_string(fdt, "vm,uuid", buf)));
g_free(buf);
+ if (qemu_get_vm_name()) {
+ _FDT((fdt_property_string(fdt, "ibm,partition-name",
+ qemu_get_vm_name())));
+ }
+
_FDT((fdt_property_cell(fdt, "#address-cells", 0x2)));
_FDT((fdt_property_cell(fdt, "#size-cells", 0x2)));
@@ -427,6 +434,10 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base,
_FDT((fdt_property_cell(fdt, "rtas-event-scan-rate",
RTAS_EVENT_SCAN_RATE)));
+ if (msi_supported) {
+ _FDT((fdt_property(fdt, "ibm,change-msix-capable", NULL, 0)));
+ }
+
/*
* According to PAPR, rtas ibm,os-term does not guarantee a return
* back to the guest cpu.
@@ -495,44 +506,7 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base,
return fdt;
}
-int spapr_h_cas_compose_response(sPAPRMachineState *spapr,
- target_ulong addr, target_ulong size)
-{
- void *fdt, *fdt_skel;
- sPAPRDeviceTreeUpdateHeader hdr = { .version_id = 1 };
-
- size -= sizeof(hdr);
-
- /* Create sceleton */
- fdt_skel = g_malloc0(size);
- _FDT((fdt_create(fdt_skel, size)));
- _FDT((fdt_begin_node(fdt_skel, "")));
- _FDT((fdt_end_node(fdt_skel)));
- _FDT((fdt_finish(fdt_skel)));
- fdt = g_malloc0(size);
- _FDT((fdt_open_into(fdt_skel, fdt, size)));
- g_free(fdt_skel);
-
- /* Fix skeleton up */
- _FDT((spapr_fixup_cpu_dt(fdt, spapr)));
-
- /* Pack resulting tree */
- _FDT((fdt_pack(fdt)));
-
- if (fdt_totalsize(fdt) + sizeof(hdr) > size) {
- trace_spapr_cas_failed(size);
- return -1;
- }
-
- cpu_physical_memory_write(addr, &hdr, sizeof(hdr));
- cpu_physical_memory_write(addr + sizeof(hdr), fdt, fdt_totalsize(fdt));
- trace_spapr_cas_continue(fdt_totalsize(fdt) + sizeof(hdr));
- g_free(fdt);
-
- return 0;
-}
-
-static void spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start,
+static int spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start,
hwaddr size)
{
uint32_t associativity[] = {
@@ -555,6 +529,7 @@ static void spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start,
sizeof(mem_reg_property))));
_FDT((fdt_setprop(fdt, off, "ibm,associativity", associativity,
sizeof(associativity))));
+ return off;
}
static int spapr_populate_memory(sPAPRMachineState *spapr, void *fdt)
@@ -620,11 +595,27 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset,
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 vcpus_per_socket = smp_threads * smp_cores;
uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)};
+ /* Note: we keep CI large pages off for now because a 64K capable guest
+ * provisioned with large pages might otherwise try to map a qemu
+ * framebuffer (or other kind of memory mapped PCI BAR) using 64K pages
+ * even if that qemu runs on a 4k host.
+ *
+ * We can later add this bit back when we are confident this is not
+ * an issue (!HV KVM or 64K host)
+ */
+ uint8_t pa_features_206[] = { 6, 0,
+ 0xf6, 0x1f, 0xc7, 0x00, 0x80, 0xc0 };
+ uint8_t pa_features_207[] = { 24, 0,
+ 0xf6, 0x1f, 0xc7, 0xc0, 0x80, 0xf0,
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00,
+ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 };
+ uint8_t *pa_features;
+ size_t pa_size;
+
_FDT((fdt_setprop_cell(fdt, offset, "reg", index)));
_FDT((fdt_setprop_string(fdt, offset, "device_type", "cpu")));
@@ -653,6 +644,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset,
_FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq)));
_FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq)));
+ _FDT((fdt_setprop_cell(fdt, offset, "slb-size", env->slb_nr)));
_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)));
@@ -690,8 +682,21 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset,
page_sizes_prop, page_sizes_prop_size)));
}
+ /* Do the ibm,pa-features property, adjust it for ci-large-pages */
+ if (env->mmu_model == POWERPC_MMU_2_06) {
+ pa_features = pa_features_206;
+ pa_size = sizeof(pa_features_206);
+ } else /* env->mmu_model == POWERPC_MMU_2_07 */ {
+ pa_features = pa_features_207;
+ pa_size = sizeof(pa_features_207);
+ }
+ if (env->ci_large_pages) {
+ pa_features[3] |= 0x20;
+ }
+ _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", pa_features, pa_size)));
+
_FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id",
- cs->cpu_index / cpus_per_socket)));
+ cs->cpu_index / vcpus_per_socket)));
_FDT((fdt_setprop(fdt, offset, "ibm,pft-size",
pft_size_prop, sizeof(pft_size_prop))));
@@ -738,12 +743,155 @@ static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr)
}
+/*
+ * Adds ibm,dynamic-reconfiguration-memory node.
+ * Refer to docs/specs/ppc-spapr-hotplug.txt for the documentation
+ * of this device tree node.
+ */
+static int spapr_populate_drconf_memory(sPAPRMachineState *spapr, void *fdt)
+{
+ MachineState *machine = MACHINE(spapr);
+ int ret, i, offset;
+ uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE;
+ uint32_t prop_lmb_size[] = {0, cpu_to_be32(lmb_size)};
+ uint32_t nr_lmbs = (machine->maxram_size - machine->ram_size)/lmb_size;
+ uint32_t *int_buf, *cur_index, buf_len;
+ int nr_nodes = nb_numa_nodes ? nb_numa_nodes : 1;
+
+ /*
+ * Allocate enough buffer size to fit in ibm,dynamic-memory
+ * or ibm,associativity-lookup-arrays
+ */
+ buf_len = MAX(nr_lmbs * SPAPR_DR_LMB_LIST_ENTRY_SIZE + 1, nr_nodes * 4 + 2)
+ * sizeof(uint32_t);
+ cur_index = int_buf = g_malloc0(buf_len);
+
+ offset = fdt_add_subnode(fdt, 0, "ibm,dynamic-reconfiguration-memory");
+
+ ret = fdt_setprop(fdt, offset, "ibm,lmb-size", prop_lmb_size,
+ sizeof(prop_lmb_size));
+ if (ret < 0) {
+ goto out;
+ }
+
+ ret = fdt_setprop_cell(fdt, offset, "ibm,memory-flags-mask", 0xff);
+ if (ret < 0) {
+ goto out;
+ }
+
+ ret = fdt_setprop_cell(fdt, offset, "ibm,memory-preservation-time", 0x0);
+ if (ret < 0) {
+ goto out;
+ }
+
+ /* ibm,dynamic-memory */
+ int_buf[0] = cpu_to_be32(nr_lmbs);
+ cur_index++;
+ for (i = 0; i < nr_lmbs; i++) {
+ sPAPRDRConnector *drc;
+ sPAPRDRConnectorClass *drck;
+ uint64_t addr = i * lmb_size + spapr->hotplug_memory.base;;
+ uint32_t *dynamic_memory = cur_index;
+
+ drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB,
+ addr/lmb_size);
+ g_assert(drc);
+ drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+
+ dynamic_memory[0] = cpu_to_be32(addr >> 32);
+ dynamic_memory[1] = cpu_to_be32(addr & 0xffffffff);
+ dynamic_memory[2] = cpu_to_be32(drck->get_index(drc));
+ dynamic_memory[3] = cpu_to_be32(0); /* reserved */
+ dynamic_memory[4] = cpu_to_be32(numa_get_node(addr, NULL));
+ if (addr < machine->ram_size ||
+ memory_region_present(get_system_memory(), addr)) {
+ dynamic_memory[5] = cpu_to_be32(SPAPR_LMB_FLAGS_ASSIGNED);
+ } else {
+ dynamic_memory[5] = cpu_to_be32(0);
+ }
+
+ cur_index += SPAPR_DR_LMB_LIST_ENTRY_SIZE;
+ }
+ ret = fdt_setprop(fdt, offset, "ibm,dynamic-memory", int_buf, buf_len);
+ if (ret < 0) {
+ goto out;
+ }
+
+ /* ibm,associativity-lookup-arrays */
+ cur_index = int_buf;
+ int_buf[0] = cpu_to_be32(nr_nodes);
+ int_buf[1] = cpu_to_be32(4); /* Number of entries per associativity list */
+ cur_index += 2;
+ for (i = 0; i < nr_nodes; i++) {
+ uint32_t associativity[] = {
+ cpu_to_be32(0x0),
+ cpu_to_be32(0x0),
+ cpu_to_be32(0x0),
+ cpu_to_be32(i)
+ };
+ memcpy(cur_index, associativity, sizeof(associativity));
+ cur_index += 4;
+ }
+ ret = fdt_setprop(fdt, offset, "ibm,associativity-lookup-arrays", int_buf,
+ (cur_index - int_buf) * sizeof(uint32_t));
+out:
+ g_free(int_buf);
+ return ret;
+}
+
+int spapr_h_cas_compose_response(sPAPRMachineState *spapr,
+ target_ulong addr, target_ulong size,
+ bool cpu_update, bool memory_update)
+{
+ void *fdt, *fdt_skel;
+ sPAPRDeviceTreeUpdateHeader hdr = { .version_id = 1 };
+ sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(qdev_get_machine());
+
+ size -= sizeof(hdr);
+
+ /* Create sceleton */
+ fdt_skel = g_malloc0(size);
+ _FDT((fdt_create(fdt_skel, size)));
+ _FDT((fdt_begin_node(fdt_skel, "")));
+ _FDT((fdt_end_node(fdt_skel)));
+ _FDT((fdt_finish(fdt_skel)));
+ fdt = g_malloc0(size);
+ _FDT((fdt_open_into(fdt_skel, fdt, size)));
+ g_free(fdt_skel);
+
+ /* Fixup cpu nodes */
+ if (cpu_update) {
+ _FDT((spapr_fixup_cpu_dt(fdt, spapr)));
+ }
+
+ /* Generate memory nodes or ibm,dynamic-reconfiguration-memory node */
+ if (memory_update && smc->dr_lmb_enabled) {
+ _FDT((spapr_populate_drconf_memory(spapr, fdt)));
+ }
+
+ /* Pack resulting tree */
+ _FDT((fdt_pack(fdt)));
+
+ if (fdt_totalsize(fdt) + sizeof(hdr) > size) {
+ trace_spapr_cas_failed(size);
+ return -1;
+ }
+
+ cpu_physical_memory_write(addr, &hdr, sizeof(hdr));
+ cpu_physical_memory_write(addr + sizeof(hdr), fdt, fdt_totalsize(fdt));
+ trace_spapr_cas_continue(fdt_totalsize(fdt) + sizeof(hdr));
+ g_free(fdt);
+
+ return 0;
+}
+
static void spapr_finalize_fdt(sPAPRMachineState *spapr,
hwaddr fdt_addr,
hwaddr rtas_addr,
hwaddr rtas_size)
{
MachineState *machine = MACHINE(qdev_get_machine());
+ sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine);
const char *boot_device = machine->boot_order;
int ret, i;
size_t cb = 0;
@@ -768,6 +916,14 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr,
exit(1);
}
+ if (object_resolve_path_type("", TYPE_SPAPR_RNG, NULL)) {
+ ret = spapr_rng_populate_dt(fdt);
+ if (ret < 0) {
+ fprintf(stderr, "could not set up rng device in the fdt\n");
+ exit(1);
+ }
+ }
+
QLIST_FOREACH(phb, &spapr->phbs, list) {
ret = spapr_populate_pci_dt(phb, PHANDLE_XICP, fdt);
}
@@ -814,6 +970,10 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr,
spapr_populate_chosen_stdout(fdt, spapr->vio_bus);
}
+ if (smc->dr_lmb_enabled) {
+ _FDT(spapr_drc_populate_dt(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_LMB));
+ }
+
_FDT((fdt_pack(fdt)));
if (fdt_totalsize(fdt) > FDT_MAX_SIZE) {
@@ -822,6 +982,7 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr,
exit(1);
}
+ qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt));
cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt));
g_free(bootlist);
@@ -851,7 +1012,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(sPAPRMachineState *spapr)
+static void spapr_alloc_htab(sPAPRMachineState *spapr)
{
long shift;
int index;
@@ -861,23 +1022,62 @@ static void spapr_reset_htab(sPAPRMachineState *spapr)
* RAM */
shift = kvmppc_reset_htab(spapr->htab_shift);
+ if (shift < 0) {
+ /*
+ * For HV KVM, host kernel will return -ENOMEM when requested
+ * HTAB size can't be allocated.
+ */
+ error_setg(&error_abort, "Failed to allocate HTAB of requested size, try with smaller maxmem");
+ } else if (shift > 0) {
+ /*
+ * Kernel handles htab, we don't need to allocate one
+ *
+ * Older kernels can fall back to lower HTAB shift values,
+ * but we don't allow booting of such guests.
+ */
+ if (shift != spapr->htab_shift) {
+ error_setg(&error_abort, "Failed to allocate HTAB of requested size, try with smaller maxmem");
+ }
- if (shift > 0) {
- /* Kernel handles htab, we don't need to allocate one */
spapr->htab_shift = shift;
kvmppc_kern_htab = true;
+ } else {
+ /* Allocate htab */
+ spapr->htab = qemu_memalign(HTAB_SIZE(spapr), HTAB_SIZE(spapr));
+
+ /* And clear it */
+ memset(spapr->htab, 0, HTAB_SIZE(spapr));
+
+ for (index = 0; index < HTAB_SIZE(spapr) / HASH_PTE_SIZE_64; index++) {
+ DIRTY_HPTE(HPTE(spapr->htab, index));
+ }
+ }
+}
+
+/*
+ * Clear HTAB entries during reset.
+ *
+ * If host kernel has allocated HTAB, KVM_PPC_ALLOCATE_HTAB ioctl is
+ * used to clear HTAB. Otherwise QEMU-allocated HTAB is cleared manually.
+ */
+static void spapr_reset_htab(sPAPRMachineState *spapr)
+{
+ long shift;
+ int index;
+
+ shift = kvmppc_reset_htab(spapr->htab_shift);
+ if (shift < 0) {
+ error_setg(&error_abort, "Failed to reset HTAB");
+ } else if (shift > 0) {
+ if (shift != spapr->htab_shift) {
+ error_setg(&error_abort, "Requested HTAB allocation failed during reset");
+ }
/* Tell readers to update their file descriptor */
if (spapr->htab_fd >= 0) {
spapr->htab_fd_stale = true;
}
} else {
- if (!spapr->htab) {
- /* Allocate an htab if we don't yet have one */
- spapr->htab = qemu_memalign(HTAB_SIZE(spapr), HTAB_SIZE(spapr));
- }
-
- /* And clear it */
memset(spapr->htab, 0, HTAB_SIZE(spapr));
for (index = 0; index < HTAB_SIZE(spapr) / HASH_PTE_SIZE_64; index++) {
@@ -1041,6 +1241,7 @@ static int spapr_vga_init(PCIBus *pci_bus)
case VGA_DEVICE:
return true;
case VGA_STD:
+ case VGA_VIRTIO:
return pci_vga_init(pci_bus) != NULL;
default:
fprintf(stderr, "This vga model is not supported,"
@@ -1329,6 +1530,8 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id)
if (section_hdr) {
/* First section, just the hash shift */
if (spapr->htab_shift != section_hdr) {
+ error_report("htab_shift mismatch: source %d target %d",
+ section_hdr, spapr->htab_shift);
return -EINVAL;
}
return 0;
@@ -1398,7 +1601,7 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id)
static SaveVMHandlers savevm_htab_handlers = {
.save_live_setup = htab_save_setup,
.save_live_iterate = htab_save_iterate,
- .save_live_complete = htab_save_complete,
+ .save_live_complete_precopy = htab_save_complete,
.load_state = htab_load,
};
@@ -1437,10 +1640,77 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu)
qemu_register_reset(spapr_cpu_reset, cpu);
}
+/*
+ * Reset routine for LMB DR devices.
+ *
+ * Unlike PCI DR devices, LMB DR devices explicitly register this reset
+ * routine. Reset for PCI DR devices will be handled by PHB reset routine
+ * when it walks all its children devices. LMB devices reset occurs
+ * as part of spapr_ppc_reset().
+ */
+static void spapr_drc_reset(void *opaque)
+{
+ sPAPRDRConnector *drc = opaque;
+ DeviceState *d = DEVICE(drc);
+
+ if (d) {
+ device_reset(d);
+ }
+}
+
+static void spapr_create_lmb_dr_connectors(sPAPRMachineState *spapr)
+{
+ MachineState *machine = MACHINE(spapr);
+ uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE;
+ uint32_t nr_lmbs = (machine->maxram_size - machine->ram_size)/lmb_size;
+ int i;
+
+ for (i = 0; i < nr_lmbs; i++) {
+ sPAPRDRConnector *drc;
+ uint64_t addr;
+
+ addr = i * lmb_size + spapr->hotplug_memory.base;
+ drc = spapr_dr_connector_new(OBJECT(spapr), SPAPR_DR_CONNECTOR_TYPE_LMB,
+ addr/lmb_size);
+ qemu_register_reset(spapr_drc_reset, drc);
+ }
+}
+
+/*
+ * If RAM size, maxmem size and individual node mem sizes aren't aligned
+ * to SPAPR_MEMORY_BLOCK_SIZE(256MB), then refuse to start the guest
+ * since we can't support such unaligned sizes with DRCONF_MEMORY.
+ */
+static void spapr_validate_node_memory(MachineState *machine)
+{
+ int i;
+
+ if (machine->maxram_size % SPAPR_MEMORY_BLOCK_SIZE ||
+ machine->ram_size % SPAPR_MEMORY_BLOCK_SIZE) {
+ error_report("Can't support memory configuration where RAM size "
+ "0x" RAM_ADDR_FMT " or maxmem size "
+ "0x" RAM_ADDR_FMT " isn't aligned to %llu MB",
+ machine->ram_size, machine->maxram_size,
+ SPAPR_MEMORY_BLOCK_SIZE/M_BYTE);
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < nb_numa_nodes; i++) {
+ if (numa_info[i].node_mem % SPAPR_MEMORY_BLOCK_SIZE) {
+ error_report("Can't support memory configuration where memory size"
+ " %" PRIx64 " of node %d isn't aligned to %llu MB",
+ numa_info[i].node_mem, i,
+ SPAPR_MEMORY_BLOCK_SIZE/M_BYTE);
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
/* pSeries LPAR / sPAPR hardware init */
static void ppc_spapr_init(MachineState *machine)
{
sPAPRMachineState *spapr = SPAPR_MACHINE(machine);
+ sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine);
const char *kernel_filename = machine->kernel_filename;
const char *kernel_cmdline = machine->kernel_cmdline;
const char *initrd_filename = machine->initrd_filename;
@@ -1507,11 +1777,12 @@ 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)) >= machine->ram_size) {
+ if ((1ULL << (spapr->htab_shift + 7)) >= machine->maxram_size) {
break;
}
spapr->htab_shift++;
}
+ spapr_alloc_htab(spapr);
/* Set up Interrupt Controller before we create the VCPUs */
spapr->icp = xics_system_init(machine,
@@ -1519,6 +1790,10 @@ static void ppc_spapr_init(MachineState *machine)
smp_threads),
XICS_IRQS);
+ if (smc->dr_lmb_enabled) {
+ spapr_validate_node_memory(machine);
+ }
+
/* init CPUs */
if (machine->cpu_model == NULL) {
machine->cpu_model = kvm_enabled() ? "host" : "POWER7";
@@ -1535,6 +1810,7 @@ static void ppc_spapr_init(MachineState *machine)
if (kvm_enabled()) {
/* Enable H_LOGICAL_CI_* so SLOF can talk to in-kernel devices */
kvmppc_enable_logical_ci_hcalls();
+ kvmppc_enable_set_mode_hcall();
}
/* allocate RAM */
@@ -1550,6 +1826,28 @@ static void ppc_spapr_init(MachineState *machine)
memory_region_add_subregion(sysmem, 0, rma_region);
}
+ /* initialize hotplug memory address space */
+ if (machine->ram_size < machine->maxram_size) {
+ ram_addr_t hotplug_mem_size = machine->maxram_size - machine->ram_size;
+
+ if (machine->ram_slots > SPAPR_MAX_RAM_SLOTS) {
+ error_report("Specified number of memory slots %"PRIu64" exceeds max supported %d\n",
+ machine->ram_slots, SPAPR_MAX_RAM_SLOTS);
+ exit(EXIT_FAILURE);
+ }
+
+ spapr->hotplug_memory.base = ROUND_UP(machine->ram_size,
+ SPAPR_HOTPLUG_MEM_ALIGN);
+ memory_region_init(&spapr->hotplug_memory.mr, OBJECT(spapr),
+ "hotplug-memory", hotplug_mem_size);
+ memory_region_add_subregion(sysmem, spapr->hotplug_memory.base,
+ &spapr->hotplug_memory.mr);
+ }
+
+ if (smc->dr_lmb_enabled) {
+ spapr_create_lmb_dr_connectors(spapr);
+ }
+
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "spapr-rtas.bin");
if (!filename) {
error_report("Could not find LPAR rtas '%s'", "spapr-rtas.bin");
@@ -1636,11 +1934,11 @@ static void ppc_spapr_init(MachineState *machine)
uint64_t lowaddr = 0;
kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
- NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0);
+ NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, 0);
if (kernel_size == ELF_LOAD_WRONG_ENDIAN) {
kernel_size = load_elf(kernel_filename,
translate_kernel_address, NULL,
- NULL, &lowaddr, NULL, 0, ELF_MACHINE, 0);
+ NULL, &lowaddr, NULL, 0, PPC_ELF_MACHINE, 0);
kernel_le = kernel_size > 0;
}
if (kernel_size < 0) {
@@ -1820,22 +2118,166 @@ static void spapr_nmi(NMIState *n, int cpu_index, Error **errp)
}
}
+static void spapr_add_lmbs(DeviceState *dev, uint64_t addr, uint64_t size,
+ uint32_t node, Error **errp)
+{
+ sPAPRDRConnector *drc;
+ sPAPRDRConnectorClass *drck;
+ uint32_t nr_lmbs = size/SPAPR_MEMORY_BLOCK_SIZE;
+ int i, fdt_offset, fdt_size;
+ void *fdt;
+
+ /*
+ * Check for DRC connectors and send hotplug notification to the
+ * guest only in case of hotplugged memory. This allows cold plugged
+ * memory to be specified at boot time.
+ */
+ if (!dev->hotplugged) {
+ return;
+ }
+
+ for (i = 0; i < nr_lmbs; i++) {
+ drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB,
+ addr/SPAPR_MEMORY_BLOCK_SIZE);
+ g_assert(drc);
+
+ fdt = create_device_tree(&fdt_size);
+ fdt_offset = spapr_populate_memory_node(fdt, node, addr,
+ SPAPR_MEMORY_BLOCK_SIZE);
+
+ drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ drck->attach(drc, dev, fdt, fdt_offset, !dev->hotplugged, errp);
+ addr += SPAPR_MEMORY_BLOCK_SIZE;
+ }
+ spapr_hotplug_req_add_by_count(SPAPR_DR_CONNECTOR_TYPE_LMB, nr_lmbs);
+}
+
+static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
+ uint32_t node, Error **errp)
+{
+ Error *local_err = NULL;
+ sPAPRMachineState *ms = SPAPR_MACHINE(hotplug_dev);
+ PCDIMMDevice *dimm = PC_DIMM(dev);
+ PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
+ MemoryRegion *mr = ddc->get_memory_region(dimm);
+ uint64_t align = memory_region_get_alignment(mr);
+ uint64_t size = memory_region_size(mr);
+ uint64_t addr;
+
+ if (size % SPAPR_MEMORY_BLOCK_SIZE) {
+ error_setg(&local_err, "Hotplugged memory size must be a multiple of "
+ "%lld MB", SPAPR_MEMORY_BLOCK_SIZE/M_BYTE);
+ goto out;
+ }
+
+ pc_dimm_memory_plug(dev, &ms->hotplug_memory, mr, align, &local_err);
+ if (local_err) {
+ goto out;
+ }
+
+ addr = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP, &local_err);
+ if (local_err) {
+ pc_dimm_memory_unplug(dev, &ms->hotplug_memory, mr);
+ goto out;
+ }
+
+ spapr_add_lmbs(dev, addr, size, node, &error_abort);
+
+out:
+ error_propagate(errp, local_err);
+}
+
+static void spapr_machine_device_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(qdev_get_machine());
+
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ int node;
+
+ if (!smc->dr_lmb_enabled) {
+ error_setg(errp, "Memory hotplug not supported for this machine");
+ return;
+ }
+ node = object_property_get_int(OBJECT(dev), PC_DIMM_NODE_PROP, errp);
+ if (*errp) {
+ return;
+ }
+
+ /*
+ * Currently PowerPC kernel doesn't allow hot-adding memory to
+ * memory-less node, but instead will silently add the memory
+ * to the first node that has some memory. This causes two
+ * unexpected behaviours for the user.
+ *
+ * - Memory gets hotplugged to a different node than what the user
+ * specified.
+ * - Since pc-dimm subsystem in QEMU still thinks that memory belongs
+ * to memory-less node, a reboot will set things accordingly
+ * and the previously hotplugged memory now ends in the right node.
+ * This appears as if some memory moved from one node to another.
+ *
+ * So until kernel starts supporting memory hotplug to memory-less
+ * nodes, just prevent such attempts upfront in QEMU.
+ */
+ if (nb_numa_nodes && !numa_info[node].node_mem) {
+ error_setg(errp, "Can't hotplug memory to memory-less node %d",
+ node);
+ return;
+ }
+
+ spapr_memory_plug(hotplug_dev, dev, node, errp);
+ }
+}
+
+static void spapr_machine_device_unplug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ error_setg(errp, "Memory hot unplug not supported by sPAPR");
+ }
+}
+
+static HotplugHandler *spapr_get_hotpug_handler(MachineState *machine,
+ DeviceState *dev)
+{
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ return HOTPLUG_HANDLER(machine);
+ }
+ return NULL;
+}
+
+static unsigned spapr_cpu_index_to_socket_id(unsigned cpu_index)
+{
+ /* Allocate to NUMA nodes on a "socket" basis (not that concept of
+ * socket means much for the paravirtualized PAPR platform) */
+ return cpu_index / smp_threads / smp_cores;
+}
+
static void spapr_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
+ sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(oc);
FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc);
NMIClass *nc = NMI_CLASS(oc);
+ HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
mc->init = ppc_spapr_init;
mc->reset = ppc_spapr_reset;
mc->block_default_type = IF_SCSI;
- mc->max_cpus = MAX_CPUS;
+ mc->max_cpus = MAX_CPUMASK_BITS;
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;
+ mc->pci_allow_0_address = true;
+ mc->get_hotplug_handler = spapr_get_hotpug_handler;
+ hc->plug = spapr_machine_device_plug;
+ hc->unplug = spapr_machine_device_unplug;
+ mc->cpu_index_to_socket_id = spapr_cpu_index_to_socket_id;
+ smc->dr_lmb_enabled = false;
fwc->get_dev_path = spapr_get_fw_dev_path;
nc->nmi_monitor_handler = spapr_nmi;
}
@@ -1851,11 +2293,16 @@ static const TypeInfo spapr_machine_info = {
.interfaces = (InterfaceInfo[]) {
{ TYPE_FW_PATH_PROVIDER },
{ TYPE_NMI },
+ { TYPE_HOTPLUG_HANDLER },
{ }
},
};
+#define SPAPR_COMPAT_2_4 \
+ HW_COMPAT_2_4
+
#define SPAPR_COMPAT_2_3 \
+ SPAPR_COMPAT_2_4 \
HW_COMPAT_2_3 \
{\
.driver = "spapr-pci-host-bridge",\
@@ -1880,6 +2327,7 @@ static void spapr_compat_2_3(Object *obj)
{
savevm_skip_section_footers();
global_state_set_optional();
+ savevm_skip_configuration();
}
static void spapr_compat_2_2(Object *obj)
@@ -1918,13 +2366,12 @@ static void spapr_machine_2_1_class_init(ObjectClass *oc, void *data)
{ /* end of list */ }
};
- mc->name = "pseries-2.1";
mc->desc = "pSeries Logical Partition (PAPR compliant) v2.1";
mc->compat_props = compat_props;
}
static const TypeInfo spapr_machine_2_1_info = {
- .name = TYPE_SPAPR_MACHINE "2.1",
+ .name = MACHINE_TYPE_NAME("pseries-2.1"),
.parent = TYPE_SPAPR_MACHINE,
.class_init = spapr_machine_2_1_class_init,
.instance_init = spapr_machine_2_1_instance_init,
@@ -1938,13 +2385,12 @@ static void spapr_machine_2_2_class_init(ObjectClass *oc, void *data)
};
MachineClass *mc = MACHINE_CLASS(oc);
- mc->name = "pseries-2.2";
mc->desc = "pSeries Logical Partition (PAPR compliant) v2.2";
mc->compat_props = compat_props;
}
static const TypeInfo spapr_machine_2_2_info = {
- .name = TYPE_SPAPR_MACHINE "2.2",
+ .name = MACHINE_TYPE_NAME("pseries-2.2"),
.parent = TYPE_SPAPR_MACHINE,
.class_init = spapr_machine_2_2_class_init,
.instance_init = spapr_machine_2_2_instance_init,
@@ -1958,13 +2404,12 @@ static void spapr_machine_2_3_class_init(ObjectClass *oc, void *data)
};
MachineClass *mc = MACHINE_CLASS(oc);
- mc->name = "pseries-2.3";
mc->desc = "pSeries Logical Partition (PAPR compliant) v2.3";
mc->compat_props = compat_props;
}
static const TypeInfo spapr_machine_2_3_info = {
- .name = TYPE_SPAPR_MACHINE "2.3",
+ .name = MACHINE_TYPE_NAME("pseries-2.3"),
.parent = TYPE_SPAPR_MACHINE,
.class_init = spapr_machine_2_3_class_init,
.instance_init = spapr_machine_2_3_instance_init,
@@ -1972,20 +2417,40 @@ static const TypeInfo spapr_machine_2_3_info = {
static void spapr_machine_2_4_class_init(ObjectClass *oc, void *data)
{
+ static GlobalProperty compat_props[] = {
+ SPAPR_COMPAT_2_4
+ { /* end of list */ }
+ };
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;
+ mc->compat_props = compat_props;
}
static const TypeInfo spapr_machine_2_4_info = {
- .name = TYPE_SPAPR_MACHINE "2.4",
+ .name = MACHINE_TYPE_NAME("pseries-2.4"),
.parent = TYPE_SPAPR_MACHINE,
.class_init = spapr_machine_2_4_class_init,
};
+static void spapr_machine_2_5_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(oc);
+
+ mc->name = "pseries-2.5";
+ mc->desc = "pSeries Logical Partition (PAPR compliant) v2.5";
+ mc->alias = "pseries";
+ mc->is_default = 1;
+ smc->dr_lmb_enabled = true;
+}
+
+static const TypeInfo spapr_machine_2_5_info = {
+ .name = MACHINE_TYPE_NAME("pseries-2.5"),
+ .parent = TYPE_SPAPR_MACHINE,
+ .class_init = spapr_machine_2_5_class_init,
+};
+
static void spapr_machine_register_types(void)
{
type_register_static(&spapr_machine_info);
@@ -1993,6 +2458,7 @@ static void spapr_machine_register_types(void)
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_register_static(&spapr_machine_2_5_info);
}
type_init(spapr_machine_register_types)
diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
index ee874326e..8be62c349 100644
--- a/hw/ppc/spapr_drc.c
+++ b/hw/ppc/spapr_drc.c
@@ -15,6 +15,7 @@
#include "hw/qdev.h"
#include "qapi/visitor.h"
#include "qemu/error-report.h"
+#include "hw/ppc/spapr.h" /* for RTAS return codes */
/* #define DEBUG_SPAPR_DRC */
@@ -32,7 +33,7 @@
#define DRC_CONTAINER_PATH "/dr-connector"
#define DRC_INDEX_TYPE_SHIFT 28
-#define DRC_INDEX_ID_MASK (~(~0 << DRC_INDEX_TYPE_SHIFT))
+#define DRC_INDEX_ID_MASK ((1ULL << DRC_INDEX_TYPE_SHIFT) - 1)
static sPAPRDRConnectorTypeShift get_type_shift(sPAPRDRConnectorType type)
{
@@ -59,13 +60,23 @@ static uint32_t get_index(sPAPRDRConnector *drc)
(drc->id & DRC_INDEX_ID_MASK);
}
-static int set_isolation_state(sPAPRDRConnector *drc,
- sPAPRDRIsolationState state)
+static uint32_t 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);
+ if (state == SPAPR_DR_ISOLATION_STATE_UNISOLATED) {
+ /* cannot unisolate a non-existant resource, and, or resources
+ * which are in an 'UNUSABLE' allocation state. (PAPR 2.7, 13.5.3.5)
+ */
+ if (!drc->dev ||
+ drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) {
+ return RTAS_OUT_NO_SUCH_INDICATOR;
+ }
+ }
+
drc->isolation_state = state;
if (drc->isolation_state == SPAPR_DR_ISOLATION_STATE_ISOLATED) {
@@ -89,24 +100,35 @@ static int set_isolation_state(sPAPRDRConnector *drc,
drc->configured = false;
}
- return 0;
+ return RTAS_OUT_SUCCESS;
}
-static int set_indicator_state(sPAPRDRConnector *drc,
- sPAPRDRIndicatorState state)
+static uint32_t set_indicator_state(sPAPRDRConnector *drc,
+ sPAPRDRIndicatorState state)
{
DPRINTFN("drc: %x, set_indicator_state: %x", get_index(drc), state);
drc->indicator_state = state;
- return 0;
+ return RTAS_OUT_SUCCESS;
}
-static int set_allocation_state(sPAPRDRConnector *drc,
- sPAPRDRAllocationState state)
+static uint32_t 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 (state == SPAPR_DR_ALLOCATION_STATE_USABLE) {
+ /* if there's no resource/device associated with the DRC, there's
+ * no way for us to put it in an allocation state consistent with
+ * being 'USABLE'. PAPR 2.7, 13.5.3.4 documents that this should
+ * result in an RTAS return code of -3 / "no such indicator"
+ */
+ if (!drc->dev) {
+ return RTAS_OUT_NO_SUCH_INDICATOR;
+ }
+ }
+
if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI) {
drc->allocation_state = state;
if (drc->awaiting_release &&
@@ -116,7 +138,7 @@ static int set_allocation_state(sPAPRDRConnector *drc,
drc->detach_cb_opaque, NULL);
}
}
- return 0;
+ return RTAS_OUT_SUCCESS;
}
static uint32_t get_type(sPAPRDRConnector *drc)
@@ -157,10 +179,8 @@ static void set_configured(sPAPRDRConnector *drc)
* based on the current allocation/indicator/power states
* for the DR connector.
*/
-static sPAPRDREntitySense entity_sense(sPAPRDRConnector *drc)
+static uint32_t entity_sense(sPAPRDRConnector *drc, sPAPRDREntitySense *state)
{
- sPAPRDREntitySense state;
-
if (drc->dev) {
if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI &&
drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) {
@@ -169,7 +189,7 @@ static sPAPRDREntitySense entity_sense(sPAPRDRConnector *drc)
* Otherwise, report the state as USABLE/PRESENT,
* as we would for PCI.
*/
- state = SPAPR_DR_ENTITY_SENSE_UNUSABLE;
+ *state = SPAPR_DR_ENTITY_SENSE_UNUSABLE;
} else {
/* this assumes all PCI devices are assigned to
* a 'live insertion' power domain, where QEMU
@@ -177,21 +197,21 @@ static sPAPRDREntitySense entity_sense(sPAPRDRConnector *drc)
* to the guest. present, non-PCI resources are
* unaffected by power state.
*/
- state = SPAPR_DR_ENTITY_SENSE_PRESENT;
+ *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;
+ *state = SPAPR_DR_ENTITY_SENSE_EMPTY;
} else {
- state = SPAPR_DR_ENTITY_SENSE_UNUSABLE;
+ *state = SPAPR_DR_ENTITY_SENSE_UNUSABLE;
}
}
DPRINTFN("drc: %x, entity_sense: %x", get_index(drc), state);
- return state;
+ return RTAS_OUT_SUCCESS;
}
static void prop_get_index(Object *obj, Visitor *v, void *opaque,
@@ -224,7 +244,9 @@ static void prop_get_entity_sense(Object *obj, Visitor *v, void *opaque,
{
sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
- uint32_t value = (uint32_t)drck->entity_sense(drc);
+ uint32_t value;
+
+ drck->entity_sense(drc, &value);
visit_type_uint32(v, &value, name, errp);
}
@@ -232,10 +254,16 @@ static void prop_get_fdt(Object *obj, Visitor *v, void *opaque,
const char *name, Error **errp)
{
sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
+ Error *err = NULL;
int fdt_offset_next, fdt_offset, fdt_depth;
void *fdt;
if (!drc->fdt) {
+ visit_start_struct(v, NULL, NULL, name, 0, &err);
+ if (!err) {
+ visit_end_struct(v, &err);
+ }
+ error_propagate(errp, err);
return;
}
@@ -254,24 +282,43 @@ static void prop_get_fdt(Object *obj, Visitor *v, void *opaque,
case FDT_BEGIN_NODE:
fdt_depth++;
name = fdt_get_name(fdt, fdt_offset, &name_len);
- visit_start_struct(v, NULL, NULL, name, 0, NULL);
+ visit_start_struct(v, NULL, NULL, name, 0, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
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);
+ visit_end_struct(v, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
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);
+ visit_start_list(v, name, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
for (i = 0; i < prop_len; i++) {
- visit_type_uint8(v, (uint8_t *)&prop->data[i], NULL, NULL);
-
+ visit_type_uint8(v, (uint8_t *)&prop->data[i], NULL, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ }
+ visit_end_list(v, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
}
- visit_end_list(v, NULL);
break;
}
default:
@@ -310,7 +357,7 @@ static void attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt,
drc->dev = d;
drc->fdt = fdt;
drc->fdt_start_offset = fdt_start_offset;
- drc->configured = false;
+ drc->configured = coldplug;
object_property_add_link(OBJECT(drc), "device",
object_get_typename(OBJECT(drc->dev)),
@@ -451,14 +498,17 @@ sPAPRDRConnector *spapr_dr_connector_new(Object *owner,
{
sPAPRDRConnector *drc =
SPAPR_DR_CONNECTOR(object_new(TYPE_SPAPR_DR_CONNECTOR));
+ char *prop_name;
g_assert(type);
drc->type = type;
drc->id = id;
drc->owner = owner;
- object_property_add_child(owner, "dr-connector[*]", OBJECT(drc), NULL);
+ prop_name = g_strdup_printf("dr-connector[%"PRIu32"]", get_index(drc));
+ object_property_add_child(owner, prop_name, OBJECT(drc), NULL);
object_property_set_bool(OBJECT(drc), true, "realized", NULL);
+ g_free(prop_name);
/* human-readable name for a DRC to encode into the DT
* description. this is mainly only used within a guest in place
@@ -549,6 +599,10 @@ static void spapr_dr_connector_class_init(ObjectClass *k, void *data)
drck->attach = attach;
drck->detach = detach;
drck->release_pending = release_pending;
+ /*
+ * Reason: it crashes FIXME find and document the real reason
+ */
+ dk->cannot_instantiate_with_device_add_yet = true;
}
static const TypeInfo spapr_dr_connector_info = {
@@ -632,6 +686,7 @@ int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner,
{
Object *root_container;
ObjectProperty *prop;
+ ObjectPropertyIterator *iter;
uint32_t drc_count = 0;
GArray *drc_indexes, *drc_power_domains;
GString *drc_names, *drc_types;
@@ -655,7 +710,8 @@ int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner,
*/
root_container = container_get(object_get_root(), DRC_CONTAINER_PATH);
- QTAILQ_FOREACH(prop, &root_container->properties, node) {
+ iter = object_property_iter_init(root_container);
+ while ((prop = object_property_iter_next(iter))) {
Object *obj;
sPAPRDRConnector *drc;
sPAPRDRConnectorClass *drck;
@@ -696,6 +752,7 @@ int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner,
spapr_drc_get_type_str(drc->type));
drc_types = g_string_insert_len(drc_types, -1, "\0", 1);
}
+ object_property_iter_free(iter);
/* now write the drc count into the space we reserved at the
* beginning of the arrays previously
diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c
index f626eb7b3..744ea62ac 100644
--- a/hw/ppc/spapr_events.c
+++ b/hw/ppc/spapr_events.c
@@ -386,7 +386,9 @@ static void spapr_powerdown_req(Notifier *n, void *opaque)
qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
}
-static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action)
+static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action,
+ sPAPRDRConnectorType drc_type,
+ uint32_t drc)
{
sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
struct hp_log_full *new_hp;
@@ -395,8 +397,6 @@ static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action)
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;
@@ -427,14 +427,15 @@ static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action)
hp->hdr.section_length = cpu_to_be16(sizeof(*hp));
hp->hdr.section_version = 1; /* includes extended modifier */
hp->hotplug_action = hp_action;
-
+ hp->hotplug_identifier = hp_id;
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;
+ case SPAPR_DR_CONNECTOR_TYPE_LMB:
+ hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_MEMORY;
+ break;
default:
/* we shouldn't be signaling hotplug events for resources
* that don't support them
@@ -443,19 +444,49 @@ static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action)
return;
}
+ if (hp_id == RTAS_LOG_V6_HP_ID_DRC_COUNT) {
+ hp->drc.count = cpu_to_be32(drc);
+ } else if (hp_id == RTAS_LOG_V6_HP_ID_DRC_INDEX) {
+ hp->drc.index = cpu_to_be32(drc);
+ }
+
rtas_event_log_queue(RTAS_LOG_TYPE_HOTPLUG, new_hp, true);
qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
}
-void spapr_hotplug_req_add_event(sPAPRDRConnector *drc)
+void spapr_hotplug_req_add_by_index(sPAPRDRConnector *drc)
+{
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ sPAPRDRConnectorType drc_type = drck->get_type(drc);
+ uint32 index = drck->get_index(drc);
+
+ spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_INDEX,
+ RTAS_LOG_V6_HP_ACTION_ADD, drc_type, index);
+}
+
+void spapr_hotplug_req_remove_by_index(sPAPRDRConnector *drc)
+{
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ sPAPRDRConnectorType drc_type = drck->get_type(drc);
+ uint32 index = drck->get_index(drc);
+
+ spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_INDEX,
+ RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, index);
+}
+
+void spapr_hotplug_req_add_by_count(sPAPRDRConnectorType drc_type,
+ uint32_t count)
{
- spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_ADD);
+ spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_COUNT,
+ RTAS_LOG_V6_HP_ACTION_ADD, drc_type, count);
}
-void spapr_hotplug_req_remove_event(sPAPRDRConnector *drc)
+void spapr_hotplug_req_remove_by_count(sPAPRDRConnectorType drc_type,
+ uint32_t count)
{
- spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_REMOVE);
+ spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_COUNT,
+ RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, count);
}
static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr,
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index 652ddf6e3..cebceea69 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -808,6 +808,32 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPRMachineState *spapr,
return ret;
}
+/*
+ * Return the offset to the requested option vector @vector in the
+ * option vector table @table.
+ */
+static target_ulong cas_get_option_vector(int vector, target_ulong table)
+{
+ int i;
+ char nr_vectors, nr_entries;
+
+ if (!table) {
+ return 0;
+ }
+
+ nr_vectors = (ldl_phys(&address_space_memory, table) >> 24) + 1;
+ if (!vector || vector > nr_vectors) {
+ return 0;
+ }
+ table++; /* skip nr option vectors */
+
+ for (i = 0; i < vector - 1; i++) {
+ nr_entries = ldl_phys(&address_space_memory, table) >> 24;
+ table += nr_entries + 2;
+ }
+ return table;
+}
+
typedef struct {
PowerPCCPU *cpu;
uint32_t cpu_version;
@@ -828,19 +854,22 @@ static void do_set_compat(void *arg)
((cpuver) == CPU_POWERPC_LOGICAL_2_06_PLUS) ? 2061 : \
((cpuver) == CPU_POWERPC_LOGICAL_2_07) ? 2070 : 0)
+#define OV5_DRCONF_MEMORY 0x20
+
static target_ulong h_client_architecture_support(PowerPCCPU *cpu_,
sPAPRMachineState *spapr,
target_ulong opcode,
target_ulong *args)
{
- target_ulong list = args[0];
+ target_ulong list = args[0], ov_table;
PowerPCCPUClass *pcc_ = POWERPC_CPU_GET_CLASS(cpu_);
CPUState *cs;
- bool cpu_match = false;
+ bool cpu_match = false, cpu_update = true, memory_update = false;
unsigned old_cpu_version = cpu_->cpu_version;
unsigned compat_lvl = 0, cpu_version = 0;
unsigned max_lvl = get_compat_level(cpu_->max_compat);
int counter;
+ char ov5_byte2;
/* Parse PVR list */
for (counter = 0; counter < 512; ++counter) {
@@ -890,8 +919,6 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_,
}
}
- /* For the future use: here @list points to the first capability */
-
/* Parsing finished */
trace_spapr_cas_pvr(cpu_->cpu_version, cpu_match,
cpu_version, pcc_->pcr_mask);
@@ -915,14 +942,26 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_,
}
if (!cpu_version) {
- return H_SUCCESS;
+ cpu_update = false;
}
+ /* For the future use: here @ov_table points to the first option vector */
+ ov_table = list;
+
+ list = cas_get_option_vector(5, ov_table);
if (!list) {
return H_SUCCESS;
}
- if (spapr_h_cas_compose_response(spapr, args[1], args[2])) {
+ /* @list now points to OV 5 */
+ list += 2;
+ ov5_byte2 = rtas_ld(list, 0) >> 24;
+ if (ov5_byte2 & OV5_DRCONF_MEMORY) {
+ memory_update = true;
+ }
+
+ if (spapr_h_cas_compose_response(spapr, args[1], args[2],
+ cpu_update, memory_update)) {
qemu_system_reset_request();
}
@@ -971,7 +1010,8 @@ target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode,
}
}
- hcall_dprintf("Unimplemented hcall 0x" TARGET_FMT_lx "\n", opcode);
+ qemu_log_mask(LOG_UNIMP, "Unimplemented SPAPR hcall 0x" TARGET_FMT_lx "\n",
+ opcode);
return H_FUNCTION;
}
diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c
index f61504e0c..ed28565d8 100644
--- a/hw/ppc/spapr_iommu.c
+++ b/hw/ppc/spapr_iommu.c
@@ -146,7 +146,7 @@ static int spapr_tce_table_realize(DeviceState *dev)
tcet->table = kvmppc_create_spapr_tce(tcet->liobn,
window_size,
&tcet->fd,
- tcet->vfio_accel);
+ tcet->need_vfio);
}
if (!tcet->table) {
@@ -168,11 +168,43 @@ static int spapr_tce_table_realize(DeviceState *dev)
return 0;
}
+void spapr_tce_set_need_vfio(sPAPRTCETable *tcet, bool need_vfio)
+{
+ size_t table_size = tcet->nb_table * sizeof(uint64_t);
+ void *newtable;
+
+ if (need_vfio == tcet->need_vfio) {
+ /* Nothing to do */
+ return;
+ }
+
+ if (!need_vfio) {
+ /* FIXME: We don't support transition back to KVM accelerated
+ * TCEs yet */
+ return;
+ }
+
+ tcet->need_vfio = true;
+
+ if (tcet->fd < 0) {
+ /* Table is already in userspace, nothing to be do */
+ return;
+ }
+
+ newtable = g_malloc(table_size);
+ memcpy(newtable, tcet->table, table_size);
+
+ kvmppc_remove_spapr_tce(tcet->table, tcet->fd, tcet->nb_table);
+
+ tcet->fd = -1;
+ tcet->table = newtable;
+}
+
sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn,
uint64_t bus_offset,
uint32_t page_shift,
uint32_t nb_table,
- bool vfio_accel)
+ bool need_vfio)
{
sPAPRTCETable *tcet;
char tmp[64];
@@ -192,7 +224,7 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn,
tcet->bus_offset = bus_offset;
tcet->page_shift = page_shift;
tcet->nb_table = nb_table;
- tcet->vfio_accel = vfio_accel;
+ tcet->need_vfio = need_vfio;
snprintf(tmp, sizeof(tmp), "tce-table-%x", liobn);
object_property_add_child(OBJECT(owner), tmp, OBJECT(tcet), NULL);
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index 119fa5e46..55fa8db9e 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -140,7 +140,7 @@ static void rtas_ibm_read_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr,
return;
}
- buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ buid = rtas_ldq(args, 1);
size = rtas_ld(args, 3);
addr = rtas_ld(args, 0);
@@ -206,7 +206,7 @@ static void rtas_ibm_write_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr,
return;
}
- buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ buid = rtas_ldq(args, 1);
val = rtas_ld(args, 4);
size = rtas_ld(args, 3);
addr = rtas_ld(args, 0);
@@ -269,7 +269,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong rets)
{
uint32_t config_addr = rtas_ld(args, 0);
- uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ uint64_t buid = rtas_ldq(args, 1);
unsigned int func = rtas_ld(args, 3);
unsigned int req_num = rtas_ld(args, 4); /* 0 == remove all */
unsigned int seq_num = rtas_ld(args, 5);
@@ -375,7 +375,9 @@ out:
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
rtas_st(rets, 1, req_num);
rtas_st(rets, 2, ++seq_num);
- rtas_st(rets, 3, ret_intr_type);
+ if (nret > 3) {
+ rtas_st(rets, 3, ret_intr_type);
+ }
trace_spapr_pci_rtas_ibm_change_msi(config_addr, func, req_num, irq);
}
@@ -389,7 +391,7 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu,
target_ulong rets)
{
uint32_t config_addr = rtas_ld(args, 0);
- uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ uint64_t buid = rtas_ldq(args, 1);
unsigned int intr_src_num = -1, ioa_intr_num = rtas_ld(args, 3);
sPAPRPHBState *phb = NULL;
PCIDevice *pdev = NULL;
@@ -429,7 +431,6 @@ static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu,
{
sPAPRPHBState *sphb;
sPAPRPHBClass *spc;
- PCIDevice *pdev;
uint32_t addr, option;
uint64_t buid;
int ret;
@@ -438,7 +439,7 @@ static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu,
goto param_error_exit;
}
- buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ buid = rtas_ldq(args, 1);
addr = rtas_ld(args, 0);
option = rtas_ld(args, 3);
@@ -447,12 +448,6 @@ static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu,
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;
@@ -482,7 +477,7 @@ static void rtas_ibm_get_config_addr_info2(PowerPCCPU *cpu,
goto param_error_exit;
}
- buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ buid = rtas_ldq(args, 1);
sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
@@ -537,7 +532,7 @@ static void rtas_ibm_read_slot_reset_state2(PowerPCCPU *cpu,
goto param_error_exit;
}
- buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ buid = rtas_ldq(args, 1);
sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
@@ -582,7 +577,7 @@ static void rtas_ibm_set_slot_reset(PowerPCCPU *cpu,
goto param_error_exit;
}
- buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ buid = rtas_ldq(args, 1);
option = rtas_ld(args, 3);
sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
@@ -617,7 +612,7 @@ static void rtas_ibm_configure_pe(PowerPCCPU *cpu,
goto param_error_exit;
}
- buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ buid = rtas_ldq(args, 1);
sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
@@ -652,7 +647,7 @@ static void rtas_ibm_slot_error_detail(PowerPCCPU *cpu,
goto param_error_exit;
}
- buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
+ buid = rtas_ldq(args, 1);
sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
@@ -1088,6 +1083,12 @@ static void spapr_phb_add_pci_device(sPAPRDRConnector *drc,
void *fdt = NULL;
int fdt_start_offset = 0, fdt_size;
+ if (object_dynamic_cast(OBJECT(pdev), "vfio-pci")) {
+ sPAPRTCETable *tcet = spapr_tce_find_by_liobn(phb->dma_liobn);
+
+ spapr_tce_set_need_vfio(tcet, true);
+ }
+
if (dev->hotplugged) {
fdt = create_device_tree(&fdt_size);
fdt_start_offset = spapr_create_pci_child_dt(phb, pdev, fdt, 0);
@@ -1185,7 +1186,7 @@ static void spapr_phb_hot_plug_child(HotplugHandler *plug_handler,
return;
}
if (plugged_dev->hotplugged) {
- spapr_hotplug_req_add_event(drc);
+ spapr_hotplug_req_add_by_index(drc);
}
}
@@ -1213,7 +1214,7 @@ static void spapr_phb_hot_unplug_child(HotplugHandler *plug_handler,
error_propagate(errp, local_err);
return;
}
- spapr_hotplug_req_remove_event(drc);
+ spapr_hotplug_req_remove_by_index(drc);
}
}
@@ -1392,7 +1393,7 @@ 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;
+ nb_table = sphb->dma_win_size >> SPAPR_TCE_PAGE_SHIFT;
tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn,
0, SPAPR_TCE_PAGE_SHIFT, nb_table, false);
if (!tcet) {
@@ -1402,7 +1403,7 @@ static void spapr_phb_finish_realize(sPAPRPHBState *sphb, Error **errp)
}
/* Register default 32bit DMA window */
- memory_region_add_subregion(&sphb->iommu_root, 0,
+ memory_region_add_subregion(&sphb->iommu_root, sphb->dma_win_addr,
spapr_tce_get_iommu(tcet));
}
@@ -1435,6 +1436,9 @@ static Property spapr_phb_properties[] = {
SPAPR_PCI_IO_WIN_SIZE),
DEFINE_PROP_BOOL("dynamic-reconfiguration", sPAPRPHBState, dr_enabled,
true),
+ /* Default DMA window is 0..1GB */
+ DEFINE_PROP_UINT64("dma_win_addr", sPAPRPHBState, dma_win_addr, 0),
+ DEFINE_PROP_UINT64("dma_win_size", sPAPRPHBState, dma_win_size, 0x40000000),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1468,10 +1472,8 @@ static void spapr_pci_pre_save(void *opaque)
gpointer key, value;
int i;
- if (sphb->msi_devs) {
- g_free(sphb->msi_devs);
- sphb->msi_devs = NULL;
- }
+ g_free(sphb->msi_devs);
+ sphb->msi_devs = NULL;
sphb->msi_devs_num = g_hash_table_size(sphb->msi);
if (!sphb->msi_devs_num) {
return;
@@ -1498,10 +1500,8 @@ static int spapr_pci_post_load(void *opaque, int version_id)
sizeof(sphb->msi_devs[i].value));
g_hash_table_insert(sphb->msi, key, value);
}
- if (sphb->msi_devs) {
- g_free(sphb->msi_devs);
- sphb->msi_devs = NULL;
- }
+ g_free(sphb->msi_devs);
+ sphb->msi_devs = NULL;
sphb->msi_devs_num = 0;
return 0;
diff --git a/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c
index cca45ed31..a61b41813 100644
--- a/hw/ppc/spapr_pci_vfio.c
+++ b/hw/ppc/spapr_pci_vfio.c
@@ -117,7 +117,7 @@ static int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb,
phb = PCI_HOST_BRIDGE(sphb);
pdev = pci_find_device(phb->bus,
(addr >> 16) & 0xFF, (addr >> 8) & 0xFF);
- if (!pdev) {
+ if (!pdev || !object_dynamic_cast(OBJECT(pdev), "vfio-pci")) {
return RTAS_OUT_PARAM_ERROR;
}
diff --git a/hw/ppc/spapr_rng.c b/hw/ppc/spapr_rng.c
new file mode 100644
index 000000000..ed43d5e04
--- /dev/null
+++ b/hw/ppc/spapr_rng.c
@@ -0,0 +1,186 @@
+/*
+ * QEMU sPAPR random number generator "device" for H_RANDOM hypercall
+ *
+ * Copyright 2015 Thomas Huth, Red Hat Inc.
+ *
+ * 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/error-report.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/device_tree.h"
+#include "sysemu/rng.h"
+#include "hw/ppc/spapr.h"
+#include "kvm_ppc.h"
+
+#define SPAPR_RNG(obj) \
+ OBJECT_CHECK(sPAPRRngState, (obj), TYPE_SPAPR_RNG)
+
+struct sPAPRRngState {
+ /*< private >*/
+ DeviceState ds;
+ RngBackend *backend;
+ bool use_kvm;
+};
+typedef struct sPAPRRngState sPAPRRngState;
+
+struct HRandomData {
+ QemuSemaphore sem;
+ union {
+ uint64_t v64;
+ uint8_t v8[8];
+ } val;
+ int received;
+};
+typedef struct HRandomData HRandomData;
+
+/* Callback function for the RngBackend */
+static void random_recv(void *dest, const void *src, size_t size)
+{
+ HRandomData *hrdp = dest;
+
+ if (src && size > 0) {
+ assert(size + hrdp->received <= sizeof(hrdp->val.v8));
+ memcpy(&hrdp->val.v8[hrdp->received], src, size);
+ hrdp->received += size;
+ }
+
+ qemu_sem_post(&hrdp->sem);
+}
+
+/* Handler for the H_RANDOM hypercall */
+static target_ulong h_random(PowerPCCPU *cpu, sPAPRMachineState *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ sPAPRRngState *rngstate;
+ HRandomData hrdata;
+
+ rngstate = SPAPR_RNG(object_resolve_path_type("", TYPE_SPAPR_RNG, NULL));
+
+ if (!rngstate || !rngstate->backend) {
+ return H_HARDWARE;
+ }
+
+ qemu_sem_init(&hrdata.sem, 0);
+ hrdata.val.v64 = 0;
+ hrdata.received = 0;
+
+ qemu_mutex_unlock_iothread();
+ while (hrdata.received < 8) {
+ rng_backend_request_entropy(rngstate->backend, 8 - hrdata.received,
+ random_recv, &hrdata);
+ qemu_sem_wait(&hrdata.sem);
+ }
+ qemu_mutex_lock_iothread();
+
+ qemu_sem_destroy(&hrdata.sem);
+ args[0] = hrdata.val.v64;
+
+ return H_SUCCESS;
+}
+
+static void spapr_rng_instance_init(Object *obj)
+{
+ sPAPRRngState *rngstate = SPAPR_RNG(obj);
+
+ if (object_resolve_path_type("", TYPE_SPAPR_RNG, NULL) != NULL) {
+ error_report("spapr-rng can not be instantiated twice!");
+ return;
+ }
+
+ object_property_add_link(obj, "rng", TYPE_RNG_BACKEND,
+ (Object **)&rngstate->backend,
+ object_property_allow_set_link,
+ OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL);
+ object_property_set_description(obj, "rng",
+ "ID of the random number generator backend",
+ NULL);
+}
+
+static void spapr_rng_realize(DeviceState *dev, Error **errp)
+{
+
+ sPAPRRngState *rngstate = SPAPR_RNG(dev);
+
+ if (rngstate->use_kvm) {
+ if (kvmppc_enable_hwrng() == 0) {
+ return;
+ }
+ /*
+ * If user specified both, use-kvm and a backend, we fall back to
+ * the backend now. If not, provide an appropriate error message.
+ */
+ if (!rngstate->backend) {
+ error_setg(errp, "Could not initialize in-kernel H_RANDOM call!");
+ return;
+ }
+ }
+
+ if (rngstate->backend) {
+ spapr_register_hypercall(H_RANDOM, h_random);
+ } else {
+ error_setg(errp, "spapr-rng needs an RNG backend!");
+ }
+}
+
+int spapr_rng_populate_dt(void *fdt)
+{
+ int node;
+ int ret;
+
+ node = qemu_fdt_add_subnode(fdt, "/ibm,platform-facilities");
+ if (node <= 0) {
+ return -1;
+ }
+ ret = fdt_setprop_string(fdt, node, "device_type",
+ "ibm,platform-facilities");
+ ret |= fdt_setprop_cell(fdt, node, "#address-cells", 0x1);
+ ret |= fdt_setprop_cell(fdt, node, "#size-cells", 0x0);
+
+ node = fdt_add_subnode(fdt, node, "ibm,random-v1");
+ if (node <= 0) {
+ return -1;
+ }
+ ret |= fdt_setprop_string(fdt, node, "compatible", "ibm,random");
+
+ return ret ? -1 : 0;
+}
+
+static Property spapr_rng_properties[] = {
+ DEFINE_PROP_BOOL("use-kvm", sPAPRRngState, use_kvm, false),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void spapr_rng_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = spapr_rng_realize;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->props = spapr_rng_properties;
+}
+
+static const TypeInfo spapr_rng_info = {
+ .name = TYPE_SPAPR_RNG,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(sPAPRRngState),
+ .instance_init = spapr_rng_instance_init,
+ .class_init = spapr_rng_class_init,
+};
+
+static void spapr_rng_register_type(void)
+{
+ type_register_static(&spapr_rng_info);
+}
+type_init(spapr_rng_register_type)
diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c
index 2986f94f0..34b12a3b9 100644
--- a/hw/ppc/spapr_rtas.c
+++ b/hw/ppc/spapr_rtas.c
@@ -34,6 +34,7 @@
#include "hw/ppc/spapr.h"
#include "hw/ppc/spapr_vio.h"
#include "qapi-event.h"
+#include "hw/boards.h"
#include <libfdt.h>
#include "hw/ppc/spapr_drc.h"
@@ -214,7 +215,7 @@ static void rtas_stop_self(PowerPCCPU *cpu, sPAPRMachineState *spapr,
CPUPPCState *env = &cpu->env;
cs->halted = 1;
- cpu_exit(cs);
+ qemu_cpu_kick(cs);
/*
* While stopping a CPU, the guest calls H_CPPR which
* effectively disables interrupts on XICS level.
@@ -240,8 +241,14 @@ static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu,
switch (parameter) {
case RTAS_SYSPARM_SPLPAR_CHARACTERISTICS: {
- char *param_val = g_strdup_printf("MaxEntCap=%d,MaxPlatProcs=%d",
- max_cpus, smp_cpus);
+ char *param_val = g_strdup_printf("MaxEntCap=%d,"
+ "DesMem=%llu,"
+ "DesProcs=%d,"
+ "MaxPlatProcs=%d",
+ max_cpus,
+ current_machine->ram_size / M_BYTE,
+ smp_cpus,
+ max_cpus);
rtas_st_buffer(buffer, length, (uint8_t *)param_val, strlen(param_val));
g_free(param_val);
break;
@@ -365,12 +372,13 @@ static void rtas_set_indicator(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint32_t sensor_type;
uint32_t sensor_index;
uint32_t sensor_state;
+ uint32_t ret = RTAS_OUT_SUCCESS;
sPAPRDRConnector *drc;
sPAPRDRConnectorClass *drck;
if (nargs != 3 || nret != 1) {
- rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
- return;
+ ret = RTAS_OUT_PARAM_ERROR;
+ goto out;
}
sensor_type = rtas_ld(args, 0);
@@ -386,8 +394,8 @@ static void rtas_set_indicator(PowerPCCPU *cpu, sPAPRMachineState *spapr,
if (!drc) {
DPRINTF("rtas_set_indicator: invalid sensor/DRC index: %xh\n",
sensor_index);
- rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
- return;
+ ret = RTAS_OUT_PARAM_ERROR;
+ goto out;
}
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
@@ -406,19 +414,20 @@ static void rtas_set_indicator(PowerPCCPU *cpu, sPAPRMachineState *spapr,
spapr_ccs_remove(spapr, ccs);
}
}
- drck->set_isolation_state(drc, sensor_state);
+ ret = drck->set_isolation_state(drc, sensor_state);
break;
case RTAS_SENSOR_TYPE_DR:
- drck->set_indicator_state(drc, sensor_state);
+ ret = drck->set_indicator_state(drc, sensor_state);
break;
case RTAS_SENSOR_TYPE_ALLOCATION_STATE:
- drck->set_allocation_state(drc, sensor_state);
+ ret = drck->set_allocation_state(drc, sensor_state);
break;
default:
goto out_unimplemented;
}
- rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+out:
+ rtas_st(rets, 0, ret);
return;
out_unimplemented:
@@ -435,13 +444,14 @@ static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPRMachineState *spapr,
{
uint32_t sensor_type;
uint32_t sensor_index;
+ uint32_t sensor_state = 0;
sPAPRDRConnector *drc;
sPAPRDRConnectorClass *drck;
- uint32_t entity_sense;
+ uint32_t ret = RTAS_OUT_SUCCESS;
if (nargs != 2 || nret != 2) {
- rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
- return;
+ ret = RTAS_OUT_PARAM_ERROR;
+ goto out;
}
sensor_type = rtas_ld(args, 0);
@@ -451,22 +461,23 @@ static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPRMachineState *spapr,
/* 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;
+ ret = RTAS_OUT_NOT_SUPPORTED;
+ goto out;
}
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;
+ ret = RTAS_OUT_PARAM_ERROR;
+ goto out;
}
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
- entity_sense = drck->entity_sense(drc);
+ ret = drck->entity_sense(drc, &sensor_state);
- rtas_st(rets, 0, RTAS_OUT_SUCCESS);
- rtas_st(rets, 1, entity_sense);
+out:
+ rtas_st(rets, 0, ret);
+ rtas_st(rets, 1, sensor_state);
}
/* configure-connector work area offsets, int32_t units for field
@@ -515,6 +526,12 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu,
drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
fdt = drck->get_fdt(drc, NULL);
+ if (!fdt) {
+ DPRINTF("rtas_ibm_configure_connector: Missing FDT for DRC index: %xh\n",
+ drc_index);
+ rc = SPAPR_DR_CC_RESPONSE_NOT_CONFIGURABLE;
+ goto out;
+ }
ccs = spapr_ccs_find(spapr, drc_index);
if (!ccs) {
diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c
index de86f7c64..c2b5e441a 100644
--- a/hw/ppc/virtex_ml507.c
+++ b/hw/ppc/virtex_ml507.c
@@ -257,7 +257,7 @@ static void virtex_init(MachineState *machine)
/* Boots a kernel elf binary. */
kernel_size = load_elf(kernel_filename, NULL, NULL,
- &entry, &low, &high, 1, ELF_MACHINE, 0);
+ &entry, &low, &high, 1, PPC_ELF_MACHINE, 0);
boot_info.bootstrap_pc = entry & 0x00ffffff;
if (kernel_size < 0) {
@@ -297,15 +297,10 @@ static void virtex_init(MachineState *machine)
env->load_info = &boot_info;
}
-static QEMUMachine virtex_machine = {
- .name = "virtex-ml507",
- .desc = "Xilinx Virtex ML507 reference design",
- .init = virtex_init,
-};
-
-static void virtex_machine_init(void)
+static void virtex_machine_init(MachineClass *mc)
{
- qemu_register_machine(&virtex_machine);
+ mc->desc = "Xilinx Virtex ML507 reference design";
+ mc->init = virtex_init;
}
-machine_init(virtex_machine_init);
+DEFINE_MACHINE("virtex-ml507", virtex_machine_init)
diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs
index 27cd75a93..527d75400 100644
--- a/hw/s390x/Makefile.objs
+++ b/hw/s390x/Makefile.objs
@@ -9,3 +9,5 @@ obj-y += css.o
obj-y += s390-virtio-ccw.o
obj-y += virtio-ccw.o
obj-y += s390-pci-bus.o s390-pci-inst.o
+obj-y += s390-skeys.o
+obj-$(CONFIG_KVM) += s390-skeys-kvm.o
diff --git a/hw/s390x/css.c b/hw/s390x/css.c
index 97d93d56d..c6ca8bec4 100644
--- a/hw/s390x/css.c
+++ b/hw/s390x/css.c
@@ -261,6 +261,9 @@ static CCW1 copy_ccw_from_guest(hwaddr addr, bool fmt1)
ret.flags = tmp0.flags;
ret.count = be16_to_cpu(tmp0.count);
ret.cda = be16_to_cpu(tmp0.cda1) | (tmp0.cda0 << 16);
+ if ((ret.cmd_code & 0x0f) == CCW_CMD_TIC) {
+ ret.cmd_code &= 0x0f;
+ }
}
return ret;
}
@@ -287,6 +290,10 @@ static int css_interpret_ccw(SubchDev *sch, hwaddr ccw_addr)
((ccw.cmd_code & 0xf0) != 0)) {
return -EINVAL;
}
+ if (!sch->ccw_fmt_1 && (ccw.count == 0) &&
+ (ccw.cmd_code != CCW_CMD_TIC)) {
+ return -EINVAL;
+ }
if (ccw.flags & CCW_FLAG_SUSPEND) {
return -EINPROGRESS;
@@ -694,7 +701,7 @@ int css_do_csch(SubchDev *sch)
/* Trigger the clear function. */
s->ctrl &= ~(SCSW_CTRL_MASK_FCTL | SCSW_CTRL_MASK_ACTL);
- s->ctrl |= SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_CLEAR_FUNC;
+ s->ctrl |= SCSW_FCTL_CLEAR_FUNC | SCSW_ACTL_CLEAR_PEND;
do_subchannel_work(sch, NULL);
ret = 0;
@@ -885,8 +892,14 @@ int css_do_tsch_get_irb(SubchDev *sch, IRB *target_irb, int *irb_len)
/* If a unit check is pending, copy sense data. */
if ((s->dstat & SCSW_DSTAT_UNIT_CHECK) &&
(p->chars & PMCW_CHARS_MASK_CSENSE)) {
+ int i;
+
irb.scsw.flags |= SCSW_FLAGS_MASK_ESWF | SCSW_FLAGS_MASK_ECTL;
+ /* Attention: sense_data is already BE! */
memcpy(irb.ecw, sch->sense_data, sizeof(sch->sense_data));
+ for (i = 0; i < ARRAY_SIZE(irb.ecw); i++) {
+ irb.ecw[i] = be32_to_cpu(irb.ecw[i]);
+ }
irb.esw[1] = 0x01000000 | (sizeof(sch->sense_data) << 8);
}
}
diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c
index 0c700effb..907b48560 100644
--- a/hw/s390x/event-facility.c
+++ b/hw/s390x/event-facility.c
@@ -31,8 +31,6 @@ struct SCLPEventFacility {
unsigned int receive_mask;
};
-static SCLPEvent cpu_hotplug;
-
/* return true if any child has event pending set */
static bool event_pending(SCLPEventFacility *ef)
{
@@ -240,12 +238,13 @@ static void read_event_data(SCLPEventFacility *ef, SCCB *sccb)
sclp_active_selection_mask = sclp_cp_receive_mask;
break;
case SCLP_SELECTIVE_READ:
- if (!(sclp_cp_receive_mask & be32_to_cpu(red->mask))) {
+ sclp_active_selection_mask = be32_to_cpu(red->mask);
+ if (!sclp_cp_receive_mask ||
+ (sclp_active_selection_mask & ~sclp_cp_receive_mask)) {
sccb->h.response_code =
cpu_to_be16(SCLP_RC_INVALID_SELECTION_MASK);
goto out;
}
- sclp_active_selection_mask = be32_to_cpu(red->mask);
break;
default:
sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_FUNCTION);
@@ -286,8 +285,26 @@ out:
#define TYPE_SCLP_EVENTS_BUS "s390-sclp-events-bus"
+static void sclp_events_bus_realize(BusState *bus, Error **errp)
+{
+ BusChild *kid;
+
+ /* TODO: recursive realization has to be done in common code */
+ QTAILQ_FOREACH(kid, &bus->children, sibling) {
+ DeviceState *dev = kid->child;
+
+ object_property_set_bool(OBJECT(dev), true, "realized", errp);
+ if (*errp) {
+ return;
+ }
+ }
+}
+
static void sclp_events_bus_class_init(ObjectClass *klass, void *data)
{
+ BusClass *bc = BUS_CLASS(klass);
+
+ bc->realize = sclp_events_bus_realize;
}
static const TypeInfo sclp_events_bus_info = {
@@ -324,26 +341,26 @@ static const VMStateDescription vmstate_event_facility = {
}
};
-static int init_event_facility(SCLPEventFacility *event_facility)
+static void init_event_facility(Object *obj)
{
- DeviceState *sdev = DEVICE(event_facility);
- DeviceState *quiesce;
+ SCLPEventFacility *event_facility = EVENT_FACILITY(obj);
+ DeviceState *sdev = DEVICE(obj);
+ Object *new;
/* Spawn a new bus for SCLP events */
qbus_create_inplace(&event_facility->sbus, sizeof(event_facility->sbus),
TYPE_SCLP_EVENTS_BUS, sdev, NULL);
- quiesce = qdev_create(&event_facility->sbus.qbus, "sclpquiesce");
- if (!quiesce) {
- return -1;
- }
- qdev_init_nofail(quiesce);
-
- object_initialize(&cpu_hotplug, sizeof(cpu_hotplug), TYPE_SCLP_CPU_HOTPLUG);
- qdev_set_parent_bus(DEVICE(&cpu_hotplug), BUS(&event_facility->sbus));
- object_property_set_bool(OBJECT(&cpu_hotplug), true, "realized", NULL);
+ new = object_new(TYPE_SCLP_QUIESCE);
+ object_property_add_child(obj, TYPE_SCLP_QUIESCE, new, NULL);
+ object_unref(new);
+ qdev_set_parent_bus(DEVICE(new), &event_facility->sbus.qbus);
- return 0;
+ new = object_new(TYPE_SCLP_CPU_HOTPLUG);
+ object_property_add_child(obj, TYPE_SCLP_CPU_HOTPLUG, new, NULL);
+ object_unref(new);
+ qdev_set_parent_bus(DEVICE(new), &event_facility->sbus.qbus);
+ /* the facility will automatically realize the devices via the bus */
}
static void reset_event_facility(DeviceState *dev)
@@ -362,7 +379,6 @@ 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;
}
@@ -370,6 +386,7 @@ static void init_event_facility_class(ObjectClass *klass, void *data)
static const TypeInfo sclp_event_facility_info = {
.name = TYPE_SCLP_EVENT_FACILITY,
.parent = TYPE_SYS_BUS_DEVICE,
+ .instance_init = init_event_facility,
.instance_size = sizeof(SCLPEventFacility),
.class_init = init_event_facility_class,
.class_size = sizeof(SCLPEventFacilityClass),
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 2e0a8b6e0..b91fcc6e7 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -15,7 +15,6 @@
#include "cpu.h"
#include "elf.h"
#include "hw/loader.h"
-#include "hw/sysbus.h"
#include "hw/s390x/virtio-ccw.h"
#include "hw/s390x/css.h"
#include "ipl.h"
@@ -29,44 +28,6 @@
#define ZIPL_IMAGE_START 0x009000UL
#define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64)
-#define TYPE_S390_IPL "s390-ipl"
-#define S390_IPL(obj) \
- OBJECT_CHECK(S390IPLState, (obj), TYPE_S390_IPL)
-#if 0
-#define S390_IPL_CLASS(klass) \
- OBJECT_CLASS_CHECK(S390IPLState, (klass), TYPE_S390_IPL)
-#define S390_IPL_GET_CLASS(obj) \
- OBJECT_GET_CLASS(S390IPLState, (obj), TYPE_S390_IPL)
-#endif
-
-typedef struct S390IPLClass {
- /*< private >*/
- SysBusDeviceClass parent_class;
- /*< public >*/
-
- void (*parent_reset) (SysBusDevice *dev);
-} S390IPLClass;
-
-typedef struct S390IPLState {
- /*< private >*/
- SysBusDevice parent_obj;
- uint64_t start_addr;
- uint64_t bios_start_addr;
- bool enforce_bios;
- IplParameterBlock iplb;
- bool iplb_valid;
- bool reipl_requested;
-
- /*< public >*/
- char *kernel;
- char *initrd;
- char *cmdline;
- char *firmware;
- uint8_t cssid;
- uint8_t ssid;
- uint16_t devno;
-} S390IPLState;
-
static const VMStateDescription vmstate_iplb = {
.name = "ipl/iplb",
.version_id = 0,
@@ -95,6 +56,11 @@ static const VMStateDescription vmstate_ipl = {
}
};
+static S390IPLState *get_ipl_device(void)
+{
+ return S390_IPL(object_resolve_path_type("", TYPE_S390_IPL, NULL));
+}
+
static uint64_t bios_translate_addr(void *opaque, uint64_t srcaddr)
{
uint64_t dstaddr = *(uint64_t *) opaque;
@@ -105,11 +71,12 @@ static uint64_t bios_translate_addr(void *opaque, uint64_t srcaddr)
return srcaddr + dstaddr;
}
-static int s390_ipl_init(SysBusDevice *dev)
+static void s390_ipl_realize(DeviceState *dev, Error **errp)
{
S390IPLState *ipl = S390_IPL(dev);
uint64_t pentry = KERN_IMAGE_START;
int kernel_size;
+ Error *l_err = NULL;
int bios_size;
char *bios_filename;
@@ -127,12 +94,13 @@ static int s390_ipl_init(SysBusDevice *dev)
bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
if (bios_filename == NULL) {
- hw_error("could not find stage1 bootloader\n");
+ error_setg(&l_err, "could not find stage1 bootloader\n");
+ goto error;
}
bios_size = load_elf(bios_filename, bios_translate_addr, &fwbase,
&ipl->bios_start_addr, NULL, NULL, 1,
- ELF_MACHINE, 0);
+ EM_S390, 0);
if (bios_size > 0) {
/* Adjust ELF start address to final location */
ipl->bios_start_addr += fwbase;
@@ -145,7 +113,8 @@ static int s390_ipl_init(SysBusDevice *dev)
g_free(bios_filename);
if (bios_size == -1) {
- hw_error("could not load bootloader '%s'\n", bios_name);
+ error_setg(&l_err, "could not load bootloader '%s'\n", bios_name);
+ goto error;
}
/* default boot target is the bios */
@@ -154,13 +123,13 @@ static int s390_ipl_init(SysBusDevice *dev)
if (ipl->kernel) {
kernel_size = load_elf(ipl->kernel, NULL, NULL, &pentry, NULL,
- NULL, 1, ELF_MACHINE, 0);
+ NULL, 1, EM_S390, 0);
if (kernel_size < 0) {
kernel_size = load_image_targphys(ipl->kernel, 0, ram_size);
}
if (kernel_size < 0) {
- fprintf(stderr, "could not load kernel '%s'\n", ipl->kernel);
- return -1;
+ error_setg(&l_err, "could not load kernel '%s'\n", ipl->kernel);
+ goto error;
}
/*
* Is it a Linux kernel (starting at 0x10000)? If yes, we fill in the
@@ -187,9 +156,8 @@ static int s390_ipl_init(SysBusDevice *dev)
initrd_size = load_image_targphys(ipl->initrd, initrd_offset,
ram_size - initrd_offset);
if (initrd_size == -1) {
- fprintf(stderr, "qemu: could not load initrd '%s'\n",
- ipl->initrd);
- exit(1);
+ error_setg(&l_err, "could not load initrd '%s'\n", ipl->initrd);
+ goto error;
}
/*
@@ -200,7 +168,9 @@ static int s390_ipl_init(SysBusDevice *dev)
stq_p(rom_ptr(INITRD_PARM_SIZE), initrd_size);
}
}
- return 0;
+ qemu_register_reset(qdev_reset_all_fn, dev);
+error:
+ error_propagate(errp, l_err);
}
static Property s390_ipl_properties[] = {
@@ -218,7 +188,7 @@ static Property s390_ipl_properties[] = {
* - -1 if no valid boot device was found
* - ccw id of the boot device otherwise
*/
-static uint64_t s390_update_iplstate(CPUS390XState *env, S390IPLState *ipl)
+static uint64_t s390_update_iplstate(S390IPLState *ipl)
{
DeviceState *dev_st;
@@ -251,25 +221,19 @@ out:
return (uint32_t) (ipl->cssid << 24 | ipl->ssid << 16 | ipl->devno);
}
-int s390_ipl_update_diag308(IplParameterBlock *iplb)
+void s390_ipl_update_diag308(IplParameterBlock *iplb)
{
- S390IPLState *ipl;
+ S390IPLState *ipl = get_ipl_device();
- ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL));
- if (ipl) {
- ipl->iplb = *iplb;
- ipl->iplb_valid = true;
- return 0;
- }
- return -1;
+ ipl->iplb = *iplb;
+ ipl->iplb_valid = true;
}
IplParameterBlock *s390_ipl_get_iplb(void)
{
- S390IPLState *ipl;
+ S390IPLState *ipl = get_ipl_device();
- ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL));
- if (!ipl || !ipl->iplb_valid) {
+ if (!ipl->iplb_valid) {
return NULL;
}
return &ipl->iplb;
@@ -277,41 +241,40 @@ IplParameterBlock *s390_ipl_get_iplb(void)
void s390_reipl_request(void)
{
- S390IPLState *ipl;
+ S390IPLState *ipl = get_ipl_device();
- ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL));
ipl->reipl_requested = true;
qemu_system_reset_request();
}
+void s390_ipl_prepare_cpu(S390CPU *cpu)
+{
+ S390IPLState *ipl = get_ipl_device();
+
+ cpu->env.psw.addr = ipl->start_addr;
+ cpu->env.psw.mask = IPL_PSW_MASK;
+
+ if (!ipl->kernel || ipl->iplb_valid) {
+ cpu->env.psw.addr = ipl->bios_start_addr;
+ cpu->env.regs[7] = s390_update_iplstate(ipl);
+ }
+}
+
static void s390_ipl_reset(DeviceState *dev)
{
S390IPLState *ipl = S390_IPL(dev);
- S390CPU *cpu = S390_CPU(qemu_get_cpu(0));
- CPUS390XState *env = &cpu->env;
-
- env->psw.addr = ipl->start_addr;
- env->psw.mask = IPL_PSW_MASK;
if (!ipl->reipl_requested) {
ipl->iplb_valid = false;
}
ipl->reipl_requested = false;
-
- if (!ipl->kernel || ipl->iplb_valid) {
- env->psw.addr = ipl->bios_start_addr;
- env->regs[7] = s390_update_iplstate(env, ipl);
- }
-
- s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
}
static void s390_ipl_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = s390_ipl_init;
+ dc->realize = s390_ipl_realize;
dc->props = s390_ipl_properties;
dc->reset = s390_ipl_reset;
dc->vmsd = &vmstate_ipl;
@@ -320,8 +283,8 @@ static void s390_ipl_class_init(ObjectClass *klass, void *data)
static const TypeInfo s390_ipl_info = {
.class_init = s390_ipl_class_init,
- .parent = TYPE_SYS_BUS_DEVICE,
- .name = "s390-ipl",
+ .parent = TYPE_DEVICE,
+ .name = TYPE_S390_IPL,
.instance_size = sizeof(S390IPLState),
};
diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index 70497bc65..6b48ed7b9 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -12,14 +12,42 @@
#ifndef HW_S390_IPL_H
#define HW_S390_IPL_H
+#include "hw/qdev.h"
+#include "cpu.h"
+
typedef struct IplParameterBlock {
uint8_t reserved1[110];
uint16_t devno;
uint8_t reserved2[88];
} IplParameterBlock;
-int s390_ipl_update_diag308(IplParameterBlock *iplb);
+void s390_ipl_update_diag308(IplParameterBlock *iplb);
+void s390_ipl_prepare_cpu(S390CPU *cpu);
IplParameterBlock *s390_ipl_get_iplb(void);
void s390_reipl_request(void);
+#define TYPE_S390_IPL "s390-ipl"
+#define S390_IPL(obj) OBJECT_CHECK(S390IPLState, (obj), TYPE_S390_IPL)
+
+struct S390IPLState {
+ /*< private >*/
+ DeviceState parent_obj;
+ uint64_t start_addr;
+ uint64_t bios_start_addr;
+ bool enforce_bios;
+ IplParameterBlock iplb;
+ bool iplb_valid;
+ bool reipl_requested;
+
+ /*< public >*/
+ char *kernel;
+ char *initrd;
+ char *cmdline;
+ char *firmware;
+ uint8_t cssid;
+ uint8_t ssid;
+ uint16_t devno;
+};
+typedef struct S390IPLState S390IPLState;
+
#endif
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
index 560b66a50..98c726cfc 100644
--- a/hw/s390x/s390-pci-bus.c
+++ b/hw/s390x/s390-pci-bus.c
@@ -308,9 +308,8 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr,
{
uint64_t pte;
uint32_t flags;
- S390PCIBusDevice *pbdev = container_of(iommu, S390PCIBusDevice, mr);
- S390pciState *s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pbdev->pdev)
- ->qbus.parent);
+ S390PCIBusDevice *pbdev = container_of(iommu, S390PCIBusDevice, iommu_mr);
+ S390pciState *s;
IOMMUTLBEntry ret = {
.target_as = &address_space_memory,
.iova = 0,
@@ -319,8 +318,13 @@ static IOMMUTLBEntry s390_translate_iommu(MemoryRegion *iommu, hwaddr addr,
.perm = IOMMU_NONE,
};
+ if (!pbdev->configured || !pbdev->pdev) {
+ return ret;
+ }
+
DPRINTF("iommu trans addr 0x%" PRIx64 "\n", addr);
+ s = S390_PCI_HOST_BRIDGE(pci_device_root_bus(pbdev->pdev)->qbus.parent);
/* s390 does not have an APIC mapped to main storage so we use
* a separate AddressSpace only for msix notifications
*/
@@ -450,14 +454,32 @@ static const MemoryRegionOps s390_msi_ctrl_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
+void s390_pcihost_iommu_configure(S390PCIBusDevice *pbdev, bool enable)
+{
+ pbdev->configured = false;
+
+ if (enable) {
+ uint64_t size = pbdev->pal - pbdev->pba + 1;
+ memory_region_init_iommu(&pbdev->iommu_mr, OBJECT(&pbdev->mr),
+ &s390_iommu_ops, "iommu-s390", size);
+ memory_region_add_subregion(&pbdev->mr, pbdev->pba, &pbdev->iommu_mr);
+ } else {
+ memory_region_del_subregion(&pbdev->mr, &pbdev->iommu_mr);
+ }
+
+ pbdev->configured = true;
+}
+
static void s390_pcihost_init_as(S390pciState *s)
{
int i;
+ S390PCIBusDevice *pbdev;
for (i = 0; i < PCI_SLOT_MAX; i++) {
- memory_region_init_iommu(&s->pbdev[i].mr, OBJECT(s),
- &s390_iommu_ops, "iommu-s390", UINT64_MAX);
- address_space_init(&s->pbdev[i].as, &s->pbdev[i].mr, "iommu-pci");
+ pbdev = &s->pbdev[i];
+ memory_region_init(&pbdev->mr, OBJECT(s),
+ "iommu-root-s390", UINT64_MAX);
+ address_space_init(&pbdev->as, &pbdev->mr, "iommu-pci");
}
memory_region_init_io(&s->msix_notify_mr, OBJECT(s),
diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h
index 464a92eed..80345dacb 100644
--- a/hw/s390x/s390-pci-bus.h
+++ b/hw/s390x/s390-pci-bus.h
@@ -231,6 +231,7 @@ typedef struct S390PCIBusDevice {
AdapterRoutes routes;
AddressSpace as;
MemoryRegion mr;
+ MemoryRegion iommu_mr;
} S390PCIBusDevice;
typedef struct S390pciState {
@@ -244,6 +245,7 @@ typedef struct S390pciState {
int chsc_sei_nt2_get_event(void *res);
int chsc_sei_nt2_have_event(void);
void s390_pci_sclp_configure(int configure, SCCB *sccb);
+void s390_pcihost_iommu_configure(S390PCIBusDevice *pbdev, bool enable);
S390PCIBusDevice *s390_pci_find_dev_by_idx(uint32_t idx);
S390PCIBusDevice *s390_pci_find_dev_by_fh(uint32_t fh);
S390PCIBusDevice *s390_pci_find_dev_by_fid(uint32_t fid);
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index f9151a9af..8c1dc82b1 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -528,7 +528,7 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2)
goto out;
}
- mr = pci_device_iommu_address_space(pbdev->pdev)->root;
+ mr = &pbdev->iommu_mr;
while (start < end) {
entry = mr->iommu_ops->translate(mr, start, 0);
@@ -689,6 +689,9 @@ static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib)
pbdev->pba = pba;
pbdev->pal = pal;
pbdev->g_iota = g_iota;
+
+ s390_pcihost_iommu_configure(pbdev, true);
+
return 0;
}
@@ -697,6 +700,8 @@ static void dereg_ioat(S390PCIBusDevice *pbdev)
pbdev->pba = 0;
pbdev->pal = 0;
pbdev->g_iota = 0;
+
+ s390_pcihost_iommu_configure(pbdev, false);
}
int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar)
diff --git a/hw/s390x/s390-skeys-kvm.c b/hw/s390x/s390-skeys-kvm.c
new file mode 100644
index 000000000..682949afb
--- /dev/null
+++ b/hw/s390x/s390-skeys-kvm.c
@@ -0,0 +1,75 @@
+/*
+ * s390 storage key device
+ *
+ * Copyright 2015 IBM Corp.
+ * Author(s): Jason J. Herne <jjherne@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 "hw/s390x/storage-keys.h"
+#include "sysemu/kvm.h"
+#include "qemu/error-report.h"
+
+static int kvm_s390_skeys_enabled(S390SKeysState *ss)
+{
+ S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
+ uint8_t single_key;
+ int r;
+
+ r = skeyclass->get_skeys(ss, 0, 1, &single_key);
+ if (r != 0 && r != KVM_S390_GET_SKEYS_NONE) {
+ error_report("S390_GET_KEYS error %d\n", r);
+ }
+ return (r == 0);
+}
+
+static int kvm_s390_skeys_get(S390SKeysState *ss, uint64_t start_gfn,
+ uint64_t count, uint8_t *keys)
+{
+ struct kvm_s390_skeys args = {
+ .start_gfn = start_gfn,
+ .count = count,
+ .skeydata_addr = (__u64)keys
+ };
+
+ return kvm_vm_ioctl(kvm_state, KVM_S390_GET_SKEYS, &args);
+}
+
+static int kvm_s390_skeys_set(S390SKeysState *ss, uint64_t start_gfn,
+ uint64_t count, uint8_t *keys)
+{
+ struct kvm_s390_skeys args = {
+ .start_gfn = start_gfn,
+ .count = count,
+ .skeydata_addr = (__u64)keys
+ };
+
+ return kvm_vm_ioctl(kvm_state, KVM_S390_SET_SKEYS, &args);
+}
+
+static void kvm_s390_skeys_class_init(ObjectClass *oc, void *data)
+{
+ S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc);
+
+ skeyclass->skeys_enabled = kvm_s390_skeys_enabled;
+ skeyclass->get_skeys = kvm_s390_skeys_get;
+ skeyclass->set_skeys = kvm_s390_skeys_set;
+}
+
+static const TypeInfo kvm_s390_skeys_info = {
+ .name = TYPE_KVM_S390_SKEYS,
+ .parent = TYPE_S390_SKEYS,
+ .instance_size = sizeof(S390SKeysState),
+ .class_init = kvm_s390_skeys_class_init,
+ .class_size = sizeof(S390SKeysClass),
+};
+
+static void kvm_s390_skeys_register_types(void)
+{
+ type_register_static(&kvm_s390_skeys_info);
+}
+
+type_init(kvm_s390_skeys_register_types)
diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c
new file mode 100644
index 000000000..539ef6d3a
--- /dev/null
+++ b/hw/s390x/s390-skeys.c
@@ -0,0 +1,415 @@
+/*
+ * s390 storage key device
+ *
+ * Copyright 2015 IBM Corp.
+ * Author(s): Jason J. Herne <jjherne@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 "hw/boards.h"
+#include "qmp-commands.h"
+#include "migration/qemu-file.h"
+#include "hw/s390x/storage-keys.h"
+#include "qemu/error-report.h"
+
+#define S390_SKEYS_BUFFER_SIZE 131072 /* Room for 128k storage keys */
+#define S390_SKEYS_SAVE_FLAG_EOS 0x01
+#define S390_SKEYS_SAVE_FLAG_SKEYS 0x02
+#define S390_SKEYS_SAVE_FLAG_ERROR 0x04
+
+S390SKeysState *s390_get_skeys_device(void)
+{
+ S390SKeysState *ss;
+
+ ss = S390_SKEYS(object_resolve_path_type("", TYPE_S390_SKEYS, NULL));
+ assert(ss);
+ return ss;
+}
+
+void s390_skeys_init(void)
+{
+ Object *obj;
+
+ if (kvm_enabled()) {
+ obj = object_new(TYPE_KVM_S390_SKEYS);
+ } else {
+ obj = object_new(TYPE_QEMU_S390_SKEYS);
+ }
+ object_property_add_child(qdev_get_machine(), TYPE_S390_SKEYS,
+ obj, NULL);
+ object_unref(obj);
+
+ qdev_init_nofail(DEVICE(obj));
+}
+
+static void write_keys(QEMUFile *f, uint8_t *keys, uint64_t startgfn,
+ uint64_t count, Error **errp)
+{
+ uint64_t curpage = startgfn;
+ uint64_t maxpage = curpage + count - 1;
+ const char *fmt = "page=%03" PRIx64 ": key(%d) => ACC=%X, FP=%d, REF=%d,"
+ " ch=%d, reserved=%d\n";
+ char buf[128];
+ int len;
+
+ for (; curpage <= maxpage; curpage++) {
+ uint8_t acc = (*keys & 0xF0) >> 4;
+ int fp = (*keys & 0x08);
+ int ref = (*keys & 0x04);
+ int ch = (*keys & 0x02);
+ int res = (*keys & 0x01);
+
+ len = snprintf(buf, sizeof(buf), fmt, curpage,
+ *keys, acc, fp, ref, ch, res);
+ assert(len < sizeof(buf));
+ qemu_put_buffer(f, (uint8_t *)buf, len);
+ keys++;
+ }
+}
+
+void hmp_info_skeys(Monitor *mon, const QDict *qdict)
+{
+ S390SKeysState *ss = s390_get_skeys_device();
+ S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
+ uint64_t addr = qdict_get_int(qdict, "addr");
+ uint8_t key;
+ int r;
+
+ /* Quick check to see if guest is using storage keys*/
+ if (!skeyclass->skeys_enabled(ss)) {
+ monitor_printf(mon, "Error: This guest is not using storage keys\n");
+ return;
+ }
+
+ r = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key);
+ if (r < 0) {
+ monitor_printf(mon, "Error: %s\n", strerror(-r));
+ return;
+ }
+
+ monitor_printf(mon, " key: 0x%X\n", key);
+}
+
+void hmp_dump_skeys(Monitor *mon, const QDict *qdict)
+{
+ const char *filename = qdict_get_str(qdict, "filename");
+ Error *err = NULL;
+
+ qmp_dump_skeys(filename, &err);
+ if (err) {
+ monitor_printf(mon, "%s\n", error_get_pretty(err));
+ error_free(err);
+ }
+}
+
+void qmp_dump_skeys(const char *filename, Error **errp)
+{
+ S390SKeysState *ss = s390_get_skeys_device();
+ S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
+ const uint64_t total_count = ram_size / TARGET_PAGE_SIZE;
+ uint64_t handled_count = 0, cur_count;
+ Error *lerr = NULL;
+ vaddr cur_gfn = 0;
+ uint8_t *buf;
+ int ret;
+ QEMUFile *f;
+
+ /* Quick check to see if guest is using storage keys*/
+ if (!skeyclass->skeys_enabled(ss)) {
+ error_setg(errp, "This guest is not using storage keys - "
+ "nothing to dump");
+ return;
+ }
+
+ f = qemu_fopen(filename, "wb");
+ if (!f) {
+ error_setg_file_open(errp, errno, filename);
+ return;
+ }
+
+ buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
+ if (!buf) {
+ error_setg(errp, "Could not allocate memory");
+ goto out;
+ }
+
+ /* we'll only dump initial memory for now */
+ while (handled_count < total_count) {
+ /* Calculate how many keys to ask for & handle overflow case */
+ cur_count = MIN(total_count - handled_count, S390_SKEYS_BUFFER_SIZE);
+
+ ret = skeyclass->get_skeys(ss, cur_gfn, cur_count, buf);
+ if (ret < 0) {
+ error_setg(errp, "get_keys error %d", ret);
+ goto out_free;
+ }
+
+ /* write keys to stream */
+ write_keys(f, buf, cur_gfn, cur_count, &lerr);
+ if (lerr) {
+ goto out_free;
+ }
+
+ cur_gfn += cur_count;
+ handled_count += cur_count;
+ }
+
+out_free:
+ error_propagate(errp, lerr);
+ g_free(buf);
+out:
+ qemu_fclose(f);
+}
+
+static void qemu_s390_skeys_init(Object *obj)
+{
+ QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(obj);
+ MachineState *machine = MACHINE(qdev_get_machine());
+
+ skeys->key_count = machine->maxram_size / TARGET_PAGE_SIZE;
+ skeys->keydata = g_malloc0(skeys->key_count);
+}
+
+static int qemu_s390_skeys_enabled(S390SKeysState *ss)
+{
+ return 1;
+}
+
+/*
+ * TODO: for memory hotplug support qemu_s390_skeys_set and qemu_s390_skeys_get
+ * will have to make sure that the given gfn belongs to a memory region and not
+ * a memory hole.
+ */
+static int qemu_s390_skeys_set(S390SKeysState *ss, uint64_t start_gfn,
+ uint64_t count, uint8_t *keys)
+{
+ QEMUS390SKeysState *skeydev = QEMU_S390_SKEYS(ss);
+ int i;
+
+ /* Check for uint64 overflow and access beyond end of key data */
+ if (start_gfn + count > skeydev->key_count || start_gfn + count < count) {
+ error_report("Error: Setting storage keys for page beyond the end "
+ "of memory: gfn=%" PRIx64 " count=%" PRId64 "\n", start_gfn,
+ count);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < count; i++) {
+ skeydev->keydata[start_gfn + i] = keys[i];
+ }
+ return 0;
+}
+
+static int qemu_s390_skeys_get(S390SKeysState *ss, uint64_t start_gfn,
+ uint64_t count, uint8_t *keys)
+{
+ QEMUS390SKeysState *skeydev = QEMU_S390_SKEYS(ss);
+ int i;
+
+ /* Check for uint64 overflow and access beyond end of key data */
+ if (start_gfn + count > skeydev->key_count || start_gfn + count < count) {
+ error_report("Error: Getting storage keys for page beyond the end "
+ "of memory: gfn=%" PRIx64 " count=%" PRId64 "\n", start_gfn,
+ count);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < count; i++) {
+ keys[i] = skeydev->keydata[start_gfn + i];
+ }
+ return 0;
+}
+
+static void qemu_s390_skeys_class_init(ObjectClass *oc, void *data)
+{
+ S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc);
+
+ skeyclass->skeys_enabled = qemu_s390_skeys_enabled;
+ skeyclass->get_skeys = qemu_s390_skeys_get;
+ skeyclass->set_skeys = qemu_s390_skeys_set;
+}
+
+static const TypeInfo qemu_s390_skeys_info = {
+ .name = TYPE_QEMU_S390_SKEYS,
+ .parent = TYPE_S390_SKEYS,
+ .instance_init = qemu_s390_skeys_init,
+ .instance_size = sizeof(QEMUS390SKeysState),
+ .class_init = qemu_s390_skeys_class_init,
+ .instance_size = sizeof(S390SKeysClass),
+};
+
+static void s390_storage_keys_save(QEMUFile *f, void *opaque)
+{
+ S390SKeysState *ss = S390_SKEYS(opaque);
+ S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
+ uint64_t pages_left = ram_size / TARGET_PAGE_SIZE;
+ uint64_t read_count, eos = S390_SKEYS_SAVE_FLAG_EOS;
+ vaddr cur_gfn = 0;
+ int error = 0;
+ uint8_t *buf;
+
+ if (!skeyclass->skeys_enabled(ss)) {
+ goto end_stream;
+ }
+
+ buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
+ if (!buf) {
+ error_report("storage key save could not allocate memory\n");
+ goto end_stream;
+ }
+
+ /* We only support initial memory. Standby memory is not handled yet. */
+ qemu_put_be64(f, (cur_gfn * TARGET_PAGE_SIZE) | S390_SKEYS_SAVE_FLAG_SKEYS);
+ qemu_put_be64(f, pages_left);
+
+ while (pages_left) {
+ read_count = MIN(pages_left, S390_SKEYS_BUFFER_SIZE);
+
+ if (!error) {
+ error = skeyclass->get_skeys(ss, cur_gfn, read_count, buf);
+ if (error) {
+ /*
+ * If error: we want to fill the stream with valid data instead
+ * of stopping early so we pad the stream with 0x00 values and
+ * use S390_SKEYS_SAVE_FLAG_ERROR to indicate failure to the
+ * reading side.
+ */
+ error_report("S390_GET_KEYS error %d\n", error);
+ memset(buf, 0, S390_SKEYS_BUFFER_SIZE);
+ eos = S390_SKEYS_SAVE_FLAG_ERROR;
+ }
+ }
+
+ qemu_put_buffer(f, buf, read_count);
+ cur_gfn += read_count;
+ pages_left -= read_count;
+ }
+
+ g_free(buf);
+end_stream:
+ qemu_put_be64(f, eos);
+}
+
+static int s390_storage_keys_load(QEMUFile *f, void *opaque, int version_id)
+{
+ S390SKeysState *ss = S390_SKEYS(opaque);
+ S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss);
+ int ret = 0;
+
+ while (!ret) {
+ ram_addr_t addr;
+ int flags;
+
+ addr = qemu_get_be64(f);
+ flags = addr & ~TARGET_PAGE_MASK;
+ addr &= TARGET_PAGE_MASK;
+
+ switch (flags) {
+ case S390_SKEYS_SAVE_FLAG_SKEYS: {
+ const uint64_t total_count = qemu_get_be64(f);
+ uint64_t handled_count = 0, cur_count;
+ uint64_t cur_gfn = addr / TARGET_PAGE_SIZE;
+ uint8_t *buf = g_try_malloc(S390_SKEYS_BUFFER_SIZE);
+
+ if (!buf) {
+ error_report("storage key load could not allocate memory\n");
+ ret = -ENOMEM;
+ break;
+ }
+
+ while (handled_count < total_count) {
+ cur_count = MIN(total_count - handled_count,
+ S390_SKEYS_BUFFER_SIZE);
+ qemu_get_buffer(f, buf, cur_count);
+
+ ret = skeyclass->set_skeys(ss, cur_gfn, cur_count, buf);
+ if (ret < 0) {
+ error_report("S390_SET_KEYS error %d\n", ret);
+ break;
+ }
+ handled_count += cur_count;
+ cur_gfn += cur_count;
+ }
+ g_free(buf);
+ break;
+ }
+ case S390_SKEYS_SAVE_FLAG_ERROR: {
+ error_report("Storage key data is incomplete");
+ ret = -EINVAL;
+ break;
+ }
+ case S390_SKEYS_SAVE_FLAG_EOS:
+ /* normal exit */
+ return 0;
+ default:
+ error_report("Unexpected storage key flag data: %#x", flags);
+ ret = -EINVAL;
+ }
+ }
+
+ return ret;
+}
+
+static inline bool s390_skeys_get_migration_enabled(Object *obj, Error **errp)
+{
+ S390SKeysState *ss = S390_SKEYS(obj);
+
+ return ss->migration_enabled;
+}
+
+static inline void s390_skeys_set_migration_enabled(Object *obj, bool value,
+ Error **errp)
+{
+ S390SKeysState *ss = S390_SKEYS(obj);
+
+ /* Prevent double registration of savevm handler */
+ if (ss->migration_enabled == value) {
+ return;
+ }
+
+ ss->migration_enabled = value;
+
+ if (ss->migration_enabled) {
+ register_savevm(NULL, TYPE_S390_SKEYS, 0, 1, s390_storage_keys_save,
+ s390_storage_keys_load, ss);
+ } else {
+ unregister_savevm(DEVICE(ss), TYPE_S390_SKEYS, ss);
+ }
+}
+
+static void s390_skeys_instance_init(Object *obj)
+{
+ object_property_add_bool(obj, "migration-enabled",
+ s390_skeys_get_migration_enabled,
+ s390_skeys_set_migration_enabled, NULL);
+ object_property_set_bool(obj, true, "migration-enabled", NULL);
+}
+
+static void s390_skeys_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->hotpluggable = false;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+}
+
+static const TypeInfo s390_skeys_info = {
+ .name = TYPE_S390_SKEYS,
+ .parent = TYPE_DEVICE,
+ .instance_init = s390_skeys_instance_init,
+ .instance_size = sizeof(S390SKeysState),
+ .class_init = s390_skeys_class_init,
+ .class_size = sizeof(S390SKeysClass),
+ .abstract = true,
+};
+
+static void qemu_s390_skeys_register_types(void)
+{
+ type_register_static(&s390_skeys_info);
+ type_register_static(&qemu_s390_skeys_info);
+}
+
+type_init(qemu_s390_skeys_register_types)
diff --git a/hw/s390x/s390-virtio-bus.c b/hw/s390x/s390-virtio-bus.c
index 77aec8a5b..98cb1293c 100644
--- a/hw/s390x/s390-virtio-bus.c
+++ b/hw/s390x/s390-virtio-bus.c
@@ -577,17 +577,12 @@ static const TypeInfo s390_virtio_blk = {
.class_init = s390_virtio_blk_class_init,
};
-static Property s390_virtio_serial_properties[] = {
- DEFINE_PROP_END_OF_LIST(),
-};
-
static void s390_virtio_serial_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
k->realize = s390_virtio_serial_realize;
- dc->props = s390_virtio_serial_properties;
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
}
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index 4c51d1a5b..5a52ff26e 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -19,6 +19,8 @@
#include "virtio-ccw.h"
#include "qemu/config-file.h"
#include "s390-pci-bus.h"
+#include "hw/s390x/storage-keys.h"
+#include "hw/compat.h"
#define TYPE_S390_CCW_MACHINE "s390-ccw-machine"
@@ -34,26 +36,23 @@ typedef struct S390CcwMachineState {
bool dea_key_wrap;
} S390CcwMachineState;
-void io_subsystem_reset(void)
+static const char *const reset_dev_types[] = {
+ "virtual-css-bridge",
+ "s390-sclp-event-facility",
+ "s390-flic",
+ "diag288",
+};
+
+void subsystem_reset(void)
{
- DeviceState *css, *sclp, *flic, *diag288;
+ DeviceState *dev;
+ int i;
- css = DEVICE(object_resolve_path_type("", "virtual-css-bridge", NULL));
- if (css) {
- qdev_reset_all(css);
- }
- sclp = DEVICE(object_resolve_path_type("",
- "s390-sclp-event-facility", NULL));
- if (sclp) {
- qdev_reset_all(sclp);
- }
- flic = DEVICE(object_resolve_path_type("", "s390-flic", NULL));
- if (flic) {
- qdev_reset_all(flic);
- }
- diag288 = DEVICE(object_resolve_path_type("", "diag288", NULL));
- if (diag288) {
- qdev_reset_all(diag288);
+ for (i = 0; i < ARRAY_SIZE(reset_dev_types); i++) {
+ dev = DEVICE(object_resolve_path_type("", reset_dev_types[i], NULL));
+ if (dev) {
+ qdev_reset_all(dev);
+ }
}
}
@@ -99,58 +98,30 @@ static void virtio_ccw_register_hcalls(void)
virtio_ccw_hcall_early_printk);
}
-static void ccw_init(MachineState *machine)
+void s390_memory_init(ram_addr_t mem_size)
{
- ram_addr_t my_ram_size = machine->ram_size;
MemoryRegion *sysmem = get_system_memory();
MemoryRegion *ram = g_new(MemoryRegion, 1);
- sclpMemoryHotplugDev *mhd = init_sclp_memory_hotplug_dev();
- uint8_t *storage_keys;
+
+ /* allocate RAM for core */
+ memory_region_allocate_system_memory(ram, NULL, "s390.ram", mem_size);
+ memory_region_add_subregion(sysmem, 0, ram);
+
+ /* Initialize storage key device */
+ s390_skeys_init();
+}
+
+static void ccw_init(MachineState *machine)
+{
int ret;
VirtualCssBus *css_bus;
DeviceState *dev;
- QemuOpts *opts = qemu_opts_find(qemu_find_opts("memory"), NULL);
- ram_addr_t pad_size = 0;
- ram_addr_t maxmem = qemu_opt_get_size(opts, "maxmem", my_ram_size);
- ram_addr_t standby_mem_size = maxmem - my_ram_size;
- uint64_t kvm_limit;
-
- /* The storage increment size is a multiple of 1M and is a power of 2.
- * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer.
- * The variable 'mhd->increment_size' is an exponent of 2 that can be
- * used to calculate the size (in bytes) of an increment. */
- mhd->increment_size = 20;
- while ((my_ram_size >> mhd->increment_size) > MAX_STORAGE_INCREMENTS) {
- mhd->increment_size++;
- }
- while ((standby_mem_size >> mhd->increment_size) > MAX_STORAGE_INCREMENTS) {
- mhd->increment_size++;
- }
- /* The core and standby memory areas need to be aligned with
- * the increment size. In effect, this can cause the
- * user-specified memory size to be rounded down to align
- * with the nearest increment boundary. */
- standby_mem_size = standby_mem_size >> mhd->increment_size
- << mhd->increment_size;
- my_ram_size = my_ram_size >> mhd->increment_size
- << mhd->increment_size;
-
- /* let's propagate the changed ram size into the global variable. */
- ram_size = my_ram_size;
- machine->maxram_size = my_ram_size + standby_mem_size;
-
- ret = s390_set_memory_limit(machine->maxram_size, &kvm_limit);
- if (ret == -E2BIG) {
- hw_error("qemu: host supports a maximum of %" PRIu64 " GB",
- kvm_limit >> 30);
- } else if (ret) {
- hw_error("qemu: setting the guest size failed");
- }
+ s390_sclp_init();
+ s390_memory_init(machine->ram_size);
/* get a BUS */
css_bus = virtual_css_bus_init();
- s390_sclp_init();
s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline,
machine->initrd_filename, "s390-ccw.img", true);
s390_flic_init();
@@ -163,27 +134,8 @@ static void ccw_init(MachineState *machine)
/* register hypercalls */
virtio_ccw_register_hcalls();
- /* allocate RAM for core */
- memory_region_init_ram(ram, NULL, "s390.ram", my_ram_size, &error_abort);
- vmstate_register_ram_global(ram);
- memory_region_add_subregion(sysmem, 0, ram);
-
- /* If the size of ram is not on a MEM_SECTION_SIZE boundary,
- calculate the pad size necessary to force this boundary. */
- if (standby_mem_size) {
- if (my_ram_size % MEM_SECTION_SIZE) {
- pad_size = MEM_SECTION_SIZE - my_ram_size % MEM_SECTION_SIZE;
- }
- my_ram_size += standby_mem_size + pad_size;
- mhd->pad_size = pad_size;
- mhd->standby_mem_size = standby_mem_size;
- }
-
- /* allocate storage keys */
- storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE);
-
/* init CPUs */
- s390_init_cpus(machine->cpu_model, storage_keys);
+ s390_init_cpus(machine->cpu_model);
if (kvm_enabled()) {
kvm_s390_enable_css_support(s390_cpu_addr2state(0));
@@ -209,6 +161,7 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data)
NMIClass *nc = NMI_CLASS(oc);
mc->init = ccw_init;
+ mc->reset = s390_machine_reset;
mc->block_default_type = IF_VIRTIO;
mc->no_cdrom = 1;
mc->no_floppy = 1;
@@ -282,26 +235,84 @@ static const TypeInfo ccw_machine_info = {
},
};
+#define CCW_COMPAT_2_4 \
+ HW_COMPAT_2_4 \
+ {\
+ .driver = TYPE_S390_SKEYS,\
+ .property = "migration-enabled",\
+ .value = "off",\
+ },{\
+ .driver = "virtio-blk-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },{\
+ .driver = "virtio-balloon-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },{\
+ .driver = "virtio-serial-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },{\
+ .driver = "virtio-9p-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },{\
+ .driver = "virtio-rng-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },{\
+ .driver = "virtio-net-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },{\
+ .driver = "virtio-scsi-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },{\
+ .driver = "vhost-scsi-ccw",\
+ .property = "max_revision",\
+ .value = "0",\
+ },
+
static void ccw_machine_2_4_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
+ static GlobalProperty compat_props[] = {
+ CCW_COMPAT_2_4
+ { /* end of list */ }
+ };
- 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;
+ mc->compat_props = compat_props;
}
static const TypeInfo ccw_machine_2_4_info = {
- .name = TYPE_S390_CCW_MACHINE "2.4",
+ .name = MACHINE_TYPE_NAME("s390-ccw-virtio-2.4"),
.parent = TYPE_S390_CCW_MACHINE,
.class_init = ccw_machine_2_4_class_init,
};
+static void ccw_machine_2_5_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->alias = "s390-ccw-virtio";
+ mc->desc = "VirtIO-ccw based S390 machine v2.5";
+ mc->is_default = 1;
+}
+
+static const TypeInfo ccw_machine_2_5_info = {
+ .name = MACHINE_TYPE_NAME("s390-ccw-virtio-2.5"),
+ .parent = TYPE_S390_CCW_MACHINE,
+ .class_init = ccw_machine_2_5_class_init,
+};
+
static void ccw_machine_register_types(void)
{
type_register_static(&ccw_machine_info);
type_register_static(&ccw_machine_2_4_info);
+ type_register_static(&ccw_machine_2_5_info);
}
type_init(ccw_machine_register_types)
diff --git a/hw/s390x/s390-virtio.c b/hw/s390x/s390-virtio.c
index 1284e77b2..ae55760d6 100644
--- a/hw/s390x/s390-virtio.c
+++ b/hw/s390x/s390-virtio.c
@@ -23,6 +23,7 @@
#include "hw/hw.h"
#include "qapi/qmp/qerror.h"
+#include "qemu/error-report.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
#include "sysemu/sysemu.h"
@@ -30,14 +31,16 @@
#include "hw/boards.h"
#include "hw/loader.h"
#include "hw/virtio/virtio.h"
-#include "hw/sysbus.h"
#include "sysemu/kvm.h"
#include "exec/address-spaces.h"
+#include "sysemu/qtest.h"
#include "hw/s390x/s390-virtio-bus.h"
#include "hw/s390x/sclp.h"
#include "hw/s390x/s390_flic.h"
#include "hw/s390x/s390-virtio.h"
+#include "hw/s390x/storage-keys.h"
+#include "hw/s390x/ipl.h"
#include "cpu.h"
//#define DEBUG_S390
@@ -52,7 +55,8 @@
#define MAX_BLK_DEVS 10
#define ZIPL_FILENAME "s390-zipl.rom"
-#define TYPE_S390_MACHINE "s390-machine"
+#define S390_MACHINE "s390-virtio"
+#define TYPE_S390_MACHINE MACHINE_TYPE_NAME(S390_MACHINE)
#define S390_TOD_CLOCK_VALUE_MISSING 0x00
#define S390_TOD_CLOCK_VALUE_PRESENT 0x01
@@ -147,9 +151,9 @@ void s390_init_ipl_dev(const char *kernel_filename,
const char *firmware,
bool enforce_bios)
{
- DeviceState *dev;
+ Object *new = object_new(TYPE_S390_IPL);
+ DeviceState *dev = DEVICE(new);
- dev = qdev_create(NULL, "s390-ipl");
if (kernel_filename) {
qdev_prop_set_string(dev, "kernel", kernel_filename);
}
@@ -159,12 +163,13 @@ void s390_init_ipl_dev(const char *kernel_filename,
qdev_prop_set_string(dev, "cmdline", kernel_cmdline);
qdev_prop_set_string(dev, "firmware", firmware);
qdev_prop_set_bit(dev, "enforce_bios", enforce_bios);
- object_property_add_child(qdev_get_machine(), "s390-ipl",
- OBJECT(dev), NULL);
+ object_property_add_child(qdev_get_machine(), TYPE_S390_IPL,
+ new, NULL);
+ object_unref(new);
qdev_init_nofail(dev);
}
-void s390_init_cpus(const char *cpu_model, uint8_t *storage_keys)
+void s390_init_cpus(const char *cpu_model)
{
int i;
@@ -184,7 +189,6 @@ void s390_init_cpus(const char *cpu_model, uint8_t *storage_keys)
ipi_states[i] = cpu;
cs->halted = 1;
cs->exception_index = EXCP_HLT;
- cpu->env.storage_keys = storage_keys;
}
}
@@ -260,31 +264,26 @@ int gtod_load(QEMUFile *f, void *opaque, int version_id)
/* PC hardware initialisation */
static void s390_init(MachineState *machine)
{
- ram_addr_t my_ram_size = machine->ram_size;
- MemoryRegion *sysmem = get_system_memory();
- MemoryRegion *ram = g_new(MemoryRegion, 1);
- int increment_size = 20;
- uint8_t *storage_keys;
+ ram_addr_t my_ram_size;
void *virtio_region;
hwaddr virtio_region_len;
hwaddr virtio_region_start;
- /*
- * The storage increment size is a multiple of 1M and is a power of 2.
- * The number of storage increments must be MAX_STORAGE_INCREMENTS or
- * fewer.
- */
- while ((my_ram_size >> increment_size) > MAX_STORAGE_INCREMENTS) {
- increment_size++;
+ if (!qtest_enabled()) {
+ error_printf("WARNING\n"
+ "The s390-virtio machine (non-ccw) is deprecated.\n"
+ "It will be removed in 2.6. Please use s390-ccw-virtio\n");
}
- my_ram_size = my_ram_size >> increment_size << increment_size;
- /* let's propagate the changed ram size into the global variable. */
- ram_size = my_ram_size;
+ if (machine->ram_slots) {
+ error_report("Memory hotplug not supported by the selected machine.");
+ exit(EXIT_FAILURE);
+ }
+ s390_sclp_init();
+ my_ram_size = machine->ram_size;
/* get a BUS */
s390_bus = s390_virtio_bus_init(&my_ram_size);
- s390_sclp_init();
s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline,
machine->initrd_filename, ZIPL_FILENAME, false);
s390_flic_init();
@@ -293,9 +292,7 @@ static void s390_init(MachineState *machine)
s390_virtio_register_hcalls();
/* allocate RAM */
- memory_region_init_ram(ram, NULL, "s390.ram", my_ram_size, &error_abort);
- vmstate_register_ram_global(ram);
- memory_region_add_subregion(sysmem, 0, ram);
+ s390_memory_init(my_ram_size);
/* clear virtio region */
virtio_region_len = my_ram_size - ram_size;
@@ -306,11 +303,8 @@ static void s390_init(MachineState *machine)
cpu_physical_memory_unmap(virtio_region, virtio_region_len, 1,
virtio_region_len);
- /* allocate storage keys */
- storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE);
-
/* init CPUs */
- s390_init_cpus(machine->cpu_model, storage_keys);
+ s390_init_cpus(machine->cpu_model);
/* Create VirtIO network adapters */
s390_create_virtio_net((BusState *)s390_bus, "virtio-net-s390");
@@ -328,15 +322,28 @@ void s390_nmi(NMIState *n, int cpu_index, Error **errp)
}
}
+void s390_machine_reset(void)
+{
+ S390CPU *ipl_cpu = S390_CPU(qemu_get_cpu(0));
+
+ qemu_devices_reset();
+ s390_cmma_reset();
+ s390_crypto_reset();
+
+ /* all cpus are stopped - configure and start the ipl cpu only */
+ s390_ipl_prepare_cpu(ipl_cpu);
+ s390_cpu_set_state(CPU_STATE_OPERATING, ipl_cpu);
+}
+
static void s390_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
NMIClass *nc = NMI_CLASS(oc);
- mc->name = "s390-virtio";
mc->alias = "s390";
- mc->desc = "VirtIO based S390 machine";
+ mc->desc = "VirtIO based S390 machine (deprecated)";
mc->init = s390_init;
+ mc->reset = s390_machine_reset;
mc->block_default_type = IF_VIRTIO;
mc->max_cpus = 255;
mc->no_serial = 1;
diff --git a/hw/s390x/s390-virtio.h b/hw/s390x/s390-virtio.h
index c84785395..eebce8e5e 100644
--- a/hw/s390x/s390-virtio.h
+++ b/hw/s390x/s390-virtio.h
@@ -19,7 +19,7 @@
typedef int (*s390_virtio_fn)(const uint64_t *args);
void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn);
-void s390_init_cpus(const char *cpu_model, uint8_t *storage_keys);
+void s390_init_cpus(const char *cpu_model);
void s390_init_ipl_dev(const char *kernel_filename,
const char *kernel_cmdline,
const char *initrd_filename,
@@ -27,4 +27,6 @@ void s390_init_ipl_dev(const char *kernel_filename,
bool enforce_bios);
void s390_create_virtio_net(BusState *bus, const char *name);
void s390_nmi(NMIState *n, int cpu_index, Error **errp);
+void s390_machine_reset(void);
+void s390_memory_init(ram_addr_t mem_size);
#endif
diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c
index b3a6c5e5a..a061b49f1 100644
--- a/hw/s390x/sclp.c
+++ b/hw/s390x/sclp.c
@@ -17,37 +17,27 @@
#include "exec/memory.h"
#include "sysemu/sysemu.h"
#include "exec/address-spaces.h"
-#include "qemu/config-file.h"
+#include "hw/boards.h"
#include "hw/s390x/sclp.h"
#include "hw/s390x/event-facility.h"
#include "hw/s390x/s390-pci-bus.h"
-static inline SCLPEventFacility *get_event_facility(void)
+static inline SCLPDevice *get_sclp_device(void)
{
- ObjectProperty *op = object_property_find(qdev_get_machine(),
- TYPE_SCLP_EVENT_FACILITY,
- NULL);
- assert(op);
- return op->opaque;
+ return SCLP(object_resolve_path_type("", TYPE_SCLP, NULL));
}
/* Provide information about the configuration, CPUs and storage */
-static void read_SCP_info(SCCB *sccb)
+static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb)
{
ReadInfo *read_info = (ReadInfo *) sccb;
+ MachineState *machine = MACHINE(qdev_get_machine());
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
CPUState *cpu;
int cpu_count = 0;
int i = 0;
- int increment_size = 20;
int rnsize, rnmax;
- QemuOpts *opts = qemu_opts_find(qemu_find_opts("memory"), NULL);
- int slots = qemu_opt_get_number(opts, "slots", 0);
- int max_avail_slots = s390_get_memslot_count(kvm_state);
-
- if (slots > max_avail_slots) {
- slots = max_avail_slots;
- }
+ int slots = MIN(machine->ram_slots, s390_get_memslot_count(kvm_state));
CPU_FOREACH(cpu) {
cpu_count++;
@@ -66,23 +56,8 @@ static void read_SCP_info(SCCB *sccb)
read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO |
SCLP_HAS_PCI_RECONFIG);
- /*
- * The storage increment size is a multiple of 1M and is a power of 2.
- * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer.
- */
- while ((ram_size >> increment_size) > MAX_STORAGE_INCREMENTS) {
- increment_size++;
- }
- rnmax = ram_size >> increment_size;
-
/* Memory Hotplug is only supported for the ccw machine type */
if (mhd) {
- while ((mhd->standby_mem_size >> increment_size) >
- MAX_STORAGE_INCREMENTS) {
- increment_size++;
- }
- assert(increment_size == mhd->increment_size);
-
mhd->standby_subregion_size = MEM_SECTION_SIZE;
/* Deduct the memory slot already used for core */
if (slots > 0) {
@@ -108,13 +83,11 @@ static void read_SCP_info(SCCB *sccb)
}
mhd->padded_ram_size = ram_size + mhd->pad_size;
mhd->rzm = 1 << mhd->increment_size;
- rnmax = ((ram_size + mhd->standby_mem_size + mhd->pad_size)
- >> mhd->increment_size);
read_info->facilities |= cpu_to_be64(SCLP_FC_ASSIGN_ATTACH_READ_STOR);
}
- rnsize = 1 << (increment_size - 20);
+ rnsize = 1 << (sclp->increment_size - 20);
if (rnsize <= 128) {
read_info->rnsize = rnsize;
} else {
@@ -122,6 +95,7 @@ static void read_SCP_info(SCCB *sccb)
read_info->rnsize2 = cpu_to_be32(rnsize);
}
+ rnmax = machine->maxram_size >> sclp->increment_size;
if (rnmax < 0x10000) {
read_info->rnmax = cpu_to_be16(rnmax);
} else {
@@ -132,14 +106,17 @@ static void read_SCP_info(SCCB *sccb)
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION);
}
-static void read_storage_element0_info(SCCB *sccb)
+static void read_storage_element0_info(SCLPDevice *sclp, SCCB *sccb)
{
int i, assigned;
int subincrement_id = SCLP_STARTING_SUBINCREMENT_ID;
ReadStorageElementInfo *storage_info = (ReadStorageElementInfo *) sccb;
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
- assert(mhd);
+ if (!mhd) {
+ sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
+ return;
+ }
if ((ram_size >> mhd->increment_size) >= 0x10000) {
sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION);
@@ -158,12 +135,15 @@ static void read_storage_element0_info(SCCB *sccb)
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION);
}
-static void read_storage_element1_info(SCCB *sccb)
+static void read_storage_element1_info(SCLPDevice *sclp, SCCB *sccb)
{
ReadStorageElementInfo *storage_info = (ReadStorageElementInfo *) sccb;
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
- assert(mhd);
+ if (!mhd) {
+ sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
+ return;
+ }
if ((mhd->standby_mem_size >> mhd->increment_size) >= 0x10000) {
sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION);
@@ -179,13 +159,17 @@ static void read_storage_element1_info(SCCB *sccb)
sccb->h.response_code = cpu_to_be16(SCLP_RC_STANDBY_READ_COMPLETION);
}
-static void attach_storage_element(SCCB *sccb, uint16_t element)
+static void attach_storage_element(SCLPDevice *sclp, SCCB *sccb,
+ uint16_t element)
{
int i, assigned, subincrement_id;
AttachStorageElement *attach_info = (AttachStorageElement *) sccb;
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
- assert(mhd);
+ if (!mhd) {
+ sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
+ return;
+ }
if (element != 1) {
sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
@@ -203,20 +187,26 @@ static void attach_storage_element(SCCB *sccb, uint16_t element)
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION);
}
-static void assign_storage(SCCB *sccb)
+static void assign_storage(SCLPDevice *sclp, SCCB *sccb)
{
MemoryRegion *mr = NULL;
uint64_t this_subregion_size;
AssignStorage *assign_info = (AssignStorage *) sccb;
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
- assert(mhd);
- ram_addr_t assign_addr = (assign_info->rn - 1) * mhd->rzm;
+ ram_addr_t assign_addr;
MemoryRegion *sysmem = get_system_memory();
+ if (!mhd) {
+ sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
+ return;
+ }
+ assign_addr = (assign_info->rn - 1) * mhd->rzm;
+
if ((assign_addr % MEM_SECTION_SIZE == 0) &&
(assign_addr >= mhd->padded_ram_size)) {
/* Re-use existing memory region if found */
mr = memory_region_find(sysmem, assign_addr, 1).mr;
+ memory_region_unref(mr);
if (!mr) {
MemoryRegion *standby_ram = g_new(MemoryRegion, 1);
@@ -241,7 +231,13 @@ static void assign_storage(SCCB *sccb)
this_subregion_size = mhd->standby_subregion_size;
}
- memory_region_init_ram(standby_ram, NULL, id, this_subregion_size, &error_abort);
+ memory_region_init_ram(standby_ram, NULL, id, this_subregion_size,
+ &error_fatal);
+ /* This is a hack to make memory hotunplug work again. Once we have
+ * subdevices, we have to unparent them when unassigning memory,
+ * instead of doing it via the ref count of the MemoryRegion. */
+ object_ref(OBJECT(standby_ram));
+ object_unparent(OBJECT(standby_ram));
vmstate_register_ram_global(standby_ram);
memory_region_add_subregion(sysmem, offset, standby_ram);
}
@@ -252,15 +248,20 @@ static void assign_storage(SCCB *sccb)
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION);
}
-static void unassign_storage(SCCB *sccb)
+static void unassign_storage(SCLPDevice *sclp, SCCB *sccb)
{
MemoryRegion *mr = NULL;
AssignStorage *assign_info = (AssignStorage *) sccb;
sclpMemoryHotplugDev *mhd = get_sclp_memory_hotplug_dev();
- assert(mhd);
- ram_addr_t unassign_addr = (assign_info->rn - 1) * mhd->rzm;
+ ram_addr_t unassign_addr;
MemoryRegion *sysmem = get_system_memory();
+ if (!mhd) {
+ sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
+ return;
+ }
+ unassign_addr = (assign_info->rn - 1) * mhd->rzm;
+
/* if the addr is a multiple of 256 MB */
if ((unassign_addr % MEM_SECTION_SIZE == 0) &&
(unassign_addr >= mhd->padded_ram_size)) {
@@ -269,6 +270,7 @@ static void unassign_storage(SCCB *sccb)
/* find the specified memory region and destroy it */
mr = memory_region_find(sysmem, unassign_addr, 1).mr;
+ memory_region_unref(mr);
if (mr) {
int i;
int is_removable = 1;
@@ -287,8 +289,7 @@ static void unassign_storage(SCCB *sccb)
}
if (is_removable) {
memory_region_del_subregion(sysmem, mr);
- object_unparent(OBJECT(mr));
- g_free(mr);
+ object_unref(OBJECT(mr));
}
}
}
@@ -296,7 +297,7 @@ static void unassign_storage(SCCB *sccb)
}
/* Provide information about the CPU */
-static void sclp_read_cpu_info(SCCB *sccb)
+static void sclp_read_cpu_info(SCLPDevice *sclp, SCCB *sccb)
{
ReadCpuInfo *cpu_info = (ReadCpuInfo *) sccb;
CPUState *cpu;
@@ -323,34 +324,35 @@ static void sclp_read_cpu_info(SCCB *sccb)
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION);
}
-static void sclp_execute(SCCB *sccb, uint32_t code)
+static void sclp_execute(SCLPDevice *sclp, SCCB *sccb, uint32_t code)
{
- SCLPEventFacility *ef = get_event_facility();
+ SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp);
+ SCLPEventFacility *ef = sclp->event_facility;
SCLPEventFacilityClass *efc = EVENT_FACILITY_GET_CLASS(ef);
switch (code & SCLP_CMD_CODE_MASK) {
case SCLP_CMDW_READ_SCP_INFO:
case SCLP_CMDW_READ_SCP_INFO_FORCED:
- read_SCP_info(sccb);
+ sclp_c->read_SCP_info(sclp, sccb);
break;
case SCLP_CMDW_READ_CPU_INFO:
- sclp_read_cpu_info(sccb);
+ sclp_c->read_cpu_info(sclp, sccb);
break;
case SCLP_READ_STORAGE_ELEMENT_INFO:
if (code & 0xff00) {
- read_storage_element1_info(sccb);
+ sclp_c->read_storage_element1_info(sclp, sccb);
} else {
- read_storage_element0_info(sccb);
+ sclp_c->read_storage_element0_info(sclp, sccb);
}
break;
case SCLP_ATTACH_STORAGE_ELEMENT:
- attach_storage_element(sccb, (code & 0xff00) >> 8);
+ sclp_c->attach_storage_element(sclp, sccb, (code & 0xff00) >> 8);
break;
case SCLP_ASSIGN_STORAGE:
- assign_storage(sccb);
+ sclp_c->assign_storage(sclp, sccb);
break;
case SCLP_UNASSIGN_STORAGE:
- unassign_storage(sccb);
+ sclp_c->unassign_storage(sclp, sccb);
break;
case SCLP_CMDW_CONFIGURE_PCI:
s390_pci_sclp_configure(1, sccb);
@@ -366,6 +368,8 @@ static void sclp_execute(SCCB *sccb, uint32_t code)
int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code)
{
+ SCLPDevice *sclp = get_sclp_device();
+ SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp);
int r = 0;
SCCB work_sccb;
@@ -400,20 +404,20 @@ int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code)
goto out;
}
- sclp_execute((SCCB *)&work_sccb, code);
+ sclp_c->execute(sclp, (SCCB *)&work_sccb, code);
cpu_physical_memory_write(sccb, &work_sccb,
be16_to_cpu(work_sccb.h.length));
- sclp_service_interrupt(sccb);
+ sclp_c->service_interrupt(sclp, sccb);
out:
return r;
}
-void sclp_service_interrupt(uint32_t sccb)
+static void service_interrupt(SCLPDevice *sclp, uint32_t sccb)
{
- SCLPEventFacility *ef = get_event_facility();
+ SCLPEventFacility *ef = sclp->event_facility;
SCLPEventFacilityClass *efc = EVENT_FACILITY_GET_CLASS(ef);
uint32_t param = sccb & ~3;
@@ -428,17 +432,148 @@ void sclp_service_interrupt(uint32_t sccb)
s390_sclp_extint(param);
}
+void sclp_service_interrupt(uint32_t sccb)
+{
+ SCLPDevice *sclp = get_sclp_device();
+ SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp);
+
+ sclp_c->service_interrupt(sclp, sccb);
+}
+
/* qemu object creation and initialization functions */
void s390_sclp_init(void)
{
- DeviceState *dev = qdev_create(NULL, TYPE_SCLP_EVENT_FACILITY);
+ Object *new = object_new(TYPE_SCLP);
- object_property_add_child(qdev_get_machine(), TYPE_SCLP_EVENT_FACILITY,
- OBJECT(dev), NULL);
- qdev_init_nofail(dev);
+ object_property_add_child(qdev_get_machine(), TYPE_SCLP, new,
+ NULL);
+ object_unref(OBJECT(new));
+ qdev_init_nofail(DEVICE(new));
+}
+
+static void sclp_realize(DeviceState *dev, Error **errp)
+{
+ MachineState *machine = MACHINE(qdev_get_machine());
+ SCLPDevice *sclp = SCLP(dev);
+ Error *l_err = NULL;
+ uint64_t hw_limit;
+ int ret;
+
+ object_property_set_bool(OBJECT(sclp->event_facility), true, "realized",
+ &l_err);
+ if (l_err) {
+ goto error;
+ }
+
+ ret = s390_set_memory_limit(machine->maxram_size, &hw_limit);
+ if (ret == -E2BIG) {
+ error_setg(&l_err, "qemu: host supports a maximum of %" PRIu64 " GB",
+ hw_limit >> 30);
+ goto error;
+ } else if (ret) {
+ error_setg(&l_err, "qemu: setting the guest size failed");
+ goto error;
+ }
+ return;
+error:
+ assert(l_err);
+ error_propagate(errp, l_err);
+}
+
+static void sclp_memory_init(SCLPDevice *sclp)
+{
+ MachineState *machine = MACHINE(qdev_get_machine());
+ ram_addr_t initial_mem = machine->ram_size;
+ ram_addr_t max_mem = machine->maxram_size;
+ ram_addr_t standby_mem = max_mem - initial_mem;
+ ram_addr_t pad_mem = 0;
+ int increment_size = 20;
+
+ /* The storage increment size is a multiple of 1M and is a power of 2.
+ * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer.
+ * The variable 'increment_size' is an exponent of 2 that can be
+ * used to calculate the size (in bytes) of an increment. */
+ while ((initial_mem >> increment_size) > MAX_STORAGE_INCREMENTS) {
+ increment_size++;
+ }
+ if (machine->ram_slots) {
+ while ((standby_mem >> increment_size) > MAX_STORAGE_INCREMENTS) {
+ increment_size++;
+ }
+ }
+ sclp->increment_size = increment_size;
+
+ /* The core and standby memory areas need to be aligned with
+ * the increment size. In effect, this can cause the
+ * user-specified memory size to be rounded down to align
+ * with the nearest increment boundary. */
+ initial_mem = initial_mem >> increment_size << increment_size;
+ standby_mem = standby_mem >> increment_size << increment_size;
+
+ /* If the size of ram is not on a MEM_SECTION_SIZE boundary,
+ calculate the pad size necessary to force this boundary. */
+ if (machine->ram_slots && standby_mem) {
+ sclpMemoryHotplugDev *mhd = init_sclp_memory_hotplug_dev();
+
+ if (initial_mem % MEM_SECTION_SIZE) {
+ pad_mem = MEM_SECTION_SIZE - initial_mem % MEM_SECTION_SIZE;
+ }
+ mhd->increment_size = increment_size;
+ mhd->pad_size = pad_mem;
+ mhd->standby_mem_size = standby_mem;
+ }
+ machine->ram_size = initial_mem;
+ machine->maxram_size = initial_mem + pad_mem + standby_mem;
+ /* let's propagate the changed ram size into the global variable. */
+ ram_size = initial_mem;
+}
+
+static void sclp_init(Object *obj)
+{
+ SCLPDevice *sclp = SCLP(obj);
+ Object *new;
+
+ new = object_new(TYPE_SCLP_EVENT_FACILITY);
+ object_property_add_child(obj, TYPE_SCLP_EVENT_FACILITY, new, NULL);
+ /* qdev_device_add searches the sysbus for TYPE_SCLP_EVENTS_BUS */
+ qdev_set_parent_bus(DEVICE(new), sysbus_get_default());
+ object_unref(new);
+ sclp->event_facility = EVENT_FACILITY(new);
+
+ sclp_memory_init(sclp);
}
+static void sclp_class_init(ObjectClass *oc, void *data)
+{
+ SCLPDeviceClass *sc = SCLP_CLASS(oc);
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->desc = "SCLP (Service-Call Logical Processor)";
+ dc->realize = sclp_realize;
+ dc->hotpluggable = false;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+
+ sc->read_SCP_info = read_SCP_info;
+ sc->read_storage_element0_info = read_storage_element0_info;
+ sc->read_storage_element1_info = read_storage_element1_info;
+ sc->attach_storage_element = attach_storage_element;
+ sc->assign_storage = assign_storage;
+ sc->unassign_storage = unassign_storage;
+ sc->read_cpu_info = sclp_read_cpu_info;
+ sc->execute = sclp_execute;
+ sc->service_interrupt = service_interrupt;
+}
+
+static TypeInfo sclp_info = {
+ .name = TYPE_SCLP,
+ .parent = TYPE_DEVICE,
+ .instance_init = sclp_init,
+ .instance_size = sizeof(SCLPDevice),
+ .class_init = sclp_class_init,
+ .class_size = sizeof(SCLPDeviceClass),
+};
+
sclpMemoryHotplugDev *init_sclp_memory_hotplug_dev(void)
{
DeviceState *dev;
@@ -475,5 +610,6 @@ static TypeInfo sclp_memory_hotplug_dev_info = {
static void register_types(void)
{
type_register_static(&sclp_memory_hotplug_dev_info);
+ type_register_static(&sclp_info);
}
type_init(register_types);
diff --git a/hw/s390x/sclpcpu.c b/hw/s390x/sclpcpu.c
index 2fe8b5aa4..322eb31d9 100644
--- a/hw/s390x/sclpcpu.c
+++ b/hw/s390x/sclpcpu.c
@@ -25,13 +25,16 @@ typedef struct ConfigMgtData {
uint8_t event_qualifier;
} QEMU_PACKED ConfigMgtData;
-static qemu_irq *irq_cpu_hotplug; /* Only used in this file */
-
#define EVENT_QUAL_CPU_CHANGE 1
void raise_irq_cpu_hotplug(void)
{
- qemu_irq_raise(*irq_cpu_hotplug);
+ Object *obj = object_resolve_path_type("", TYPE_SCLP_CPU_HOTPLUG, NULL);
+
+ SCLP_EVENT(obj)->event_pending = true;
+
+ /* Trigger SCLP read operation */
+ sclp_service_interrupt(0);
}
static unsigned int send_mask(void)
@@ -70,36 +73,19 @@ static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr,
return 1;
}
-static void trigger_signal(void *opaque, int n, int level)
-{
- SCLPEvent *event = opaque;
- event->event_pending = true;
-
- /* Trigger SCLP read operation */
- sclp_service_interrupt(0);
-}
-
-static int irq_cpu_hotplug_init(SCLPEvent *event)
-{
- irq_cpu_hotplug = qemu_allocate_irqs(trigger_signal, event, 1);
- return 0;
-}
-
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 = {
- .name = "sclp-cpu-hotplug",
+ .name = TYPE_SCLP_CPU_HOTPLUG,
.parent = TYPE_SCLP_EVENT,
.instance_size = sizeof(SCLPEvent),
.class_init = cpu_class_init,
diff --git a/hw/s390x/sclpquiesce.c b/hw/s390x/sclpquiesce.c
index ffa555313..15b06e108 100644
--- a/hw/s390x/sclpquiesce.c
+++ b/hw/s390x/sclpquiesce.c
@@ -66,7 +66,7 @@ static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr,
}
static const VMStateDescription vmstate_sclpquiesce = {
- .name = "sclpquiesce",
+ .name = TYPE_SCLP_QUIESCE,
.version_id = 0,
.minimum_version_id = 0,
.fields = (VMStateField[]) {
@@ -127,7 +127,7 @@ static void quiesce_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo sclp_quiesce_info = {
- .name = "sclpquiesce",
+ .name = TYPE_SCLP_QUIESCE,
.parent = TYPE_SCLP_EVENT,
.instance_size = sizeof(SCLPEvent),
.class_init = quiesce_class_init,
diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
index d36373e88..63da30386 100644
--- a/hw/s390x/virtio-ccw.c
+++ b/hw/s390x/virtio-ccw.c
@@ -307,11 +307,18 @@ static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info,
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 */
- if (virtio_queue_get_num(vdev, index) > num) {
+ if (info) {
+ /* virtio-1 allows changing the ring size. */
+ if (virtio_queue_get_num(vdev, index) < num) {
+ /* Fail if we exceed the maximum number. */
+ return -EINVAL;
+ }
+ virtio_queue_set_num(vdev, index, num);
+ } else if (virtio_queue_get_num(vdev, index) > num) {
+ /* Fail if we don't have a big enough queue. */
return -EINVAL;
}
+ /* We ignore possible increased num for legacy for compatibility. */
virtio_queue_set_vector(vdev, index, index);
}
/* tell notify handler in case of config change */
@@ -460,16 +467,19 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
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);
+ if (dev->revision >= 1) {
+ /* Don't offer legacy features for modern devices. */
+ features.features = (uint32_t)
+ (vdev->host_features & ~VIRTIO_LEGACY_FEATURES);
+ } else {
+ features.features = (uint32_t)vdev->host_features;
+ }
+ } else if ((features.index == 1) && (dev->revision >= 1)) {
/*
- * Don't offer version 1 to the guest if it did not
- * negotiate at least revision 1.
+ * Only offer feature bits beyond 31 if the guest has
+ * negotiated at least revision 1.
*/
- if (dev->revision <= 0) {
- features.features &= ~(1 << (VIRTIO_F_VERSION_1 - 32));
- }
+ features.features = (uint32_t)(vdev->host_features >> 32);
} else {
/* Return zeroes if the guest supports more feature bits. */
features.features = 0;
@@ -508,14 +518,12 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
virtio_set_features(vdev,
(vdev->guest_features & 0xffffffff00000000ULL) |
features.features);
- } else if (features.index == 1) {
+ } else if ((features.index == 1) && (dev->revision >= 1)) {
/*
- * The guest should not set version 1 if it didn't
- * negotiate a revision >= 1.
+ * If the guest did not negotiate at least revision 1,
+ * we did not offer it any feature bits beyond 31. Such a
+ * guest passing us any bit here is therefore buggy.
*/
- 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));
@@ -766,7 +774,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
* need to fetch it here. Nothing to do for now, though.
*/
if (dev->revision >= 0 ||
- revinfo.revision > virtio_ccw_rev_max(vdev)) {
+ revinfo.revision > virtio_ccw_rev_max(dev)) {
ret = -ENOSYS;
break;
}
@@ -1539,10 +1547,25 @@ static void virtio_ccw_device_plugged(DeviceState *d, Error **errp)
sch->id.cu_model = virtio_bus_get_vdev_id(&dev->bus);
+ if (dev->max_rev >= 1) {
+ virtio_add_feature(&vdev->host_features, VIRTIO_F_VERSION_1);
+ }
+
css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid,
d->hotplugged, 1);
}
+static void virtio_ccw_post_plugged(DeviceState *d, Error **errp)
+{
+ VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
+ VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
+
+ if (!virtio_host_has_feature(vdev, VIRTIO_F_VERSION_1)) {
+ /* A backend didn't support modern virtio. */
+ dev->max_rev = 0;
+ }
+}
+
static void virtio_ccw_device_unplugged(DeviceState *d)
{
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
@@ -1555,6 +1578,8 @@ static Property virtio_ccw_net_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1582,6 +1607,8 @@ static Property virtio_ccw_blk_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1609,6 +1636,8 @@ static Property virtio_ccw_serial_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1636,6 +1665,8 @@ static Property virtio_ccw_balloon_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1663,6 +1694,8 @@ static Property virtio_ccw_scsi_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1689,6 +1722,8 @@ static const TypeInfo virtio_ccw_scsi = {
#ifdef CONFIG_VHOST_SCSI
static Property vhost_ccw_scsi_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1727,6 +1762,8 @@ static Property virtio_ccw_rng_properties[] = {
DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1865,6 +1902,7 @@ static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data)
k->save_config = virtio_ccw_save_config;
k->load_config = virtio_ccw_load_config;
k->device_plugged = virtio_ccw_device_plugged;
+ k->post_plugged = virtio_ccw_post_plugged;
k->device_unplugged = virtio_ccw_device_unplugged;
}
@@ -1880,6 +1918,8 @@ 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_UINT32("max_revision", VirtioCcwDevice, max_rev,
+ VIRTIO_CCW_MAX_REV),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h
index 692ddd731..7ab8367ba 100644
--- a/hw/s390x/virtio-ccw.h
+++ b/hw/s390x/virtio-ccw.h
@@ -88,6 +88,7 @@ struct VirtioCcwDevice {
SubchDev *sch;
char *bus_id;
int revision;
+ uint32_t max_rev;
VirtioBusState bus;
bool ioeventfd_started;
bool ioeventfd_disabled;
@@ -102,9 +103,10 @@ struct VirtioCcwDevice {
};
/* The maximum virtio revision we support. */
-static inline int virtio_ccw_rev_max(VirtIODevice *vdev)
+#define VIRTIO_CCW_MAX_REV 1
+static inline int virtio_ccw_rev_max(VirtioCcwDevice *dev)
{
- return 0;
+ return dev->max_rev;
}
/* virtual css bus type */
diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c
index a04369c5a..576f56cbf 100644
--- a/hw/scsi/megasas.c
+++ b/hw/scsi/megasas.c
@@ -431,7 +431,7 @@ static uint64_t megasas_fw_time(void)
static uint64_t megasas_get_sata_addr(uint16_t id)
{
uint64_t addr = (0x1221ULL << 48);
- return addr & (id << 24);
+ return addr | ((uint64_t)id << 24);
}
/*
@@ -718,7 +718,7 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd)
BusChild *kid;
int num_pd_disks = 0;
- memset(&info, 0x0, cmd->iov_size);
+ memset(&info, 0x0, dcmd_size);
if (cmd->iov_size < dcmd_size) {
trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size,
dcmd_size);
@@ -757,7 +757,7 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd)
memcpy(info.product_name, base_class->product_name, 24);
snprintf(info.serial_number, 32, "%s", s->hba_serial);
- snprintf(info.package_version, 0x60, "%s-QEMU", QEMU_VERSION);
+ snprintf(info.package_version, 0x60, "%s-QEMU", qemu_hw_version());
memcpy(info.image_component[0].name, "APP", 3);
snprintf(info.image_component[0].version, 10, "%s-QEMU",
base_class->product_version);
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
index f0ae4625f..fd1171e48 100644
--- a/hw/scsi/scsi-bus.c
+++ b/hw/scsi/scsi-bus.c
@@ -136,7 +136,8 @@ static void scsi_dma_restart_cb(void *opaque, int running, RunState state)
return;
}
if (!s->bh) {
- s->bh = qemu_bh_new(scsi_dma_restart_bh, s);
+ AioContext *ctx = blk_get_aio_context(s->conf.blk);
+ s->bh = aio_bh_new(ctx, scsi_dma_restart_bh, s);
qemu_bh_schedule(s->bh);
}
}
@@ -452,7 +453,7 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r)
r->buf[7] = 0x10 | (r->req.bus->info->tcq ? 0x02 : 0); /* Sync, TCQ. */
memcpy(&r->buf[8], "QEMU ", 8);
memcpy(&r->buf[16], "QEMU TARGET ", 16);
- pstrcpy((char *) &r->buf[32], 4, qemu_get_version());
+ pstrcpy((char *) &r->buf[32], 4, qemu_hw_version());
}
return true;
}
@@ -557,7 +558,7 @@ SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d,
const int memset_off = offsetof(SCSIRequest, sense)
+ sizeof(req->sense);
- req = g_slice_alloc(reqops->size);
+ req = g_malloc(reqops->size);
memset((uint8_t *)req + memset_off, 0, reqops->size - memset_off);
req->refcount = 1;
req->bus = bus;
@@ -1621,7 +1622,7 @@ void scsi_req_unref(SCSIRequest *req)
}
object_unref(OBJECT(req->dev));
object_unref(OBJECT(qbus->parent));
- g_slice_free1(req->ops->size, req);
+ g_free(req);
}
}
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index 0e0bc6440..4797d8368 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -90,7 +90,7 @@ struct SCSIDiskState
bool tray_locked;
};
-static int scsi_handle_rw_error(SCSIDiskReq *r, int error);
+static int scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed);
static void scsi_free_request(SCSIRequest *req)
{
@@ -169,18 +169,18 @@ static void scsi_aio_complete(void *opaque, int ret)
assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, true)) {
goto done;
}
}
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
scsi_req_complete(&r->req, GOOD);
done:
@@ -217,6 +217,8 @@ static void scsi_write_do_fua(SCSIDiskReq *r)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+ assert(r->req.aiocb == NULL);
+
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
@@ -235,22 +237,17 @@ done:
scsi_req_unref(&r->req);
}
-static void scsi_dma_complete_noio(void *opaque, int ret)
+static void scsi_dma_complete_noio(SCSIDiskReq *r, int ret)
{
- SCSIDiskReq *r = (SCSIDiskReq *)opaque;
- SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+ assert(r->req.aiocb == NULL);
- if (r->req.aiocb != NULL) {
- r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
- }
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, false)) {
goto done;
}
}
@@ -271,9 +268,17 @@ done:
static void scsi_dma_complete(void *opaque, int ret)
{
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
assert(r->req.aiocb != NULL);
- scsi_dma_complete_noio(opaque, ret);
+ r->req.aiocb = NULL;
+
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ }
+ scsi_dma_complete_noio(r, ret);
}
static void scsi_read_complete(void * opaque, int ret)
@@ -284,18 +289,18 @@ static void scsi_read_complete(void * opaque, int ret)
assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, true)) {
goto done;
}
}
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
DPRINTF("Data ready tag=0x%x len=%zd\n", r->req.tag, r->qiov.size);
n = r->qiov.size / 512;
@@ -308,23 +313,20 @@ done:
}
/* Actually issue a read to the block device. */
-static void scsi_do_read(void *opaque, int ret)
+static void scsi_do_read(SCSIDiskReq *r, int ret)
{
- SCSIDiskReq *r = opaque;
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
uint32_t n;
- if (r->req.aiocb != NULL) {
- r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
- }
+ assert (r->req.aiocb == NULL);
+
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, false)) {
goto done;
}
}
@@ -349,6 +351,22 @@ done:
scsi_req_unref(&r->req);
}
+static void scsi_do_read_cb(void *opaque, int ret)
+{
+ SCSIDiskReq *r = (SCSIDiskReq *)opaque;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+
+ assert (r->req.aiocb != NULL);
+ r->req.aiocb = NULL;
+
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ }
+ scsi_do_read(opaque, ret);
+}
+
/* Read more data from scsi device into buffer. */
static void scsi_read_data(SCSIRequest *req)
{
@@ -384,7 +402,7 @@ static void scsi_read_data(SCSIRequest *req)
if (first && scsi_is_cmd_fua(&r->req.cmd)) {
block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0,
BLOCK_ACCT_FLUSH);
- r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_do_read, r);
+ r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_do_read_cb, r);
} else {
scsi_do_read(r, 0);
}
@@ -397,14 +415,17 @@ static void scsi_read_data(SCSIRequest *req)
* scsi_handle_rw_error always manages its reference counts, independent
* of the return value.
*/
-static int scsi_handle_rw_error(SCSIDiskReq *r, int error)
+static int scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed)
{
- bool is_read = (r->req.cmd.xfer == SCSI_XFER_FROM_DEV);
+ bool is_read = (r->req.cmd.mode == SCSI_XFER_FROM_DEV);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
BlockErrorAction action = blk_get_error_action(s->qdev.conf.blk,
is_read, error);
if (action == BLOCK_ERROR_ACTION_REPORT) {
+ if (acct_failed) {
+ block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ }
switch (error) {
case ENOMEDIUM:
scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
@@ -430,23 +451,19 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error)
return action != BLOCK_ERROR_ACTION_IGNORE;
}
-static void scsi_write_complete(void * opaque, int ret)
+static void scsi_write_complete_noio(SCSIDiskReq *r, int ret)
{
- SCSIDiskReq *r = (SCSIDiskReq *)opaque;
- SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
uint32_t n;
- if (r->req.aiocb != NULL) {
- r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
- }
+ assert (r->req.aiocb == NULL);
+
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, false)) {
goto done;
}
}
@@ -467,6 +484,22 @@ done:
scsi_req_unref(&r->req);
}
+static void scsi_write_complete(void * opaque, int ret)
+{
+ SCSIDiskReq *r = (SCSIDiskReq *)opaque;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+
+ assert (r->req.aiocb != NULL);
+ r->req.aiocb = NULL;
+
+ if (ret < 0) {
+ block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ } else {
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
+ }
+ scsi_write_complete_noio(r, ret);
+}
+
static void scsi_write_data(SCSIRequest *req)
{
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
@@ -480,18 +513,18 @@ static void scsi_write_data(SCSIRequest *req)
scsi_req_ref(&r->req);
if (r->req.cmd.mode != SCSI_XFER_TO_DEV) {
DPRINTF("Data transfer direction invalid\n");
- scsi_write_complete(r, -EINVAL);
+ scsi_write_complete_noio(r, -EINVAL);
return;
}
if (!r->req.sg && !r->qiov.size) {
/* Called for the first time. Ask the driver to send us more data. */
r->started = true;
- scsi_write_complete(r, 0);
+ scsi_write_complete_noio(r, 0);
return;
}
if (s->tray_open) {
- scsi_write_complete(r, -ENOMEDIUM);
+ scsi_write_complete_noio(r, -ENOMEDIUM);
return;
}
@@ -500,7 +533,7 @@ static void scsi_write_data(SCSIRequest *req)
if (r->req.sg) {
scsi_dma_complete_noio(r, 0);
} else {
- scsi_write_complete(r, 0);
+ scsi_write_complete_noio(r, 0);
}
return;
}
@@ -1557,22 +1590,24 @@ typedef struct UnmapCBData {
int count;
} UnmapCBData;
-static void scsi_unmap_complete(void *opaque, int ret)
+static void scsi_unmap_complete(void *opaque, int ret);
+
+static void scsi_unmap_complete_noio(UnmapCBData *data, int ret)
{
- UnmapCBData *data = opaque;
SCSIDiskReq *r = data->r;
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
uint64_t sector_num;
uint32_t nb_sectors;
- r->req.aiocb = NULL;
+ assert(r->req.aiocb == NULL);
+
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, false)) {
goto done;
}
}
@@ -1601,6 +1636,17 @@ done:
g_free(data);
}
+static void scsi_unmap_complete(void *opaque, int ret)
+{
+ UnmapCBData *data = opaque;
+ SCSIDiskReq *r = data->r;
+
+ assert(r->req.aiocb != NULL);
+ r->req.aiocb = NULL;
+
+ scsi_unmap_complete_noio(data, ret);
+}
+
static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
@@ -1638,7 +1684,7 @@ static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
/* The matching unref is in scsi_unmap_complete, before data is freed. */
scsi_req_ref(&r->req);
- scsi_unmap_complete(data, 0);
+ scsi_unmap_complete_noio(data, 0);
return;
invalid_param_len:
@@ -1665,18 +1711,19 @@ static void scsi_write_same_complete(void *opaque, int ret)
assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
- block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret)) {
+ if (scsi_handle_rw_error(r, -ret, true)) {
goto done;
}
}
+ block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
+
data->nb_sectors -= data->iov.iov_len / 512;
data->sector += data->iov.iov_len / 512;
data->iov.iov_len = MIN(data->nb_sectors * 512, data->iov.iov_len);
@@ -2284,7 +2331,7 @@ static void scsi_realize(SCSIDevice *dev, Error **errp)
}
if (!s->version) {
- s->version = g_strdup(qemu_get_version());
+ s->version = g_strdup(qemu_hw_version());
}
if (!s->vendor) {
s->vendor = g_strdup("QEMU");
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
index e53470f85..a4626f72c 100644
--- a/hw/scsi/scsi-generic.c
+++ b/hw/scsi/scsi-generic.c
@@ -88,12 +88,12 @@ static void scsi_free_request(SCSIRequest *req)
}
/* Helper function for command completion. */
-static void scsi_command_complete(void *opaque, int ret)
+static void scsi_command_complete_noio(SCSIGenericReq *r, int ret)
{
int status;
- SCSIGenericReq *r = (SCSIGenericReq *)opaque;
- r->req.aiocb = NULL;
+ assert(r->req.aiocb == NULL);
+
if (r->req.io_canceled) {
scsi_req_cancel_complete(&r->req);
goto done;
@@ -142,6 +142,15 @@ done:
scsi_req_unref(&r->req);
}
+static void scsi_command_complete(void *opaque, int ret)
+{
+ SCSIGenericReq *r = (SCSIGenericReq *)opaque;
+
+ assert(r->req.aiocb != NULL);
+ r->req.aiocb = NULL;
+ scsi_command_complete_noio(r, ret);
+}
+
static int execute_command(BlockBackend *blk,
SCSIGenericReq *r, int direction,
BlockCompletionFunc *complete)
@@ -172,33 +181,51 @@ static void scsi_read_complete(void * opaque, int ret)
SCSIDevice *s = r->req.dev;
int len;
+ assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
+
if (ret || r->req.io_canceled) {
- scsi_command_complete(r, ret);
+ scsi_command_complete_noio(r, ret);
return;
}
+
len = r->io_header.dxfer_len - r->io_header.resid;
DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len);
r->len = -1;
if (len == 0) {
- scsi_command_complete(r, 0);
- } else {
- /* Snoop READ CAPACITY output to set the blocksize. */
- if (r->req.cmd.buf[0] == READ_CAPACITY_10 &&
- (ldl_be_p(&r->buf[0]) != 0xffffffffU || s->max_lba == 0)) {
- s->blocksize = ldl_be_p(&r->buf[4]);
- s->max_lba = ldl_be_p(&r->buf[0]) & 0xffffffffULL;
- } else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 &&
- (r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) {
- s->blocksize = ldl_be_p(&r->buf[8]);
- s->max_lba = ldq_be_p(&r->buf[0]);
- }
- blk_set_guest_block_size(s->conf.blk, s->blocksize);
+ scsi_command_complete_noio(r, 0);
+ return;
+ }
- scsi_req_data(&r->req, len);
- scsi_req_unref(&r->req);
+ /* Snoop READ CAPACITY output to set the blocksize. */
+ if (r->req.cmd.buf[0] == READ_CAPACITY_10 &&
+ (ldl_be_p(&r->buf[0]) != 0xffffffffU || s->max_lba == 0)) {
+ s->blocksize = ldl_be_p(&r->buf[4]);
+ s->max_lba = ldl_be_p(&r->buf[0]) & 0xffffffffULL;
+ } else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 &&
+ (r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) {
+ s->blocksize = ldl_be_p(&r->buf[8]);
+ s->max_lba = ldq_be_p(&r->buf[0]);
}
+ blk_set_guest_block_size(s->conf.blk, s->blocksize);
+
+ /* Patch MODE SENSE device specific parameters if the BDS is opened
+ * readonly.
+ */
+ if ((s->type == TYPE_DISK || s->type == TYPE_TAPE) &&
+ blk_is_read_only(s->conf.blk) &&
+ (r->req.cmd.buf[0] == MODE_SENSE ||
+ r->req.cmd.buf[0] == MODE_SENSE_10) &&
+ (r->req.cmd.buf[1] & 0x8) == 0) {
+ if (r->req.cmd.buf[0] == MODE_SENSE) {
+ r->buf[2] |= 0x80;
+ } else {
+ r->buf[3] |= 0x80;
+ }
+ }
+ scsi_req_data(&r->req, len);
+ scsi_req_unref(&r->req);
}
/* Read more data from scsi device into buffer. */
@@ -213,14 +240,14 @@ static void scsi_read_data(SCSIRequest *req)
/* The request is used as the AIO opaque value, so add a ref. */
scsi_req_ref(&r->req);
if (r->len == -1) {
- scsi_command_complete(r, 0);
+ scsi_command_complete_noio(r, 0);
return;
}
ret = execute_command(s->conf.blk, r, SG_DXFER_FROM_DEV,
scsi_read_complete);
if (ret < 0) {
- scsi_command_complete(r, ret);
+ scsi_command_complete_noio(r, ret);
}
}
@@ -230,9 +257,12 @@ static void scsi_write_complete(void * opaque, int ret)
SCSIDevice *s = r->req.dev;
DPRINTF("scsi_write_complete() ret = %d\n", ret);
+
+ assert(r->req.aiocb != NULL);
r->req.aiocb = NULL;
+
if (ret || r->req.io_canceled) {
- scsi_command_complete(r, ret);
+ scsi_command_complete_noio(r, ret);
return;
}
@@ -242,7 +272,7 @@ static void scsi_write_complete(void * opaque, int ret)
DPRINTF("block size %d\n", s->blocksize);
}
- scsi_command_complete(r, ret);
+ scsi_command_complete_noio(r, ret);
}
/* Write data to a scsi device. Returns nonzero on failure.
@@ -264,7 +294,7 @@ static void scsi_write_data(SCSIRequest *req)
scsi_req_ref(&r->req);
ret = execute_command(s->conf.blk, r, SG_DXFER_TO_DEV, scsi_write_complete);
if (ret < 0) {
- scsi_command_complete(r, ret);
+ scsi_command_complete_noio(r, ret);
}
}
@@ -306,7 +336,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
ret = execute_command(s->conf.blk, r, SG_DXFER_NONE,
scsi_command_complete);
if (ret < 0) {
- scsi_command_complete(r, ret);
+ scsi_command_complete_noio(r, ret);
return 0;
}
return 0;
diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c
index 891424fae..f4f5140a4 100644
--- a/hw/scsi/spapr_vscsi.c
+++ b/hw/scsi/spapr_vscsi.c
@@ -750,7 +750,6 @@ static void vscsi_report_luns(VSCSIState *s, vscsi_req *req)
len = n+8;
resp_data = g_malloc0(len);
- memset(resp_data, 0, len);
stl_be_p(resp_data, n);
i = found_lun0 ? 8 : 16;
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c
index 62d91003b..00cdac62f 100644
--- a/hw/scsi/vhost-scsi.c
+++ b/hw/scsi/vhost-scsi.c
@@ -26,6 +26,7 @@
#include "hw/virtio/virtio-bus.h"
#include "hw/virtio/virtio-access.h"
#include "hw/fw-path-provider.h"
+#include "linux/vhost.h"
/* Features supported by host kernel. */
static const int kernel_feature_bits[] = {
@@ -45,7 +46,7 @@ static int vhost_scsi_set_endpoint(VHostSCSI *s)
memset(&backend, 0, sizeof(backend));
pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn);
- ret = vhost_ops->vhost_call(&s->dev, VHOST_SCSI_SET_ENDPOINT, &backend);
+ ret = vhost_ops->vhost_scsi_set_endpoint(&s->dev, &backend);
if (ret < 0) {
return -errno;
}
@@ -60,7 +61,7 @@ static void vhost_scsi_clear_endpoint(VHostSCSI *s)
memset(&backend, 0, sizeof(backend));
pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn);
- vhost_ops->vhost_call(&s->dev, VHOST_SCSI_CLEAR_ENDPOINT, &backend);
+ vhost_ops->vhost_scsi_clear_endpoint(&s->dev, &backend);
}
static int vhost_scsi_start(VHostSCSI *s)
@@ -76,8 +77,7 @@ static int vhost_scsi_start(VHostSCSI *s)
return -ENOSYS;
}
- ret = vhost_ops->vhost_call(&s->dev,
- VHOST_SCSI_GET_ABI_VERSION, &abi_version);
+ ret = vhost_ops->vhost_scsi_get_abi_version(&s->dev, &abi_version);
if (ret < 0) {
return -errno;
}
@@ -118,7 +118,7 @@ static int vhost_scsi_start(VHostSCSI *s)
* enabling/disabling irqfd.
*/
for (i = 0; i < s->dev.nvqs; i++) {
- vhost_virtqueue_mask(&s->dev, vdev, i, false);
+ vhost_virtqueue_mask(&s->dev, vdev, s->dev.vq_index + i, false);
}
return ret;
@@ -277,6 +277,7 @@ static void vhost_scsi_unrealize(DeviceState *dev, Error **errp)
/* This will stop vhost backend. */
vhost_scsi_set_status(vdev, 0);
+ vhost_dev_cleanup(&s->dev);
g_free(s->dev.vqs);
virtio_scsi_common_unrealize(dev, errp);
diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c
index 5575648a9..0d8d71e24 100644
--- a/hw/scsi/virtio-scsi-dataplane.c
+++ b/hw/scsi/virtio-scsi-dataplane.c
@@ -57,10 +57,10 @@ static VirtIOSCSIVring *virtio_scsi_vring_init(VirtIOSCSI *s,
return NULL;
}
- r = g_slice_new(VirtIOSCSIVring);
+ r = g_new(VirtIOSCSIVring, 1);
r->host_notifier = *virtio_queue_get_host_notifier(vq);
r->guest_notifier = *virtio_queue_get_guest_notifier(vq);
- aio_set_event_notifier(s->ctx, &r->host_notifier, handler);
+ aio_set_event_notifier(s->ctx, &r->host_notifier, true, handler);
r->parent = s;
@@ -71,9 +71,9 @@ static VirtIOSCSIVring *virtio_scsi_vring_init(VirtIOSCSI *s,
return r;
fail_vring:
- aio_set_event_notifier(s->ctx, &r->host_notifier, NULL);
+ aio_set_event_notifier(s->ctx, &r->host_notifier, true, NULL);
k->set_host_notifier(qbus->parent, n, false);
- g_slice_free(VirtIOSCSIVring, r);
+ g_free(r);
return NULL;
}
@@ -162,14 +162,17 @@ static void virtio_scsi_clear_aio(VirtIOSCSI *s)
int i;
if (s->ctrl_vring) {
- aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier, NULL);
+ aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier,
+ true, NULL);
}
if (s->event_vring) {
- aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier, NULL);
+ aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier,
+ true, NULL);
}
if (s->cmd_vrings) {
for (i = 0; i < vs->conf.num_queues && s->cmd_vrings[i]; i++) {
- aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier, NULL);
+ aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier,
+ true, NULL);
}
}
}
@@ -182,18 +185,18 @@ static void virtio_scsi_vring_teardown(VirtIOSCSI *s)
if (s->ctrl_vring) {
vring_teardown(&s->ctrl_vring->vring, vdev, 0);
- g_slice_free(VirtIOSCSIVring, s->ctrl_vring);
+ g_free(s->ctrl_vring);
s->ctrl_vring = NULL;
}
if (s->event_vring) {
vring_teardown(&s->event_vring->vring, vdev, 1);
- g_slice_free(VirtIOSCSIVring, s->event_vring);
+ g_free(s->event_vring);
s->event_vring = NULL;
}
if (s->cmd_vrings) {
for (i = 0; i < vs->conf.num_queues && s->cmd_vrings[i]; i++) {
vring_teardown(&s->cmd_vrings[i]->vring, vdev, 2 + i);
- g_slice_free(VirtIOSCSIVring, s->cmd_vrings[i]);
+ g_free(s->cmd_vrings[i]);
s->cmd_vrings[i] = NULL;
}
free(s->cmd_vrings);
@@ -290,10 +293,13 @@ void virtio_scsi_dataplane_stop(VirtIOSCSI *s)
aio_context_acquire(s->ctx);
- aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier, NULL);
- aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier, NULL);
+ aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier,
+ true, NULL);
+ aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier,
+ true, NULL);
for (i = 0; i < vs->conf.num_queues; i++) {
- aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier, NULL);
+ aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier,
+ true, NULL);
}
blk_drain_all(); /* ensure there are no in-flight requests */
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index 93c33e11f..3a4f520fb 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -47,7 +47,7 @@ VirtIOSCSIReq *virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq)
const size_t zero_skip = offsetof(VirtIOSCSIReq, elem)
+ sizeof(VirtQueueElement);
- req = g_slice_alloc(sizeof(*req) + vs->cdb_size);
+ req = g_malloc(sizeof(*req) + vs->cdb_size);
req->vq = vq;
req->dev = s;
qemu_sglist_init(&req->qsgl, DEVICE(s), 8, &address_space_memory);
@@ -58,11 +58,9 @@ VirtIOSCSIReq *virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq)
void virtio_scsi_free_req(VirtIOSCSIReq *req)
{
- VirtIOSCSICommon *vs = (VirtIOSCSICommon *)req->dev;
-
qemu_iovec_destroy(&req->resp_iov);
qemu_sglist_destroy(&req->qsgl);
- g_slice_free1(sizeof(*req) + vs->cdb_size, req);
+ g_free(req);
}
static void virtio_scsi_complete_req(VirtIOSCSIReq *req)
@@ -207,15 +205,8 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq)
assert(n < vs->conf.num_queues);
req = virtio_scsi_init_req(s, vs->cmd_vqs[n]);
qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem));
- /* TODO: add a way for SCSIBusInfo's load_request to fail,
- * and fail migration instead of asserting here.
- * When we do, we might be able to re-enable NDEBUG below.
- */
-#ifdef NDEBUG
-#error building with NDEBUG is not supported
-#endif
- assert(req->elem.in_num <= ARRAY_SIZE(req->elem.in_sg));
- assert(req->elem.out_num <= ARRAY_SIZE(req->elem.out_sg));
+
+ virtqueue_map(&req->elem);
if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size,
sizeof(VirtIOSCSICmdResp) + vs->sense_size) < 0) {
@@ -245,7 +236,7 @@ static void virtio_scsi_cancel_notify(Notifier *notifier, void *data)
if (--n->tmf_req->remaining == 0) {
virtio_scsi_complete_req(n->tmf_req);
}
- g_slice_free(VirtIOSCSICancelNotifier, n);
+ g_free(n);
}
/* Return 0 if the request is ready to be completed and return to guest;
@@ -259,7 +250,7 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
int target;
int ret = 0;
- if (s->dataplane_started) {
+ if (s->dataplane_started && d) {
assert(blk_get_aio_context(d->conf.blk) == s->ctx);
}
/* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE". */
@@ -296,7 +287,7 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
VirtIOSCSICancelNotifier *notifier;
req->remaining = 1;
- notifier = g_slice_new(VirtIOSCSICancelNotifier);
+ notifier = g_new(VirtIOSCSICancelNotifier, 1);
notifier->tmf_req = req;
notifier->notifier.notify = virtio_scsi_cancel_notify;
scsi_req_cancel_async(r, &notifier->notifier);
@@ -345,7 +336,7 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
VirtIOSCSICancelNotifier *notifier;
req->remaining++;
- notifier = g_slice_new(VirtIOSCSICancelNotifier);
+ notifier = g_new(VirtIOSCSICancelNotifier, 1);
notifier->notifier.notify = virtio_scsi_cancel_notify;
notifier->tmf_req = req;
scsi_req_cancel_async(r, &notifier->notifier);
diff --git a/hw/sd/milkymist-memcard.c b/hw/sd/milkymist-memcard.c
index 2209ef1d5..b430d5668 100644
--- a/hw/sd/milkymist-memcard.c
+++ b/hw/sd/milkymist-memcard.c
@@ -28,7 +28,7 @@
#include "qemu/error-report.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
-#include "hw/sd.h"
+#include "hw/sd/sd.h"
enum {
ENABLE_CMD_TX = (1<<0),
diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c
index d072deca1..5bc47193f 100644
--- a/hw/sd/omap_mmc.c
+++ b/hw/sd/omap_mmc.c
@@ -18,7 +18,7 @@
*/
#include "hw/hw.h"
#include "hw/arm/omap.h"
-#include "hw/sd.h"
+#include "hw/sd/sd.h"
struct omap_mmc_s {
qemu_irq irq;
@@ -578,8 +578,7 @@ struct omap_mmc_s *omap_mmc_init(hwaddr base,
BlockBackend *blk,
qemu_irq irq, qemu_irq dma[], omap_clk clk)
{
- struct omap_mmc_s *s = (struct omap_mmc_s *)
- g_malloc0(sizeof(struct omap_mmc_s));
+ struct omap_mmc_s *s = g_new0(struct omap_mmc_s, 1);
s->irq = irq;
s->dma = dma;
@@ -605,8 +604,7 @@ struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta,
BlockBackend *blk, qemu_irq irq, qemu_irq dma[],
omap_clk fclk, omap_clk iclk)
{
- struct omap_mmc_s *s = (struct omap_mmc_s *)
- g_malloc0(sizeof(struct omap_mmc_s));
+ struct omap_mmc_s *s = g_new0(struct omap_mmc_s, 1);
s->irq = irq;
s->dma = dma;
diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c
index 11fcd479d..326c53ad9 100644
--- a/hw/sd/pl181.c
+++ b/hw/sd/pl181.c
@@ -10,7 +10,7 @@
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
#include "hw/sysbus.h"
-#include "hw/sd.h"
+#include "hw/sd/sd.h"
//#define DEBUG_PL181 1
@@ -46,7 +46,7 @@ typedef struct PL181State {
int32_t fifo_pos;
int32_t fifo_len;
/* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives
- while it is reading the FIFO. We hack around this be defering
+ while it is reading the FIFO. We hack around this by deferring
subsequent transfers until after the driver polls the status word.
http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1
*/
diff --git a/hw/sd/pxa2xx_mmci.c b/hw/sd/pxa2xx_mmci.c
index d1fe6d58e..b21708007 100644
--- a/hw/sd/pxa2xx_mmci.c
+++ b/hw/sd/pxa2xx_mmci.c
@@ -12,7 +12,7 @@
#include "hw/hw.h"
#include "hw/arm/pxa.h"
-#include "hw/sd.h"
+#include "hw/sd/sd.h"
#include "hw/qdev.h"
struct PXA2xxMMCIState {
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index a1ff465a6..1a9935cf9 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -31,7 +31,7 @@
#include "hw/hw.h"
#include "sysemu/block-backend.h"
-#include "hw/sd.h"
+#include "hw/sd/sd.h"
#include "qemu/bitmap.h"
//#define DEBUG_SD 1
@@ -412,8 +412,7 @@ static void sd_reset(SDState *sd)
sd_set_cardstatus(sd);
sd_set_sdstatus(sd);
- if (sd->wp_groups)
- g_free(sd->wp_groups);
+ g_free(sd->wp_groups);
sd->wp_switch = sd->blk ? blk_is_read_only(sd->blk) : false;
sd->wpgrps_size = sect;
sd->wp_groups = bitmap_new(sd->wpgrps_size);
@@ -493,7 +492,10 @@ SDState *sd_init(BlockBackend *blk, bool is_spi)
sd->blk = blk;
sd_reset(sd);
if (sd->blk) {
- blk_attach_dev_nofail(sd->blk, sd);
+ /* Attach dev if not already attached. (This call ignores an
+ * error return code if sd->blk is already attached.) */
+ /* FIXME ignoring blk_attach_dev() failure is dangerously brittle */
+ blk_attach_dev(sd->blk, sd);
blk_set_dev_ops(sd->blk, &sd_block_ops, sd);
}
vmstate_register(NULL, -1, &sd_vmstate, sd);
diff --git a/hw/sd/sdhci.h b/hw/sd/sdhci-internal.h
index 3352d23d6..c712daf4e 100644
--- a/hw/sd/sdhci.h
+++ b/hw/sd/sdhci-internal.h
@@ -21,14 +21,10 @@
* 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 SDHCI_INTERNAL_H
+#define SDHCI_INTERNAL_H
-#ifndef SDHCI_H
-#define SDHCI_H
-
-#include "qemu-common.h"
-#include "hw/pci/pci.h"
-#include "hw/sysbus.h"
-#include "hw/sd.h"
+#include "hw/sd/sdhci.h"
/* R/W SDMA System Address register 0x0 */
#define SDHC_SYSAD 0x00
@@ -231,65 +227,6 @@ enum {
sdhc_gap_write = 2 /* SDHC stopped at block gap during write operation */
};
-/* SD/MMC host controller state */
-typedef struct SDHCIState {
- union {
- PCIDevice pcidev;
- SysBusDevice busdev;
- };
- SDState *card;
- MemoryRegion iomem;
-
- QEMUTimer *insert_timer; /* timer for 'changing' sd card. */
- QEMUTimer *transfer_timer;
- qemu_irq eject_cb;
- qemu_irq ro_cb;
- qemu_irq irq;
-
- uint32_t sdmasysad; /* SDMA System Address register */
- uint16_t blksize; /* Host DMA Buff Boundary and Transfer BlkSize Reg */
- uint16_t blkcnt; /* Blocks count for current transfer */
- uint32_t argument; /* Command Argument Register */
- uint16_t trnmod; /* Transfer Mode Setting Register */
- uint16_t cmdreg; /* Command Register */
- uint32_t rspreg[4]; /* Response Registers 0-3 */
- uint32_t prnsts; /* Present State Register */
- uint8_t hostctl; /* Host Control Register */
- uint8_t pwrcon; /* Power control Register */
- uint8_t blkgap; /* Block Gap Control Register */
- uint8_t wakcon; /* WakeUp Control Register */
- uint16_t clkcon; /* Clock control Register */
- uint8_t timeoutcon; /* Timeout Control Register */
- uint8_t admaerr; /* ADMA Error Status Register */
- uint16_t norintsts; /* Normal Interrupt Status Register */
- uint16_t errintsts; /* Error Interrupt Status Register */
- uint16_t norintstsen; /* Normal Interrupt Status Enable Register */
- uint16_t errintstsen; /* Error Interrupt Status Enable Register */
- uint16_t norintsigen; /* Normal Interrupt Signal Enable Register */
- uint16_t errintsigen; /* Error Interrupt Signal Enable Register */
- uint16_t acmd12errsts; /* Auto CMD12 error status register */
- uint64_t admasysaddr; /* ADMA System Address Register */
-
- uint32_t capareg; /* Capabilities Register */
- uint32_t maxcurr; /* Maximum Current Capabilities Register */
- uint8_t *fifo_buffer; /* SD host i/o FIFO buffer */
- uint32_t buf_maxsz;
- uint16_t data_count; /* current element in FIFO buffer */
- uint8_t stopped_state;/* Current SDHC state */
- /* Buffer Data Port Register - virtual access point to R and W buffers */
- /* Software Reset Register - always reads as 0 */
- /* Force Event Auto CMD12 Error Interrupt Reg - write only */
- /* Force Event Error Interrupt Register- write only */
- /* RO Host Controller Version Register always reads as 0x2401 */
-} SDHCIState;
-
extern const VMStateDescription sdhci_vmstate;
-#define TYPE_PCI_SDHCI "sdhci-pci"
-#define PCI_SDHCI(obj) OBJECT_CHECK(SDHCIState, (obj), TYPE_PCI_SDHCI)
-
-#define TYPE_SYSBUS_SDHCI "generic-sdhci"
-#define SYSBUS_SDHCI(obj) \
- OBJECT_CHECK(SDHCIState, (obj), TYPE_SYSBUS_SDHCI)
-
-#endif /* SDHCI_H */
+#endif
diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
index e63367ba5..861276065 100644
--- a/hw/sd/sdhci.c
+++ b/hw/sd/sdhci.c
@@ -22,38 +22,38 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include <inttypes.h>
#include "hw/hw.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
#include "sysemu/dma.h"
#include "qemu/timer.h"
#include "qemu/bitops.h"
-
-#include "sdhci.h"
+#include "sdhci-internal.h"
/* host controller debug messages */
#ifndef SDHC_DEBUG
#define SDHC_DEBUG 0
#endif
-#if SDHC_DEBUG == 0
- #define DPRINT_L1(fmt, args...) do { } while (0)
- #define DPRINT_L2(fmt, args...) do { } while (0)
- #define ERRPRINT(fmt, args...) do { } while (0)
-#elif SDHC_DEBUG == 1
- #define DPRINT_L1(fmt, args...) \
- do {fprintf(stderr, "QEMU SDHC: "fmt, ## args); } while (0)
- #define DPRINT_L2(fmt, args...) do { } while (0)
- #define ERRPRINT(fmt, args...) \
- do {fprintf(stderr, "QEMU SDHC ERROR: "fmt, ## args); } while (0)
-#else
- #define DPRINT_L1(fmt, args...) \
- do {fprintf(stderr, "QEMU SDHC: "fmt, ## args); } while (0)
- #define DPRINT_L2(fmt, args...) \
- do {fprintf(stderr, "QEMU SDHC: "fmt, ## args); } while (0)
- #define ERRPRINT(fmt, args...) \
- do {fprintf(stderr, "QEMU SDHC ERROR: "fmt, ## args); } while (0)
-#endif
+#define DPRINT_L1(fmt, args...) \
+ do { \
+ if (SDHC_DEBUG) { \
+ fprintf(stderr, "QEMU SDHC: " fmt, ## args); \
+ } \
+ } while (0)
+#define DPRINT_L2(fmt, args...) \
+ do { \
+ if (SDHC_DEBUG > 1) { \
+ fprintf(stderr, "QEMU SDHC: " fmt, ## args); \
+ } \
+ } while (0)
+#define ERRPRINT(fmt, args...) \
+ do { \
+ if (SDHC_DEBUG) { \
+ fprintf(stderr, "QEMU SDHC ERROR: " fmt, ## args); \
+ } \
+ } while (0)
/* Default SD/MMC host controller features information, which will be
* presented in CAPABILITIES register of generic SD host controller at reset.
@@ -719,7 +719,8 @@ static void sdhci_do_adma(SDHCIState *s)
break;
case SDHC_ADMA_ATTR_ACT_LINK: /* link to next descriptor table */
s->admasysaddr = dscr.addr;
- DPRINT_L1("ADMA link: admasysaddr=0x%lx\n", s->admasysaddr);
+ DPRINT_L1("ADMA link: admasysaddr=0x%" PRIx64 "\n",
+ s->admasysaddr);
break;
default:
s->admasysaddr += dscr.incr;
@@ -727,7 +728,8 @@ static void sdhci_do_adma(SDHCIState *s)
}
if (dscr.attr & SDHC_ADMA_ATTR_INT) {
- DPRINT_L1("ADMA interrupt: admasysaddr=0x%lx\n", s->admasysaddr);
+ DPRINT_L1("ADMA interrupt: admasysaddr=0x%" PRIx64 "\n",
+ s->admasysaddr);
if (s->norintstsen & SDHC_NISEN_DMA) {
s->norintsts |= SDHC_NIS_DMA;
}
@@ -1006,6 +1008,16 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
MASKED_WRITE(s->blksize, mask, value);
MASKED_WRITE(s->blkcnt, mask >> 16, value >> 16);
}
+
+ /* Limit block size to the maximum buffer size */
+ if (extract32(s->blksize, 0, 12) > s->buf_maxsz) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Size 0x%x is larger than " \
+ "the maximum buffer 0x%x", __func__, s->blksize,
+ s->buf_maxsz);
+
+ s->blksize = deposit32(s->blksize, 0, 12, s->buf_maxsz);
+ }
+
break;
case SDHC_ARGUMENT:
MASKED_WRITE(s->argument, mask, value);
@@ -1142,13 +1154,9 @@ static inline unsigned int sdhci_get_fifolen(SDHCIState *s)
}
}
-static void sdhci_initfn(SDHCIState *s)
+static void sdhci_initfn(SDHCIState *s, BlockBackend *blk)
{
- DriveInfo *di;
-
- /* FIXME use a qdev drive property instead of drive_get_next() */
- di = drive_get_next(IF_SD);
- s->card = sd_init(di ? blk_by_legacy_dinfo(di) : NULL, false);
+ s->card = sd_init(blk, false);
if (s->card == NULL) {
exit(1);
}
@@ -1169,10 +1177,8 @@ static void sdhci_uninitfn(SDHCIState *s)
qemu_free_irq(s->eject_cb);
qemu_free_irq(s->ro_cb);
- if (s->fifo_buffer) {
- g_free(s->fifo_buffer);
- s->fifo_buffer = NULL;
- }
+ g_free(s->fifo_buffer);
+ s->fifo_buffer = NULL;
}
const VMStateDescription sdhci_vmstate = {
@@ -1214,7 +1220,13 @@ const VMStateDescription sdhci_vmstate = {
/* Capabilities registers provide information on supported features of this
* specific host controller implementation */
-static Property sdhci_properties[] = {
+static Property sdhci_pci_properties[] = {
+ /*
+ * We currently fuse controller and card into a single device
+ * model, but we intend to separate them. For that purpose, the
+ * properties that belong to the card are marked as experimental.
+ */
+ DEFINE_PROP_DRIVE("x-drive", SDHCIState, blk),
DEFINE_PROP_UINT32("capareg", SDHCIState, capareg,
SDHC_CAPAB_REG_DEFAULT),
DEFINE_PROP_UINT32("maxcurr", SDHCIState, maxcurr, 0),
@@ -1226,7 +1238,7 @@ static void sdhci_pci_realize(PCIDevice *dev, Error **errp)
SDHCIState *s = PCI_SDHCI(dev);
dev->config[PCI_CLASS_PROG] = 0x01; /* Standard Host supported DMA */
dev->config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */
- sdhci_initfn(s);
+ sdhci_initfn(s, s->blk);
s->buf_maxsz = sdhci_get_fifolen(s);
s->fifo_buffer = g_malloc0(s->buf_maxsz);
s->irq = pci_allocate_irq(dev);
@@ -1253,9 +1265,7 @@ static void sdhci_pci_class_init(ObjectClass *klass, void *data)
k->class_id = PCI_CLASS_SYSTEM_SDHCI;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
dc->vmsd = &sdhci_vmstate;
- dc->props = sdhci_properties;
- /* Reason: realize() method uses drive_get_next() */
- dc->cannot_instantiate_with_device_add_yet = true;
+ dc->props = sdhci_pci_properties;
}
static const TypeInfo sdhci_pci_info = {
@@ -1265,10 +1275,21 @@ static const TypeInfo sdhci_pci_info = {
.class_init = sdhci_pci_class_init,
};
+static Property sdhci_sysbus_properties[] = {
+ DEFINE_PROP_UINT32("capareg", SDHCIState, capareg,
+ SDHC_CAPAB_REG_DEFAULT),
+ DEFINE_PROP_UINT32("maxcurr", SDHCIState, maxcurr, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void sdhci_sysbus_init(Object *obj)
{
SDHCIState *s = SYSBUS_SDHCI(obj);
- sdhci_initfn(s);
+ DriveInfo *di;
+
+ /* FIXME use a qdev drive property instead of drive_get_next() */
+ di = drive_get_next(IF_SD);
+ sdhci_initfn(s, di ? blk_by_legacy_dinfo(di) : NULL);
}
static void sdhci_sysbus_finalize(Object *obj)
@@ -1295,7 +1316,7 @@ static void sdhci_sysbus_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
dc->vmsd = &sdhci_vmstate;
- dc->props = sdhci_properties;
+ dc->props = sdhci_sysbus_properties;
dc->realize = sdhci_sysbus_realize;
/* Reason: instance_init() method uses drive_get_next() */
dc->cannot_instantiate_with_device_add_yet = true;
diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c
index e4b2d4f83..c49ff62f5 100644
--- a/hw/sd/ssi-sd.c
+++ b/hw/sd/ssi-sd.c
@@ -13,7 +13,7 @@
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
#include "hw/ssi.h"
-#include "hw/sd.h"
+#include "hw/sd/sd.h"
//#define DEBUG_SSI_SD 1
diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c
index 5e22ed79b..c1ff9a0cf 100644
--- a/hw/sh4/r2d.c
+++ b/hw/sh4/r2d.c
@@ -255,7 +255,7 @@ static void r2d_init(MachineState *machine)
qemu_register_reset(main_cpu_reset, reset_info);
/* Allocate memory space */
- memory_region_init_ram(sdram, NULL, "r2d.sdram", SDRAM_SIZE, &error_abort);
+ memory_region_init_ram(sdram, NULL, "r2d.sdram", SDRAM_SIZE, &error_fatal);
vmstate_register_ram_global(sdram);
memory_region_add_subregion(address_space_mem, SDRAM_BASE, sdram);
/* Register peripherals */
@@ -338,9 +338,9 @@ static void r2d_init(MachineState *machine)
}
/* initialization which should be done by firmware */
- boot_params.loader_type = 1;
- boot_params.initrd_start = INITRD_LOAD_OFFSET;
- boot_params.initrd_size = initrd_size;
+ boot_params.loader_type = tswap32(1);
+ boot_params.initrd_start = tswap32(INITRD_LOAD_OFFSET);
+ boot_params.initrd_size = tswap32(initrd_size);
}
if (kernel_cmdline) {
@@ -354,15 +354,10 @@ static void r2d_init(MachineState *machine)
SDRAM_BASE + BOOT_PARAMS_OFFSET);
}
-static QEMUMachine r2d_machine = {
- .name = "r2d",
- .desc = "r2d-plus board",
- .init = r2d_init,
-};
-
-static void r2d_machine_init(void)
+static void r2d_machine_init(MachineClass *mc)
{
- qemu_register_machine(&r2d_machine);
+ mc->desc = "r2d-plus board";
+ mc->init = r2d_init;
}
-machine_init(r2d_machine_init);
+DEFINE_MACHINE("r2d", r2d_machine_init)
diff --git a/hw/sh4/shix.c b/hw/sh4/shix.c
index f93f98e56..d508be958 100644
--- a/hw/sh4/shix.c
+++ b/hw/sh4/shix.c
@@ -59,16 +59,16 @@ static void shix_init(MachineState *machine)
}
/* Allocate memory space */
- memory_region_init_ram(rom, NULL, "shix.rom", 0x4000, &error_abort);
+ memory_region_init_ram(rom, NULL, "shix.rom", 0x4000, &error_fatal);
vmstate_register_ram_global(rom);
memory_region_set_readonly(rom, true);
memory_region_add_subregion(sysmem, 0x00000000, rom);
memory_region_init_ram(&sdram[0], NULL, "shix.sdram1", 0x01000000,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&sdram[0]);
memory_region_add_subregion(sysmem, 0x08000000, &sdram[0]);
memory_region_init_ram(&sdram[1], NULL, "shix.sdram2", 0x01000000,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&sdram[1]);
memory_region_add_subregion(sysmem, 0x0c000000, &sdram[1]);
@@ -87,16 +87,11 @@ static void shix_init(MachineState *machine)
tc58128_init(s, "shix_linux_nand.bin", NULL);
}
-static QEMUMachine shix_machine = {
- .name = "shix",
- .desc = "shix card",
- .init = shix_init,
- .is_default = 1,
-};
-
-static void shix_machine_init(void)
+static void shix_machine_init(MachineClass *mc)
{
- qemu_register_machine(&shix_machine);
+ mc->desc = "shix card";
+ mc->init = shix_init;
+ mc->is_default = 1;
}
-machine_init(shix_machine_init);
+DEFINE_MACHINE("shix", shix_machine_init)
diff --git a/hw/smbios/Makefile.objs b/hw/smbios/Makefile.objs
new file mode 100644
index 000000000..f69a92f96
--- /dev/null
+++ b/hw/smbios/Makefile.objs
@@ -0,0 +1 @@
+common-obj-$(CONFIG_SMBIOS) += smbios.o
diff --git a/hw/i386/smbios.c b/hw/smbios/smbios.c
index 1341e0234..b81a1d349 100644
--- a/hw/i386/smbios.c
+++ b/hw/smbios/smbios.c
@@ -19,10 +19,9 @@
#include "qemu/error-report.h"
#include "sysemu/sysemu.h"
#include "sysemu/cpus.h"
-#include "hw/i386/pc.h"
-#include "hw/i386/smbios.h"
+#include "hw/smbios/smbios.h"
#include "hw/loader.h"
-
+#include "exec/cpu-common.h"
/* legacy structures and constants for <= 2.0 machines */
struct smbios_header {
@@ -56,7 +55,9 @@ static uint8_t *smbios_tables;
static size_t smbios_tables_len;
static unsigned smbios_table_max;
static unsigned smbios_table_cnt;
-static struct smbios_entry_point ep;
+static SmbiosEntryPointType smbios_ep_type = SMBIOS_ENTRY_POINT_21;
+
+static SmbiosEntryPoint ep;
static int smbios_type4_count = 0;
static bool smbios_immutable;
@@ -772,11 +773,12 @@ void smbios_set_cpuid(uint32_t version, uint32_t features)
void smbios_set_defaults(const char *manufacturer, const char *product,
const char *version, bool legacy_mode,
- bool uuid_encoded)
+ bool uuid_encoded, SmbiosEntryPointType ep_type)
{
smbios_have_defaults = true;
smbios_legacy = legacy_mode;
smbios_uuid_encoded = uuid_encoded;
+ smbios_ep_type = ep_type;
/* drop unwanted version of command-line file blob(s) */
if (smbios_legacy) {
@@ -809,32 +811,61 @@ void smbios_set_defaults(const char *manufacturer, const char *product,
static void smbios_entry_point_setup(void)
{
- memcpy(ep.anchor_string, "_SM_", 4);
- memcpy(ep.intermediate_anchor_string, "_DMI_", 5);
- ep.length = sizeof(struct smbios_entry_point);
- ep.entry_point_revision = 0; /* formatted_area reserved, per spec v2.1+ */
- memset(ep.formatted_area, 0, 5);
-
- /* compliant with smbios spec v2.8 */
- ep.smbios_major_version = 2;
- ep.smbios_minor_version = 8;
- ep.smbios_bcd_revision = 0x28;
-
- /* set during table construction, but BIOS may override: */
- ep.structure_table_length = cpu_to_le16(smbios_tables_len);
- ep.max_structure_size = cpu_to_le16(smbios_table_max);
- ep.number_of_structures = cpu_to_le16(smbios_table_cnt);
-
- /* BIOS must recalculate: */
- ep.checksum = 0;
- ep.intermediate_checksum = 0;
- ep.structure_table_address = cpu_to_le32(0);
+ switch (smbios_ep_type) {
+ case SMBIOS_ENTRY_POINT_21:
+ memcpy(ep.ep21.anchor_string, "_SM_", 4);
+ memcpy(ep.ep21.intermediate_anchor_string, "_DMI_", 5);
+ ep.ep21.length = sizeof(struct smbios_21_entry_point);
+ ep.ep21.entry_point_revision = 0; /* formatted_area reserved */
+ memset(ep.ep21.formatted_area, 0, 5);
+
+ /* compliant with smbios spec v2.8 */
+ ep.ep21.smbios_major_version = 2;
+ ep.ep21.smbios_minor_version = 8;
+ ep.ep21.smbios_bcd_revision = 0x28;
+
+ /* set during table construction, but BIOS may override: */
+ ep.ep21.structure_table_length = cpu_to_le16(smbios_tables_len);
+ ep.ep21.max_structure_size = cpu_to_le16(smbios_table_max);
+ ep.ep21.number_of_structures = cpu_to_le16(smbios_table_cnt);
+
+ /* BIOS must recalculate */
+ ep.ep21.checksum = 0;
+ ep.ep21.intermediate_checksum = 0;
+ ep.ep21.structure_table_address = cpu_to_le32(0);
+
+ break;
+ case SMBIOS_ENTRY_POINT_30:
+ memcpy(ep.ep30.anchor_string, "_SM3_", 5);
+ ep.ep30.length = sizeof(struct smbios_30_entry_point);
+ ep.ep30.entry_point_revision = 1;
+ ep.ep30.reserved = 0;
+
+ /* compliant with smbios spec 3.0 */
+ ep.ep30.smbios_major_version = 3;
+ ep.ep30.smbios_minor_version = 0;
+ ep.ep30.smbios_doc_rev = 0;
+
+ /* set during table construct, but BIOS might override */
+ ep.ep30.structure_table_max_size = cpu_to_le32(smbios_tables_len);
+
+ /* BIOS must recalculate */
+ ep.ep30.checksum = 0;
+ ep.ep30.structure_table_address = cpu_to_le64(0);
+
+ break;
+ default:
+ abort();
+ break;
+ }
}
-void smbios_get_tables(uint8_t **tables, size_t *tables_len,
+void smbios_get_tables(const struct smbios_phys_mem_area *mem_array,
+ const unsigned int mem_array_size,
+ uint8_t **tables, size_t *tables_len,
uint8_t **anchor, size_t *anchor_len)
{
- unsigned i, dimm_cnt, instance;
+ unsigned i, dimm_cnt;
if (smbios_legacy) {
*tables = *anchor = NULL;
@@ -867,11 +898,9 @@ void smbios_get_tables(uint8_t **tables, size_t *tables_len,
smbios_build_type_17_table(i, GET_DIMM_SZ);
}
- for (i = 0, instance = 0; i < e820_get_num_entries(); i++) {
- uint64_t address, length;
- if (e820_get_entry(i, E820_RAM, &address, &length)) {
- smbios_build_type_19_table(instance++, address, length);
- }
+ for (i = 0; i < mem_array_size; i++) {
+ smbios_build_type_19_table(i, mem_array[i].address,
+ mem_array[i].length);
}
smbios_build_type_32_table();
@@ -886,7 +915,15 @@ void smbios_get_tables(uint8_t **tables, size_t *tables_len,
*tables = smbios_tables;
*tables_len = smbios_tables_len;
*anchor = (uint8_t *)&ep;
- *anchor_len = sizeof(struct smbios_entry_point);
+
+ /* calculate length based on anchor string */
+ if (!strncmp((char *)&ep, "_SM_", 4)) {
+ *anchor_len = sizeof(struct smbios_21_entry_point);
+ } else if (!strncmp((char *)&ep, "_SM3_", 5)) {
+ *anchor_len = sizeof(struct smbios_30_entry_point);
+ } else {
+ abort();
+ }
}
static void save_opt(const char **dest, QemuOpts *opts, const char *name)
diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c
index 7f5dcd6d8..22d1e7e31 100644
--- a/hw/sparc/leon3.c
+++ b/hw/sparc/leon3.c
@@ -156,7 +156,7 @@ static void leon3_generic_hw_init(MachineState *machine)
/* Allocate BIOS */
prom_size = 8 * 1024 * 1024; /* 8Mb */
- memory_region_init_ram(prom, NULL, "Leon3.bios", prom_size, &error_abort);
+ memory_region_init_ram(prom, NULL, "Leon3.bios", prom_size, &error_fatal);
vmstate_register_ram_global(prom);
memory_region_set_readonly(prom, true);
memory_region_add_subregion(address_space_mem, 0x00000000, prom);
@@ -193,7 +193,7 @@ static void leon3_generic_hw_init(MachineState *machine)
uint64_t entry;
kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL,
- 1 /* big endian */, ELF_MACHINE, 0);
+ 1 /* big endian */, EM_SPARC, 0);
if (kernel_size < 0) {
fprintf(stderr, "qemu: could not load kernel '%s'\n",
kernel_filename);
@@ -216,15 +216,10 @@ static void leon3_generic_hw_init(MachineState *machine)
}
}
-static QEMUMachine leon3_generic_machine = {
- .name = "leon3_generic",
- .desc = "Leon-3 generic",
- .init = leon3_generic_hw_init,
-};
-
-static void leon3_machine_init(void)
+static void leon3_generic_machine_init(MachineClass *mc)
{
- qemu_register_machine(&leon3_generic_machine);
+ mc->desc = "Leon-3 generic";
+ mc->init = leon3_generic_hw_init;
}
-machine_init(leon3_machine_init);
+DEFINE_MACHINE("leon3_generic", leon3_generic_machine_init)
diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c
index 68ac4d8bb..230dac955 100644
--- a/hw/sparc/sun4m.c
+++ b/hw/sparc/sun4m.c
@@ -109,9 +109,9 @@ int DMA_write_memory (int nchan, void *buf, int pos, int size)
}
void DMA_hold_DREQ (int nchan) {}
void DMA_release_DREQ (int nchan) {}
-void DMA_schedule(int nchan) {}
+void DMA_schedule(void) {}
-void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit)
+void DMA_init(int high_page_enable)
{
}
@@ -300,7 +300,7 @@ static unsigned long sun4m_load_kernel(const char *kernel_filename,
bswap_needed = 0;
#endif
kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
- NULL, NULL, NULL, 1, ELF_MACHINE, 0);
+ NULL, NULL, NULL, 1, EM_SPARC, 0);
if (kernel_size < 0)
kernel_size = load_aout(kernel_filename, KERNEL_LOAD_ADDR,
RAM_size - KERNEL_LOAD_ADDR, bswap_needed,
@@ -641,7 +641,7 @@ static int idreg_init1(SysBusDevice *dev)
IDRegState *s = MACIO_ID_REGISTER(dev);
memory_region_init_ram(&s->mem, OBJECT(s),
- "sun4m.idreg", sizeof(idreg_data), &error_abort);
+ "sun4m.idreg", sizeof(idreg_data), &error_fatal);
vmstate_register_ram_global(&s->mem);
memory_region_set_readonly(&s->mem, true);
sysbus_init_mmio(dev, &s->mem);
@@ -688,7 +688,7 @@ static int afx_init1(SysBusDevice *dev)
{
AFXState *s = TCX_AFX(dev);
- memory_region_init_ram(&s->mem, OBJECT(s), "sun4m.afx", 4, &error_abort);
+ memory_region_init_ram(&s->mem, OBJECT(s), "sun4m.afx", 4, &error_fatal);
vmstate_register_ram_global(&s->mem);
sysbus_init_mmio(dev, &s->mem);
return 0;
@@ -744,7 +744,7 @@ static void prom_init(hwaddr addr, const char *bios_name)
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
if (filename) {
ret = load_elf(filename, translate_prom_address, &addr, NULL,
- NULL, NULL, 1, ELF_MACHINE, 0);
+ NULL, NULL, 1, EM_SPARC, 0);
if (ret < 0 || ret > PROM_SIZE_MAX) {
ret = load_image_targphys(filename, addr, PROM_SIZE_MAX);
}
@@ -763,7 +763,7 @@ static int prom_init1(SysBusDevice *dev)
PROMState *s = OPENPROM(dev);
memory_region_init_ram(&s->prom, OBJECT(s), "sun4m.prom", PROM_SIZE_MAX,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&s->prom);
memory_region_set_readonly(&s->prom, true);
sysbus_init_mmio(dev, &s->prom);
@@ -1420,80 +1420,152 @@ static void sbook_init(MachineState *machine)
sun4m_hw_init(&sun4m_hwdefs[8], machine);
}
-static QEMUMachine ss5_machine = {
- .name = "SS-5",
- .desc = "Sun4m platform, SPARCstation 5",
- .init = ss5_init,
- .block_default_type = IF_SCSI,
- .is_default = 1,
- .default_boot_order = "c",
+static void ss5_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4m platform, SPARCstation 5";
+ mc->init = ss5_init;
+ mc->block_default_type = IF_SCSI;
+ mc->is_default = 1;
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo ss5_type = {
+ .name = MACHINE_TYPE_NAME("SS-5"),
+ .parent = TYPE_MACHINE,
+ .class_init = ss5_class_init,
};
-static QEMUMachine ss10_machine = {
- .name = "SS-10",
- .desc = "Sun4m platform, SPARCstation 10",
- .init = ss10_init,
- .block_default_type = IF_SCSI,
- .max_cpus = 4,
- .default_boot_order = "c",
+static void ss10_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4m platform, SPARCstation 10";
+ mc->init = ss10_init;
+ mc->block_default_type = IF_SCSI;
+ mc->max_cpus = 4;
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo ss10_type = {
+ .name = MACHINE_TYPE_NAME("SS-10"),
+ .parent = TYPE_MACHINE,
+ .class_init = ss10_class_init,
};
-static QEMUMachine ss600mp_machine = {
- .name = "SS-600MP",
- .desc = "Sun4m platform, SPARCserver 600MP",
- .init = ss600mp_init,
- .block_default_type = IF_SCSI,
- .max_cpus = 4,
- .default_boot_order = "c",
+static void ss600mp_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4m platform, SPARCserver 600MP";
+ mc->init = ss600mp_init;
+ mc->block_default_type = IF_SCSI;
+ mc->max_cpus = 4;
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo ss600mp_type = {
+ .name = MACHINE_TYPE_NAME("SS-600MP"),
+ .parent = TYPE_MACHINE,
+ .class_init = ss600mp_class_init,
};
-static QEMUMachine ss20_machine = {
- .name = "SS-20",
- .desc = "Sun4m platform, SPARCstation 20",
- .init = ss20_init,
- .block_default_type = IF_SCSI,
- .max_cpus = 4,
- .default_boot_order = "c",
+static void ss20_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4m platform, SPARCstation 20";
+ mc->init = ss20_init;
+ mc->block_default_type = IF_SCSI;
+ mc->max_cpus = 4;
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo ss20_type = {
+ .name = MACHINE_TYPE_NAME("SS-20"),
+ .parent = TYPE_MACHINE,
+ .class_init = ss20_class_init,
};
-static QEMUMachine voyager_machine = {
- .name = "Voyager",
- .desc = "Sun4m platform, SPARCstation Voyager",
- .init = vger_init,
- .block_default_type = IF_SCSI,
- .default_boot_order = "c",
+static void voyager_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4m platform, SPARCstation Voyager";
+ mc->init = vger_init;
+ mc->block_default_type = IF_SCSI;
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo voyager_type = {
+ .name = MACHINE_TYPE_NAME("Voyager"),
+ .parent = TYPE_MACHINE,
+ .class_init = voyager_class_init,
};
-static QEMUMachine ss_lx_machine = {
- .name = "LX",
- .desc = "Sun4m platform, SPARCstation LX",
- .init = ss_lx_init,
- .block_default_type = IF_SCSI,
- .default_boot_order = "c",
+static void ss_lx_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4m platform, SPARCstation LX";
+ mc->init = ss_lx_init;
+ mc->block_default_type = IF_SCSI;
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo ss_lx_type = {
+ .name = MACHINE_TYPE_NAME("LX"),
+ .parent = TYPE_MACHINE,
+ .class_init = ss_lx_class_init,
};
-static QEMUMachine ss4_machine = {
- .name = "SS-4",
- .desc = "Sun4m platform, SPARCstation 4",
- .init = ss4_init,
- .block_default_type = IF_SCSI,
- .default_boot_order = "c",
+static void ss4_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4m platform, SPARCstation 4";
+ mc->init = ss4_init;
+ mc->block_default_type = IF_SCSI;
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo ss4_type = {
+ .name = MACHINE_TYPE_NAME("SS-4"),
+ .parent = TYPE_MACHINE,
+ .class_init = ss4_class_init,
};
-static QEMUMachine scls_machine = {
- .name = "SPARCClassic",
- .desc = "Sun4m platform, SPARCClassic",
- .init = scls_init,
- .block_default_type = IF_SCSI,
- .default_boot_order = "c",
+static void scls_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4m platform, SPARCClassic";
+ mc->init = scls_init;
+ mc->block_default_type = IF_SCSI;
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo scls_type = {
+ .name = MACHINE_TYPE_NAME("SPARCClassic"),
+ .parent = TYPE_MACHINE,
+ .class_init = scls_class_init,
};
-static QEMUMachine sbook_machine = {
- .name = "SPARCbook",
- .desc = "Sun4m platform, SPARCbook",
- .init = sbook_init,
- .block_default_type = IF_SCSI,
- .default_boot_order = "c",
+static void sbook_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4m platform, SPARCbook";
+ mc->init = sbook_init;
+ mc->block_default_type = IF_SCSI;
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo sbook_type = {
+ .name = MACHINE_TYPE_NAME("SPARCbook"),
+ .parent = TYPE_MACHINE,
+ .class_init = sbook_class_init,
};
static void sun4m_register_types(void)
@@ -1506,16 +1578,16 @@ static void sun4m_register_types(void)
static void sun4m_machine_init(void)
{
- qemu_register_machine(&ss5_machine);
- qemu_register_machine(&ss10_machine);
- qemu_register_machine(&ss600mp_machine);
- qemu_register_machine(&ss20_machine);
- qemu_register_machine(&voyager_machine);
- qemu_register_machine(&ss_lx_machine);
- qemu_register_machine(&ss4_machine);
- qemu_register_machine(&scls_machine);
- qemu_register_machine(&sbook_machine);
+ type_register_static(&ss5_type);
+ type_register_static(&ss10_type);
+ type_register_static(&ss600mp_type);
+ type_register_static(&ss20_type);
+ type_register_static(&voyager_type);
+ type_register_static(&ss_lx_type);
+ type_register_static(&ss4_type);
+ type_register_static(&scls_type);
+ type_register_static(&sbook_type);
}
type_init(sun4m_register_types)
-machine_init(sun4m_machine_init);
+machine_init(sun4m_machine_init)
diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c
index 30cfa0e0a..d6b929cf8 100644
--- a/hw/sparc64/sun4u.c
+++ b/hw/sparc64/sun4u.c
@@ -112,9 +112,9 @@ int DMA_write_memory (int nchan, void *buf, int pos, int size)
}
void DMA_hold_DREQ (int nchan) {}
void DMA_release_DREQ (int nchan) {}
-void DMA_schedule(int nchan) {}
+void DMA_schedule(void) {}
-void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit)
+void DMA_init(int high_page_enable)
{
}
@@ -208,7 +208,7 @@ static uint64_t sun4u_load_kernel(const char *kernel_filename,
bswap_needed = 0;
#endif
kernel_size = load_elf(kernel_filename, NULL, NULL, kernel_entry,
- kernel_addr, &kernel_top, 1, ELF_MACHINE, 0);
+ kernel_addr, &kernel_top, 1, EM_SPARCV9, 0);
if (kernel_size < 0) {
*kernel_addr = KERNEL_LOAD_ADDR;
*kernel_entry = KERNEL_LOAD_ADDR;
@@ -671,7 +671,7 @@ static void prom_init(hwaddr addr, const char *bios_name)
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
if (filename) {
ret = load_elf(filename, translate_prom_address, &addr,
- NULL, NULL, NULL, 1, ELF_MACHINE, 0);
+ NULL, NULL, NULL, 1, EM_SPARCV9, 0);
if (ret < 0 || ret > PROM_SIZE_MAX) {
ret = load_image_targphys(filename, addr, PROM_SIZE_MAX);
}
@@ -690,7 +690,7 @@ static int prom_init1(SysBusDevice *dev)
PROMState *s = OPENPROM(dev);
memory_region_init_ram(&s->prom, OBJECT(s), "sun4u.prom", PROM_SIZE_MAX,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&s->prom);
memory_region_set_readonly(&s->prom, true);
sysbus_init_mmio(dev, &s->prom);
@@ -734,7 +734,7 @@ static int ram_init1(SysBusDevice *dev)
RamDevice *d = SUN4U_RAM(dev);
memory_region_init_ram(&d->ram, OBJECT(d), "sun4u.ram", d->size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(&d->ram);
sysbus_init_mmio(dev, &d->ram);
return 0;
@@ -965,29 +965,53 @@ static void niagara_init(MachineState *machine)
sun4uv_init(get_system_memory(), machine, &hwdefs[2]);
}
-static QEMUMachine sun4u_machine = {
- .name = "sun4u",
- .desc = "Sun4u platform",
- .init = sun4u_init,
- .max_cpus = 1, // XXX for now
- .is_default = 1,
- .default_boot_order = "c",
+static void sun4u_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4u platform";
+ mc->init = sun4u_init;
+ mc->max_cpus = 1; /* XXX for now */
+ mc->is_default = 1;
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo sun4u_type = {
+ .name = MACHINE_TYPE_NAME("sun4u"),
+ .parent = TYPE_MACHINE,
+ .class_init = sun4u_class_init,
};
-static QEMUMachine sun4v_machine = {
- .name = "sun4v",
- .desc = "Sun4v platform",
- .init = sun4v_init,
- .max_cpus = 1, // XXX for now
- .default_boot_order = "c",
+static void sun4v_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4v platform";
+ mc->init = sun4v_init;
+ mc->max_cpus = 1; /* XXX for now */
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo sun4v_type = {
+ .name = MACHINE_TYPE_NAME("sun4v"),
+ .parent = TYPE_MACHINE,
+ .class_init = sun4v_class_init,
};
-static QEMUMachine niagara_machine = {
- .name = "Niagara",
- .desc = "Sun4v platform, Niagara",
- .init = niagara_init,
- .max_cpus = 1, // XXX for now
- .default_boot_order = "c",
+static void niagara_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Sun4v platform, Niagara";
+ mc->init = niagara_init;
+ mc->max_cpus = 1; /* XXX for now */
+ mc->default_boot_order = "c";
+}
+
+static const TypeInfo niagara_type = {
+ .name = MACHINE_TYPE_NAME("Niagara"),
+ .parent = TYPE_MACHINE,
+ .class_init = niagara_class_init,
};
static void sun4u_register_types(void)
@@ -999,10 +1023,10 @@ static void sun4u_register_types(void)
static void sun4u_machine_init(void)
{
- qemu_register_machine(&sun4u_machine);
- qemu_register_machine(&sun4v_machine);
- qemu_register_machine(&niagara_machine);
+ type_register_static(&sun4u_type);
+ type_register_static(&sun4v_type);
+ type_register_static(&niagara_type);
}
type_init(sun4u_register_types)
-machine_init(sun4u_machine_init);
+machine_init(sun4u_machine_init)
diff --git a/hw/ssi/omap_spi.c b/hw/ssi/omap_spi.c
index 119e325a6..27263299b 100644
--- a/hw/ssi/omap_spi.c
+++ b/hw/ssi/omap_spi.c
@@ -342,8 +342,7 @@ static const MemoryRegionOps omap_mcspi_ops = {
struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta, int chnum,
qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk)
{
- struct omap_mcspi_s *s = (struct omap_mcspi_s *)
- g_malloc0(sizeof(struct omap_mcspi_s));
+ struct omap_mcspi_s *s = g_new0(struct omap_mcspi_s, 1);
struct omap_mcspi_ch_s *ch = s->ch;
s->irq = irq;
diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c
index 2bb62211c..7f0391c78 100644
--- a/hw/timer/hpet.c
+++ b/hw/timer/hpet.c
@@ -116,22 +116,22 @@ static uint32_t timer_enabled(HPETTimer *t)
static uint32_t hpet_time_after(uint64_t a, uint64_t b)
{
- return ((int32_t)(b) - (int32_t)(a) < 0);
+ return ((int32_t)(b - a) < 0);
}
static uint32_t hpet_time_after64(uint64_t a, uint64_t b)
{
- return ((int64_t)(b) - (int64_t)(a) < 0);
+ return ((int64_t)(b - a) < 0);
}
static uint64_t ticks_to_ns(uint64_t value)
{
- return (muldiv64(value, HPET_CLK_PERIOD, FS_PER_NS));
+ return value * HPET_CLK_PERIOD;
}
static uint64_t ns_to_ticks(uint64_t value)
{
- return (muldiv64(value, FS_PER_NS, HPET_CLK_PERIOD));
+ return value / HPET_CLK_PERIOD;
}
static uint64_t hpet_fixup_reg(uint64_t new, uint64_t old, uint64_t mask)
@@ -758,7 +758,7 @@ static void hpet_realize(DeviceState *dev, Error **errp)
/* 64-bit main counter; LegacyReplacementRoute. */
s->capability = 0x8086a001ULL;
s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT;
- s->capability |= ((HPET_CLK_PERIOD) << 32);
+ s->capability |= ((uint64_t)(HPET_CLK_PERIOD * FS_PER_NS) << 32);
qdev_init_gpio_in(dev, hpet_handle_legacy_irq, 2);
qdev_init_gpio_out(dev, &s->pit_enabled, 1);
diff --git a/hw/timer/imx_epit.c b/hw/timer/imx_epit.c
index ffefc22f4..967be4ad2 100644
--- a/hw/timer/imx_epit.c
+++ b/hw/timer/imx_epit.c
@@ -5,25 +5,28 @@
* Copyright (c) 2011 NICTA Pty Ltd
* Originally written by Hans Jiang
* Updated by Peter Chubb
- * Updated by Jean-Christophe Dubois
+ * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
*
* This code is licensed under GPL version 2 or later. See
* the COPYING file in the top-level directory.
*
*/
-#include "hw/hw.h"
-#include "qemu/bitops.h"
-#include "qemu/timer.h"
-#include "hw/ptimer.h"
-#include "hw/sysbus.h"
-#include "hw/arm/imx.h"
+#include "hw/timer/imx_epit.h"
+#include "hw/misc/imx_ccm.h"
#include "qemu/main-loop.h"
-#define TYPE_IMX_EPIT "imx.epit"
+#ifndef DEBUG_IMX_EPIT
+#define DEBUG_IMX_EPIT 0
+#endif
-#define DEBUG_TIMER 0
-#if DEBUG_TIMER
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX_EPIT) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_EPIT, \
+ __func__, ##args); \
+ } \
+ } while (0)
static char const *imx_epit_reg_name(uint32_t reg)
{
@@ -43,48 +46,6 @@ static char const *imx_epit_reg_name(uint32_t reg)
}
}
-# define DPRINTF(fmt, args...) \
- do { fprintf(stderr, "%s: " fmt , __func__, ##args); } while (0)
-#else
-# define DPRINTF(fmt, args...) do {} while (0)
-#endif
-
-/*
- * Define to 1 for messages about attempts to
- * access unimplemented registers or similar.
- */
-#define DEBUG_IMPLEMENTATION 1
-#if DEBUG_IMPLEMENTATION
-# define IPRINTF(fmt, args...) \
- do { fprintf(stderr, "%s: " fmt, __func__, ##args); } while (0)
-#else
-# define IPRINTF(fmt, args...) do {} while (0)
-#endif
-
-#define IMX_EPIT(obj) \
- OBJECT_CHECK(IMXEPITState, (obj), TYPE_IMX_EPIT)
-
-/*
- * EPIT: Enhanced periodic interrupt timer
- */
-
-#define CR_EN (1 << 0)
-#define CR_ENMOD (1 << 1)
-#define CR_OCIEN (1 << 2)
-#define CR_RLD (1 << 3)
-#define CR_PRESCALE_SHIFT (4)
-#define CR_PRESCALE_MASK (0xfff)
-#define CR_SWR (1 << 16)
-#define CR_IOVW (1 << 17)
-#define CR_DBGEN (1 << 18)
-#define CR_WAITEN (1 << 19)
-#define CR_DOZEN (1 << 20)
-#define CR_STOPEN (1 << 21)
-#define CR_CLKSRC_SHIFT (24)
-#define CR_CLKSRC_MASK (0x3 << CR_CLKSRC_SHIFT)
-
-#define EPIT_TIMER_MAX 0XFFFFFFFFUL
-
/*
* Exact clock frequencies vary from board to board.
* These are typical.
@@ -96,23 +57,6 @@ static const IMXClk imx_epit_clocks[] = {
CLK_32k, /* 11 ipg_clk_32k -- ~32kHz */
};
-typedef struct {
- SysBusDevice busdev;
- ptimer_state *timer_reload;
- ptimer_state *timer_cmp;
- MemoryRegion iomem;
- DeviceState *ccm;
-
- uint32_t cr;
- uint32_t sr;
- uint32_t lr;
- uint32_t cmp;
- uint32_t cnt;
-
- uint32_t freq;
- qemu_irq irq;
-} IMXEPITState;
-
/*
* Update interrupt status
*/
@@ -174,18 +118,17 @@ static void imx_epit_reset(DeviceState *dev)
static uint32_t imx_epit_update_count(IMXEPITState *s)
{
- s->cnt = ptimer_get_count(s->timer_reload);
+ s->cnt = ptimer_get_count(s->timer_reload);
- return s->cnt;
+ return s->cnt;
}
static uint64_t imx_epit_read(void *opaque, hwaddr offset, unsigned size)
{
IMXEPITState *s = IMX_EPIT(opaque);
uint32_t reg_value = 0;
- uint32_t reg = offset >> 2;
- switch (reg) {
+ switch (offset >> 2) {
case 0: /* Control Register */
reg_value = s->cr;
break;
@@ -208,11 +151,12 @@ static uint64_t imx_epit_read(void *opaque, hwaddr offset, unsigned size)
break;
default:
- IPRINTF("Bad offset %x\n", reg);
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_EPIT, __func__, offset);
break;
}
- DPRINTF("(%s) = 0x%08x\n", imx_epit_reg_name(reg), reg_value);
+ DPRINTF("(%s) = 0x%08x\n", imx_epit_reg_name(offset >> 2), reg_value);
return reg_value;
}
@@ -237,12 +181,12 @@ static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
IMXEPITState *s = IMX_EPIT(opaque);
- uint32_t reg = offset >> 2;
uint64_t oldcr;
- DPRINTF("(%s, value = 0x%08x)\n", imx_epit_reg_name(reg), (uint32_t)value);
+ DPRINTF("(%s, value = 0x%08x)\n", imx_epit_reg_name(offset >> 2),
+ (uint32_t)value);
- switch (reg) {
+ switch (offset >> 2) {
case 0: /* CR */
oldcr = s->cr;
@@ -318,7 +262,8 @@ static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value,
break;
default:
- IPRINTF("Bad offset %x\n", reg);
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_EPIT, __func__, offset);
break;
}
@@ -333,24 +278,14 @@ static void imx_epit_cmp(void *opaque)
imx_epit_update_int(s);
}
-void imx_timerp_create(const hwaddr addr, qemu_irq irq, DeviceState *ccm)
-{
- IMXEPITState *pp;
- DeviceState *dev;
-
- dev = sysbus_create_simple(TYPE_IMX_EPIT, addr, irq);
- pp = IMX_EPIT(dev);
- pp->ccm = ccm;
-}
-
static const MemoryRegionOps imx_epit_ops = {
- .read = imx_epit_read,
- .write = imx_epit_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
+ .read = imx_epit_read,
+ .write = imx_epit_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
};
static const VMStateDescription vmstate_imx_timer_epit = {
- .name = "imx.epit",
+ .name = TYPE_IMX_EPIT,
.version_id = 2,
.minimum_version_id = 2,
.fields = (VMStateField[]) {
diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c
index 3b3101084..7257f4201 100644
--- a/hw/timer/imx_gpt.c
+++ b/hw/timer/imx_gpt.c
@@ -5,28 +5,28 @@
* Copyright (c) 2011 NICTA Pty Ltd
* Originally written by Hans Jiang
* Updated by Peter Chubb
- * Updated by Jean-Christophe Dubois
+ * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
*
* This code is licensed under GPL version 2 or later. See
* the COPYING file in the top-level directory.
*
*/
-#include "hw/hw.h"
-#include "qemu/bitops.h"
-#include "qemu/timer.h"
-#include "hw/ptimer.h"
-#include "hw/sysbus.h"
-#include "hw/arm/imx.h"
+#include "hw/timer/imx_gpt.h"
+#include "hw/misc/imx_ccm.h"
#include "qemu/main-loop.h"
-#define TYPE_IMX_GPT "imx.gpt"
+#ifndef DEBUG_IMX_GPT
+#define DEBUG_IMX_GPT 0
+#endif
-/*
- * Define to 1 for debug messages
- */
-#define DEBUG_TIMER 0
-#if DEBUG_TIMER
+#define DPRINTF(fmt, args...) \
+ do { \
+ if (DEBUG_IMX_GPT) { \
+ fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_GPT, \
+ __func__, ##args); \
+ } \
+ } while (0)
static char const *imx_gpt_reg_name(uint32_t reg)
{
@@ -56,94 +56,8 @@ static char const *imx_gpt_reg_name(uint32_t reg)
}
}
-# define DPRINTF(fmt, args...) \
- do { printf("%s: " fmt , __func__, ##args); } while (0)
-#else
-# define DPRINTF(fmt, args...) do {} while (0)
-#endif
-
-/*
- * Define to 1 for messages about attempts to
- * access unimplemented registers or similar.
- */
-#define DEBUG_IMPLEMENTATION 1
-#if DEBUG_IMPLEMENTATION
-# define IPRINTF(fmt, args...) \
- do { fprintf(stderr, "%s: " fmt, __func__, ##args); } while (0)
-#else
-# define IPRINTF(fmt, args...) do {} while (0)
-#endif
-
-#define IMX_GPT(obj) \
- OBJECT_CHECK(IMXGPTState, (obj), TYPE_IMX_GPT)
-/*
- * GPT : General purpose timer
- *
- * This timer counts up continuously while it is enabled, resetting itself
- * to 0 when it reaches GPT_TIMER_MAX (in freerun mode) or when it
- * reaches the value of one of the ocrX (in periodic mode).
- */
-
-#define GPT_TIMER_MAX 0XFFFFFFFFUL
-
-/* Control register. Not all of these bits have any effect (yet) */
-#define GPT_CR_EN (1 << 0) /* GPT Enable */
-#define GPT_CR_ENMOD (1 << 1) /* GPT Enable Mode */
-#define GPT_CR_DBGEN (1 << 2) /* GPT Debug mode enable */
-#define GPT_CR_WAITEN (1 << 3) /* GPT Wait Mode Enable */
-#define GPT_CR_DOZEN (1 << 4) /* GPT Doze mode enable */
-#define GPT_CR_STOPEN (1 << 5) /* GPT Stop Mode Enable */
-#define GPT_CR_CLKSRC_SHIFT (6)
-#define GPT_CR_CLKSRC_MASK (0x7)
-
-#define GPT_CR_FRR (1 << 9) /* Freerun or Restart */
-#define GPT_CR_SWR (1 << 15) /* Software Reset */
-#define GPT_CR_IM1 (3 << 16) /* Input capture channel 1 mode (2 bits) */
-#define GPT_CR_IM2 (3 << 18) /* Input capture channel 2 mode (2 bits) */
-#define GPT_CR_OM1 (7 << 20) /* Output Compare Channel 1 Mode (3 bits) */
-#define GPT_CR_OM2 (7 << 23) /* Output Compare Channel 2 Mode (3 bits) */
-#define GPT_CR_OM3 (7 << 26) /* Output Compare Channel 3 Mode (3 bits) */
-#define GPT_CR_FO1 (1 << 29) /* Force Output Compare Channel 1 */
-#define GPT_CR_FO2 (1 << 30) /* Force Output Compare Channel 2 */
-#define GPT_CR_FO3 (1 << 31) /* Force Output Compare Channel 3 */
-
-#define GPT_SR_OF1 (1 << 0)
-#define GPT_SR_OF2 (1 << 1)
-#define GPT_SR_OF3 (1 << 2)
-#define GPT_SR_ROV (1 << 5)
-
-#define GPT_IR_OF1IE (1 << 0)
-#define GPT_IR_OF2IE (1 << 1)
-#define GPT_IR_OF3IE (1 << 2)
-#define GPT_IR_ROVIE (1 << 5)
-
-typedef struct {
- SysBusDevice busdev;
- ptimer_state *timer;
- MemoryRegion iomem;
- DeviceState *ccm;
-
- uint32_t cr;
- uint32_t pr;
- uint32_t sr;
- uint32_t ir;
- uint32_t ocr1;
- uint32_t ocr2;
- uint32_t ocr3;
- uint32_t icr1;
- uint32_t icr2;
- uint32_t cnt;
-
- uint32_t next_timeout;
- uint32_t next_int;
-
- uint32_t freq;
-
- qemu_irq irq;
-} IMXGPTState;
-
static const VMStateDescription vmstate_imx_timer_gpt = {
- .name = "imx.gpt",
+ .name = TYPE_IMX_GPT,
.version_id = 3,
.minimum_version_id = 3,
.fields = (VMStateField[]) {
@@ -180,7 +94,7 @@ static void imx_gpt_set_freq(IMXGPTState *s)
{
uint32_t clksrc = extract32(s->cr, GPT_CR_CLKSRC_SHIFT, 3);
uint32_t freq = imx_clock_frequency(s->ccm, imx_gpt_clocks[clksrc])
- / (1 + s->pr);
+ / (1 + s->pr);
s->freq = freq;
DPRINTF("Setting clksrc %d to frequency %d\n", clksrc, freq);
@@ -207,7 +121,7 @@ static uint32_t imx_gpt_update_count(IMXGPTState *s)
}
static inline uint32_t imx_gpt_find_limit(uint32_t count, uint32_t reg,
- uint32_t timeout)
+ uint32_t timeout)
{
if ((count < reg) && (timeout > reg)) {
timeout = reg;
@@ -298,9 +212,8 @@ static uint64_t imx_gpt_read(void *opaque, hwaddr offset, unsigned size)
{
IMXGPTState *s = IMX_GPT(opaque);
uint32_t reg_value = 0;
- uint32_t reg = offset >> 2;
- switch (reg) {
+ switch (offset >> 2) {
case 0: /* Control Register */
reg_value = s->cr;
break;
@@ -330,12 +243,14 @@ static uint64_t imx_gpt_read(void *opaque, hwaddr offset, unsigned size)
break;
case 7: /* input Capture Register 1 */
- qemu_log_mask(LOG_UNIMP, "icr1 feature is not implemented\n");
+ qemu_log_mask(LOG_UNIMP, "[%s]%s: icr1 feature is not implemented\n",
+ TYPE_IMX_GPT, __func__);
reg_value = s->icr1;
break;
case 8: /* input Capture Register 2 */
- qemu_log_mask(LOG_UNIMP, "icr2 feature is not implemented\n");
+ qemu_log_mask(LOG_UNIMP, "[%s]%s: icr2 feature is not implemented\n",
+ TYPE_IMX_GPT, __func__);
reg_value = s->icr2;
break;
@@ -345,11 +260,12 @@ static uint64_t imx_gpt_read(void *opaque, hwaddr offset, unsigned size)
break;
default:
- IPRINTF("Bad offset %x\n", reg);
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_GPT, __func__, offset);
break;
}
- DPRINTF("(%s) = 0x%08x\n", imx_gpt_reg_name(reg), reg_value);
+ DPRINTF("(%s) = 0x%08x\n", imx_gpt_reg_name(offset >> 2), reg_value);
return reg_value;
}
@@ -396,12 +312,11 @@ static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value,
{
IMXGPTState *s = IMX_GPT(opaque);
uint32_t oldreg;
- uint32_t reg = offset >> 2;
- DPRINTF("(%s, value = 0x%08x)\n", imx_gpt_reg_name(reg),
+ DPRINTF("(%s, value = 0x%08x)\n", imx_gpt_reg_name(offset >> 2),
(uint32_t)value);
- switch (reg) {
+ switch (offset >> 2) {
case 0:
oldreg = s->cr;
s->cr = value & ~0x7c14;
@@ -477,7 +392,8 @@ static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value,
break;
default:
- IPRINTF("Bad offset %x\n", reg);
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ HWADDR_PRIx "\n", TYPE_IMX_GPT, __func__, offset);
break;
}
}
@@ -522,16 +438,6 @@ static void imx_gpt_realize(DeviceState *dev, Error **errp)
s->timer = ptimer_init(bh);
}
-void imx_timerg_create(const hwaddr addr, qemu_irq irq, DeviceState *ccm)
-{
- IMXGPTState *pp;
- DeviceState *dev;
-
- dev = sysbus_create_simple(TYPE_IMX_GPT, addr, irq);
- pp = IMX_GPT(dev);
- pp->ccm = ccm;
-}
-
static void imx_gpt_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
diff --git a/hw/timer/m48t59.c b/hw/timer/m48t59.c
index 8ab683dda..b3df8f9ec 100644
--- a/hw/timer/m48t59.c
+++ b/hw/timer/m48t59.c
@@ -590,10 +590,8 @@ static void nvram_writel (void *opaque, hwaddr addr, uint32_t value)
static uint32_t nvram_readb (void *opaque, hwaddr addr)
{
M48t59State *NVRAM = opaque;
- uint32_t retval;
- retval = m48t59_read(NVRAM, addr);
- return retval;
+ return m48t59_read(NVRAM, addr);
}
static uint32_t nvram_readw (void *opaque, hwaddr addr)
diff --git a/hw/timer/omap_gptimer.c b/hw/timer/omap_gptimer.c
index b8c8c0137..dcf706c46 100644
--- a/hw/timer/omap_gptimer.c
+++ b/hw/timer/omap_gptimer.c
@@ -468,8 +468,7 @@ static const MemoryRegionOps omap_gp_timer_ops = {
struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta,
qemu_irq irq, omap_clk fclk, omap_clk iclk)
{
- struct omap_gp_timer_s *s = (struct omap_gp_timer_s *)
- g_malloc0(sizeof(struct omap_gp_timer_s));
+ struct omap_gp_timer_s *s = g_new0(struct omap_gp_timer_s, 1);
s->ta = ta;
s->irq = irq;
diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c
index 79a8f98af..be160c19b 100644
--- a/hw/tpm/tpm_passthrough.c
+++ b/hw/tpm/tpm_passthrough.c
@@ -22,8 +22,6 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>
*/
-#include <dirent.h>
-
#include "qemu-common.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c
index 0806b5f82..ff073d501 100644
--- a/hw/tpm/tpm_tis.c
+++ b/hw/tpm/tpm_tis.c
@@ -141,7 +141,7 @@
#define TPM_TIS_IFACE_ID_SUPPORTED_FLAGS1_3 \
(TPM_TIS_IFACE_ID_INTERFACE_TIS1_3 | \
- (~0 << 4)/* all of it is don't care */)
+ (~0u << 4)/* all of it is don't care */)
/* if backend was a TPM 2.0: */
#define TPM_TIS_IFACE_ID_SUPPORTED_FLAGS2_0 \
diff --git a/hw/tricore/tricore_testboard.c b/hw/tricore/tricore_testboard.c
index a059a20a3..4ff5e7b68 100644
--- a/hw/tricore/tricore_testboard.c
+++ b/hw/tricore/tricore_testboard.c
@@ -44,7 +44,7 @@ static void tricore_load_kernel(CPUTriCoreState *env)
kernel_size = load_elf(tricoretb_binfo.kernel_filename, NULL,
NULL, (uint64_t *)&entry, NULL,
NULL, 0,
- ELF_MACHINE, 1);
+ EM_TRICORE, 1);
if (kernel_size <= 0) {
error_report("qemu: no kernel file '%s'",
tricoretb_binfo.kernel_filename);
@@ -76,17 +76,23 @@ static void tricore_testboard_init(MachineState *machine, int board_id)
exit(1);
}
env = &cpu->env;
- memory_region_init_ram(ext_cram, NULL, "powerlink_ext_c.ram", 2*1024*1024, &error_abort);
+ memory_region_init_ram(ext_cram, NULL, "powerlink_ext_c.ram", 2*1024*1024,
+ &error_fatal);
vmstate_register_ram_global(ext_cram);
- memory_region_init_ram(ext_dram, NULL, "powerlink_ext_d.ram", 4*1024*1024, &error_abort);
+ memory_region_init_ram(ext_dram, NULL, "powerlink_ext_d.ram", 4*1024*1024,
+ &error_fatal);
vmstate_register_ram_global(ext_dram);
- memory_region_init_ram(int_cram, NULL, "powerlink_int_c.ram", 48*1024, &error_abort);
+ memory_region_init_ram(int_cram, NULL, "powerlink_int_c.ram", 48*1024,
+ &error_fatal);
vmstate_register_ram_global(int_cram);
- memory_region_init_ram(int_dram, NULL, "powerlink_int_d.ram", 48*1024, &error_abort);
+ memory_region_init_ram(int_dram, NULL, "powerlink_int_d.ram", 48*1024,
+ &error_fatal);
vmstate_register_ram_global(int_dram);
- memory_region_init_ram(pcp_data, NULL, "powerlink_pcp_data.ram", 16*1024, &error_abort);
+ memory_region_init_ram(pcp_data, NULL, "powerlink_pcp_data.ram", 16*1024,
+ &error_fatal);
vmstate_register_ram_global(pcp_data);
- memory_region_init_ram(pcp_text, NULL, "powerlink_pcp_text.ram", 32*1024, &error_abort);
+ memory_region_init_ram(pcp_text, NULL, "powerlink_pcp_text.ram", 32*1024,
+ &error_fatal);
vmstate_register_ram_global(pcp_text);
memory_region_add_subregion(sysmem, 0x80000000, ext_cram);
@@ -109,16 +115,11 @@ static void tricoreboard_init(MachineState *machine)
tricore_testboard_init(machine, 0x183);
}
-static QEMUMachine ttb_machine = {
- .name = "tricore_testboard",
- .desc = "a minimal TriCore board",
- .init = tricoreboard_init,
- .is_default = 0,
-};
-
-static void tricore_testboard_machine_init(void)
+static void ttb_machine_init(MachineClass *mc)
{
- qemu_register_machine(&ttb_machine);
+ mc->desc = "a minimal TriCore board";
+ mc->init = tricoreboard_init;
+ mc->is_default = 0;
}
-machine_init(tricore_testboard_machine_init);
+DEFINE_MACHINE("tricore_testboard", ttb_machine_init)
diff --git a/hw/unicore32/puv3.c b/hw/unicore32/puv3.c
index 703e29d6d..91117b2b9 100644
--- a/hw/unicore32/puv3.c
+++ b/hw/unicore32/puv3.c
@@ -75,7 +75,7 @@ static void puv3_board_init(CPUUniCore32State *env, ram_addr_t ram_size)
/* SDRAM at address zero. */
memory_region_init_ram(ram_memory, NULL, "puv3.ram", ram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(ram_memory);
memory_region_add_subregion(get_system_memory(), 0, ram_memory);
}
@@ -130,16 +130,11 @@ static void puv3_init(MachineState *machine)
puv3_load_kernel(kernel_filename);
}
-static QEMUMachine puv3_machine = {
- .name = "puv3",
- .desc = "PKUnity Version-3 based on UniCore32",
- .init = puv3_init,
- .is_default = 1,
-};
-
-static void puv3_machine_init(void)
+static void puv3_machine_init(MachineClass *mc)
{
- qemu_register_machine(&puv3_machine);
+ mc->desc = "PKUnity Version-3 based on UniCore32";
+ mc->init = puv3_init;
+ mc->is_default = 1;
}
-machine_init(puv3_machine_init)
+DEFINE_MACHINE("puv3", puv3_machine_init)
diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs
index 7443e386b..8f00fbd8f 100644
--- a/hw/usb/Makefile.objs
+++ b/hw/usb/Makefile.objs
@@ -23,9 +23,8 @@ common-obj-$(CONFIG_USB_BLUETOOTH) += dev-bluetooth.o
ifeq ($(CONFIG_USB_SMARTCARD),y)
common-obj-y += dev-smartcard-reader.o
-common-obj-y += ccid-card-passthru.o
-common-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o
-ccid-card-emulated.o-cflags := -I$(SRC_PATH)/libcacard
+common-obj-$(CONFIG_SMARTCARD) += ccid-card-passthru.o
+common-obj-$(CONFIG_SMARTCARD) += ccid-card-emulated.o
endif
ifeq ($(CONFIG_POSIX),y)
diff --git a/hw/usb/bus.c b/hw/usb/bus.c
index 5f39e1e3a..ee6b43abc 100644
--- a/hw/usb/bus.c
+++ b/hw/usb/bus.c
@@ -655,9 +655,12 @@ void hmp_info_usb(Monitor *mon, const QDict *qdict)
dev = port->dev;
if (!dev)
continue;
- monitor_printf(mon, " Device %d.%d, Port %s, Speed %s Mb/s, Product %s\n",
- bus->busnr, dev->addr, port->path, usb_speed(dev->speed),
- dev->product_desc);
+ monitor_printf(mon, " Device %d.%d, Port %s, Speed %s Mb/s, "
+ "Product %s%s%s\n",
+ bus->busnr, dev->addr, port->path,
+ usb_speed(dev->speed), dev->product_desc,
+ dev->qdev.id ? ", ID: " : "",
+ dev->qdev.id ?: "");
}
}
}
diff --git a/hw/usb/ccid-card-emulated.c b/hw/usb/ccid-card-emulated.c
index 72329ed7d..869a63c5b 100644
--- a/hw/usb/ccid-card-emulated.c
+++ b/hw/usb/ccid-card-emulated.c
@@ -166,7 +166,7 @@ static void emulated_push_event(EmulatedState *card, EmulEvent *event)
static void emulated_push_type(EmulatedState *card, uint32_t type)
{
- EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent));
+ EmulEvent *event = g_new(EmulEvent, 1);
assert(event);
event->p.gen.type = type;
@@ -175,7 +175,7 @@ static void emulated_push_type(EmulatedState *card, uint32_t type)
static void emulated_push_error(EmulatedState *card, uint64_t code)
{
- EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent));
+ EmulEvent *event = g_new(EmulEvent, 1);
assert(event);
event->p.error.type = EMUL_ERROR;
diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c
index 85a4fc3e5..9f49c0537 100644
--- a/hw/usb/ccid-card-passthru.c
+++ b/hw/usb/ccid-card-passthru.c
@@ -12,7 +12,7 @@
#include "qemu/error-report.h"
#include "qemu/sockets.h"
#include "ccid.h"
-#include "libcacard/vscard_common.h"
+#include "cacard/vscard_common.h"
#define DPRINTF(card, lvl, fmt, ...) \
do { \
diff --git a/hw/usb/core.c b/hw/usb/core.c
index d0025db60..7f4637029 100644
--- a/hw/usb/core.c
+++ b/hw/usb/core.c
@@ -128,9 +128,16 @@ static void do_token_setup(USBDevice *s, USBPacket *p)
}
usb_packet_copy(p, s->setup_buf, p->iov.size);
+ s->setup_index = 0;
p->actual_length = 0;
s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6];
- s->setup_index = 0;
+ if (s->setup_len > sizeof(s->data_buf)) {
+ fprintf(stderr,
+ "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n",
+ s->setup_len, sizeof(s->data_buf));
+ p->status = USB_RET_STALL;
+ return;
+ }
request = (s->setup_buf[0] << 8) | s->setup_buf[1];
value = (s->setup_buf[3] << 8) | s->setup_buf[2];
@@ -151,13 +158,6 @@ static void do_token_setup(USBDevice *s, USBPacket *p)
}
s->setup_state = SETUP_STATE_DATA;
} else {
- if (s->setup_len > sizeof(s->data_buf)) {
- fprintf(stderr,
- "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n",
- s->setup_len, sizeof(s->data_buf));
- p->status = USB_RET_STALL;
- return;
- }
if (s->setup_len == 0)
s->setup_state = SETUP_STATE_ACK;
else
@@ -176,7 +176,7 @@ static void do_token_in(USBDevice *s, USBPacket *p)
request = (s->setup_buf[0] << 8) | s->setup_buf[1];
value = (s->setup_buf[3] << 8) | s->setup_buf[2];
index = (s->setup_buf[5] << 8) | s->setup_buf[4];
-
+
switch(s->setup_state) {
case SETUP_STATE_ACK:
if (!(s->setup_buf[0] & USB_DIR_IN)) {
diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c
index f092bb849..02fb1103c 100644
--- a/hw/usb/dev-audio.c
+++ b/hw/usb/dev-audio.c
@@ -664,7 +664,7 @@ static const VMStateDescription vmstate_usb_audio = {
static Property usb_audio_properties[] = {
DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0),
DEFINE_PROP_UINT32("buffer", USBAudioState, buffer,
- 8 * USBAUDIO_PACKET_SIZE),
+ 32 * USBAUDIO_PACKET_SIZE),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c
index 809b1cb11..a2762679e 100644
--- a/hw/usb/dev-mtp.c
+++ b/hw/usb/dev-mtp.c
@@ -359,8 +359,7 @@ static void usb_mtp_object_readdir(MTPState *s, MTPObject *o)
}
while ((entry = readdir(dir)) != NULL) {
if ((o->nchildren % 32) == 0) {
- o->children = g_realloc(o->children,
- (o->nchildren + 32) * sizeof(MTPObject *));
+ o->children = g_renew(MTPObject *, o->children, o->nchildren + 32);
}
o->children[o->nchildren] =
usb_mtp_object_alloc(s, s->next_handle++, o, entry->d_name);
diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c
index 7800ceea5..180adce61 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -653,7 +653,8 @@ typedef struct USBNetState {
static int is_rndis(USBNetState *s)
{
- return s->dev.config->bConfigurationValue == DEV_RNDIS_CONFIG_VALUE;
+ return s->dev.config ?
+ s->dev.config->bConfigurationValue == DEV_RNDIS_CONFIG_VALUE : 0;
}
static int ndis_query(USBNetState *s, uint32_t oid,
@@ -914,8 +915,9 @@ static int rndis_query_response(USBNetState *s,
bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8;
buflen = le32_to_cpu(buf->InformationBufferLength);
- if (bufoffs + buflen > length)
+ if (buflen > length || bufoffs >= length || bufoffs + buflen > length) {
return USB_RET_STALL;
+ }
infobuflen = ndis_query(s, le32_to_cpu(buf->OID),
bufoffs + (uint8_t *) buf, buflen, infobuf,
@@ -960,8 +962,9 @@ static int rndis_set_response(USBNetState *s,
bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8;
buflen = le32_to_cpu(buf->InformationBufferLength);
- if (bufoffs + buflen > length)
+ if (buflen > length || bufoffs >= length || bufoffs + buflen > length) {
return USB_RET_STALL;
+ }
ret = ndis_set(s, le32_to_cpu(buf->OID),
bufoffs + (uint8_t *) buf, buflen);
@@ -1211,8 +1214,9 @@ static void usb_net_handle_dataout(USBNetState *s, USBPacket *p)
if (le32_to_cpu(msg->MessageType) == RNDIS_PACKET_MSG) {
uint32_t offs = 8 + le32_to_cpu(msg->DataOffset);
uint32_t size = le32_to_cpu(msg->DataLength);
- if (offs + size <= len)
+ if (offs < len && size < len && offs + size <= len) {
qemu_send_packet(qemu_get_queue(s->nic), s->out_buf + offs, size);
+ }
}
s->out_ptr -= len;
memmove(s->out_buf, &s->out_buf[len], s->out_ptr);
diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
index 9a4e7dc0c..597d8fd18 100644
--- a/hw/usb/dev-storage.c
+++ b/hw/usb/dev-storage.c
@@ -613,20 +613,22 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
return;
}
- bdrv_add_key(blk_bs(blk), NULL, &err);
- if (err) {
- if (monitor_cur_is_qmp()) {
- error_propagate(errp, err);
- return;
- }
- error_free(err);
- err = NULL;
- if (cur_mon) {
- monitor_read_bdrv_key_start(cur_mon, blk_bs(blk),
- usb_msd_password_cb, s);
- s->dev.auto_attach = 0;
- } else {
- autostart = 0;
+ if (blk_bs(blk)) {
+ bdrv_add_key(blk_bs(blk), NULL, &err);
+ if (err) {
+ if (monitor_cur_is_qmp()) {
+ error_propagate(errp, err);
+ return;
+ }
+ error_free(err);
+ err = NULL;
+ if (cur_mon) {
+ monitor_read_bdrv_key_start(cur_mon, blk_bs(blk),
+ usb_msd_password_cb, s);
+ s->dev.auto_attach = 0;
+ } else {
+ autostart = 0;
+ }
}
}
diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c
index 7afa5f9d6..16fb845d0 100644
--- a/hw/usb/hcd-ehci-pci.c
+++ b/hw/usb/hcd-ehci-pci.c
@@ -95,10 +95,8 @@ static void usb_ehci_pci_exit(PCIDevice *dev)
usb_ehci_unrealize(s, DEVICE(dev), NULL);
- if (s->irq) {
- g_free(s->irq);
- s->irq = NULL;
- }
+ g_free(s->irq);
+ s->irq = NULL;
}
static void usb_ehci_pci_reset(DeviceState *dev)
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 64a54c6e8..4120bb7c3 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -726,7 +726,7 @@ static void ehci_detach(USBPort *port)
ehci_queues_rip_device(s, port->dev, 0);
ehci_queues_rip_device(s, port->dev, 1);
- *portsc &= ~(PORTSC_CONNECT|PORTSC_PED);
+ *portsc &= ~(PORTSC_CONNECT|PORTSC_PED|PORTSC_SUSPEND);
*portsc |= PORTSC_CSC;
ehci_raise_irq(s, USBSTS_PCD);
@@ -865,6 +865,7 @@ void ehci_reset(void *opaque)
s->usbsts = USBSTS_HALT;
s->usbsts_pending = 0;
s->usbsts_frindex = 0;
+ ehci_update_irq(s);
s->astate = EST_INACTIVE;
s->pstate = EST_INACTIVE;
@@ -1404,21 +1405,23 @@ static int ehci_process_itd(EHCIState *ehci,
if (itd->transact[i] & ITD_XACT_ACTIVE) {
pg = get_field(itd->transact[i], ITD_XACT_PGSEL);
off = itd->transact[i] & ITD_XACT_OFFSET_MASK;
- ptr1 = (itd->bufptr[pg] & ITD_BUFPTR_MASK);
- ptr2 = (itd->bufptr[pg+1] & ITD_BUFPTR_MASK);
len = get_field(itd->transact[i], ITD_XACT_LENGTH);
if (len > max * mult) {
len = max * mult;
}
-
- if (len > BUFF_SIZE) {
+ if (len > BUFF_SIZE || pg > 6) {
return -1;
}
+ ptr1 = (itd->bufptr[pg] & ITD_BUFPTR_MASK);
qemu_sglist_init(&ehci->isgl, ehci->device, 2, ehci->as);
if (off + len > 4096) {
/* transfer crosses page border */
+ if (pg == 6) {
+ return -1; /* avoid page pg + 1 */
+ }
+ ptr2 = (itd->bufptr[pg + 1] & ITD_BUFPTR_MASK);
uint32_t len2 = off + len - 4096;
uint32_t len1 = len - len2;
qemu_sglist_add(&ehci->isgl, ptr1 + off, len1);
@@ -2000,6 +2003,7 @@ static int ehci_state_writeback(EHCIQueue *q)
static void ehci_advance_state(EHCIState *ehci, int async)
{
EHCIQueue *q = NULL;
+ int itd_count = 0;
int again;
do {
@@ -2024,10 +2028,12 @@ static void ehci_advance_state(EHCIState *ehci, int async)
case EST_FETCHITD:
again = ehci_state_fetchitd(ehci, async);
+ itd_count++;
break;
case EST_FETCHSITD:
again = ehci_state_fetchsitd(ehci, async);
+ itd_count++;
break;
case EST_ADVANCEQUEUE:
@@ -2076,7 +2082,8 @@ static void ehci_advance_state(EHCIState *ehci, int async)
break;
}
- if (again < 0) {
+ if (again < 0 || itd_count > 16) {
+ /* TODO: notify guest (raise HSE irq?) */
fprintf(stderr, "processing error - resetting ehci HC\n");
ehci_reset(ehci);
again = 0;
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index c673bed4c..268ab3646 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -1453,9 +1453,7 @@ static int xhci_ep_nuke_one_xfer(XHCITransfer *t, TRBCCode report)
t->running_retry = 0;
killed = 1;
}
- if (t->trbs) {
- g_free(t->trbs);
- }
+ g_free(t->trbs);
t->trbs = NULL;
t->trb_count = t->trb_alloced = 0;
@@ -2190,7 +2188,7 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
xfer->trbs = NULL;
}
if (!xfer->trbs) {
- xfer->trbs = g_malloc(sizeof(XHCITRB) * length);
+ xfer->trbs = g_new(XHCITRB, length);
xfer->trb_alloced = length;
}
xfer->trb_count = length;
diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c
index 11429f5e7..3f8e540bd 100644
--- a/hw/usb/host-libusb.c
+++ b/hw/usb/host-libusb.c
@@ -451,6 +451,7 @@ static void usb_host_req_complete_iso(struct libusb_transfer *transfer)
}
if (xfer->ring->ep->pid == USB_TOKEN_IN) {
QTAILQ_INSERT_TAIL(&xfer->ring->copy, xfer, next);
+ usb_wakeup(xfer->ring->ep, 0);
} else {
QTAILQ_INSERT_TAIL(&xfer->ring->unused, xfer, next);
}
@@ -1239,7 +1240,7 @@ static void usb_host_handle_control(USBDevice *udev, USBPacket *p,
/* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices
* to work redirected to a not superspeed capable hcd */
- if (udev->speed == USB_SPEED_SUPER &&
+ if ((udev->speedmask & USB_SPEED_MASK_SUPER) &&
!(udev->port->speedmask & USB_SPEED_MASK_SUPER) &&
request == 0x8006 && value == 0x100 && index == 0) {
r->usb3ep0quirk = true;
@@ -1429,7 +1430,7 @@ static void usb_host_free_streams(USBDevice *udev, USBEndpoint **eps,
* still present in the first place. Attemping to contine where we
* left off is impossible.
*
- * What we are going to to to here is emulate a surprise removal of
+ * What we are going to do here is emulate a surprise removal of
* the usb device passed through, then kick host scan so the device
* will get re-attached (and re-initialized by the guest) in case it
* is still present.
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index 25df25fd0..30ff74273 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -33,9 +33,7 @@
#include "qemu/iov.h"
#include "sysemu/char.h"
-#include <dirent.h>
#include <sys/ioctl.h>
-#include <signal.h>
#include <usbredirparser.h>
#include <usbredirfilter.h>
@@ -324,7 +322,7 @@ static void packet_id_queue_add(struct PacketIdQueue *q, uint64_t id)
DPRINTF("adding packet id %"PRIu64" to %s queue\n", id, q->name);
- e = g_malloc0(sizeof(struct PacketIdQueueEntry));
+ e = g_new0(struct PacketIdQueueEntry, 1);
e->id = id;
QTAILQ_INSERT_TAIL(&q->head, e, next);
q->size++;
@@ -470,7 +468,7 @@ static void bufp_alloc(USBRedirDevice *dev, uint8_t *data, uint16_t len,
dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0;
}
- bufp = g_malloc(sizeof(struct buf_packet));
+ bufp = g_new(struct buf_packet, 1);
bufp->data = data;
bufp->len = len;
bufp->offset = 0;
@@ -2236,7 +2234,7 @@ static int usbredir_get_bufpq(QEMUFile *f, void *priv, size_t unused)
endp->bufpq_size = qemu_get_be32(f);
for (i = 0; i < endp->bufpq_size; i++) {
- bufp = g_malloc(sizeof(struct buf_packet));
+ bufp = g_new(struct buf_packet, 1);
bufp->len = qemu_get_be32(f);
bufp->status = qemu_get_be32(f);
bufp->offset = 0;
diff --git a/hw/vfio/Makefile.objs b/hw/vfio/Makefile.objs
index d540c9d14..d3248633c 100644
--- a/hw/vfio/Makefile.objs
+++ b/hw/vfio/Makefile.objs
@@ -1,6 +1,6 @@
ifeq ($(CONFIG_LINUX), y)
obj-$(CONFIG_SOFTMMU) += common.o
-obj-$(CONFIG_PCI) += pci.o
+obj-$(CONFIG_PCI) += pci.o pci-quirks.o
obj-$(CONFIG_SOFTMMU) += platform.o
obj-$(CONFIG_SOFTMMU) += calxeda-xgmac.o
endif
diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index 85ee9b005..6797208cc 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -226,7 +226,7 @@ static int vfio_dma_map(VFIOContainer *container, hwaddr iova,
/*
* Try the mapping, if it fails with EBUSY, unmap the region and try
* again. This shouldn't be necessary, but we sometimes see it in
- * the the VGA ROM space.
+ * the VGA ROM space.
*/
if (ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map) == 0 ||
(errno == EBUSY && vfio_dma_unmap(container, iova, size) == 0 &&
@@ -312,11 +312,15 @@ out:
rcu_read_unlock();
}
+static hwaddr vfio_container_granularity(VFIOContainer *container)
+{
+ return (hwaddr)1 << ctz64(container->iova_pgsizes);
+}
+
static void vfio_listener_region_add(MemoryListener *listener,
MemoryRegionSection *section)
{
- VFIOContainer *container = container_of(listener, VFIOContainer,
- iommu_data.type1.listener);
+ VFIOContainer *container = container_of(listener, VFIOContainer, listener);
hwaddr iova, end;
Int128 llend;
void *vaddr;
@@ -344,14 +348,22 @@ static void vfio_listener_region_add(MemoryListener *listener,
if (int128_ge(int128_make64(iova), llend)) {
return;
}
+ end = int128_get64(llend);
+
+ if ((iova < container->min_iova) || ((end - 1) > container->max_iova)) {
+ error_report("vfio: IOMMU container %p can't map guest IOVA region"
+ " 0x%"HWADDR_PRIx"..0x%"HWADDR_PRIx,
+ container, iova, end - 1);
+ ret = -EFAULT;
+ goto fail;
+ }
memory_region_ref(section->mr);
if (memory_region_is_iommu(section->mr)) {
VFIOGuestIOMMU *giommu;
- trace_vfio_listener_region_add_iommu(iova,
- int128_get64(int128_sub(llend, int128_one())));
+ trace_vfio_listener_region_add_iommu(iova, end - 1);
/*
* FIXME: We should do some checking to see if the
* capabilities of the host VFIO IOMMU are adequate to model
@@ -362,33 +374,22 @@ static void vfio_listener_region_add(MemoryListener *listener,
* would be the right place to wire that up (tell the KVM
* device emulation the VFIO iommu handles to use).
*/
- /*
- * This assumes that the guest IOMMU is empty of
- * mappings at this point.
- *
- * One way of doing this is:
- * 1. Avoid sharing IOMMUs between emulated devices or different
- * IOMMU groups.
- * 2. Implement VFIO_IOMMU_ENABLE in the host kernel to fail if
- * there are some mappings in IOMMU.
- *
- * VFIO on SPAPR does that. Other IOMMU models may do that different,
- * they must make sure there are no existing mappings or
- * loop through existing mappings to map them into VFIO.
- */
giommu = g_malloc0(sizeof(*giommu));
giommu->iommu = section->mr;
giommu->container = container;
giommu->n.notify = vfio_iommu_map_notify;
QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next);
+
memory_region_register_iommu_notifier(giommu->iommu, &giommu->n);
+ memory_region_iommu_replay(giommu->iommu, &giommu->n,
+ vfio_container_granularity(container),
+ false);
return;
}
/* Here we assume that memory_region_is_ram(section->mr)==true */
- end = int128_get64(llend);
vaddr = memory_region_get_ram_ptr(section->mr) +
section->offset_within_region +
(iova - section->offset_within_address_space);
@@ -400,27 +401,30 @@ static void vfio_listener_region_add(MemoryListener *listener,
error_report("vfio_dma_map(%p, 0x%"HWADDR_PRIx", "
"0x%"HWADDR_PRIx", %p) = %d (%m)",
container, iova, end - iova, vaddr, ret);
+ goto fail;
+ }
- /*
- * On the initfn path, store the first error in the container so we
- * can gracefully fail. Runtime, there's not much we can do other
- * than throw a hardware error.
- */
- if (!container->iommu_data.type1.initialized) {
- if (!container->iommu_data.type1.error) {
- container->iommu_data.type1.error = ret;
- }
- } else {
- hw_error("vfio: DMA mapping failed, unable to continue");
+ return;
+
+fail:
+ /*
+ * On the initfn path, store the first error in the container so we
+ * can gracefully fail. Runtime, there's not much we can do other
+ * than throw a hardware error.
+ */
+ if (!container->initialized) {
+ if (!container->error) {
+ container->error = ret;
}
+ } else {
+ hw_error("vfio: DMA mapping failed, unable to continue");
}
}
static void vfio_listener_region_del(MemoryListener *listener,
MemoryRegionSection *section)
{
- VFIOContainer *container = container_of(listener, VFIOContainer,
- iommu_data.type1.listener);
+ VFIOContainer *container = container_of(listener, VFIOContainer, listener);
hwaddr iova, end;
int ret;
@@ -485,7 +489,7 @@ static const MemoryListener vfio_memory_listener = {
static void vfio_listener_release(VFIOContainer *container)
{
- memory_listener_unregister(&container->iommu_data.type1.listener);
+ memory_listener_unregister(&container->listener);
}
int vfio_mmap_region(Object *obj, VFIORegion *region,
@@ -496,7 +500,7 @@ int vfio_mmap_region(Object *obj, VFIORegion *region,
int ret = 0;
VFIODevice *vbasedev = region->vbasedev;
- if (vbasedev->allow_mmap && size && region->flags &
+ if (!vbasedev->no_mmap && size && region->flags &
VFIO_REGION_INFO_FLAG_MMAP) {
int prot = 0;
@@ -668,6 +672,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU) ||
ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1v2_IOMMU)) {
bool v2 = !!ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1v2_IOMMU);
+ struct vfio_iommu_type1_info info;
ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd);
if (ret) {
@@ -684,21 +689,27 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
goto free_container_exit;
}
- container->iommu_data.type1.listener = vfio_memory_listener;
- container->iommu_data.release = vfio_listener_release;
-
- memory_listener_register(&container->iommu_data.type1.listener,
- container->space->as);
-
- if (container->iommu_data.type1.error) {
- ret = container->iommu_data.type1.error;
- error_report("vfio: memory listener initialization failed for container");
- goto listener_release_exit;
+ /*
+ * FIXME: This assumes that a Type1 IOMMU can map any 64-bit
+ * IOVA whatsoever. That's not actually true, but the current
+ * kernel interface doesn't tell us what it can map, and the
+ * existing Type1 IOMMUs generally support any IOVA we're
+ * going to actually try in practice.
+ */
+ container->min_iova = 0;
+ container->max_iova = (hwaddr)-1;
+
+ /* Assume just 4K IOVA page size */
+ container->iova_pgsizes = 0x1000;
+ info.argsz = sizeof(info);
+ ret = ioctl(fd, VFIO_IOMMU_GET_INFO, &info);
+ /* Ignore errors */
+ if ((ret == 0) && (info.flags & VFIO_IOMMU_INFO_PGSIZES)) {
+ container->iova_pgsizes = info.iova_pgsizes;
}
-
- container->iommu_data.type1.initialized = true;
-
} else if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_IOMMU)) {
+ struct vfio_iommu_spapr_tce_info info;
+
ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd);
if (ret) {
error_report("vfio: failed to set group container: %m");
@@ -724,18 +735,41 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
goto free_container_exit;
}
- container->iommu_data.type1.listener = vfio_memory_listener;
- container->iommu_data.release = vfio_listener_release;
-
- memory_listener_register(&container->iommu_data.type1.listener,
- container->space->as);
+ /*
+ * This only considers the host IOMMU's 32-bit window. At
+ * some point we need to add support for the optional 64-bit
+ * window and dynamic windows
+ */
+ info.argsz = sizeof(info);
+ ret = ioctl(fd, VFIO_IOMMU_SPAPR_TCE_GET_INFO, &info);
+ if (ret) {
+ error_report("vfio: VFIO_IOMMU_SPAPR_TCE_GET_INFO failed: %m");
+ ret = -errno;
+ goto free_container_exit;
+ }
+ container->min_iova = info.dma32_window_start;
+ container->max_iova = container->min_iova + info.dma32_window_size - 1;
+ /* Assume just 4K IOVA pages for now */
+ container->iova_pgsizes = 0x1000;
} else {
error_report("vfio: No available IOMMU models");
ret = -EINVAL;
goto free_container_exit;
}
+ container->listener = vfio_memory_listener;
+
+ memory_listener_register(&container->listener, container->space->as);
+
+ if (container->error) {
+ ret = container->error;
+ error_report("vfio: memory listener initialization failed for container");
+ goto listener_release_exit;
+ }
+
+ container->initialized = true;
+
QLIST_INIT(&container->group_list);
QLIST_INSERT_HEAD(&space->containers, container, next);
@@ -774,9 +808,7 @@ static void vfio_disconnect_container(VFIOGroup *group)
VFIOAddressSpace *space = container->space;
VFIOGuestIOMMU *giommu, *tmp;
- if (container->iommu_data.release) {
- container->iommu_data.release(container);
- }
+ vfio_listener_release(container);
QLIST_REMOVE(container, next);
QLIST_FOREACH_SAFE(giommu, &container->giommu_list, giommu_next, tmp) {
diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c
new file mode 100644
index 000000000..30c68a1e2
--- /dev/null
+++ b/hw/vfio/pci-quirks.c
@@ -0,0 +1,1204 @@
+/*
+ * device quirks for PCI devices
+ *
+ * Copyright Red Hat, Inc. 2012-2015
+ *
+ * Authors:
+ * Alex Williamson <alex.williamson@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "pci.h"
+#include "trace.h"
+#include "qemu/range.h"
+
+/* Use uin32_t for vendor & device so PCI_ANY_ID expands and cannot match hw */
+static bool vfio_pci_is(VFIOPCIDevice *vdev, uint32_t vendor, uint32_t device)
+{
+ return (vendor == PCI_ANY_ID || vendor == vdev->vendor_id) &&
+ (device == PCI_ANY_ID || device == vdev->device_id);
+}
+
+static bool vfio_is_vga(VFIOPCIDevice *vdev)
+{
+ PCIDevice *pdev = &vdev->pdev;
+ uint16_t class = pci_get_word(pdev->config + PCI_CLASS_DEVICE);
+
+ return class == PCI_CLASS_DISPLAY_VGA;
+}
+
+/*
+ * List of device ids/vendor ids for which to disable
+ * option rom loading. This avoids the guest hangs during rom
+ * execution as noticed with the BCM 57810 card for lack of a
+ * more better way to handle such issues.
+ * The user can still override by specifying a romfile or
+ * rombar=1.
+ * Please see https://bugs.launchpad.net/qemu/+bug/1284874
+ * for an analysis of the 57810 card hang. When adding
+ * a new vendor id/device id combination below, please also add
+ * your card/environment details and information that could
+ * help in debugging to the bug tracking this issue
+ */
+static const struct {
+ uint32_t vendor;
+ uint32_t device;
+} romblacklist[] = {
+ { 0x14e4, 0x168e }, /* Broadcom BCM 57810 */
+};
+
+bool vfio_blacklist_opt_rom(VFIOPCIDevice *vdev)
+{
+ int i;
+
+ for (i = 0 ; i < ARRAY_SIZE(romblacklist); i++) {
+ if (vfio_pci_is(vdev, romblacklist[i].vendor, romblacklist[i].device)) {
+ trace_vfio_quirk_rom_blacklisted(vdev->vbasedev.name,
+ romblacklist[i].vendor,
+ romblacklist[i].device);
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * Device specific region quirks (mostly backdoors to PCI config space)
+ */
+
+/*
+ * The generic window quirks operate on an address and data register,
+ * vfio_generic_window_address_quirk handles the address register and
+ * vfio_generic_window_data_quirk handles the data register. These ops
+ * pass reads and writes through to hardware until a value matching the
+ * stored address match/mask is written. When this occurs, the data
+ * register access emulated PCI config space for the device rather than
+ * passing through accesses. This enables devices where PCI config space
+ * is accessible behind a window register to maintain the virtualization
+ * provided through vfio.
+ */
+typedef struct VFIOConfigWindowMatch {
+ uint32_t match;
+ uint32_t mask;
+} VFIOConfigWindowMatch;
+
+typedef struct VFIOConfigWindowQuirk {
+ struct VFIOPCIDevice *vdev;
+
+ uint32_t address_val;
+
+ uint32_t address_offset;
+ uint32_t data_offset;
+
+ bool window_enabled;
+ uint8_t bar;
+
+ MemoryRegion *addr_mem;
+ MemoryRegion *data_mem;
+
+ uint32_t nr_matches;
+ VFIOConfigWindowMatch matches[];
+} VFIOConfigWindowQuirk;
+
+static uint64_t vfio_generic_window_quirk_address_read(void *opaque,
+ hwaddr addr,
+ unsigned size)
+{
+ VFIOConfigWindowQuirk *window = opaque;
+ VFIOPCIDevice *vdev = window->vdev;
+
+ return vfio_region_read(&vdev->bars[window->bar].region,
+ addr + window->address_offset, size);
+}
+
+static void vfio_generic_window_quirk_address_write(void *opaque, hwaddr addr,
+ uint64_t data,
+ unsigned size)
+{
+ VFIOConfigWindowQuirk *window = opaque;
+ VFIOPCIDevice *vdev = window->vdev;
+ int i;
+
+ window->window_enabled = false;
+
+ vfio_region_write(&vdev->bars[window->bar].region,
+ addr + window->address_offset, data, size);
+
+ for (i = 0; i < window->nr_matches; i++) {
+ if ((data & ~window->matches[i].mask) == window->matches[i].match) {
+ window->window_enabled = true;
+ window->address_val = data & window->matches[i].mask;
+ trace_vfio_quirk_generic_window_address_write(vdev->vbasedev.name,
+ memory_region_name(window->addr_mem), data);
+ break;
+ }
+ }
+}
+
+static const MemoryRegionOps vfio_generic_window_address_quirk = {
+ .read = vfio_generic_window_quirk_address_read,
+ .write = vfio_generic_window_quirk_address_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t vfio_generic_window_quirk_data_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIOConfigWindowQuirk *window = opaque;
+ VFIOPCIDevice *vdev = window->vdev;
+ uint64_t data;
+
+ /* Always read data reg, discard if window enabled */
+ data = vfio_region_read(&vdev->bars[window->bar].region,
+ addr + window->data_offset, size);
+
+ if (window->window_enabled) {
+ data = vfio_pci_read_config(&vdev->pdev, window->address_val, size);
+ trace_vfio_quirk_generic_window_data_read(vdev->vbasedev.name,
+ memory_region_name(window->data_mem), data);
+ }
+
+ return data;
+}
+
+static void vfio_generic_window_quirk_data_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIOConfigWindowQuirk *window = opaque;
+ VFIOPCIDevice *vdev = window->vdev;
+
+ if (window->window_enabled) {
+ vfio_pci_write_config(&vdev->pdev, window->address_val, data, size);
+ trace_vfio_quirk_generic_window_data_write(vdev->vbasedev.name,
+ memory_region_name(window->data_mem), data);
+ return;
+ }
+
+ vfio_region_write(&vdev->bars[window->bar].region,
+ addr + window->data_offset, data, size);
+}
+
+static const MemoryRegionOps vfio_generic_window_data_quirk = {
+ .read = vfio_generic_window_quirk_data_read,
+ .write = vfio_generic_window_quirk_data_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+/*
+ * The generic mirror quirk handles devices which expose PCI config space
+ * through a region within a BAR. When enabled, reads and writes are
+ * redirected through to emulated PCI config space. XXX if PCI config space
+ * used memory regions, this could just be an alias.
+ */
+typedef struct VFIOConfigMirrorQuirk {
+ struct VFIOPCIDevice *vdev;
+ uint32_t offset;
+ uint8_t bar;
+ MemoryRegion *mem;
+} VFIOConfigMirrorQuirk;
+
+static uint64_t vfio_generic_quirk_mirror_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIOConfigMirrorQuirk *mirror = opaque;
+ VFIOPCIDevice *vdev = mirror->vdev;
+ uint64_t data;
+
+ /* Read and discard in case the hardware cares */
+ (void)vfio_region_read(&vdev->bars[mirror->bar].region,
+ addr + mirror->offset, size);
+
+ data = vfio_pci_read_config(&vdev->pdev, addr, size);
+ trace_vfio_quirk_generic_mirror_read(vdev->vbasedev.name,
+ memory_region_name(mirror->mem),
+ addr, data);
+ return data;
+}
+
+static void vfio_generic_quirk_mirror_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIOConfigMirrorQuirk *mirror = opaque;
+ VFIOPCIDevice *vdev = mirror->vdev;
+
+ vfio_pci_write_config(&vdev->pdev, addr, data, size);
+ trace_vfio_quirk_generic_mirror_write(vdev->vbasedev.name,
+ memory_region_name(mirror->mem),
+ addr, data);
+}
+
+static const MemoryRegionOps vfio_generic_mirror_quirk = {
+ .read = vfio_generic_quirk_mirror_read,
+ .write = vfio_generic_quirk_mirror_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+/* Is range1 fully contained within range2? */
+static bool vfio_range_contained(uint64_t first1, uint64_t len1,
+ uint64_t first2, uint64_t len2) {
+ return (first1 >= first2 && first1 + len1 <= first2 + len2);
+}
+
+#define PCI_VENDOR_ID_ATI 0x1002
+
+/*
+ * Radeon HD cards (HD5450 & HD7850) report the upper byte of the I/O port BAR
+ * through VGA register 0x3c3. On newer cards, the I/O port BAR is always
+ * BAR4 (older cards like the X550 used BAR1, but we don't care to support
+ * those). Note that on bare metal, a read of 0x3c3 doesn't always return the
+ * I/O port BAR address. Originally this was coded to return the virtual BAR
+ * address only if the physical register read returns the actual BAR address,
+ * but users have reported greater success if we return the virtual address
+ * unconditionally.
+ */
+static uint64_t vfio_ati_3c3_quirk_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIOPCIDevice *vdev = opaque;
+ uint64_t data = vfio_pci_read_config(&vdev->pdev,
+ PCI_BASE_ADDRESS_4 + 1, size);
+
+ trace_vfio_quirk_ati_3c3_read(vdev->vbasedev.name, data);
+
+ return data;
+}
+
+static const MemoryRegionOps vfio_ati_3c3_quirk = {
+ .read = vfio_ati_3c3_quirk_read,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void vfio_vga_probe_ati_3c3_quirk(VFIOPCIDevice *vdev)
+{
+ VFIOQuirk *quirk;
+
+ /*
+ * As long as the BAR is >= 256 bytes it will be aligned such that the
+ * lower byte is always zero. Filter out anything else, if it exists.
+ */
+ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_ATI, PCI_ANY_ID) ||
+ !vdev->bars[4].ioport || vdev->bars[4].region.size < 256) {
+ return;
+ }
+
+ quirk = g_malloc0(sizeof(*quirk));
+ quirk->mem = g_new0(MemoryRegion, 1);
+ quirk->nr_mem = 1;
+
+ memory_region_init_io(quirk->mem, OBJECT(vdev), &vfio_ati_3c3_quirk, vdev,
+ "vfio-ati-3c3-quirk", 1);
+ memory_region_add_subregion(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem,
+ 3 /* offset 3 bytes from 0x3c0 */, quirk->mem);
+
+ QLIST_INSERT_HEAD(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks,
+ quirk, next);
+
+ trace_vfio_quirk_ati_3c3_probe(vdev->vbasedev.name);
+}
+
+/*
+ * Newer ATI/AMD devices, including HD5450 and HD7850, have a mirror to PCI
+ * config space through MMIO BAR2 at offset 0x4000. Nothing seems to access
+ * the MMIO space directly, but a window to this space is provided through
+ * I/O port BAR4. Offset 0x0 is the address register and offset 0x4 is the
+ * data register. When the address is programmed to a range of 0x4000-0x4fff
+ * PCI configuration space is available. Experimentation seems to indicate
+ * that read-only may be provided by hardware.
+ */
+static void vfio_probe_ati_bar4_quirk(VFIOPCIDevice *vdev, int nr)
+{
+ VFIOQuirk *quirk;
+ VFIOConfigWindowQuirk *window;
+
+ /* This windows doesn't seem to be used except by legacy VGA code */
+ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_ATI, PCI_ANY_ID) ||
+ !vdev->has_vga || nr != 4) {
+ return;
+ }
+
+ quirk = g_malloc0(sizeof(*quirk));
+ quirk->mem = g_new0(MemoryRegion, 2);
+ quirk->nr_mem = 2;
+ window = quirk->data = g_malloc0(sizeof(*window) +
+ sizeof(VFIOConfigWindowMatch));
+ window->vdev = vdev;
+ window->address_offset = 0;
+ window->data_offset = 4;
+ window->nr_matches = 1;
+ window->matches[0].match = 0x4000;
+ window->matches[0].mask = PCIE_CONFIG_SPACE_SIZE - 1;
+ window->bar = nr;
+ window->addr_mem = &quirk->mem[0];
+ window->data_mem = &quirk->mem[1];
+
+ memory_region_init_io(window->addr_mem, OBJECT(vdev),
+ &vfio_generic_window_address_quirk, window,
+ "vfio-ati-bar4-window-address-quirk", 4);
+ memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
+ window->address_offset,
+ window->addr_mem, 1);
+
+ memory_region_init_io(window->data_mem, OBJECT(vdev),
+ &vfio_generic_window_data_quirk, window,
+ "vfio-ati-bar4-window-data-quirk", 4);
+ memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
+ window->data_offset,
+ window->data_mem, 1);
+
+ QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
+
+ trace_vfio_quirk_ati_bar4_probe(vdev->vbasedev.name);
+}
+
+/*
+ * Trap the BAR2 MMIO mirror to config space as well.
+ */
+static void vfio_probe_ati_bar2_quirk(VFIOPCIDevice *vdev, int nr)
+{
+ VFIOQuirk *quirk;
+ VFIOConfigMirrorQuirk *mirror;
+
+ /* Only enable on newer devices where BAR2 is 64bit */
+ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_ATI, PCI_ANY_ID) ||
+ !vdev->has_vga || nr != 2 || !vdev->bars[2].mem64) {
+ return;
+ }
+
+ quirk = g_malloc0(sizeof(*quirk));
+ mirror = quirk->data = g_malloc0(sizeof(*mirror));
+ mirror->mem = quirk->mem = g_new0(MemoryRegion, 1);
+ quirk->nr_mem = 1;
+ mirror->vdev = vdev;
+ mirror->offset = 0x4000;
+ mirror->bar = nr;
+
+ memory_region_init_io(mirror->mem, OBJECT(vdev),
+ &vfio_generic_mirror_quirk, mirror,
+ "vfio-ati-bar2-4000-quirk", PCI_CONFIG_SPACE_SIZE);
+ memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
+ mirror->offset, mirror->mem, 1);
+
+ QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
+
+ trace_vfio_quirk_ati_bar2_probe(vdev->vbasedev.name);
+}
+
+/*
+ * Older ATI/AMD cards like the X550 have a similar window to that above.
+ * I/O port BAR1 provides a window to a mirror of PCI config space located
+ * in BAR2 at offset 0xf00. We don't care to support such older cards, but
+ * note it for future reference.
+ */
+
+#define PCI_VENDOR_ID_NVIDIA 0x10de
+
+/*
+ * Nvidia has several different methods to get to config space, the
+ * nouveu project has several of these documented here:
+ * https://github.com/pathscale/envytools/tree/master/hwdocs
+ *
+ * The first quirk is actually not documented in envytools and is found
+ * on 10de:01d1 (NVIDIA Corporation G72 [GeForce 7300 LE]). This is an
+ * NV46 chipset. The backdoor uses the legacy VGA I/O ports to access
+ * the mirror of PCI config space found at BAR0 offset 0x1800. The access
+ * sequence first writes 0x338 to I/O port 0x3d4. The target offset is
+ * then written to 0x3d0. Finally 0x538 is written for a read and 0x738
+ * is written for a write to 0x3d4. The BAR0 offset is then accessible
+ * through 0x3d0. This quirk doesn't seem to be necessary on newer cards
+ * that use the I/O port BAR5 window but it doesn't hurt to leave it.
+ */
+typedef enum {NONE = 0, SELECT, WINDOW, READ, WRITE} VFIONvidia3d0State;
+static const char *nv3d0_states[] = { "NONE", "SELECT",
+ "WINDOW", "READ", "WRITE" };
+
+typedef struct VFIONvidia3d0Quirk {
+ VFIOPCIDevice *vdev;
+ VFIONvidia3d0State state;
+ uint32_t offset;
+} VFIONvidia3d0Quirk;
+
+static uint64_t vfio_nvidia_3d4_quirk_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIONvidia3d0Quirk *quirk = opaque;
+ VFIOPCIDevice *vdev = quirk->vdev;
+
+ quirk->state = NONE;
+
+ return vfio_vga_read(&vdev->vga.region[QEMU_PCI_VGA_IO_HI],
+ addr + 0x14, size);
+}
+
+static void vfio_nvidia_3d4_quirk_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIONvidia3d0Quirk *quirk = opaque;
+ VFIOPCIDevice *vdev = quirk->vdev;
+ VFIONvidia3d0State old_state = quirk->state;
+
+ quirk->state = NONE;
+
+ switch (data) {
+ case 0x338:
+ if (old_state == NONE) {
+ quirk->state = SELECT;
+ trace_vfio_quirk_nvidia_3d0_state(vdev->vbasedev.name,
+ nv3d0_states[quirk->state]);
+ }
+ break;
+ case 0x538:
+ if (old_state == WINDOW) {
+ quirk->state = READ;
+ trace_vfio_quirk_nvidia_3d0_state(vdev->vbasedev.name,
+ nv3d0_states[quirk->state]);
+ }
+ break;
+ case 0x738:
+ if (old_state == WINDOW) {
+ quirk->state = WRITE;
+ trace_vfio_quirk_nvidia_3d0_state(vdev->vbasedev.name,
+ nv3d0_states[quirk->state]);
+ }
+ break;
+ }
+
+ vfio_vga_write(&vdev->vga.region[QEMU_PCI_VGA_IO_HI],
+ addr + 0x14, data, size);
+}
+
+static const MemoryRegionOps vfio_nvidia_3d4_quirk = {
+ .read = vfio_nvidia_3d4_quirk_read,
+ .write = vfio_nvidia_3d4_quirk_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t vfio_nvidia_3d0_quirk_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIONvidia3d0Quirk *quirk = opaque;
+ VFIOPCIDevice *vdev = quirk->vdev;
+ VFIONvidia3d0State old_state = quirk->state;
+ uint64_t data = vfio_vga_read(&vdev->vga.region[QEMU_PCI_VGA_IO_HI],
+ addr + 0x10, size);
+
+ quirk->state = NONE;
+
+ if (old_state == READ &&
+ (quirk->offset & ~(PCI_CONFIG_SPACE_SIZE - 1)) == 0x1800) {
+ uint8_t offset = quirk->offset & (PCI_CONFIG_SPACE_SIZE - 1);
+
+ data = vfio_pci_read_config(&vdev->pdev, offset, size);
+ trace_vfio_quirk_nvidia_3d0_read(vdev->vbasedev.name,
+ offset, size, data);
+ }
+
+ return data;
+}
+
+static void vfio_nvidia_3d0_quirk_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIONvidia3d0Quirk *quirk = opaque;
+ VFIOPCIDevice *vdev = quirk->vdev;
+ VFIONvidia3d0State old_state = quirk->state;
+
+ quirk->state = NONE;
+
+ if (old_state == SELECT) {
+ quirk->offset = (uint32_t)data;
+ quirk->state = WINDOW;
+ trace_vfio_quirk_nvidia_3d0_state(vdev->vbasedev.name,
+ nv3d0_states[quirk->state]);
+ } else if (old_state == WRITE) {
+ if ((quirk->offset & ~(PCI_CONFIG_SPACE_SIZE - 1)) == 0x1800) {
+ uint8_t offset = quirk->offset & (PCI_CONFIG_SPACE_SIZE - 1);
+
+ vfio_pci_write_config(&vdev->pdev, offset, data, size);
+ trace_vfio_quirk_nvidia_3d0_write(vdev->vbasedev.name,
+ offset, data, size);
+ return;
+ }
+ }
+
+ vfio_vga_write(&vdev->vga.region[QEMU_PCI_VGA_IO_HI],
+ addr + 0x10, data, size);
+}
+
+static const MemoryRegionOps vfio_nvidia_3d0_quirk = {
+ .read = vfio_nvidia_3d0_quirk_read,
+ .write = vfio_nvidia_3d0_quirk_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void vfio_vga_probe_nvidia_3d0_quirk(VFIOPCIDevice *vdev)
+{
+ VFIOQuirk *quirk;
+ VFIONvidia3d0Quirk *data;
+
+ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID) ||
+ !vdev->bars[1].region.size) {
+ return;
+ }
+
+ quirk = g_malloc0(sizeof(*quirk));
+ quirk->data = data = g_malloc0(sizeof(*data));
+ quirk->mem = g_new0(MemoryRegion, 2);
+ quirk->nr_mem = 2;
+ data->vdev = vdev;
+
+ memory_region_init_io(&quirk->mem[0], OBJECT(vdev), &vfio_nvidia_3d4_quirk,
+ data, "vfio-nvidia-3d4-quirk", 2);
+ memory_region_add_subregion(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem,
+ 0x14 /* 0x3c0 + 0x14 */, &quirk->mem[0]);
+
+ memory_region_init_io(&quirk->mem[1], OBJECT(vdev), &vfio_nvidia_3d0_quirk,
+ data, "vfio-nvidia-3d0-quirk", 2);
+ memory_region_add_subregion(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem,
+ 0x10 /* 0x3c0 + 0x10 */, &quirk->mem[1]);
+
+ QLIST_INSERT_HEAD(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks,
+ quirk, next);
+
+ trace_vfio_quirk_nvidia_3d0_probe(vdev->vbasedev.name);
+}
+
+/*
+ * The second quirk is documented in envytools. The I/O port BAR5 is just
+ * a set of address/data ports to the MMIO BARs. The BAR we care about is
+ * again BAR0. This backdoor is apparently a bit newer than the one above
+ * so we need to not only trap 256 bytes @0x1800, but all of PCI config
+ * space, including extended space is available at the 4k @0x88000.
+ */
+typedef struct VFIONvidiaBAR5Quirk {
+ uint32_t master;
+ uint32_t enable;
+ MemoryRegion *addr_mem;
+ MemoryRegion *data_mem;
+ bool enabled;
+ VFIOConfigWindowQuirk window; /* last for match data */
+} VFIONvidiaBAR5Quirk;
+
+static void vfio_nvidia_bar5_enable(VFIONvidiaBAR5Quirk *bar5)
+{
+ VFIOPCIDevice *vdev = bar5->window.vdev;
+
+ if (((bar5->master & bar5->enable) & 0x1) == bar5->enabled) {
+ return;
+ }
+
+ bar5->enabled = !bar5->enabled;
+ trace_vfio_quirk_nvidia_bar5_state(vdev->vbasedev.name,
+ bar5->enabled ? "Enable" : "Disable");
+ memory_region_set_enabled(bar5->addr_mem, bar5->enabled);
+ memory_region_set_enabled(bar5->data_mem, bar5->enabled);
+}
+
+static uint64_t vfio_nvidia_bar5_quirk_master_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIONvidiaBAR5Quirk *bar5 = opaque;
+ VFIOPCIDevice *vdev = bar5->window.vdev;
+
+ return vfio_region_read(&vdev->bars[5].region, addr, size);
+}
+
+static void vfio_nvidia_bar5_quirk_master_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIONvidiaBAR5Quirk *bar5 = opaque;
+ VFIOPCIDevice *vdev = bar5->window.vdev;
+
+ vfio_region_write(&vdev->bars[5].region, addr, data, size);
+
+ bar5->master = data;
+ vfio_nvidia_bar5_enable(bar5);
+}
+
+static const MemoryRegionOps vfio_nvidia_bar5_quirk_master = {
+ .read = vfio_nvidia_bar5_quirk_master_read,
+ .write = vfio_nvidia_bar5_quirk_master_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t vfio_nvidia_bar5_quirk_enable_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIONvidiaBAR5Quirk *bar5 = opaque;
+ VFIOPCIDevice *vdev = bar5->window.vdev;
+
+ return vfio_region_read(&vdev->bars[5].region, addr + 4, size);
+}
+
+static void vfio_nvidia_bar5_quirk_enable_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIONvidiaBAR5Quirk *bar5 = opaque;
+ VFIOPCIDevice *vdev = bar5->window.vdev;
+
+ vfio_region_write(&vdev->bars[5].region, addr + 4, data, size);
+
+ bar5->enable = data;
+ vfio_nvidia_bar5_enable(bar5);
+}
+
+static const MemoryRegionOps vfio_nvidia_bar5_quirk_enable = {
+ .read = vfio_nvidia_bar5_quirk_enable_read,
+ .write = vfio_nvidia_bar5_quirk_enable_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void vfio_probe_nvidia_bar5_quirk(VFIOPCIDevice *vdev, int nr)
+{
+ VFIOQuirk *quirk;
+ VFIONvidiaBAR5Quirk *bar5;
+ VFIOConfigWindowQuirk *window;
+
+ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID) ||
+ !vdev->has_vga || nr != 5) {
+ return;
+ }
+
+ quirk = g_malloc0(sizeof(*quirk));
+ quirk->mem = g_new0(MemoryRegion, 4);
+ quirk->nr_mem = 4;
+ bar5 = quirk->data = g_malloc0(sizeof(*bar5) +
+ (sizeof(VFIOConfigWindowMatch) * 2));
+ window = &bar5->window;
+
+ window->vdev = vdev;
+ window->address_offset = 0x8;
+ window->data_offset = 0xc;
+ window->nr_matches = 2;
+ window->matches[0].match = 0x1800;
+ window->matches[0].mask = PCI_CONFIG_SPACE_SIZE - 1;
+ window->matches[1].match = 0x88000;
+ window->matches[1].mask = PCIE_CONFIG_SPACE_SIZE - 1;
+ window->bar = nr;
+ window->addr_mem = bar5->addr_mem = &quirk->mem[0];
+ window->data_mem = bar5->data_mem = &quirk->mem[1];
+
+ memory_region_init_io(window->addr_mem, OBJECT(vdev),
+ &vfio_generic_window_address_quirk, window,
+ "vfio-nvidia-bar5-window-address-quirk", 4);
+ memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
+ window->address_offset,
+ window->addr_mem, 1);
+ memory_region_set_enabled(window->addr_mem, false);
+
+ memory_region_init_io(window->data_mem, OBJECT(vdev),
+ &vfio_generic_window_data_quirk, window,
+ "vfio-nvidia-bar5-window-data-quirk", 4);
+ memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
+ window->data_offset,
+ window->data_mem, 1);
+ memory_region_set_enabled(window->data_mem, false);
+
+ memory_region_init_io(&quirk->mem[2], OBJECT(vdev),
+ &vfio_nvidia_bar5_quirk_master, bar5,
+ "vfio-nvidia-bar5-master-quirk", 4);
+ memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
+ 0, &quirk->mem[2], 1);
+
+ memory_region_init_io(&quirk->mem[3], OBJECT(vdev),
+ &vfio_nvidia_bar5_quirk_enable, bar5,
+ "vfio-nvidia-bar5-enable-quirk", 4);
+ memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
+ 4, &quirk->mem[3], 1);
+
+ QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
+
+ trace_vfio_quirk_nvidia_bar5_probe(vdev->vbasedev.name);
+}
+
+/*
+ * Finally, BAR0 itself. We want to redirect any accesses to either
+ * 0x1800 or 0x88000 through the PCI config space access functions.
+ */
+static void vfio_nvidia_quirk_mirror_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIOConfigMirrorQuirk *mirror = opaque;
+ VFIOPCIDevice *vdev = mirror->vdev;
+ PCIDevice *pdev = &vdev->pdev;
+
+ vfio_generic_quirk_mirror_write(opaque, addr, data, size);
+
+ /*
+ * Nvidia seems to acknowledge MSI interrupts by writing 0xff to the
+ * MSI capability ID register. Both the ID and next register are
+ * read-only, so we allow writes covering either of those to real hw.
+ */
+ if ((pdev->cap_present & QEMU_PCI_CAP_MSI) &&
+ vfio_range_contained(addr, size, pdev->msi_cap, PCI_MSI_FLAGS)) {
+ vfio_region_write(&vdev->bars[mirror->bar].region,
+ addr + mirror->offset, data, size);
+ trace_vfio_quirk_nvidia_bar0_msi_ack(vdev->vbasedev.name);
+ }
+}
+
+static const MemoryRegionOps vfio_nvidia_mirror_quirk = {
+ .read = vfio_generic_quirk_mirror_read,
+ .write = vfio_nvidia_quirk_mirror_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void vfio_probe_nvidia_bar0_quirk(VFIOPCIDevice *vdev, int nr)
+{
+ VFIOQuirk *quirk;
+ VFIOConfigMirrorQuirk *mirror;
+
+ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID) ||
+ !vfio_is_vga(vdev) || nr != 0) {
+ return;
+ }
+
+ quirk = g_malloc0(sizeof(*quirk));
+ mirror = quirk->data = g_malloc0(sizeof(*mirror));
+ mirror->mem = quirk->mem = g_new0(MemoryRegion, 1);
+ quirk->nr_mem = 1;
+ mirror->vdev = vdev;
+ mirror->offset = 0x88000;
+ mirror->bar = nr;
+
+ memory_region_init_io(mirror->mem, OBJECT(vdev),
+ &vfio_nvidia_mirror_quirk, mirror,
+ "vfio-nvidia-bar0-88000-mirror-quirk",
+ PCIE_CONFIG_SPACE_SIZE);
+ memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
+ mirror->offset, mirror->mem, 1);
+
+ QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
+
+ /* The 0x1800 offset mirror only seems to get used by legacy VGA */
+ if (vdev->has_vga) {
+ quirk = g_malloc0(sizeof(*quirk));
+ mirror = quirk->data = g_malloc0(sizeof(*mirror));
+ mirror->mem = quirk->mem = g_new0(MemoryRegion, 1);
+ quirk->nr_mem = 1;
+ mirror->vdev = vdev;
+ mirror->offset = 0x1800;
+ mirror->bar = nr;
+
+ memory_region_init_io(mirror->mem, OBJECT(vdev),
+ &vfio_nvidia_mirror_quirk, mirror,
+ "vfio-nvidia-bar0-1800-mirror-quirk",
+ PCI_CONFIG_SPACE_SIZE);
+ memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
+ mirror->offset, mirror->mem, 1);
+
+ QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
+ }
+
+ trace_vfio_quirk_nvidia_bar0_probe(vdev->vbasedev.name);
+}
+
+/*
+ * TODO - Some Nvidia devices provide config access to their companion HDA
+ * device and even to their parent bridge via these config space mirrors.
+ * Add quirks for those regions.
+ */
+
+#define PCI_VENDOR_ID_REALTEK 0x10ec
+
+/*
+ * RTL8168 devices have a backdoor that can access the MSI-X table. At BAR2
+ * offset 0x70 there is a dword data register, offset 0x74 is a dword address
+ * register. According to the Linux r8169 driver, the MSI-X table is addressed
+ * when the "type" portion of the address register is set to 0x1. This appears
+ * to be bits 16:30. Bit 31 is both a write indicator and some sort of
+ * "address latched" indicator. Bits 12:15 are a mask field, which we can
+ * ignore because the MSI-X table should always be accessed as a dword (full
+ * mask). Bits 0:11 is offset within the type.
+ *
+ * Example trace:
+ *
+ * Read from MSI-X table offset 0
+ * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x74, 0x1f000, 4) // store read addr
+ * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x74, 4) = 0x8001f000 // latch
+ * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x70, 4) = 0xfee00398 // read data
+ *
+ * Write 0xfee00000 to MSI-X table offset 0
+ * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x70, 0xfee00000, 4) // write data
+ * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x74, 0x8001f000, 4) // do write
+ * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x74, 4) = 0x1f000 // complete
+ */
+typedef struct VFIOrtl8168Quirk {
+ VFIOPCIDevice *vdev;
+ uint32_t addr;
+ uint32_t data;
+ bool enabled;
+} VFIOrtl8168Quirk;
+
+static uint64_t vfio_rtl8168_quirk_address_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIOrtl8168Quirk *rtl = opaque;
+ VFIOPCIDevice *vdev = rtl->vdev;
+ uint64_t data = vfio_region_read(&vdev->bars[2].region, addr + 0x74, size);
+
+ if (rtl->enabled) {
+ data = rtl->addr ^ 0x80000000U; /* latch/complete */
+ trace_vfio_quirk_rtl8168_fake_latch(vdev->vbasedev.name, data);
+ }
+
+ return data;
+}
+
+static void vfio_rtl8168_quirk_address_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIOrtl8168Quirk *rtl = opaque;
+ VFIOPCIDevice *vdev = rtl->vdev;
+
+ rtl->enabled = false;
+
+ if ((data & 0x7fff0000) == 0x10000) { /* MSI-X table */
+ rtl->enabled = true;
+ rtl->addr = (uint32_t)data;
+
+ if (data & 0x80000000U) { /* Do write */
+ if (vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX) {
+ hwaddr offset = data & 0xfff;
+ uint64_t val = rtl->data;
+
+ trace_vfio_quirk_rtl8168_msix_write(vdev->vbasedev.name,
+ (uint16_t)offset, val);
+
+ /* Write to the proper guest MSI-X table instead */
+ memory_region_dispatch_write(&vdev->pdev.msix_table_mmio,
+ offset, val, size,
+ MEMTXATTRS_UNSPECIFIED);
+ }
+ return; /* Do not write guest MSI-X data to hardware */
+ }
+ }
+
+ vfio_region_write(&vdev->bars[2].region, addr + 0x74, data, size);
+}
+
+static const MemoryRegionOps vfio_rtl_address_quirk = {
+ .read = vfio_rtl8168_quirk_address_read,
+ .write = vfio_rtl8168_quirk_address_write,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t vfio_rtl8168_quirk_data_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ VFIOrtl8168Quirk *rtl = opaque;
+ VFIOPCIDevice *vdev = rtl->vdev;
+ uint64_t data = vfio_region_read(&vdev->bars[2].region, addr + 0x74, size);
+
+ if (rtl->enabled && (vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX)) {
+ hwaddr offset = rtl->addr & 0xfff;
+ memory_region_dispatch_read(&vdev->pdev.msix_table_mmio, offset,
+ &data, size, MEMTXATTRS_UNSPECIFIED);
+ trace_vfio_quirk_rtl8168_msix_read(vdev->vbasedev.name, offset, data);
+ }
+
+ return data;
+}
+
+static void vfio_rtl8168_quirk_data_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ VFIOrtl8168Quirk *rtl = opaque;
+ VFIOPCIDevice *vdev = rtl->vdev;
+
+ rtl->data = (uint32_t)data;
+
+ vfio_region_write(&vdev->bars[2].region, addr + 0x70, data, size);
+}
+
+static const MemoryRegionOps vfio_rtl_data_quirk = {
+ .read = vfio_rtl8168_quirk_data_read,
+ .write = vfio_rtl8168_quirk_data_write,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void vfio_probe_rtl8168_bar2_quirk(VFIOPCIDevice *vdev, int nr)
+{
+ VFIOQuirk *quirk;
+ VFIOrtl8168Quirk *rtl;
+
+ if (!vfio_pci_is(vdev, PCI_VENDOR_ID_REALTEK, 0x8168) || nr != 2) {
+ return;
+ }
+
+ quirk = g_malloc0(sizeof(*quirk));
+ quirk->mem = g_new0(MemoryRegion, 2);
+ quirk->nr_mem = 2;
+ quirk->data = rtl = g_malloc0(sizeof(*rtl));
+ rtl->vdev = vdev;
+
+ memory_region_init_io(&quirk->mem[0], OBJECT(vdev),
+ &vfio_rtl_address_quirk, rtl,
+ "vfio-rtl8168-window-address-quirk", 4);
+ memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
+ 0x74, &quirk->mem[0], 1);
+
+ memory_region_init_io(&quirk->mem[1], OBJECT(vdev),
+ &vfio_rtl_data_quirk, rtl,
+ "vfio-rtl8168-window-data-quirk", 4);
+ memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
+ 0x70, &quirk->mem[1], 1);
+
+ QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
+
+ trace_vfio_quirk_rtl8168_probe(vdev->vbasedev.name);
+}
+
+/*
+ * Common quirk probe entry points.
+ */
+void vfio_vga_quirk_setup(VFIOPCIDevice *vdev)
+{
+ vfio_vga_probe_ati_3c3_quirk(vdev);
+ vfio_vga_probe_nvidia_3d0_quirk(vdev);
+}
+
+void vfio_vga_quirk_teardown(VFIOPCIDevice *vdev)
+{
+ VFIOQuirk *quirk;
+ int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(vdev->vga.region); i++) {
+ QLIST_FOREACH(quirk, &vdev->vga.region[i].quirks, next) {
+ for (j = 0; j < quirk->nr_mem; j++) {
+ memory_region_del_subregion(&vdev->vga.region[i].mem,
+ &quirk->mem[j]);
+ }
+ }
+ }
+}
+
+void vfio_vga_quirk_free(VFIOPCIDevice *vdev)
+{
+ int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(vdev->vga.region); i++) {
+ while (!QLIST_EMPTY(&vdev->vga.region[i].quirks)) {
+ VFIOQuirk *quirk = QLIST_FIRST(&vdev->vga.region[i].quirks);
+ QLIST_REMOVE(quirk, next);
+ for (j = 0; j < quirk->nr_mem; j++) {
+ object_unparent(OBJECT(&quirk->mem[j]));
+ }
+ g_free(quirk->mem);
+ g_free(quirk->data);
+ g_free(quirk);
+ }
+ }
+}
+
+void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr)
+{
+ vfio_probe_ati_bar4_quirk(vdev, nr);
+ vfio_probe_ati_bar2_quirk(vdev, nr);
+ vfio_probe_nvidia_bar5_quirk(vdev, nr);
+ vfio_probe_nvidia_bar0_quirk(vdev, nr);
+ vfio_probe_rtl8168_bar2_quirk(vdev, nr);
+}
+
+void vfio_bar_quirk_teardown(VFIOPCIDevice *vdev, int nr)
+{
+ VFIOBAR *bar = &vdev->bars[nr];
+ VFIOQuirk *quirk;
+ int i;
+
+ QLIST_FOREACH(quirk, &bar->quirks, next) {
+ for (i = 0; i < quirk->nr_mem; i++) {
+ memory_region_del_subregion(&bar->region.mem, &quirk->mem[i]);
+ }
+ }
+}
+
+void vfio_bar_quirk_free(VFIOPCIDevice *vdev, int nr)
+{
+ VFIOBAR *bar = &vdev->bars[nr];
+ int i;
+
+ while (!QLIST_EMPTY(&bar->quirks)) {
+ VFIOQuirk *quirk = QLIST_FIRST(&bar->quirks);
+ QLIST_REMOVE(quirk, next);
+ for (i = 0; i < quirk->nr_mem; i++) {
+ object_unparent(OBJECT(&quirk->mem[i]));
+ }
+ g_free(quirk->mem);
+ g_free(quirk->data);
+ g_free(quirk);
+ }
+}
+
+/*
+ * Reset quirks
+ */
+
+/*
+ * 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 accessing
+ * 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) {
+ trace_vfio_quirk_ati_bonaire_reset_skipped(vdev->vbasedev.name);
+ 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;
+ trace_vfio_quirk_ati_bonaire_reset_no_smc(vdev->vbasedev.name);
+ 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) {
+ goto reset_smc;
+ }
+ usleep(1);
+ }
+
+ trace_vfio_quirk_ati_bonaire_reset_timeout(vdev->vbasedev.name);
+
+reset_smc:
+ /* 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);
+
+ trace_vfio_quirk_ati_bonaire_reset_done(vdev->vbasedev.name);
+
+out:
+ /* Restore PCI command register */
+ vfio_pci_write_config(pdev, PCI_COMMAND, 0, 2);
+
+ return ret;
+}
+
+void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev)
+{
+ switch (vdev->vendor_id) {
+ case 0x1002:
+ switch (vdev->device_id) {
+ /* 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;
+ trace_vfio_quirk_ati_bonaire_reset(vdev->vbasedev.name);
+ break;
+ }
+ break;
+ }
+}
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index 4023d8e82..1fb868c24 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -18,7 +18,6 @@
* Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com)
*/
-#include <dirent.h>
#include <linux/vfio.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
@@ -27,178 +26,19 @@
#include <unistd.h>
#include "config.h"
-#include "exec/address-spaces.h"
-#include "exec/memory.h"
#include "hw/pci/msi.h"
#include "hw/pci/msix.h"
-#include "hw/pci/pci.h"
-#include "qemu-common.h"
+#include "hw/pci/pci_bridge.h"
#include "qemu/error-report.h"
-#include "qemu/event_notifier.h"
-#include "qemu/queue.h"
#include "qemu/range.h"
#include "sysemu/kvm.h"
#include "sysemu/sysemu.h"
+#include "pci.h"
#include "trace.h"
-#include "hw/vfio/vfio.h"
-#include "hw/vfio/vfio-common.h"
-
-struct VFIOPCIDevice;
-
-typedef struct VFIOQuirk {
- MemoryRegion mem;
- struct VFIOPCIDevice *vdev;
- QLIST_ENTRY(VFIOQuirk) next;
- struct {
- uint32_t base_offset:TARGET_PAGE_BITS;
- uint32_t address_offset:TARGET_PAGE_BITS;
- uint32_t address_size:3;
- uint32_t bar:3;
-
- uint32_t address_match;
- uint32_t address_mask;
-
- uint32_t address_val:TARGET_PAGE_BITS;
- uint32_t data_offset:TARGET_PAGE_BITS;
- uint32_t data_size:3;
-
- uint8_t flags;
- uint8_t read_flags;
- uint8_t write_flags;
- } data;
-} VFIOQuirk;
-
-typedef struct VFIOBAR {
- VFIORegion region;
- bool ioport;
- bool mem64;
- QLIST_HEAD(, VFIOQuirk) quirks;
-} VFIOBAR;
-
-typedef struct VFIOVGARegion {
- MemoryRegion mem;
- off_t offset;
- int nr;
- QLIST_HEAD(, VFIOQuirk) quirks;
-} VFIOVGARegion;
-
-typedef struct VFIOVGA {
- off_t fd_offset;
- int fd;
- VFIOVGARegion region[QEMU_PCI_VGA_NUM_REGIONS];
-} VFIOVGA;
-
-typedef struct VFIOINTx {
- bool pending; /* interrupt pending */
- bool kvm_accel; /* set when QEMU bypass through KVM enabled */
- uint8_t pin; /* which pin to pull for qemu_set_irq */
- EventNotifier interrupt; /* eventfd triggered on interrupt */
- EventNotifier unmask; /* eventfd for unmask on QEMU bypass */
- PCIINTxRoute route; /* routing info for QEMU bypass */
- uint32_t mmap_timeout; /* delay to re-enable mmaps after interrupt */
- QEMUTimer *mmap_timer; /* enable mmaps after periods w/o interrupts */
-} VFIOINTx;
-
-typedef struct VFIOMSIVector {
- /*
- * Two interrupt paths are configured per vector. The first, is only used
- * for interrupts injected via QEMU. This is typically the non-accel path,
- * but may also be used when we want QEMU to handle masking and pending
- * bits. The KVM path bypasses QEMU and is therefore higher performance,
- * but requires masking at the device. virq is used to track the MSI route
- * through KVM, thus kvm_interrupt is only available when virq is set to a
- * valid (>= 0) value.
- */
- EventNotifier interrupt;
- EventNotifier kvm_interrupt;
- struct VFIOPCIDevice *vdev; /* back pointer to device */
- int virq;
- bool use;
-} VFIOMSIVector;
-
-enum {
- VFIO_INT_NONE = 0,
- VFIO_INT_INTx = 1,
- VFIO_INT_MSI = 2,
- VFIO_INT_MSIX = 3,
-};
-
-/* Cache of MSI-X setup plus extra mmap and memory region for split BAR map */
-typedef struct VFIOMSIXInfo {
- uint8_t table_bar;
- uint8_t pba_bar;
- uint16_t entries;
- uint32_t table_offset;
- uint32_t pba_offset;
- MemoryRegion mmap_mem;
- void *mmap;
-} VFIOMSIXInfo;
-
-typedef struct VFIOPCIDevice {
- PCIDevice pdev;
- VFIODevice vbasedev;
- VFIOINTx intx;
- unsigned int config_size;
- uint8_t *emulated_config_bits; /* QEMU emulated bits, little-endian */
- off_t config_offset; /* Offset of config space region within device fd */
- unsigned int rom_size;
- off_t rom_offset; /* Offset of ROM region within device fd */
- void *rom;
- int msi_cap_size;
- VFIOMSIVector *msi_vectors;
- VFIOMSIXInfo *msix;
- int nr_vectors; /* Number of MSI/MSIX vectors currently in use */
- int interrupt; /* Current interrupt type */
- VFIOBAR bars[PCI_NUM_REGIONS - 1]; /* No ROM */
- VFIOVGA vga; /* 0xa0000, 0x3b0, 0x3c0 */
- 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)
-#define VFIO_FEATURE_ENABLE_REQ_BIT 1
-#define VFIO_FEATURE_ENABLE_REQ (1 << VFIO_FEATURE_ENABLE_REQ_BIT)
- int32_t bootindex;
- uint8_t pm_cap;
- bool has_vga;
- bool pci_aer;
- bool req_enabled;
- bool has_flr;
- bool has_pm_reset;
- bool rom_read_failed;
-} VFIOPCIDevice;
-
-typedef struct VFIORomBlacklistEntry {
- uint16_t vendor_id;
- uint16_t device_id;
-} VFIORomBlacklistEntry;
-
-/*
- * List of device ids/vendor ids for which to disable
- * option rom loading. This avoids the guest hangs during rom
- * execution as noticed with the BCM 57810 card for lack of a
- * more better way to handle such issues.
- * The user can still override by specifying a romfile or
- * rombar=1.
- * Please see https://bugs.launchpad.net/qemu/+bug/1284874
- * for an analysis of the 57810 card hang. When adding
- * a new vendor id/device id combination below, please also add
- * your card/environment details and information that could
- * help in debugging to the bug tracking this issue
- */
-static const VFIORomBlacklistEntry romblacklist[] = {
- /* Broadcom BCM 57810 */
- { 0x14e4, 0x168e }
-};
#define MSIX_CAP_LENGTH 12
static void vfio_disable_interrupts(VFIOPCIDevice *vdev);
-static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len);
-static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr,
- uint32_t val, int len);
static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled);
/*
@@ -248,7 +88,7 @@ static void vfio_intx_interrupt(void *opaque)
}
}
-static void vfio_eoi(VFIODevice *vbasedev)
+static void vfio_intx_eoi(VFIODevice *vbasedev)
{
VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev);
@@ -256,14 +96,14 @@ static void vfio_eoi(VFIODevice *vbasedev)
return;
}
- trace_vfio_eoi(vbasedev->name);
+ trace_vfio_intx_eoi(vbasedev->name);
vdev->intx.pending = false;
pci_irq_deassert(&vdev->pdev);
vfio_unmask_single_irqindex(vbasedev, VFIO_PCI_INTX_IRQ_INDEX);
}
-static void vfio_enable_intx_kvm(VFIOPCIDevice *vdev)
+static void vfio_intx_enable_kvm(VFIOPCIDevice *vdev)
{
#ifdef CONFIG_KVM
struct kvm_irqfd irqfd = {
@@ -275,7 +115,7 @@ static void vfio_enable_intx_kvm(VFIOPCIDevice *vdev)
int ret, argsz;
int32_t *pfd;
- if (!VFIO_ALLOW_KVM_INTX || !kvm_irqfds_enabled() ||
+ if (vdev->no_kvm_intx || !kvm_irqfds_enabled() ||
vdev->intx.route.mode != PCI_INTX_ENABLED ||
!kvm_resamplefds_enabled()) {
return;
@@ -325,7 +165,7 @@ static void vfio_enable_intx_kvm(VFIOPCIDevice *vdev)
vdev->intx.kvm_accel = true;
- trace_vfio_enable_intx_kvm(vdev->vbasedev.name);
+ trace_vfio_intx_enable_kvm(vdev->vbasedev.name);
return;
@@ -340,7 +180,7 @@ fail:
#endif
}
-static void vfio_disable_intx_kvm(VFIOPCIDevice *vdev)
+static void vfio_intx_disable_kvm(VFIOPCIDevice *vdev)
{
#ifdef CONFIG_KVM
struct kvm_irqfd irqfd = {
@@ -377,11 +217,11 @@ static void vfio_disable_intx_kvm(VFIOPCIDevice *vdev)
/* If we've missed an event, let it re-fire through QEMU */
vfio_unmask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX);
- trace_vfio_disable_intx_kvm(vdev->vbasedev.name);
+ trace_vfio_intx_disable_kvm(vdev->vbasedev.name);
#endif
}
-static void vfio_update_irq(PCIDevice *pdev)
+static void vfio_intx_update(PCIDevice *pdev)
{
VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev);
PCIINTxRoute route;
@@ -396,10 +236,10 @@ static void vfio_update_irq(PCIDevice *pdev)
return; /* Nothing changed */
}
- trace_vfio_update_irq(vdev->vbasedev.name,
- vdev->intx.route.irq, route.irq);
+ trace_vfio_intx_update(vdev->vbasedev.name,
+ vdev->intx.route.irq, route.irq);
- vfio_disable_intx_kvm(vdev);
+ vfio_intx_disable_kvm(vdev);
vdev->intx.route = route;
@@ -407,13 +247,13 @@ static void vfio_update_irq(PCIDevice *pdev)
return;
}
- vfio_enable_intx_kvm(vdev);
+ vfio_intx_enable_kvm(vdev);
/* Re-enable the interrupt in cased we missed an EOI */
- vfio_eoi(&vdev->vbasedev);
+ vfio_intx_eoi(&vdev->vbasedev);
}
-static int vfio_enable_intx(VFIOPCIDevice *vdev)
+static int vfio_intx_enable(VFIOPCIDevice *vdev)
{
uint8_t pin = vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1);
int ret, argsz;
@@ -468,21 +308,21 @@ static int vfio_enable_intx(VFIOPCIDevice *vdev)
return -errno;
}
- vfio_enable_intx_kvm(vdev);
+ vfio_intx_enable_kvm(vdev);
vdev->interrupt = VFIO_INT_INTx;
- trace_vfio_enable_intx(vdev->vbasedev.name);
+ trace_vfio_intx_enable(vdev->vbasedev.name);
return 0;
}
-static void vfio_disable_intx(VFIOPCIDevice *vdev)
+static void vfio_intx_disable(VFIOPCIDevice *vdev)
{
int fd;
timer_del(vdev->intx.mmap_timer);
- vfio_disable_intx_kvm(vdev);
+ vfio_intx_disable_kvm(vdev);
vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX);
vdev->intx.pending = false;
pci_irq_deassert(&vdev->pdev);
@@ -494,7 +334,7 @@ static void vfio_disable_intx(VFIOPCIDevice *vdev)
vdev->interrupt = VFIO_INT_NONE;
- trace_vfio_disable_intx(vdev->vbasedev.name);
+ trace_vfio_intx_disable(vdev->vbasedev.name);
}
/*
@@ -504,33 +344,28 @@ static void vfio_msi_interrupt(void *opaque)
{
VFIOMSIVector *vector = opaque;
VFIOPCIDevice *vdev = vector->vdev;
+ MSIMessage (*get_msg)(PCIDevice *dev, unsigned vector);
+ void (*notify)(PCIDevice *dev, unsigned vector);
+ MSIMessage msg;
int nr = vector - vdev->msi_vectors;
if (!event_notifier_test_and_clear(&vector->interrupt)) {
return;
}
-#ifdef DEBUG_VFIO
- MSIMessage msg;
-
if (vdev->interrupt == VFIO_INT_MSIX) {
- msg = msix_get_message(&vdev->pdev, nr);
+ get_msg = msix_get_message;
+ notify = msix_notify;
} else if (vdev->interrupt == VFIO_INT_MSI) {
- msg = msi_get_message(&vdev->pdev, nr);
+ get_msg = msi_get_message;
+ notify = msi_notify;
} else {
abort();
}
+ msg = get_msg(&vdev->pdev, nr);
trace_vfio_msi_interrupt(vdev->vbasedev.name, nr, msg.address, msg.data);
-#endif
-
- if (vdev->interrupt == VFIO_INT_MSIX) {
- msix_notify(&vdev->pdev, nr);
- } else if (vdev->interrupt == VFIO_INT_MSI) {
- msi_notify(&vdev->pdev, nr);
- } else {
- error_report("vfio: MSI interrupt receieved, but not enabled?");
- }
+ notify(&vdev->pdev, nr);
}
static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix)
@@ -577,13 +412,12 @@ static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix)
return ret;
}
-static void vfio_add_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage *msg,
- bool msix)
+static void vfio_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector,
+ MSIMessage *msg, bool msix)
{
int virq;
- if ((msix && !VFIO_ALLOW_KVM_MSIX) ||
- (!msix && !VFIO_ALLOW_KVM_MSI) || !msg) {
+ if ((msix && vdev->no_kvm_msix) || (!msix && vdev->no_kvm_msi) || !msg) {
return;
}
@@ -591,7 +425,7 @@ static void vfio_add_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage *msg,
return;
}
- virq = kvm_irqchip_add_msi_route(kvm_state, *msg);
+ virq = kvm_irqchip_add_msi_route(kvm_state, *msg, &vdev->pdev);
if (virq < 0) {
event_notifier_cleanup(&vector->kvm_interrupt);
return;
@@ -616,9 +450,10 @@ static void vfio_remove_kvm_msi_virq(VFIOMSIVector *vector)
event_notifier_cleanup(&vector->kvm_interrupt);
}
-static void vfio_update_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage msg)
+static void vfio_update_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage msg,
+ PCIDevice *pdev)
{
- kvm_irqchip_update_msi_route(kvm_state, vector->virq, msg);
+ kvm_irqchip_update_msi_route(kvm_state, vector->virq, msg, pdev);
}
static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr,
@@ -653,10 +488,10 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr,
if (!msg) {
vfio_remove_kvm_msi_virq(vector);
} else {
- vfio_update_kvm_msi_virq(vector, *msg);
+ vfio_update_kvm_msi_virq(vector, *msg, pdev);
}
} else {
- vfio_add_kvm_msi_virq(vector, msg, true);
+ vfio_add_kvm_msi_virq(vdev, vector, msg, true);
}
/*
@@ -748,11 +583,11 @@ static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr)
}
}
-static void vfio_enable_msix(VFIOPCIDevice *vdev)
+static void vfio_msix_enable(VFIOPCIDevice *vdev)
{
vfio_disable_interrupts(vdev);
- vdev->msi_vectors = g_malloc0(vdev->msix->entries * sizeof(VFIOMSIVector));
+ vdev->msi_vectors = g_new0(VFIOMSIVector, vdev->msix->entries);
vdev->interrupt = VFIO_INT_MSIX;
@@ -777,10 +612,10 @@ static void vfio_enable_msix(VFIOPCIDevice *vdev)
error_report("vfio: msix_set_vector_notifiers failed");
}
- trace_vfio_enable_msix(vdev->vbasedev.name);
+ trace_vfio_msix_enable(vdev->vbasedev.name);
}
-static void vfio_enable_msi(VFIOPCIDevice *vdev)
+static void vfio_msi_enable(VFIOPCIDevice *vdev)
{
int ret, i;
@@ -788,7 +623,7 @@ static void vfio_enable_msi(VFIOPCIDevice *vdev)
vdev->nr_vectors = msi_nr_vectors_allocated(&vdev->pdev);
retry:
- vdev->msi_vectors = g_malloc0(vdev->nr_vectors * sizeof(VFIOMSIVector));
+ vdev->msi_vectors = g_new0(VFIOMSIVector, vdev->nr_vectors);
for (i = 0; i < vdev->nr_vectors; i++) {
VFIOMSIVector *vector = &vdev->msi_vectors[i];
@@ -809,7 +644,7 @@ retry:
* Attempt to enable route through KVM irqchip,
* default to userspace handling if unavailable.
*/
- vfio_add_kvm_msi_virq(vector, &msg, false);
+ vfio_add_kvm_msi_virq(vdev, vector, &msg, false);
}
/* Set interrupt type prior to possible interrupts */
@@ -853,10 +688,10 @@ retry:
return;
}
- trace_vfio_enable_msi(vdev->vbasedev.name, vdev->nr_vectors);
+ trace_vfio_msi_enable(vdev->vbasedev.name, vdev->nr_vectors);
}
-static void vfio_disable_msi_common(VFIOPCIDevice *vdev)
+static void vfio_msi_disable_common(VFIOPCIDevice *vdev)
{
int i;
@@ -877,10 +712,10 @@ static void vfio_disable_msi_common(VFIOPCIDevice *vdev)
vdev->nr_vectors = 0;
vdev->interrupt = VFIO_INT_NONE;
- vfio_enable_intx(vdev);
+ vfio_intx_enable(vdev);
}
-static void vfio_disable_msix(VFIOPCIDevice *vdev)
+static void vfio_msix_disable(VFIOPCIDevice *vdev)
{
int i;
@@ -901,17 +736,17 @@ static void vfio_disable_msix(VFIOPCIDevice *vdev)
vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX);
}
- vfio_disable_msi_common(vdev);
+ vfio_msi_disable_common(vdev);
- trace_vfio_disable_msix(vdev->vbasedev.name);
+ trace_vfio_msix_disable(vdev->vbasedev.name);
}
-static void vfio_disable_msi(VFIOPCIDevice *vdev)
+static void vfio_msi_disable(VFIOPCIDevice *vdev)
{
vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSI_IRQ_INDEX);
- vfio_disable_msi_common(vdev);
+ vfio_msi_disable_common(vdev);
- trace_vfio_disable_msi(vdev->vbasedev.name);
+ trace_vfio_msi_disable(vdev->vbasedev.name);
}
static void vfio_update_msi(VFIOPCIDevice *vdev)
@@ -927,7 +762,7 @@ static void vfio_update_msi(VFIOPCIDevice *vdev)
}
msg = msi_get_message(&vdev->pdev, i);
- vfio_update_kvm_msi_virq(vector, msg);
+ vfio_update_kvm_msi_virq(vector, msg, &vdev->pdev);
}
}
@@ -1034,26 +869,6 @@ static const MemoryRegionOps vfio_rom_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
-static bool vfio_blacklist_opt_rom(VFIOPCIDevice *vdev)
-{
- PCIDevice *pdev = &vdev->pdev;
- uint16_t vendor_id, device_id;
- int count = 0;
-
- vendor_id = pci_get_word(pdev->config + PCI_VENDOR_ID);
- device_id = pci_get_word(pdev->config + PCI_DEVICE_ID);
-
- while (count < ARRAY_SIZE(romblacklist)) {
- if (romblacklist[count].vendor_id == vendor_id &&
- romblacklist[count].device_id == device_id) {
- return true;
- }
- count++;
- }
-
- return false;
-}
-
static void vfio_pci_size_rom(VFIOPCIDevice *vdev)
{
uint32_t orig, size = cpu_to_le32((uint32_t)PCI_ROM_ADDRESS_MASK);
@@ -1131,7 +946,7 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev)
vdev->rom_read_failed = false;
}
-static void vfio_vga_write(void *opaque, hwaddr addr,
+void vfio_vga_write(void *opaque, hwaddr addr,
uint64_t data, unsigned size)
{
VFIOVGARegion *region = opaque;
@@ -1167,7 +982,7 @@ static void vfio_vga_write(void *opaque, hwaddr addr,
trace_vfio_vga_write(region->offset + addr, data, size);
}
-static uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size)
+uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size)
{
VFIOVGARegion *region = opaque;
VFIOVGA *vga = container_of(region, VFIOVGA, region[region->nr]);
@@ -1213,858 +1028,9 @@ static const MemoryRegionOps vfio_vga_ops = {
};
/*
- * Device specific quirks
- */
-
-/* Is range1 fully contained within range2? */
-static bool vfio_range_contained(uint64_t first1, uint64_t len1,
- uint64_t first2, uint64_t len2) {
- return (first1 >= first2 && first1 + len1 <= first2 + len2);
-}
-
-static bool vfio_flags_enabled(uint8_t flags, uint8_t mask)
-{
- return (mask && (flags & mask) == mask);
-}
-
-static uint64_t vfio_generic_window_quirk_read(void *opaque,
- hwaddr addr, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIOPCIDevice *vdev = quirk->vdev;
- uint64_t data;
-
- if (vfio_flags_enabled(quirk->data.flags, quirk->data.read_flags) &&
- ranges_overlap(addr, size,
- quirk->data.data_offset, quirk->data.data_size)) {
- hwaddr offset = addr - quirk->data.data_offset;
-
- if (!vfio_range_contained(addr, size, quirk->data.data_offset,
- quirk->data.data_size)) {
- hw_error("%s: window data read not fully contained: %s",
- __func__, memory_region_name(&quirk->mem));
- }
-
- data = vfio_pci_read_config(&vdev->pdev,
- quirk->data.address_val + offset, size);
-
- trace_vfio_generic_window_quirk_read(memory_region_name(&quirk->mem),
- vdev->vbasedev.name,
- quirk->data.bar,
- addr, size, data);
- } else {
- data = vfio_region_read(&vdev->bars[quirk->data.bar].region,
- addr + quirk->data.base_offset, size);
- }
-
- return data;
-}
-
-static void vfio_generic_window_quirk_write(void *opaque, hwaddr addr,
- uint64_t data, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIOPCIDevice *vdev = quirk->vdev;
-
- if (ranges_overlap(addr, size,
- quirk->data.address_offset, quirk->data.address_size)) {
-
- if (addr != quirk->data.address_offset) {
- hw_error("%s: offset write into address window: %s",
- __func__, memory_region_name(&quirk->mem));
- }
-
- if ((data & ~quirk->data.address_mask) == quirk->data.address_match) {
- quirk->data.flags |= quirk->data.write_flags |
- quirk->data.read_flags;
- quirk->data.address_val = data & quirk->data.address_mask;
- } else {
- quirk->data.flags &= ~(quirk->data.write_flags |
- quirk->data.read_flags);
- }
- }
-
- if (vfio_flags_enabled(quirk->data.flags, quirk->data.write_flags) &&
- ranges_overlap(addr, size,
- quirk->data.data_offset, quirk->data.data_size)) {
- hwaddr offset = addr - quirk->data.data_offset;
-
- if (!vfio_range_contained(addr, size, quirk->data.data_offset,
- quirk->data.data_size)) {
- hw_error("%s: window data write not fully contained: %s",
- __func__, memory_region_name(&quirk->mem));
- }
-
- vfio_pci_write_config(&vdev->pdev,
- quirk->data.address_val + offset, data, size);
- trace_vfio_generic_window_quirk_write(memory_region_name(&quirk->mem),
- vdev->vbasedev.name,
- quirk->data.bar,
- addr, data, size);
- return;
- }
-
- vfio_region_write(&vdev->bars[quirk->data.bar].region,
- addr + quirk->data.base_offset, data, size);
-}
-
-static const MemoryRegionOps vfio_generic_window_quirk = {
- .read = vfio_generic_window_quirk_read,
- .write = vfio_generic_window_quirk_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static uint64_t vfio_generic_quirk_read(void *opaque,
- hwaddr addr, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIOPCIDevice *vdev = quirk->vdev;
- hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK;
- hwaddr offset = quirk->data.address_match & ~TARGET_PAGE_MASK;
- uint64_t data;
-
- if (vfio_flags_enabled(quirk->data.flags, quirk->data.read_flags) &&
- ranges_overlap(addr, size, offset, quirk->data.address_mask + 1)) {
- if (!vfio_range_contained(addr, size, offset,
- quirk->data.address_mask + 1)) {
- hw_error("%s: read not fully contained: %s",
- __func__, memory_region_name(&quirk->mem));
- }
-
- data = vfio_pci_read_config(&vdev->pdev, addr - offset, size);
-
- trace_vfio_generic_quirk_read(memory_region_name(&quirk->mem),
- vdev->vbasedev.name, quirk->data.bar,
- addr + base, size, data);
- } else {
- data = vfio_region_read(&vdev->bars[quirk->data.bar].region,
- addr + base, size);
- }
-
- return data;
-}
-
-static void vfio_generic_quirk_write(void *opaque, hwaddr addr,
- uint64_t data, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIOPCIDevice *vdev = quirk->vdev;
- hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK;
- hwaddr offset = quirk->data.address_match & ~TARGET_PAGE_MASK;
-
- if (vfio_flags_enabled(quirk->data.flags, quirk->data.write_flags) &&
- ranges_overlap(addr, size, offset, quirk->data.address_mask + 1)) {
- if (!vfio_range_contained(addr, size, offset,
- quirk->data.address_mask + 1)) {
- hw_error("%s: write not fully contained: %s",
- __func__, memory_region_name(&quirk->mem));
- }
-
- vfio_pci_write_config(&vdev->pdev, addr - offset, data, size);
-
- trace_vfio_generic_quirk_write(memory_region_name(&quirk->mem),
- vdev->vbasedev.name, quirk->data.bar,
- addr + base, data, size);
- } else {
- vfio_region_write(&vdev->bars[quirk->data.bar].region,
- addr + base, data, size);
- }
-}
-
-static const MemoryRegionOps vfio_generic_quirk = {
- .read = vfio_generic_quirk_read,
- .write = vfio_generic_quirk_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-#define PCI_VENDOR_ID_ATI 0x1002
-
-/*
- * Radeon HD cards (HD5450 & HD7850) report the upper byte of the I/O port BAR
- * through VGA register 0x3c3. On newer cards, the I/O port BAR is always
- * BAR4 (older cards like the X550 used BAR1, but we don't care to support
- * those). Note that on bare metal, a read of 0x3c3 doesn't always return the
- * I/O port BAR address. Originally this was coded to return the virtual BAR
- * address only if the physical register read returns the actual BAR address,
- * but users have reported greater success if we return the virtual address
- * unconditionally.
- */
-static uint64_t vfio_ati_3c3_quirk_read(void *opaque,
- hwaddr addr, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIOPCIDevice *vdev = quirk->vdev;
- uint64_t data = vfio_pci_read_config(&vdev->pdev,
- PCI_BASE_ADDRESS_0 + (4 * 4) + 1,
- size);
- trace_vfio_ati_3c3_quirk_read(data);
-
- return data;
-}
-
-static const MemoryRegionOps vfio_ati_3c3_quirk = {
- .read = vfio_ati_3c3_quirk_read,
- .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static void vfio_vga_probe_ati_3c3_quirk(VFIOPCIDevice *vdev)
-{
- PCIDevice *pdev = &vdev->pdev;
- VFIOQuirk *quirk;
-
- if (pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI) {
- return;
- }
-
- /*
- * As long as the BAR is >= 256 bytes it will be aligned such that the
- * lower byte is always zero. Filter out anything else, if it exists.
- */
- if (!vdev->bars[4].ioport || vdev->bars[4].region.size < 256) {
- return;
- }
-
- quirk = g_malloc0(sizeof(*quirk));
- quirk->vdev = vdev;
-
- memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_ati_3c3_quirk, quirk,
- "vfio-ati-3c3-quirk", 1);
- memory_region_add_subregion(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem,
- 3 /* offset 3 bytes from 0x3c0 */, &quirk->mem);
-
- QLIST_INSERT_HEAD(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks,
- quirk, next);
-
- trace_vfio_vga_probe_ati_3c3_quirk(vdev->vbasedev.name);
-}
-
-/*
- * Newer ATI/AMD devices, including HD5450 and HD7850, have a window to PCI
- * config space through MMIO BAR2 at offset 0x4000. Nothing seems to access
- * the MMIO space directly, but a window to this space is provided through
- * I/O port BAR4. Offset 0x0 is the address register and offset 0x4 is the
- * data register. When the address is programmed to a range of 0x4000-0x4fff
- * PCI configuration space is available. Experimentation seems to indicate
- * that only read-only access is provided, but we drop writes when the window
- * is enabled to config space nonetheless.
- */
-static void vfio_probe_ati_bar4_window_quirk(VFIOPCIDevice *vdev, int nr)
-{
- PCIDevice *pdev = &vdev->pdev;
- VFIOQuirk *quirk;
-
- if (!vdev->has_vga || nr != 4 ||
- pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI) {
- return;
- }
-
- quirk = g_malloc0(sizeof(*quirk));
- quirk->vdev = vdev;
- quirk->data.address_size = 4;
- quirk->data.data_offset = 4;
- quirk->data.data_size = 4;
- quirk->data.address_match = 0x4000;
- quirk->data.address_mask = PCIE_CONFIG_SPACE_SIZE - 1;
- quirk->data.bar = nr;
- quirk->data.read_flags = quirk->data.write_flags = 1;
-
- memory_region_init_io(&quirk->mem, OBJECT(vdev),
- &vfio_generic_window_quirk, quirk,
- "vfio-ati-bar4-window-quirk", 8);
- memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
- quirk->data.base_offset, &quirk->mem, 1);
-
- QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
-
- trace_vfio_probe_ati_bar4_window_quirk(vdev->vbasedev.name);
-}
-
-#define PCI_VENDOR_ID_REALTEK 0x10ec
-
-/*
- * RTL8168 devices have a backdoor that can access the MSI-X table. At BAR2
- * offset 0x70 there is a dword data register, offset 0x74 is a dword address
- * register. According to the Linux r8169 driver, the MSI-X table is addressed
- * when the "type" portion of the address register is set to 0x1. This appears
- * to be bits 16:30. Bit 31 is both a write indicator and some sort of
- * "address latched" indicator. Bits 12:15 are a mask field, which we can
- * ignore because the MSI-X table should always be accessed as a dword (full
- * mask). Bits 0:11 is offset within the type.
- *
- * Example trace:
- *
- * Read from MSI-X table offset 0
- * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x74, 0x1f000, 4) // store read addr
- * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x74, 4) = 0x8001f000 // latch
- * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x70, 4) = 0xfee00398 // read data
- *
- * Write 0xfee00000 to MSI-X table offset 0
- * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x70, 0xfee00000, 4) // write data
- * vfio: vfio_bar_write(0000:05:00.0:BAR2+0x74, 0x8001f000, 4) // do write
- * vfio: vfio_bar_read(0000:05:00.0:BAR2+0x74, 4) = 0x1f000 // complete
- */
-
-static uint64_t vfio_rtl8168_window_quirk_read(void *opaque,
- hwaddr addr, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIOPCIDevice *vdev = quirk->vdev;
-
- switch (addr) {
- case 4: /* address */
- if (quirk->data.flags) {
- trace_vfio_rtl8168_window_quirk_read_fake(
- memory_region_name(&quirk->mem),
- vdev->vbasedev.name);
-
- return quirk->data.address_match ^ 0x80000000U;
- }
- break;
- case 0: /* data */
- if (quirk->data.flags) {
- uint64_t val;
-
- trace_vfio_rtl8168_window_quirk_read_table(
- memory_region_name(&quirk->mem),
- vdev->vbasedev.name);
-
- if (!(vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX)) {
- return 0;
- }
-
- memory_region_dispatch_read(&vdev->pdev.msix_table_mmio,
- (hwaddr)(quirk->data.address_match
- & 0xfff),
- &val,
- size,
- MEMTXATTRS_UNSPECIFIED);
- return val;
- }
- }
-
- trace_vfio_rtl8168_window_quirk_read_direct(memory_region_name(&quirk->mem),
- vdev->vbasedev.name);
-
- return vfio_region_read(&vdev->bars[quirk->data.bar].region,
- addr + 0x70, size);
-}
-
-static void vfio_rtl8168_window_quirk_write(void *opaque, hwaddr addr,
- uint64_t data, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIOPCIDevice *vdev = quirk->vdev;
-
- switch (addr) {
- case 4: /* address */
- if ((data & 0x7fff0000) == 0x10000) {
- if (data & 0x80000000U &&
- vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX) {
-
- trace_vfio_rtl8168_window_quirk_write_table(
- memory_region_name(&quirk->mem),
- vdev->vbasedev.name);
-
- 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;
- quirk->data.address_match = data;
-
- return;
- }
- quirk->data.flags = 0;
- break;
- case 0: /* data */
- quirk->data.address_mask = data;
- break;
- }
-
- trace_vfio_rtl8168_window_quirk_write_direct(
- memory_region_name(&quirk->mem),
- vdev->vbasedev.name);
-
- vfio_region_write(&vdev->bars[quirk->data.bar].region,
- addr + 0x70, data, size);
-}
-
-static const MemoryRegionOps vfio_rtl8168_window_quirk = {
- .read = vfio_rtl8168_window_quirk_read,
- .write = vfio_rtl8168_window_quirk_write,
- .valid = {
- .min_access_size = 4,
- .max_access_size = 4,
- .unaligned = false,
- },
- .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static void vfio_probe_rtl8168_bar2_window_quirk(VFIOPCIDevice *vdev, int nr)
-{
- PCIDevice *pdev = &vdev->pdev;
- VFIOQuirk *quirk;
-
- if (pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_REALTEK ||
- pci_get_word(pdev->config + PCI_DEVICE_ID) != 0x8168 || nr != 2) {
- return;
- }
-
- quirk = g_malloc0(sizeof(*quirk));
- quirk->vdev = vdev;
- quirk->data.bar = nr;
-
- memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_rtl8168_window_quirk,
- quirk, "vfio-rtl8168-window-quirk", 8);
- memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
- 0x70, &quirk->mem, 1);
-
- QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
-
- trace_vfio_probe_rtl8168_bar2_window_quirk(vdev->vbasedev.name);
-}
-/*
- * Trap the BAR2 MMIO window to config space as well.
- */
-static void vfio_probe_ati_bar2_4000_quirk(VFIOPCIDevice *vdev, int nr)
-{
- PCIDevice *pdev = &vdev->pdev;
- VFIOQuirk *quirk;
-
- /* Only enable on newer devices where BAR2 is 64bit */
- if (!vdev->has_vga || nr != 2 || !vdev->bars[2].mem64 ||
- pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_ATI) {
- return;
- }
-
- quirk = g_malloc0(sizeof(*quirk));
- quirk->vdev = vdev;
- quirk->data.flags = quirk->data.read_flags = quirk->data.write_flags = 1;
- quirk->data.address_match = 0x4000;
- quirk->data.address_mask = PCIE_CONFIG_SPACE_SIZE - 1;
- quirk->data.bar = nr;
-
- memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_generic_quirk, quirk,
- "vfio-ati-bar2-4000-quirk",
- TARGET_PAGE_ALIGN(quirk->data.address_mask + 1));
- memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
- quirk->data.address_match & TARGET_PAGE_MASK,
- &quirk->mem, 1);
-
- QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
-
- trace_vfio_probe_ati_bar2_4000_quirk(vdev->vbasedev.name);
-}
-
-/*
- * Older ATI/AMD cards like the X550 have a similar window to that above.
- * I/O port BAR1 provides a window to a mirror of PCI config space located
- * in BAR2 at offset 0xf00. We don't care to support such older cards, but
- * note it for future reference.
- */
-
-#define PCI_VENDOR_ID_NVIDIA 0x10de
-
-/*
- * Nvidia has several different methods to get to config space, the
- * nouveu project has several of these documented here:
- * https://github.com/pathscale/envytools/tree/master/hwdocs
- *
- * The first quirk is actually not documented in envytools and is found
- * on 10de:01d1 (NVIDIA Corporation G72 [GeForce 7300 LE]). This is an
- * NV46 chipset. The backdoor uses the legacy VGA I/O ports to access
- * the mirror of PCI config space found at BAR0 offset 0x1800. The access
- * sequence first writes 0x338 to I/O port 0x3d4. The target offset is
- * then written to 0x3d0. Finally 0x538 is written for a read and 0x738
- * is written for a write to 0x3d4. The BAR0 offset is then accessible
- * through 0x3d0. This quirk doesn't seem to be necessary on newer cards
- * that use the I/O port BAR5 window but it doesn't hurt to leave it.
- */
-enum {
- NV_3D0_NONE = 0,
- NV_3D0_SELECT,
- NV_3D0_WINDOW,
- NV_3D0_READ,
- NV_3D0_WRITE,
-};
-
-static uint64_t vfio_nvidia_3d0_quirk_read(void *opaque,
- hwaddr addr, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIOPCIDevice *vdev = quirk->vdev;
- PCIDevice *pdev = &vdev->pdev;
- uint64_t data = vfio_vga_read(&vdev->vga.region[QEMU_PCI_VGA_IO_HI],
- addr + quirk->data.base_offset, size);
-
- if (quirk->data.flags == NV_3D0_READ && addr == quirk->data.data_offset) {
- data = vfio_pci_read_config(pdev, quirk->data.address_val, size);
- trace_vfio_nvidia_3d0_quirk_read(size, data);
- }
-
- quirk->data.flags = NV_3D0_NONE;
-
- return data;
-}
-
-static void vfio_nvidia_3d0_quirk_write(void *opaque, hwaddr addr,
- uint64_t data, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIOPCIDevice *vdev = quirk->vdev;
- PCIDevice *pdev = &vdev->pdev;
-
- switch (quirk->data.flags) {
- case NV_3D0_NONE:
- if (addr == quirk->data.address_offset && data == 0x338) {
- quirk->data.flags = NV_3D0_SELECT;
- }
- break;
- case NV_3D0_SELECT:
- quirk->data.flags = NV_3D0_NONE;
- if (addr == quirk->data.data_offset &&
- (data & ~quirk->data.address_mask) == quirk->data.address_match) {
- quirk->data.flags = NV_3D0_WINDOW;
- quirk->data.address_val = data & quirk->data.address_mask;
- }
- break;
- case NV_3D0_WINDOW:
- quirk->data.flags = NV_3D0_NONE;
- if (addr == quirk->data.address_offset) {
- if (data == 0x538) {
- quirk->data.flags = NV_3D0_READ;
- } else if (data == 0x738) {
- quirk->data.flags = NV_3D0_WRITE;
- }
- }
- break;
- case NV_3D0_WRITE:
- quirk->data.flags = NV_3D0_NONE;
- if (addr == quirk->data.data_offset) {
- vfio_pci_write_config(pdev, quirk->data.address_val, data, size);
- trace_vfio_nvidia_3d0_quirk_write(data, size);
- return;
- }
- break;
- }
-
- vfio_vga_write(&vdev->vga.region[QEMU_PCI_VGA_IO_HI],
- addr + quirk->data.base_offset, data, size);
-}
-
-static const MemoryRegionOps vfio_nvidia_3d0_quirk = {
- .read = vfio_nvidia_3d0_quirk_read,
- .write = vfio_nvidia_3d0_quirk_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static void vfio_vga_probe_nvidia_3d0_quirk(VFIOPCIDevice *vdev)
-{
- PCIDevice *pdev = &vdev->pdev;
- VFIOQuirk *quirk;
-
- if (pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_NVIDIA ||
- !vdev->bars[1].region.size) {
- return;
- }
-
- quirk = g_malloc0(sizeof(*quirk));
- quirk->vdev = vdev;
- quirk->data.base_offset = 0x10;
- quirk->data.address_offset = 4;
- quirk->data.address_size = 2;
- quirk->data.address_match = 0x1800;
- quirk->data.address_mask = PCI_CONFIG_SPACE_SIZE - 1;
- quirk->data.data_offset = 0;
- quirk->data.data_size = 4;
-
- memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_nvidia_3d0_quirk,
- quirk, "vfio-nvidia-3d0-quirk", 6);
- memory_region_add_subregion(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].mem,
- quirk->data.base_offset, &quirk->mem);
-
- QLIST_INSERT_HEAD(&vdev->vga.region[QEMU_PCI_VGA_IO_HI].quirks,
- quirk, next);
-
- trace_vfio_vga_probe_nvidia_3d0_quirk(vdev->vbasedev.name);
-}
-
-/*
- * The second quirk is documented in envytools. The I/O port BAR5 is just
- * a set of address/data ports to the MMIO BARs. The BAR we care about is
- * again BAR0. This backdoor is apparently a bit newer than the one above
- * so we need to not only trap 256 bytes @0x1800, but all of PCI config
- * space, including extended space is available at the 4k @0x88000.
- */
-enum {
- NV_BAR5_ADDRESS = 0x1,
- NV_BAR5_ENABLE = 0x2,
- NV_BAR5_MASTER = 0x4,
- NV_BAR5_VALID = 0x7,
-};
-
-static void vfio_nvidia_bar5_window_quirk_write(void *opaque, hwaddr addr,
- uint64_t data, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
-
- switch (addr) {
- case 0x0:
- if (data & 0x1) {
- quirk->data.flags |= NV_BAR5_MASTER;
- } else {
- quirk->data.flags &= ~NV_BAR5_MASTER;
- }
- break;
- case 0x4:
- if (data & 0x1) {
- quirk->data.flags |= NV_BAR5_ENABLE;
- } else {
- quirk->data.flags &= ~NV_BAR5_ENABLE;
- }
- break;
- case 0x8:
- if (quirk->data.flags & NV_BAR5_MASTER) {
- if ((data & ~0xfff) == 0x88000) {
- quirk->data.flags |= NV_BAR5_ADDRESS;
- quirk->data.address_val = data & 0xfff;
- } else if ((data & ~0xff) == 0x1800) {
- quirk->data.flags |= NV_BAR5_ADDRESS;
- quirk->data.address_val = data & 0xff;
- } else {
- quirk->data.flags &= ~NV_BAR5_ADDRESS;
- }
- }
- break;
- }
-
- vfio_generic_window_quirk_write(opaque, addr, data, size);
-}
-
-static const MemoryRegionOps vfio_nvidia_bar5_window_quirk = {
- .read = vfio_generic_window_quirk_read,
- .write = vfio_nvidia_bar5_window_quirk_write,
- .valid.min_access_size = 4,
- .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static void vfio_probe_nvidia_bar5_window_quirk(VFIOPCIDevice *vdev, int nr)
-{
- PCIDevice *pdev = &vdev->pdev;
- VFIOQuirk *quirk;
-
- if (!vdev->has_vga || nr != 5 ||
- pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_NVIDIA) {
- return;
- }
-
- quirk = g_malloc0(sizeof(*quirk));
- quirk->vdev = vdev;
- quirk->data.read_flags = quirk->data.write_flags = NV_BAR5_VALID;
- quirk->data.address_offset = 0x8;
- quirk->data.address_size = 0; /* actually 4, but avoids generic code */
- quirk->data.data_offset = 0xc;
- quirk->data.data_size = 4;
- quirk->data.bar = nr;
-
- memory_region_init_io(&quirk->mem, OBJECT(vdev),
- &vfio_nvidia_bar5_window_quirk, quirk,
- "vfio-nvidia-bar5-window-quirk", 16);
- memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
- 0, &quirk->mem, 1);
-
- QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
-
- trace_vfio_probe_nvidia_bar5_window_quirk(vdev->vbasedev.name);
-}
-
-static void vfio_nvidia_88000_quirk_write(void *opaque, hwaddr addr,
- uint64_t data, unsigned size)
-{
- VFIOQuirk *quirk = opaque;
- VFIOPCIDevice *vdev = quirk->vdev;
- PCIDevice *pdev = &vdev->pdev;
- hwaddr base = quirk->data.address_match & TARGET_PAGE_MASK;
-
- vfio_generic_quirk_write(opaque, addr, data, size);
-
- /*
- * Nvidia seems to acknowledge MSI interrupts by writing 0xff to the
- * MSI capability ID register. Both the ID and next register are
- * read-only, so we allow writes covering either of those to real hw.
- * NB - only fixed for the 0x88000 MMIO window.
- */
- if ((pdev->cap_present & QEMU_PCI_CAP_MSI) &&
- vfio_range_contained(addr, size, pdev->msi_cap, PCI_MSI_FLAGS)) {
- vfio_region_write(&vdev->bars[quirk->data.bar].region,
- addr + base, data, size);
- }
-}
-
-static const MemoryRegionOps vfio_nvidia_88000_quirk = {
- .read = vfio_generic_quirk_read,
- .write = vfio_nvidia_88000_quirk_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-/*
- * Finally, BAR0 itself. We want to redirect any accesses to either
- * 0x1800 or 0x88000 through the PCI config space access functions.
- *
- * NB - quirk at a page granularity or else they don't seem to work when
- * BARs are mmap'd
- *
- * Here's offset 0x88000...
- */
-static void vfio_probe_nvidia_bar0_88000_quirk(VFIOPCIDevice *vdev, int nr)
-{
- PCIDevice *pdev = &vdev->pdev;
- VFIOQuirk *quirk;
- uint16_t vendor, class;
-
- vendor = pci_get_word(pdev->config + PCI_VENDOR_ID);
- class = pci_get_word(pdev->config + PCI_CLASS_DEVICE);
-
- if (nr != 0 || vendor != PCI_VENDOR_ID_NVIDIA ||
- class != PCI_CLASS_DISPLAY_VGA) {
- return;
- }
-
- quirk = g_malloc0(sizeof(*quirk));
- quirk->vdev = vdev;
- quirk->data.flags = quirk->data.read_flags = quirk->data.write_flags = 1;
- quirk->data.address_match = 0x88000;
- quirk->data.address_mask = PCIE_CONFIG_SPACE_SIZE - 1;
- quirk->data.bar = nr;
-
- memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_nvidia_88000_quirk,
- quirk, "vfio-nvidia-bar0-88000-quirk",
- TARGET_PAGE_ALIGN(quirk->data.address_mask + 1));
- memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
- quirk->data.address_match & TARGET_PAGE_MASK,
- &quirk->mem, 1);
-
- QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
-
- trace_vfio_probe_nvidia_bar0_88000_quirk(vdev->vbasedev.name);
-}
-
-/*
- * And here's the same for BAR0 offset 0x1800...
- */
-static void vfio_probe_nvidia_bar0_1800_quirk(VFIOPCIDevice *vdev, int nr)
-{
- PCIDevice *pdev = &vdev->pdev;
- VFIOQuirk *quirk;
-
- if (!vdev->has_vga || nr != 0 ||
- pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_NVIDIA) {
- return;
- }
-
- /* Log the chipset ID */
- trace_vfio_probe_nvidia_bar0_1800_quirk_id(
- (unsigned int)(vfio_region_read(&vdev->bars[0].region, 0, 4) >> 20)
- & 0xff);
-
- quirk = g_malloc0(sizeof(*quirk));
- quirk->vdev = vdev;
- quirk->data.flags = quirk->data.read_flags = quirk->data.write_flags = 1;
- quirk->data.address_match = 0x1800;
- quirk->data.address_mask = PCI_CONFIG_SPACE_SIZE - 1;
- quirk->data.bar = nr;
-
- memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_generic_quirk, quirk,
- "vfio-nvidia-bar0-1800-quirk",
- TARGET_PAGE_ALIGN(quirk->data.address_mask + 1));
- memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
- quirk->data.address_match & TARGET_PAGE_MASK,
- &quirk->mem, 1);
-
- QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
-
- trace_vfio_probe_nvidia_bar0_1800_quirk(vdev->vbasedev.name);
-}
-
-/*
- * TODO - Some Nvidia devices provide config access to their companion HDA
- * device and even to their parent bridge via these config space mirrors.
- * Add quirks for those regions.
- */
-
-/*
- * Common quirk probe entry points.
- */
-static void vfio_vga_quirk_setup(VFIOPCIDevice *vdev)
-{
- vfio_vga_probe_ati_3c3_quirk(vdev);
- vfio_vga_probe_nvidia_3d0_quirk(vdev);
-}
-
-static void vfio_vga_quirk_teardown(VFIOPCIDevice *vdev)
-{
- VFIOQuirk *quirk;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(vdev->vga.region); i++) {
- QLIST_FOREACH(quirk, &vdev->vga.region[i].quirks, next) {
- memory_region_del_subregion(&vdev->vga.region[i].mem, &quirk->mem);
- }
- }
-}
-
-static void vfio_vga_quirk_free(VFIOPCIDevice *vdev)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(vdev->vga.region); i++) {
- while (!QLIST_EMPTY(&vdev->vga.region[i].quirks)) {
- VFIOQuirk *quirk = QLIST_FIRST(&vdev->vga.region[i].quirks);
- object_unparent(OBJECT(&quirk->mem));
- QLIST_REMOVE(quirk, next);
- g_free(quirk);
- }
- }
-}
-
-static void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr)
-{
- vfio_probe_ati_bar4_window_quirk(vdev, nr);
- vfio_probe_ati_bar2_4000_quirk(vdev, nr);
- vfio_probe_nvidia_bar5_window_quirk(vdev, nr);
- vfio_probe_nvidia_bar0_88000_quirk(vdev, nr);
- vfio_probe_nvidia_bar0_1800_quirk(vdev, nr);
- vfio_probe_rtl8168_bar2_window_quirk(vdev, nr);
-}
-
-static void vfio_bar_quirk_teardown(VFIOPCIDevice *vdev, int nr)
-{
- VFIOBAR *bar = &vdev->bars[nr];
- VFIOQuirk *quirk;
-
- QLIST_FOREACH(quirk, &bar->quirks, next) {
- memory_region_del_subregion(&bar->region.mem, &quirk->mem);
- }
-}
-
-static void vfio_bar_quirk_free(VFIOPCIDevice *vdev, int nr)
-{
- VFIOBAR *bar = &vdev->bars[nr];
-
- while (!QLIST_EMPTY(&bar->quirks)) {
- VFIOQuirk *quirk = QLIST_FIRST(&bar->quirks);
- object_unparent(OBJECT(&quirk->mem));
- QLIST_REMOVE(quirk, next);
- g_free(quirk);
- }
-}
-
-/*
* PCI config space
*/
-static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len)
+uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len)
{
VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev);
uint32_t emu_bits = 0, emu_val = 0, phys_val = 0, val;
@@ -2097,8 +1063,8 @@ static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len)
return val;
}
-static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr,
- uint32_t val, int len)
+void vfio_pci_write_config(PCIDevice *pdev,
+ uint32_t addr, uint32_t val, int len)
{
VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev);
uint32_t val_le = cpu_to_le32(val);
@@ -2124,11 +1090,11 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr,
if (!was_enabled) {
if (is_enabled) {
- vfio_enable_msi(vdev);
+ vfio_msi_enable(vdev);
}
} else {
if (!is_enabled) {
- vfio_disable_msi(vdev);
+ vfio_msi_disable(vdev);
} else {
vfio_update_msi(vdev);
}
@@ -2142,9 +1108,9 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr,
is_enabled = msix_enabled(pdev);
if (!was_enabled && is_enabled) {
- vfio_enable_msix(vdev);
+ vfio_msix_enable(vdev);
} else if (was_enabled && !is_enabled) {
- vfio_disable_msix(vdev);
+ vfio_msix_disable(vdev);
}
} else {
/* Write everything to QEMU to keep emulated bits correct */
@@ -2163,17 +1129,17 @@ static void vfio_disable_interrupts(VFIOPCIDevice *vdev)
* disable MSI/X and then cleanup by disabling INTx.
*/
if (vdev->interrupt == VFIO_INT_MSIX) {
- vfio_disable_msix(vdev);
+ vfio_msix_disable(vdev);
} else if (vdev->interrupt == VFIO_INT_MSI) {
- vfio_disable_msi(vdev);
+ vfio_msi_disable(vdev);
}
if (vdev->interrupt == VFIO_INT_INTx) {
- vfio_disable_intx(vdev);
+ vfio_intx_disable(vdev);
}
}
-static int vfio_setup_msi(VFIOPCIDevice *vdev, int pos)
+static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos)
{
uint16_t ctrl;
bool msi_64bit, msi_maskbit;
@@ -2189,7 +1155,7 @@ static int vfio_setup_msi(VFIOPCIDevice *vdev, int pos)
msi_maskbit = !!(ctrl & PCI_MSI_FLAGS_MASKBIT);
entries = 1 << ((ctrl & PCI_MSI_FLAGS_QMASK) >> 1);
- trace_vfio_setup_msi(vdev->vbasedev.name, pos);
+ trace_vfio_msi_setup(vdev->vbasedev.name, pos);
ret = msi_init(&vdev->pdev, pos, entries, msi_64bit, msi_maskbit);
if (ret < 0) {
@@ -2212,12 +1178,13 @@ static int vfio_setup_msi(VFIOPCIDevice *vdev, int pos)
* need to first look for where the MSI-X table lives. So we
* unfortunately split MSI-X setup across two functions.
*/
-static int vfio_early_setup_msix(VFIOPCIDevice *vdev)
+static int vfio_msix_early_setup(VFIOPCIDevice *vdev)
{
uint8_t pos;
uint16_t ctrl;
uint32_t table, pba;
int fd = vdev->vbasedev.fd;
+ VFIOMSIXInfo *msix;
pos = pci_find_capability(&vdev->pdev, PCI_CAP_ID_MSIX);
if (!pos) {
@@ -2243,49 +1210,44 @@ static int vfio_early_setup_msix(VFIOPCIDevice *vdev)
table = le32_to_cpu(table);
pba = le32_to_cpu(pba);
- vdev->msix = g_malloc0(sizeof(*(vdev->msix)));
- vdev->msix->table_bar = table & PCI_MSIX_FLAGS_BIRMASK;
- vdev->msix->table_offset = table & ~PCI_MSIX_FLAGS_BIRMASK;
- vdev->msix->pba_bar = pba & PCI_MSIX_FLAGS_BIRMASK;
- vdev->msix->pba_offset = pba & ~PCI_MSIX_FLAGS_BIRMASK;
- vdev->msix->entries = (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1;
+ msix = g_malloc0(sizeof(*msix));
+ msix->table_bar = table & PCI_MSIX_FLAGS_BIRMASK;
+ msix->table_offset = table & ~PCI_MSIX_FLAGS_BIRMASK;
+ msix->pba_bar = pba & PCI_MSIX_FLAGS_BIRMASK;
+ msix->pba_offset = pba & ~PCI_MSIX_FLAGS_BIRMASK;
+ 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);
-
+ if (msix->pba_offset >= vdev->bars[msix->pba_bar].region.size) {
/*
* 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;
+ if (vdev->vendor_id == PCI_VENDOR_ID_CHELSIO &&
+ (vdev->device_id & 0xff00) == 0x5800) {
+ msix->pba_offset = 0x1000;
} else {
error_report("vfio: Hardware reports invalid configuration, "
"MSIX PBA outside of specified BAR");
+ g_free(msix);
return -EINVAL;
}
}
- trace_vfio_early_setup_msix(vdev->vbasedev.name, pos,
- vdev->msix->table_bar,
- vdev->msix->table_offset,
- vdev->msix->entries);
+ trace_vfio_msix_early_setup(vdev->vbasedev.name, pos, msix->table_bar,
+ msix->table_offset, msix->entries);
+ vdev->msix = msix;
return 0;
}
-static int vfio_setup_msix(VFIOPCIDevice *vdev, int pos)
+static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos)
{
int ret;
@@ -2563,13 +1525,38 @@ static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size)
}
if (!pci_bus_is_express(vdev->pdev.bus)) {
+ PCIBus *bus = vdev->pdev.bus;
+ PCIDevice *bridge;
+
/*
- * Use express capability as-is on PCI bus. It doesn't make much
- * sense to even expose, but some drivers (ex. tg3) depend on it
- * and guests don't seem to be particular about it. We'll need
- * to revist this or force express devices to express buses if we
- * ever expose an IOMMU to the guest.
+ * Traditionally PCI device assignment exposes the PCIe capability
+ * as-is on non-express buses. The reason being that some drivers
+ * simply assume that it's there, for example tg3. However when
+ * we're running on a native PCIe machine type, like Q35, we need
+ * to hide the PCIe capability. The reason for this is twofold;
+ * first Windows guests get a Code 10 error when the PCIe capability
+ * is exposed in this configuration. Therefore express devices won't
+ * work at all unless they're attached to express buses in the VM.
+ * Second, a native PCIe machine introduces the possibility of fine
+ * granularity IOMMUs supporting both translation and isolation.
+ * Guest code to discover the IOMMU visibility of a device, such as
+ * IOMMU grouping code on Linux, is very aware of device types and
+ * valid transitions between bus types. An express device on a non-
+ * express bus is not a valid combination on bare metal systems.
+ *
+ * Drivers that require a PCIe capability to make the device
+ * functional are simply going to need to have their devices placed
+ * on a PCIe bus in the VM.
*/
+ while (!pci_bus_is_root(bus)) {
+ bridge = pci_bridge_get_device(bus);
+ bus = bridge->bus;
+ }
+
+ if (pci_bus_is_express(bus)) {
+ return 0;
+ }
+
} else if (pci_bus_is_root(vdev->pdev.bus)) {
/*
* On a Root Complex bus Endpoints become Root Complex Integrated
@@ -2708,14 +1695,14 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos)
switch (cap_id) {
case PCI_CAP_ID_MSI:
- ret = vfio_setup_msi(vdev, pos);
+ ret = vfio_msi_setup(vdev, pos);
break;
case PCI_CAP_ID_EXP:
vfio_check_pcie_flr(vdev, pos);
ret = vfio_setup_pcie_cap(vdev, pos, size);
break;
case PCI_CAP_ID_MSIX:
- ret = vfio_setup_msix(vdev, pos);
+ ret = vfio_msix_setup(vdev, pos);
break;
case PCI_CAP_ID_PM:
vfio_check_pm_reset(vdev, pos);
@@ -2793,7 +1780,7 @@ static void vfio_pci_pre_reset(VFIOPCIDevice *vdev)
static void vfio_pci_post_reset(VFIOPCIDevice *vdev)
{
- vfio_enable_intx(vdev);
+ vfio_intx_enable(vdev);
}
static bool vfio_pci_host_match(PCIHostDeviceAddress *host1,
@@ -3017,7 +2004,7 @@ static void vfio_pci_compute_needs_reset(VFIODevice *vbasedev)
static VFIODeviceOps vfio_pci_ops = {
.vfio_compute_needs_reset = vfio_pci_compute_needs_reset,
.vfio_hot_reset_multi = vfio_pci_hot_reset_multi,
- .vfio_eoi = vfio_eoi,
+ .vfio_eoi = vfio_intx_eoi,
};
static int vfio_populate_device(VFIOPCIDevice *vdev)
@@ -3352,162 +2339,6 @@ 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);
@@ -3600,6 +2431,54 @@ static int vfio_initfn(PCIDevice *pdev)
/* QEMU can choose to expose the ROM or not */
memset(vdev->emulated_config_bits + PCI_ROM_ADDRESS, 0xff, 4);
+ /*
+ * The PCI spec reserves vendor ID 0xffff as an invalid value. The
+ * device ID is managed by the vendor and need only be a 16-bit value.
+ * Allow any 16-bit value for subsystem so they can be hidden or changed.
+ */
+ if (vdev->vendor_id != PCI_ANY_ID) {
+ if (vdev->vendor_id >= 0xffff) {
+ error_report("vfio: Invalid PCI vendor ID provided");
+ return -EINVAL;
+ }
+ vfio_add_emulated_word(vdev, PCI_VENDOR_ID, vdev->vendor_id, ~0);
+ trace_vfio_pci_emulated_vendor_id(vdev->vbasedev.name, vdev->vendor_id);
+ } else {
+ vdev->vendor_id = pci_get_word(pdev->config + PCI_VENDOR_ID);
+ }
+
+ if (vdev->device_id != PCI_ANY_ID) {
+ if (vdev->device_id > 0xffff) {
+ error_report("vfio: Invalid PCI device ID provided");
+ return -EINVAL;
+ }
+ vfio_add_emulated_word(vdev, PCI_DEVICE_ID, vdev->device_id, ~0);
+ trace_vfio_pci_emulated_device_id(vdev->vbasedev.name, vdev->device_id);
+ } else {
+ vdev->device_id = pci_get_word(pdev->config + PCI_DEVICE_ID);
+ }
+
+ if (vdev->sub_vendor_id != PCI_ANY_ID) {
+ if (vdev->sub_vendor_id > 0xffff) {
+ error_report("vfio: Invalid PCI subsystem vendor ID provided");
+ return -EINVAL;
+ }
+ vfio_add_emulated_word(vdev, PCI_SUBSYSTEM_VENDOR_ID,
+ vdev->sub_vendor_id, ~0);
+ trace_vfio_pci_emulated_sub_vendor_id(vdev->vbasedev.name,
+ vdev->sub_vendor_id);
+ }
+
+ if (vdev->sub_device_id != PCI_ANY_ID) {
+ if (vdev->sub_device_id > 0xffff) {
+ error_report("vfio: Invalid PCI subsystem device ID provided");
+ return -EINVAL;
+ }
+ vfio_add_emulated_word(vdev, PCI_SUBSYSTEM_ID, vdev->sub_device_id, ~0);
+ trace_vfio_pci_emulated_sub_device_id(vdev->vbasedev.name,
+ vdev->sub_device_id);
+ }
+
/* QEMU can change multi-function devices to single function, or reverse */
vdev->emulated_config_bits[PCI_HEADER_TYPE] =
PCI_HEADER_TYPE_MULTI_FUNCTION;
@@ -3621,7 +2500,7 @@ static int vfio_initfn(PCIDevice *pdev)
vfio_pci_size_rom(vdev);
- ret = vfio_early_setup_msix(vdev);
+ ret = vfio_msix_early_setup(vdev);
if (ret) {
return ret;
}
@@ -3647,8 +2526,8 @@ static int vfio_initfn(PCIDevice *pdev)
if (vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1)) {
vdev->intx.mmap_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
vfio_intx_mmap_enable, vdev);
- pci_device_set_intx_routing_notifier(&vdev->pdev, vfio_update_irq);
- ret = vfio_enable_intx(vdev);
+ pci_device_set_intx_routing_notifier(&vdev->pdev, vfio_intx_update);
+ ret = vfio_intx_enable(vdev);
if (ret) {
goto out_teardown;
}
@@ -3656,7 +2535,7 @@ static int vfio_initfn(PCIDevice *pdev)
vfio_register_err_notifier(vdev);
vfio_register_req_notifier(vdev);
- vfio_setup_resetfn(vdev);
+ vfio_setup_resetfn_quirk(vdev);
return 0;
@@ -3749,7 +2628,16 @@ static Property vfio_pci_dev_properties[] = {
VFIO_FEATURE_ENABLE_VGA_BIT, false),
DEFINE_PROP_BIT("x-req", VFIOPCIDevice, features,
VFIO_FEATURE_ENABLE_REQ_BIT, true),
- DEFINE_PROP_BOOL("x-mmap", VFIOPCIDevice, vbasedev.allow_mmap, true),
+ DEFINE_PROP_BOOL("x-no-mmap", VFIOPCIDevice, vbasedev.no_mmap, false),
+ DEFINE_PROP_BOOL("x-no-kvm-intx", VFIOPCIDevice, no_kvm_intx, false),
+ DEFINE_PROP_BOOL("x-no-kvm-msi", VFIOPCIDevice, no_kvm_msi, false),
+ DEFINE_PROP_BOOL("x-no-kvm-msix", VFIOPCIDevice, no_kvm_msix, false),
+ DEFINE_PROP_UINT32("x-pci-vendor-id", VFIOPCIDevice, vendor_id, PCI_ANY_ID),
+ DEFINE_PROP_UINT32("x-pci-device-id", VFIOPCIDevice, device_id, PCI_ANY_ID),
+ DEFINE_PROP_UINT32("x-pci-sub-vendor-id", VFIOPCIDevice,
+ sub_vendor_id, PCI_ANY_ID),
+ DEFINE_PROP_UINT32("x-pci-sub-device-id", VFIOPCIDevice,
+ sub_device_id, PCI_ANY_ID),
/*
* TODO - support passed fds... is this necessary?
* DEFINE_PROP_STRING("vfiofd", VFIOPCIDevice, vfiofd_name),
diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h
new file mode 100644
index 000000000..f004d52b6
--- /dev/null
+++ b/hw/vfio/pci.h
@@ -0,0 +1,159 @@
+/*
+ * vfio based device assignment support - PCI devices
+ *
+ * Copyright Red Hat, Inc. 2012-2015
+ *
+ * Authors:
+ * Alex Williamson <alex.williamson@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+#ifndef HW_VFIO_VFIO_PCI_H
+#define HW_VFIO_VFIO_PCI_H
+
+#include "qemu-common.h"
+#include "exec/memory.h"
+#include "hw/pci/pci.h"
+#include "hw/vfio/vfio-common.h"
+#include "qemu/event_notifier.h"
+#include "qemu/queue.h"
+#include "qemu/timer.h"
+
+#define PCI_ANY_ID (~0)
+
+struct VFIOPCIDevice;
+
+typedef struct VFIOQuirk {
+ QLIST_ENTRY(VFIOQuirk) next;
+ void *data;
+ int nr_mem;
+ MemoryRegion *mem;
+} VFIOQuirk;
+
+typedef struct VFIOBAR {
+ VFIORegion region;
+ bool ioport;
+ bool mem64;
+ QLIST_HEAD(, VFIOQuirk) quirks;
+} VFIOBAR;
+
+typedef struct VFIOVGARegion {
+ MemoryRegion mem;
+ off_t offset;
+ int nr;
+ QLIST_HEAD(, VFIOQuirk) quirks;
+} VFIOVGARegion;
+
+typedef struct VFIOVGA {
+ off_t fd_offset;
+ int fd;
+ VFIOVGARegion region[QEMU_PCI_VGA_NUM_REGIONS];
+} VFIOVGA;
+
+typedef struct VFIOINTx {
+ bool pending; /* interrupt pending */
+ bool kvm_accel; /* set when QEMU bypass through KVM enabled */
+ uint8_t pin; /* which pin to pull for qemu_set_irq */
+ EventNotifier interrupt; /* eventfd triggered on interrupt */
+ EventNotifier unmask; /* eventfd for unmask on QEMU bypass */
+ PCIINTxRoute route; /* routing info for QEMU bypass */
+ uint32_t mmap_timeout; /* delay to re-enable mmaps after interrupt */
+ QEMUTimer *mmap_timer; /* enable mmaps after periods w/o interrupts */
+} VFIOINTx;
+
+typedef struct VFIOMSIVector {
+ /*
+ * Two interrupt paths are configured per vector. The first, is only used
+ * for interrupts injected via QEMU. This is typically the non-accel path,
+ * but may also be used when we want QEMU to handle masking and pending
+ * bits. The KVM path bypasses QEMU and is therefore higher performance,
+ * but requires masking at the device. virq is used to track the MSI route
+ * through KVM, thus kvm_interrupt is only available when virq is set to a
+ * valid (>= 0) value.
+ */
+ EventNotifier interrupt;
+ EventNotifier kvm_interrupt;
+ struct VFIOPCIDevice *vdev; /* back pointer to device */
+ int virq;
+ bool use;
+} VFIOMSIVector;
+
+enum {
+ VFIO_INT_NONE = 0,
+ VFIO_INT_INTx = 1,
+ VFIO_INT_MSI = 2,
+ VFIO_INT_MSIX = 3,
+};
+
+/* Cache of MSI-X setup plus extra mmap and memory region for split BAR map */
+typedef struct VFIOMSIXInfo {
+ uint8_t table_bar;
+ uint8_t pba_bar;
+ uint16_t entries;
+ uint32_t table_offset;
+ uint32_t pba_offset;
+ MemoryRegion mmap_mem;
+ void *mmap;
+} VFIOMSIXInfo;
+
+typedef struct VFIOPCIDevice {
+ PCIDevice pdev;
+ VFIODevice vbasedev;
+ VFIOINTx intx;
+ unsigned int config_size;
+ uint8_t *emulated_config_bits; /* QEMU emulated bits, little-endian */
+ off_t config_offset; /* Offset of config space region within device fd */
+ unsigned int rom_size;
+ off_t rom_offset; /* Offset of ROM region within device fd */
+ void *rom;
+ int msi_cap_size;
+ VFIOMSIVector *msi_vectors;
+ VFIOMSIXInfo *msix;
+ int nr_vectors; /* Number of MSI/MSIX vectors currently in use */
+ int interrupt; /* Current interrupt type */
+ VFIOBAR bars[PCI_NUM_REGIONS - 1]; /* No ROM */
+ VFIOVGA vga; /* 0xa0000, 0x3b0, 0x3c0 */
+ PCIHostDeviceAddress host;
+ EventNotifier err_notifier;
+ EventNotifier req_notifier;
+ int (*resetfn)(struct VFIOPCIDevice *);
+ uint32_t vendor_id;
+ uint32_t device_id;
+ uint32_t sub_vendor_id;
+ uint32_t sub_device_id;
+ uint32_t features;
+#define VFIO_FEATURE_ENABLE_VGA_BIT 0
+#define VFIO_FEATURE_ENABLE_VGA (1 << VFIO_FEATURE_ENABLE_VGA_BIT)
+#define VFIO_FEATURE_ENABLE_REQ_BIT 1
+#define VFIO_FEATURE_ENABLE_REQ (1 << VFIO_FEATURE_ENABLE_REQ_BIT)
+ int32_t bootindex;
+ uint8_t pm_cap;
+ bool has_vga;
+ bool pci_aer;
+ bool req_enabled;
+ bool has_flr;
+ bool has_pm_reset;
+ bool rom_read_failed;
+ bool no_kvm_intx;
+ bool no_kvm_msi;
+ bool no_kvm_msix;
+} VFIOPCIDevice;
+
+uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len);
+void vfio_pci_write_config(PCIDevice *pdev,
+ uint32_t addr, uint32_t val, int len);
+
+uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size);
+void vfio_vga_write(void *opaque, hwaddr addr, uint64_t data, unsigned size);
+
+bool vfio_blacklist_opt_rom(VFIOPCIDevice *vdev);
+void vfio_vga_quirk_setup(VFIOPCIDevice *vdev);
+void vfio_vga_quirk_teardown(VFIOPCIDevice *vdev);
+void vfio_vga_quirk_free(VFIOPCIDevice *vdev);
+void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr);
+void vfio_bar_quirk_teardown(VFIOPCIDevice *vdev, int nr);
+void vfio_bar_quirk_free(VFIOPCIDevice *vdev, int nr);
+void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev);
+
+#endif /* HW_VFIO_VFIO_PCI_H */
diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c
index 60365d127..289b498ca 100644
--- a/hw/vfio/platform.c
+++ b/hw/vfio/platform.c
@@ -32,6 +32,11 @@
* Functions used whatever the injection method
*/
+static inline bool vfio_irq_is_automasked(VFIOINTp *intp)
+{
+ return intp->flags & VFIO_IRQ_INFO_AUTOMASKED;
+}
+
/**
* vfio_init_intp - allocate, initialize the IRQ struct pointer
* and add it into the list of IRQs
@@ -57,18 +62,25 @@ static VFIOINTp *vfio_init_intp(VFIODevice *vbasedev,
sysbus_init_irq(sbdev, &intp->qemuirq);
/* Get an eventfd for trigger */
- ret = event_notifier_init(&intp->interrupt, 0);
+ intp->interrupt = g_malloc0(sizeof(EventNotifier));
+ ret = event_notifier_init(intp->interrupt, 0);
if (ret) {
+ g_free(intp->interrupt);
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;
+ if (vfio_irq_is_automasked(intp)) {
+ /* Get an eventfd for resample/unmask */
+ intp->unmask = g_malloc0(sizeof(EventNotifier));
+ ret = event_notifier_init(intp->unmask, 0);
+ if (ret) {
+ g_free(intp->interrupt);
+ g_free(intp->unmask);
+ g_free(intp);
+ error_report("vfio: Error: resamplefd event_notifier_init failed");
+ return NULL;
+ }
}
QLIST_INSERT_HEAD(&vdev->intp_list, intp, next);
@@ -100,7 +112,7 @@ static int vfio_set_trigger_eventfd(VFIOINTp *intp,
irq_set->start = 0;
irq_set->count = 1;
pfd = (int32_t *)&irq_set->data;
- *pfd = event_notifier_get_fd(&intp->interrupt);
+ *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);
@@ -182,7 +194,7 @@ static void vfio_intp_mmap_enable(void *opaque)
static void vfio_intp_inject_pending_lockheld(VFIOINTp *intp)
{
trace_vfio_platform_intp_inject_pending_lockheld(intp->pin,
- event_notifier_get_fd(&intp->interrupt));
+ event_notifier_get_fd(intp->interrupt));
intp->state = VFIO_IRQ_ACTIVE;
@@ -224,18 +236,18 @@ static void vfio_intp_interrupt(VFIOINTp *intp)
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);
+ 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));
+ event_notifier_get_fd(intp->interrupt));
- ret = event_notifier_test_and_clear(&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);
+ error_report("Error when clearing fd=%d (ret = %d)",
+ event_notifier_get_fd(intp->interrupt), ret);
}
intp->state = VFIO_IRQ_ACTIVE;
@@ -283,13 +295,13 @@ static void vfio_platform_eoi(VFIODevice *vbasedev)
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));
+ 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) {
+ if (vfio_irq_is_automasked(intp)) {
/* unmasks the physical level-sensitive IRQ */
vfio_unmask_single_irqindex(vbasedev, intp->pin);
}
@@ -310,18 +322,29 @@ static void vfio_platform_eoi(VFIODevice *vbasedev)
/**
* vfio_start_eventfd_injection - starts the virtual IRQ injection using
* user-side handled eventfds
- * @intp: the IRQ struct pointer
+ * @sbdev: the sysbus device handle
+ * @irq: the qemu irq handle
*/
-static int vfio_start_eventfd_injection(VFIOINTp *intp)
+static void vfio_start_eventfd_injection(SysBusDevice *sbdev, qemu_irq irq)
{
int ret;
+ VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev);
+ VFIOINTp *intp;
+
+ QLIST_FOREACH(intp, &vdev->intp_list, next) {
+ if (intp->qemuirq == irq) {
+ break;
+ }
+ }
+ assert(intp);
ret = vfio_set_trigger_eventfd(intp, vfio_intp_interrupt);
if (ret) {
- error_report("vfio: Error: Failed to pass IRQ fd to the driver: %m");
+ error_report("vfio: failed to start eventfd signaling for IRQ %d: %m",
+ intp->pin);
+ abort();
}
- return ret;
}
/*
@@ -349,7 +372,7 @@ static int vfio_set_resample_eventfd(VFIOINTp *intp)
irq_set->start = 0;
irq_set->count = 1;
pfd = (int32_t *)&irq_set->data;
- *pfd = event_notifier_get_fd(&intp->unmask);
+ *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);
@@ -359,6 +382,15 @@ static int vfio_set_resample_eventfd(VFIOINTp *intp)
return ret;
}
+/**
+ * vfio_start_irqfd_injection - starts the virtual IRQ injection using
+ * irqfd
+ *
+ * @sbdev: the sysbus device handle
+ * @irq: the qemu irq handle
+ *
+ * In case the irqfd setup fails, we fallback to userspace handled eventfd
+ */
static void vfio_start_irqfd_injection(SysBusDevice *sbdev, qemu_irq irq)
{
VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev);
@@ -366,7 +398,7 @@ static void vfio_start_irqfd_injection(SysBusDevice *sbdev, qemu_irq irq)
if (!kvm_irqfds_enabled() || !kvm_resamplefds_enabled() ||
!vdev->irqfd_allowed) {
- return;
+ goto fail_irqfd;
}
QLIST_FOREACH(intp, &vdev->intp_list, next) {
@@ -376,39 +408,36 @@ static void vfio_start_irqfd_injection(SysBusDevice *sbdev, qemu_irq irq)
}
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) {
+ 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;
+ if (vfio_irq_is_automasked(intp)) {
+ if (vfio_set_resample_eventfd(intp) < 0) {
+ goto fail_vfio;
+ }
+ trace_vfio_platform_start_level_irqfd_injection(intp->pin,
+ event_notifier_get_fd(intp->interrupt),
+ event_notifier_get_fd(intp->unmask));
+ } else {
+ trace_vfio_platform_start_edge_irqfd_injection(intp->pin,
+ event_notifier_get_fd(intp->interrupt));
}
- /* 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);
+ kvm_irqchip_remove_irqfd_notifier(kvm_state, intp->interrupt, irq);
+ error_report("vfio: failed to start eventfd signaling for IRQ %d: %m",
+ intp->pin);
+ abort();
fail_irqfd:
- vfio_start_eventfd_injection(intp);
- vfio_unmask_single_irqindex(&vdev->vbasedev, intp->pin);
+ vfio_start_eventfd_injection(sbdev, irq);
return;
}
@@ -449,7 +478,7 @@ static int vfio_populate_device(VFIODevice *vbasedev)
struct vfio_region_info reg_info = { .argsz = sizeof(reg_info) };
VFIORegion *ptr;
- vdev->regions[i] = g_malloc0(sizeof(VFIORegion));
+ vdev->regions[i] = g_new0(VFIORegion, 1);
ptr = vdev->regions[i];
reg_info.index = i;
ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, &reg_info);
@@ -646,7 +675,6 @@ 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;
@@ -665,10 +693,6 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp)
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 = {
@@ -678,7 +702,7 @@ static const VMStateDescription vfio_platform_vmstate = {
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_BOOL("x-no-mmap", VFIOPlatformDevice, vbasedev.no_mmap, false),
DEFINE_PROP_UINT32("mmap-timeout-ms", VFIOPlatformDevice,
mmap_timeout, 1100),
DEFINE_PROP_BOOL("x-irqfd", VFIOPlatformDevice, irqfd_allowed, true),
diff --git a/hw/virtio/dataplane/vring.c b/hw/virtio/dataplane/vring.c
index 68f199443..23f667ef4 100644
--- a/hw/virtio/dataplane/vring.c
+++ b/hw/virtio/dataplane/vring.c
@@ -25,15 +25,30 @@
/* vring_map can be coupled with vring_unmap or (if you still have the
* value returned in *mr) memory_region_unref.
+ * Returns NULL on failure.
+ * Callers that can handle a partial mapping must supply mapped_len pointer to
+ * get the actual length mapped.
+ * Passing mapped_len == NULL requires either a full mapping or a failure.
*/
-static void *vring_map(MemoryRegion **mr, hwaddr phys, hwaddr len,
+static void *vring_map(MemoryRegion **mr, hwaddr phys,
+ hwaddr len, hwaddr *mapped_len,
bool is_write)
{
MemoryRegionSection section = memory_region_find(get_system_memory(), phys, len);
+ uint64_t size;
- if (!section.mr || int128_get64(section.size) < len) {
+ if (!section.mr) {
goto out;
}
+
+ size = int128_get64(section.size);
+ assert(size);
+
+ /* Passing mapped_len == NULL requires either a full mapping or a failure. */
+ if (!mapped_len && size < len) {
+ goto out;
+ }
+
if (is_write && section.readonly) {
goto out;
}
@@ -46,6 +61,10 @@ static void *vring_map(MemoryRegion **mr, hwaddr phys, hwaddr len,
goto out;
}
+ if (mapped_len) {
+ *mapped_len = MIN(size, len);
+ }
+
*mr = section.mr;
return memory_region_get_ram_ptr(section.mr) + section.offset_within_region;
@@ -78,7 +97,7 @@ bool vring_setup(Vring *vring, VirtIODevice *vdev, int 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);
+ ptr = vring_map(&vring->mr_desc, addr, size, NULL, false);
if (!ptr) {
error_report("Failed to map 0x%" HWADDR_PRIx " byte for vring desc "
"at 0x%" HWADDR_PRIx,
@@ -92,7 +111,7 @@ bool vring_setup(Vring *vring, VirtIODevice *vdev, int 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);
+ ptr = vring_map(&vring->mr_avail, addr, size, NULL, false);
if (!ptr) {
error_report("Failed to map 0x%" HWADDR_PRIx " byte for vring avail "
"at 0x%" HWADDR_PRIx,
@@ -106,7 +125,7 @@ bool vring_setup(Vring *vring, VirtIODevice *vdev, int 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);
+ ptr = vring_map(&vring->mr_used, addr, size, NULL, true);
if (!ptr) {
error_report("Failed to map 0x%" HWADDR_PRIx " byte for vring used "
"at 0x%" HWADDR_PRIx,
@@ -206,6 +225,7 @@ static int get_desc(Vring *vring, VirtQueueElement *elem,
struct iovec *iov;
hwaddr *addr;
MemoryRegion *mr;
+ hwaddr len;
if (desc->flags & VRING_DESC_F_WRITE) {
num = &elem->in_num;
@@ -224,26 +244,30 @@ static int get_desc(Vring *vring, VirtQueueElement *elem,
}
}
- /* Stop for now if there are not enough iovecs available. */
- if (*num >= VIRTQUEUE_MAX_SIZE) {
- error_report("Invalid SG num: %u", *num);
- return -EFAULT;
- }
+ while (desc->len) {
+ /* Stop for now if there are not enough iovecs available. */
+ if (*num >= VIRTQUEUE_MAX_SIZE) {
+ error_report("Invalid SG num: %u", *num);
+ return -EFAULT;
+ }
- /* TODO handle non-contiguous memory across region boundaries */
- iov->iov_base = vring_map(&mr, desc->addr, desc->len,
- desc->flags & VRING_DESC_F_WRITE);
- if (!iov->iov_base) {
- error_report("Failed to map descriptor addr %#" PRIx64 " len %u",
- (uint64_t)desc->addr, desc->len);
- return -EFAULT;
+ iov->iov_base = vring_map(&mr, desc->addr, desc->len, &len,
+ desc->flags & VRING_DESC_F_WRITE);
+ if (!iov->iov_base) {
+ error_report("Failed to map descriptor addr %#" PRIx64 " len %u",
+ (uint64_t)desc->addr, desc->len);
+ return -EFAULT;
+ }
+
+ /* The MemoryRegion is looked up again and unref'ed later, leave the
+ * ref in place. */
+ (iov++)->iov_len = len;
+ *addr++ = desc->addr;
+ desc->len -= len;
+ desc->addr += len;
+ *num += 1;
}
- /* The MemoryRegion is looked up again and unref'ed later, leave the
- * ref in place. */
- iov->iov_len = desc->len;
- *addr = desc->addr;
- *num += 1;
return 0;
}
@@ -257,6 +281,21 @@ static void copy_in_vring_desc(VirtIODevice *vdev,
host->next = virtio_lduw_p(vdev, &guest->next);
}
+static bool read_vring_desc(VirtIODevice *vdev,
+ hwaddr guest,
+ struct vring_desc *host)
+{
+ if (address_space_read(&address_space_memory, guest, MEMTXATTRS_UNSPECIFIED,
+ (uint8_t *)host, sizeof *host)) {
+ return false;
+ }
+ host->addr = virtio_tswap64(vdev, host->addr);
+ host->len = virtio_tswap32(vdev, host->len);
+ host->flags = virtio_tswap16(vdev, host->flags);
+ host->next = virtio_tswap16(vdev, host->next);
+ return true;
+}
+
/* This is stolen from linux/drivers/vhost/vhost.c. */
static int get_indirect(VirtIODevice *vdev, Vring *vring,
VirtQueueElement *elem, struct vring_desc *indirect)
@@ -284,23 +323,16 @@ static int get_indirect(VirtIODevice *vdev, Vring *vring,
}
do {
- struct vring_desc *desc_ptr;
- MemoryRegion *mr;
-
/* Translate indirect descriptor */
- desc_ptr = vring_map(&mr,
- indirect->addr + found * sizeof(desc),
- sizeof(desc), false);
- if (!desc_ptr) {
- error_report("Failed to map indirect descriptor "
+ if (!read_vring_desc(vdev, indirect->addr + found * sizeof(desc),
+ &desc)) {
+ error_report("Failed to read indirect descriptor "
"addr %#" PRIx64 " len %zu",
(uint64_t)indirect->addr + found * sizeof(desc),
sizeof(desc));
vring->broken = true;
return -EFAULT;
}
- copy_in_vring_desc(vdev, desc_ptr, &desc);
- memory_region_unref(mr);
/* Ensure descriptor has been loaded before accessing fields */
barrier(); /* read_barrier_depends(); */
diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c
index 4d68a2765..b734a601a 100644
--- a/hw/virtio/vhost-backend.c
+++ b/hw/virtio/vhost-backend.c
@@ -11,6 +11,7 @@
#include "hw/virtio/vhost.h"
#include "hw/virtio/vhost-backend.h"
#include "qemu/error-report.h"
+#include "linux/vhost.h"
#include <sys/ioctl.h>
@@ -42,11 +43,152 @@ static int vhost_kernel_cleanup(struct vhost_dev *dev)
return close(fd);
}
+static int vhost_kernel_memslots_limit(struct vhost_dev *dev)
+{
+ int limit = 64;
+ char *s;
+
+ if (g_file_get_contents("/sys/module/vhost/parameters/max_mem_regions",
+ &s, NULL, NULL)) {
+ uint64_t val = g_ascii_strtoull(s, NULL, 10);
+ if (!((val == G_MAXUINT64 || !val) && errno)) {
+ return val;
+ }
+ error_report("ignoring invalid max_mem_regions value in vhost module:"
+ " %s", s);
+ }
+ return limit;
+}
+
+static int vhost_kernel_net_set_backend(struct vhost_dev *dev,
+ struct vhost_vring_file *file)
+{
+ return vhost_kernel_call(dev, VHOST_NET_SET_BACKEND, file);
+}
+
+static int vhost_kernel_scsi_set_endpoint(struct vhost_dev *dev,
+ struct vhost_scsi_target *target)
+{
+ return vhost_kernel_call(dev, VHOST_SCSI_SET_ENDPOINT, target);
+}
+
+static int vhost_kernel_scsi_clear_endpoint(struct vhost_dev *dev,
+ struct vhost_scsi_target *target)
+{
+ return vhost_kernel_call(dev, VHOST_SCSI_CLEAR_ENDPOINT, target);
+}
+
+static int vhost_kernel_scsi_get_abi_version(struct vhost_dev *dev, int *version)
+{
+ return vhost_kernel_call(dev, VHOST_SCSI_GET_ABI_VERSION, version);
+}
+
+static int vhost_kernel_set_log_base(struct vhost_dev *dev, uint64_t base,
+ struct vhost_log *log)
+{
+ return vhost_kernel_call(dev, VHOST_SET_LOG_BASE, &base);
+}
+
+static int vhost_kernel_set_mem_table(struct vhost_dev *dev,
+ struct vhost_memory *mem)
+{
+ return vhost_kernel_call(dev, VHOST_SET_MEM_TABLE, mem);
+}
+
+static int vhost_kernel_set_vring_addr(struct vhost_dev *dev,
+ struct vhost_vring_addr *addr)
+{
+ return vhost_kernel_call(dev, VHOST_SET_VRING_ADDR, addr);
+}
+
+static int vhost_kernel_set_vring_endian(struct vhost_dev *dev,
+ struct vhost_vring_state *ring)
+{
+ return vhost_kernel_call(dev, VHOST_SET_VRING_ENDIAN, ring);
+}
+
+static int vhost_kernel_set_vring_num(struct vhost_dev *dev,
+ struct vhost_vring_state *ring)
+{
+ return vhost_kernel_call(dev, VHOST_SET_VRING_NUM, ring);
+}
+
+static int vhost_kernel_set_vring_base(struct vhost_dev *dev,
+ struct vhost_vring_state *ring)
+{
+ return vhost_kernel_call(dev, VHOST_SET_VRING_BASE, ring);
+}
+
+static int vhost_kernel_get_vring_base(struct vhost_dev *dev,
+ struct vhost_vring_state *ring)
+{
+ return vhost_kernel_call(dev, VHOST_GET_VRING_BASE, ring);
+}
+
+static int vhost_kernel_set_vring_kick(struct vhost_dev *dev,
+ struct vhost_vring_file *file)
+{
+ return vhost_kernel_call(dev, VHOST_SET_VRING_KICK, file);
+}
+
+static int vhost_kernel_set_vring_call(struct vhost_dev *dev,
+ struct vhost_vring_file *file)
+{
+ return vhost_kernel_call(dev, VHOST_SET_VRING_CALL, file);
+}
+
+static int vhost_kernel_set_features(struct vhost_dev *dev,
+ uint64_t features)
+{
+ return vhost_kernel_call(dev, VHOST_SET_FEATURES, &features);
+}
+
+static int vhost_kernel_get_features(struct vhost_dev *dev,
+ uint64_t *features)
+{
+ return vhost_kernel_call(dev, VHOST_GET_FEATURES, features);
+}
+
+static int vhost_kernel_set_owner(struct vhost_dev *dev)
+{
+ return vhost_kernel_call(dev, VHOST_SET_OWNER, NULL);
+}
+
+static int vhost_kernel_reset_device(struct vhost_dev *dev)
+{
+ return vhost_kernel_call(dev, VHOST_RESET_OWNER, NULL);
+}
+
+static int vhost_kernel_get_vq_index(struct vhost_dev *dev, int idx)
+{
+ assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs);
+
+ return idx - dev->vq_index;
+}
+
static const VhostOps kernel_ops = {
.backend_type = VHOST_BACKEND_TYPE_KERNEL,
- .vhost_call = vhost_kernel_call,
.vhost_backend_init = vhost_kernel_init,
- .vhost_backend_cleanup = vhost_kernel_cleanup
+ .vhost_backend_cleanup = vhost_kernel_cleanup,
+ .vhost_backend_memslots_limit = vhost_kernel_memslots_limit,
+ .vhost_net_set_backend = vhost_kernel_net_set_backend,
+ .vhost_scsi_set_endpoint = vhost_kernel_scsi_set_endpoint,
+ .vhost_scsi_clear_endpoint = vhost_kernel_scsi_clear_endpoint,
+ .vhost_scsi_get_abi_version = vhost_kernel_scsi_get_abi_version,
+ .vhost_set_log_base = vhost_kernel_set_log_base,
+ .vhost_set_mem_table = vhost_kernel_set_mem_table,
+ .vhost_set_vring_addr = vhost_kernel_set_vring_addr,
+ .vhost_set_vring_endian = vhost_kernel_set_vring_endian,
+ .vhost_set_vring_num = vhost_kernel_set_vring_num,
+ .vhost_set_vring_base = vhost_kernel_set_vring_base,
+ .vhost_get_vring_base = vhost_kernel_get_vring_base,
+ .vhost_set_vring_kick = vhost_kernel_set_vring_kick,
+ .vhost_set_vring_call = vhost_kernel_set_vring_call,
+ .vhost_set_features = vhost_kernel_set_features,
+ .vhost_get_features = vhost_kernel_get_features,
+ .vhost_set_owner = vhost_kernel_set_owner,
+ .vhost_reset_device = vhost_kernel_reset_device,
+ .vhost_get_vq_index = vhost_kernel_get_vq_index,
};
int vhost_set_backend_type(struct vhost_dev *dev, VhostBackendType backend_type)
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index e7ab8293d..577c95e35 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -10,11 +10,13 @@
#include "hw/virtio/vhost.h"
#include "hw/virtio/vhost-backend.h"
+#include "hw/virtio/virtio-net.h"
#include "sysemu/char.h"
#include "sysemu/kvm.h"
#include "qemu/error-report.h"
#include "qemu/sockets.h"
#include "exec/ram_addr.h"
+#include "migration/migration.h"
#include <fcntl.h>
#include <unistd.h>
@@ -24,6 +26,17 @@
#include <linux/vhost.h>
#define VHOST_MEMORY_MAX_NREGIONS 8
+#define VHOST_USER_F_PROTOCOL_FEATURES 30
+
+enum VhostUserProtocolFeature {
+ VHOST_USER_PROTOCOL_F_MQ = 0,
+ VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1,
+ VHOST_USER_PROTOCOL_F_RARP = 2,
+
+ VHOST_USER_PROTOCOL_F_MAX
+};
+
+#define VHOST_USER_PROTOCOL_FEATURE_MASK ((1 << VHOST_USER_PROTOCOL_F_MAX) - 1)
typedef enum VhostUserRequest {
VHOST_USER_NONE = 0,
@@ -41,6 +54,11 @@ typedef enum VhostUserRequest {
VHOST_USER_SET_VRING_KICK = 12,
VHOST_USER_SET_VRING_CALL = 13,
VHOST_USER_SET_VRING_ERR = 14,
+ VHOST_USER_GET_PROTOCOL_FEATURES = 15,
+ VHOST_USER_SET_PROTOCOL_FEATURES = 16,
+ VHOST_USER_GET_QUEUE_NUM = 17,
+ VHOST_USER_SET_VRING_ENABLE = 18,
+ VHOST_USER_SEND_RARP = 19,
VHOST_USER_MAX
} VhostUserRequest;
@@ -57,6 +75,11 @@ typedef struct VhostUserMemory {
VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS];
} VhostUserMemory;
+typedef struct VhostUserLog {
+ uint64_t mmap_size;
+ uint64_t mmap_offset;
+} VhostUserLog;
+
typedef struct VhostUserMsg {
VhostUserRequest request;
@@ -71,7 +94,8 @@ typedef struct VhostUserMsg {
struct vhost_vring_state state;
struct vhost_vring_addr addr;
VhostUserMemory memory;
- };
+ VhostUserLog log;
+ } payload;
} QEMU_PACKED VhostUserMsg;
static VhostUserMsg m __attribute__ ((unused));
@@ -89,37 +113,6 @@ static bool ioeventfd_enabled(void)
return kvm_enabled() && kvm_eventfds_enabled();
}
-static unsigned long int ioctl_to_vhost_user_request[VHOST_USER_MAX] = {
- -1, /* VHOST_USER_NONE */
- VHOST_GET_FEATURES, /* VHOST_USER_GET_FEATURES */
- VHOST_SET_FEATURES, /* VHOST_USER_SET_FEATURES */
- VHOST_SET_OWNER, /* VHOST_USER_SET_OWNER */
- VHOST_RESET_OWNER, /* VHOST_USER_RESET_OWNER */
- VHOST_SET_MEM_TABLE, /* VHOST_USER_SET_MEM_TABLE */
- VHOST_SET_LOG_BASE, /* VHOST_USER_SET_LOG_BASE */
- VHOST_SET_LOG_FD, /* VHOST_USER_SET_LOG_FD */
- VHOST_SET_VRING_NUM, /* VHOST_USER_SET_VRING_NUM */
- VHOST_SET_VRING_ADDR, /* VHOST_USER_SET_VRING_ADDR */
- VHOST_SET_VRING_BASE, /* VHOST_USER_SET_VRING_BASE */
- VHOST_GET_VRING_BASE, /* VHOST_USER_GET_VRING_BASE */
- VHOST_SET_VRING_KICK, /* VHOST_USER_SET_VRING_KICK */
- VHOST_SET_VRING_CALL, /* VHOST_USER_SET_VRING_CALL */
- VHOST_SET_VRING_ERR /* VHOST_USER_SET_VRING_ERR */
-};
-
-static VhostUserRequest vhost_user_request_translate(unsigned long int request)
-{
- VhostUserRequest idx;
-
- for (idx = 0; idx < VHOST_USER_MAX; idx++) {
- if (ioctl_to_vhost_user_request[idx] == request) {
- break;
- }
- }
-
- return (idx == VHOST_USER_MAX) ? VHOST_USER_NONE : idx;
-}
-
static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg)
{
CharDriverState *chr = dev->opaque;
@@ -128,8 +121,8 @@ 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.", r,
- size);
+ error_report("Failed to read msg header. Read %d instead of %d."
+ " Original request %d.", r, size, msg->request);
goto fail;
}
@@ -166,12 +159,35 @@ fail:
return -1;
}
+static bool vhost_user_one_time_request(VhostUserRequest request)
+{
+ switch (request) {
+ case VHOST_USER_SET_OWNER:
+ case VHOST_USER_RESET_OWNER:
+ case VHOST_USER_SET_MEM_TABLE:
+ case VHOST_USER_GET_QUEUE_NUM:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* most non-init callers ignore the error */
static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg,
int *fds, int fd_num)
{
CharDriverState *chr = dev->opaque;
int size = VHOST_USER_HDR_SIZE + msg->size;
+ /*
+ * For non-vring specific requests, like VHOST_USER_SET_MEM_TABLE,
+ * we just need send it once in the first time. For later such
+ * request, we just ignore it.
+ */
+ if (vhost_user_one_time_request(msg->request) && dev->vq_index != 0) {
+ return 0;
+ }
+
if (fd_num) {
qemu_chr_fe_set_msgfds(chr, fds, fd_num);
}
@@ -180,157 +196,364 @@ static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg,
0 : -1;
}
-static int vhost_user_call(struct vhost_dev *dev, unsigned long int request,
- void *arg)
+static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base,
+ struct vhost_log *log)
{
- VhostUserMsg msg;
- VhostUserRequest msg_request;
- struct vhost_vring_file *file = 0;
- int need_reply = 0;
int fds[VHOST_MEMORY_MAX_NREGIONS];
- int i, fd;
size_t fd_num = 0;
+ bool shmfd = virtio_has_feature(dev->protocol_features,
+ VHOST_USER_PROTOCOL_F_LOG_SHMFD);
+ VhostUserMsg msg = {
+ .request = VHOST_USER_SET_LOG_BASE,
+ .flags = VHOST_USER_VERSION,
+ .payload.log.mmap_size = log->size * sizeof(*(log->log)),
+ .payload.log.mmap_offset = 0,
+ .size = sizeof(msg.payload.log),
+ };
- assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER);
+ if (shmfd && log->fd != -1) {
+ fds[fd_num++] = log->fd;
+ }
- msg_request = vhost_user_request_translate(request);
- msg.request = msg_request;
- msg.flags = VHOST_USER_VERSION;
- msg.size = 0;
+ vhost_user_write(dev, &msg, fds, fd_num);
- switch (request) {
- case VHOST_GET_FEATURES:
- need_reply = 1;
- break;
-
- case VHOST_SET_FEATURES:
- case VHOST_SET_LOG_BASE:
- msg.u64 = *((__u64 *) arg);
- msg.size = sizeof(m.u64);
- break;
-
- case VHOST_SET_OWNER:
- case VHOST_RESET_OWNER:
- break;
-
- case VHOST_SET_MEM_TABLE:
- for (i = 0; i < dev->mem->nregions; ++i) {
- struct vhost_memory_region *reg = dev->mem->regions + i;
- ram_addr_t ram_addr;
-
- assert((uintptr_t)reg->userspace_addr == reg->userspace_addr);
- qemu_ram_addr_from_host((void *)(uintptr_t)reg->userspace_addr, &ram_addr);
- fd = qemu_get_ram_fd(ram_addr);
- if (fd > 0) {
- msg.memory.regions[fd_num].userspace_addr = reg->userspace_addr;
- msg.memory.regions[fd_num].memory_size = reg->memory_size;
- msg.memory.regions[fd_num].guest_phys_addr = reg->guest_phys_addr;
- msg.memory.regions[fd_num].mmap_offset = reg->userspace_addr -
- (uintptr_t) qemu_get_ram_block_host_ptr(ram_addr);
- assert(fd_num < VHOST_MEMORY_MAX_NREGIONS);
- fds[fd_num++] = fd;
- }
+ if (shmfd) {
+ msg.size = 0;
+ if (vhost_user_read(dev, &msg) < 0) {
+ return 0;
}
- msg.memory.nregions = fd_num;
-
- if (!fd_num) {
- error_report("Failed initializing vhost-user memory map, "
- "consider using -object memory-backend-file share=on");
+ if (msg.request != VHOST_USER_SET_LOG_BASE) {
+ error_report("Received unexpected msg type. "
+ "Expected %d received %d",
+ VHOST_USER_SET_LOG_BASE, msg.request);
return -1;
}
+ }
- msg.size = sizeof(m.memory.nregions);
- msg.size += sizeof(m.memory.padding);
- msg.size += fd_num * sizeof(VhostUserMemoryRegion);
-
- break;
-
- case VHOST_SET_LOG_FD:
- fds[fd_num++] = *((int *) arg);
- break;
-
- case VHOST_SET_VRING_NUM:
- case VHOST_SET_VRING_BASE:
- memcpy(&msg.state, arg, sizeof(struct vhost_vring_state));
- msg.size = sizeof(m.state);
- break;
-
- case VHOST_GET_VRING_BASE:
- memcpy(&msg.state, arg, sizeof(struct vhost_vring_state));
- msg.size = sizeof(m.state);
- need_reply = 1;
- break;
-
- case VHOST_SET_VRING_ADDR:
- memcpy(&msg.addr, arg, sizeof(struct vhost_vring_addr));
- msg.size = sizeof(m.addr);
- break;
-
- case VHOST_SET_VRING_KICK:
- case VHOST_SET_VRING_CALL:
- case VHOST_SET_VRING_ERR:
- file = arg;
- msg.u64 = file->index & VHOST_USER_VRING_IDX_MASK;
- msg.size = sizeof(m.u64);
- if (ioeventfd_enabled() && file->fd > 0) {
- fds[fd_num++] = file->fd;
- } else {
- msg.u64 |= VHOST_USER_VRING_NOFD_MASK;
+ return 0;
+}
+
+static int vhost_user_set_mem_table(struct vhost_dev *dev,
+ struct vhost_memory *mem)
+{
+ int fds[VHOST_MEMORY_MAX_NREGIONS];
+ int i, fd;
+ size_t fd_num = 0;
+ VhostUserMsg msg = {
+ .request = VHOST_USER_SET_MEM_TABLE,
+ .flags = VHOST_USER_VERSION,
+ };
+
+ for (i = 0; i < dev->mem->nregions; ++i) {
+ struct vhost_memory_region *reg = dev->mem->regions + i;
+ ram_addr_t ram_addr;
+
+ assert((uintptr_t)reg->userspace_addr == reg->userspace_addr);
+ qemu_ram_addr_from_host((void *)(uintptr_t)reg->userspace_addr,
+ &ram_addr);
+ fd = qemu_get_ram_fd(ram_addr);
+ if (fd > 0) {
+ msg.payload.memory.regions[fd_num].userspace_addr = reg->userspace_addr;
+ msg.payload.memory.regions[fd_num].memory_size = reg->memory_size;
+ msg.payload.memory.regions[fd_num].guest_phys_addr = reg->guest_phys_addr;
+ msg.payload.memory.regions[fd_num].mmap_offset = reg->userspace_addr -
+ (uintptr_t) qemu_get_ram_block_host_ptr(ram_addr);
+ assert(fd_num < VHOST_MEMORY_MAX_NREGIONS);
+ fds[fd_num++] = fd;
}
- break;
- default:
- error_report("vhost-user trying to send unhandled ioctl");
+ }
+
+ msg.payload.memory.nregions = fd_num;
+
+ if (!fd_num) {
+ error_report("Failed initializing vhost-user memory map, "
+ "consider using -object memory-backend-file share=on");
return -1;
- break;
}
- if (vhost_user_write(dev, &msg, fds, fd_num) < 0) {
+ msg.size = sizeof(msg.payload.memory.nregions);
+ msg.size += sizeof(msg.payload.memory.padding);
+ msg.size += fd_num * sizeof(VhostUserMemoryRegion);
+
+ vhost_user_write(dev, &msg, fds, fd_num);
+
+ return 0;
+}
+
+static int vhost_user_set_vring_addr(struct vhost_dev *dev,
+ struct vhost_vring_addr *addr)
+{
+ VhostUserMsg msg = {
+ .request = VHOST_USER_SET_VRING_ADDR,
+ .flags = VHOST_USER_VERSION,
+ .payload.addr = *addr,
+ .size = sizeof(msg.payload.addr),
+ };
+
+ vhost_user_write(dev, &msg, NULL, 0);
+
+ return 0;
+}
+
+static int vhost_user_set_vring_endian(struct vhost_dev *dev,
+ struct vhost_vring_state *ring)
+{
+ error_report("vhost-user trying to send unhandled ioctl");
+ return -1;
+}
+
+static int vhost_set_vring(struct vhost_dev *dev,
+ unsigned long int request,
+ struct vhost_vring_state *ring)
+{
+ VhostUserMsg msg = {
+ .request = request,
+ .flags = VHOST_USER_VERSION,
+ .payload.state = *ring,
+ .size = sizeof(msg.payload.state),
+ };
+
+ vhost_user_write(dev, &msg, NULL, 0);
+
+ return 0;
+}
+
+static int vhost_user_set_vring_num(struct vhost_dev *dev,
+ struct vhost_vring_state *ring)
+{
+ return vhost_set_vring(dev, VHOST_USER_SET_VRING_NUM, ring);
+}
+
+static int vhost_user_set_vring_base(struct vhost_dev *dev,
+ struct vhost_vring_state *ring)
+{
+ return vhost_set_vring(dev, VHOST_USER_SET_VRING_BASE, ring);
+}
+
+static int vhost_user_set_vring_enable(struct vhost_dev *dev, int enable)
+{
+ int i;
+
+ if (!virtio_has_feature(dev->features, VHOST_USER_F_PROTOCOL_FEATURES)) {
+ return -1;
+ }
+
+ for (i = 0; i < dev->nvqs; ++i) {
+ struct vhost_vring_state state = {
+ .index = dev->vq_index + i,
+ .num = enable,
+ };
+
+ vhost_set_vring(dev, VHOST_USER_SET_VRING_ENABLE, &state);
+ }
+
+ return 0;
+}
+
+static int vhost_user_get_vring_base(struct vhost_dev *dev,
+ struct vhost_vring_state *ring)
+{
+ VhostUserMsg msg = {
+ .request = VHOST_USER_GET_VRING_BASE,
+ .flags = VHOST_USER_VERSION,
+ .payload.state = *ring,
+ .size = sizeof(msg.payload.state),
+ };
+
+ vhost_user_write(dev, &msg, NULL, 0);
+
+ if (vhost_user_read(dev, &msg) < 0) {
return 0;
}
- if (need_reply) {
- if (vhost_user_read(dev, &msg) < 0) {
- return 0;
- }
+ if (msg.request != VHOST_USER_GET_VRING_BASE) {
+ error_report("Received unexpected msg type. Expected %d received %d",
+ VHOST_USER_GET_VRING_BASE, msg.request);
+ return -1;
+ }
- if (msg_request != msg.request) {
- error_report("Received unexpected msg type."
- " Expected %d received %d", msg_request, msg.request);
- return -1;
- }
+ if (msg.size != sizeof(msg.payload.state)) {
+ error_report("Received bad msg size.");
+ return -1;
+ }
- switch (msg_request) {
- case VHOST_USER_GET_FEATURES:
- if (msg.size != sizeof(m.u64)) {
- 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.");
- return -1;
- }
- memcpy(arg, &msg.state, sizeof(struct vhost_vring_state));
- break;
- default:
- error_report("Received unexpected msg type.");
- return -1;
- break;
- }
+ *ring = msg.payload.state;
+
+ return 0;
+}
+
+static int vhost_set_vring_file(struct vhost_dev *dev,
+ VhostUserRequest request,
+ struct vhost_vring_file *file)
+{
+ int fds[VHOST_MEMORY_MAX_NREGIONS];
+ size_t fd_num = 0;
+ VhostUserMsg msg = {
+ .request = request,
+ .flags = VHOST_USER_VERSION,
+ .payload.u64 = file->index & VHOST_USER_VRING_IDX_MASK,
+ .size = sizeof(msg.payload.u64),
+ };
+
+ if (ioeventfd_enabled() && file->fd > 0) {
+ fds[fd_num++] = file->fd;
+ } else {
+ msg.payload.u64 |= VHOST_USER_VRING_NOFD_MASK;
}
+ vhost_user_write(dev, &msg, fds, fd_num);
+
+ return 0;
+}
+
+static int vhost_user_set_vring_kick(struct vhost_dev *dev,
+ struct vhost_vring_file *file)
+{
+ return vhost_set_vring_file(dev, VHOST_USER_SET_VRING_KICK, file);
+}
+
+static int vhost_user_set_vring_call(struct vhost_dev *dev,
+ struct vhost_vring_file *file)
+{
+ return vhost_set_vring_file(dev, VHOST_USER_SET_VRING_CALL, file);
+}
+
+static int vhost_user_set_u64(struct vhost_dev *dev, int request, uint64_t u64)
+{
+ VhostUserMsg msg = {
+ .request = request,
+ .flags = VHOST_USER_VERSION,
+ .payload.u64 = u64,
+ .size = sizeof(msg.payload.u64),
+ };
+
+ vhost_user_write(dev, &msg, NULL, 0);
+
+ return 0;
+}
+
+static int vhost_user_set_features(struct vhost_dev *dev,
+ uint64_t features)
+{
+ return vhost_user_set_u64(dev, VHOST_USER_SET_FEATURES, features);
+}
+
+static int vhost_user_set_protocol_features(struct vhost_dev *dev,
+ uint64_t features)
+{
+ return vhost_user_set_u64(dev, VHOST_USER_SET_PROTOCOL_FEATURES, features);
+}
+
+static int vhost_user_get_u64(struct vhost_dev *dev, int request, uint64_t *u64)
+{
+ VhostUserMsg msg = {
+ .request = request,
+ .flags = VHOST_USER_VERSION,
+ };
+
+ if (vhost_user_one_time_request(request) && dev->vq_index != 0) {
+ return 0;
+ }
+
+ vhost_user_write(dev, &msg, NULL, 0);
+
+ if (vhost_user_read(dev, &msg) < 0) {
+ return 0;
+ }
+
+ if (msg.request != request) {
+ error_report("Received unexpected msg type. Expected %d received %d",
+ request, msg.request);
+ return -1;
+ }
+
+ if (msg.size != sizeof(msg.payload.u64)) {
+ error_report("Received bad msg size.");
+ return -1;
+ }
+
+ *u64 = msg.payload.u64;
+
+ return 0;
+}
+
+static int vhost_user_get_features(struct vhost_dev *dev, uint64_t *features)
+{
+ return vhost_user_get_u64(dev, VHOST_USER_GET_FEATURES, features);
+}
+
+static int vhost_user_set_owner(struct vhost_dev *dev)
+{
+ VhostUserMsg msg = {
+ .request = VHOST_USER_SET_OWNER,
+ .flags = VHOST_USER_VERSION,
+ };
+
+ vhost_user_write(dev, &msg, NULL, 0);
+
+ return 0;
+}
+
+static int vhost_user_reset_device(struct vhost_dev *dev)
+{
+ VhostUserMsg msg = {
+ .request = VHOST_USER_RESET_OWNER,
+ .flags = VHOST_USER_VERSION,
+ };
+
+ vhost_user_write(dev, &msg, NULL, 0);
+
return 0;
}
static int vhost_user_init(struct vhost_dev *dev, void *opaque)
{
+ uint64_t features;
+ int err;
+
assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER);
dev->opaque = opaque;
+ err = vhost_user_get_features(dev, &features);
+ if (err < 0) {
+ return err;
+ }
+
+ if (virtio_has_feature(features, VHOST_USER_F_PROTOCOL_FEATURES)) {
+ dev->backend_features |= 1ULL << VHOST_USER_F_PROTOCOL_FEATURES;
+
+ err = vhost_user_get_u64(dev, VHOST_USER_GET_PROTOCOL_FEATURES,
+ &features);
+ if (err < 0) {
+ return err;
+ }
+
+ dev->protocol_features = features & VHOST_USER_PROTOCOL_FEATURE_MASK;
+ err = vhost_user_set_protocol_features(dev, dev->protocol_features);
+ if (err < 0) {
+ return err;
+ }
+
+ /* query the max queues we support if backend supports Multiple Queue */
+ if (dev->protocol_features & (1ULL << VHOST_USER_PROTOCOL_F_MQ)) {
+ err = vhost_user_get_u64(dev, VHOST_USER_GET_QUEUE_NUM,
+ &dev->max_queues);
+ if (err < 0) {
+ return err;
+ }
+ }
+ }
+
+ if (dev->migration_blocker == NULL &&
+ !virtio_has_feature(dev->protocol_features,
+ VHOST_USER_PROTOCOL_F_LOG_SHMFD)) {
+ error_setg(&dev->migration_blocker,
+ "Migration disabled: vhost-user backend lacks "
+ "VHOST_USER_PROTOCOL_F_LOG_SHMFD feature.");
+ }
+
return 0;
}
@@ -343,9 +566,92 @@ static int vhost_user_cleanup(struct vhost_dev *dev)
return 0;
}
+static int vhost_user_get_vq_index(struct vhost_dev *dev, int idx)
+{
+ assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs);
+
+ return idx;
+}
+
+static int vhost_user_memslots_limit(struct vhost_dev *dev)
+{
+ return VHOST_MEMORY_MAX_NREGIONS;
+}
+
+static bool vhost_user_requires_shm_log(struct vhost_dev *dev)
+{
+ assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER);
+
+ return virtio_has_feature(dev->protocol_features,
+ VHOST_USER_PROTOCOL_F_LOG_SHMFD);
+}
+
+static int vhost_user_migration_done(struct vhost_dev *dev, char* mac_addr)
+{
+ VhostUserMsg msg = { 0 };
+ int err;
+
+ assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER);
+
+ /* If guest supports GUEST_ANNOUNCE do nothing */
+ if (virtio_has_feature(dev->acked_features, VIRTIO_NET_F_GUEST_ANNOUNCE)) {
+ return 0;
+ }
+
+ /* if backend supports VHOST_USER_PROTOCOL_F_RARP ask it to send the RARP */
+ if (virtio_has_feature(dev->protocol_features,
+ VHOST_USER_PROTOCOL_F_RARP)) {
+ msg.request = VHOST_USER_SEND_RARP;
+ msg.flags = VHOST_USER_VERSION;
+ memcpy((char *)&msg.payload.u64, mac_addr, 6);
+ msg.size = sizeof(msg.payload.u64);
+
+ err = vhost_user_write(dev, &msg, NULL, 0);
+ return err;
+ }
+ return -1;
+}
+
+static bool vhost_user_can_merge(struct vhost_dev *dev,
+ uint64_t start1, uint64_t size1,
+ uint64_t start2, uint64_t size2)
+{
+ ram_addr_t ram_addr;
+ int mfd, rfd;
+ MemoryRegion *mr;
+
+ mr = qemu_ram_addr_from_host((void *)(uintptr_t)start1, &ram_addr);
+ assert(mr);
+ mfd = qemu_get_ram_fd(ram_addr);
+
+ mr = qemu_ram_addr_from_host((void *)(uintptr_t)start2, &ram_addr);
+ assert(mr);
+ rfd = qemu_get_ram_fd(ram_addr);
+
+ return mfd == rfd;
+}
+
const VhostOps user_ops = {
.backend_type = VHOST_BACKEND_TYPE_USER,
- .vhost_call = vhost_user_call,
.vhost_backend_init = vhost_user_init,
- .vhost_backend_cleanup = vhost_user_cleanup
- };
+ .vhost_backend_cleanup = vhost_user_cleanup,
+ .vhost_backend_memslots_limit = vhost_user_memslots_limit,
+ .vhost_set_log_base = vhost_user_set_log_base,
+ .vhost_set_mem_table = vhost_user_set_mem_table,
+ .vhost_set_vring_addr = vhost_user_set_vring_addr,
+ .vhost_set_vring_endian = vhost_user_set_vring_endian,
+ .vhost_set_vring_num = vhost_user_set_vring_num,
+ .vhost_set_vring_base = vhost_user_set_vring_base,
+ .vhost_get_vring_base = vhost_user_get_vring_base,
+ .vhost_set_vring_kick = vhost_user_set_vring_kick,
+ .vhost_set_vring_call = vhost_user_set_vring_call,
+ .vhost_set_features = vhost_user_set_features,
+ .vhost_get_features = vhost_user_get_features,
+ .vhost_set_owner = vhost_user_set_owner,
+ .vhost_reset_device = vhost_user_reset_device,
+ .vhost_get_vq_index = vhost_user_get_vq_index,
+ .vhost_set_vring_enable = vhost_user_set_vring_enable,
+ .vhost_requires_shm_log = vhost_user_requires_shm_log,
+ .vhost_migration_done = vhost_user_migration_done,
+ .vhost_backend_can_merge = vhost_user_can_merge,
+};
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index a08c36bb4..90c60a7e9 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -18,6 +18,7 @@
#include "qemu/atomic.h"
#include "qemu/range.h"
#include "qemu/error-report.h"
+#include "qemu/memfd.h"
#include <linux/vhost.h>
#include "exec/address-spaces.h"
#include "hw/virtio/virtio-bus.h"
@@ -25,6 +26,23 @@
#include "migration/migration.h"
static struct vhost_log *vhost_log;
+static struct vhost_log *vhost_log_shm;
+
+static unsigned int used_memslots;
+static QLIST_HEAD(, vhost_dev) vhost_devices =
+ QLIST_HEAD_INITIALIZER(vhost_devices);
+
+bool vhost_has_free_slot(void)
+{
+ unsigned int slots_limit = ~0U;
+ struct vhost_dev *hdev;
+
+ QLIST_FOREACH(hdev, &vhost_devices, entry) {
+ unsigned int r = hdev->vhost_ops->vhost_backend_memslots_limit(hdev);
+ slots_limit = MIN(slots_limit, r);
+ }
+ return slots_limit > used_memslots;
+}
static void vhost_dev_sync_region(struct vhost_dev *dev,
MemoryRegionSection *section,
@@ -241,6 +259,13 @@ static void vhost_dev_assign_memory(struct vhost_dev *dev,
continue;
}
+ if (dev->vhost_ops->vhost_backend_can_merge &&
+ !dev->vhost_ops->vhost_backend_can_merge(dev, uaddr, size,
+ reg->userspace_addr,
+ reg->memory_size)) {
+ continue;
+ }
+
if (merged) {
--to;
assert(to >= 0);
@@ -286,25 +311,46 @@ static uint64_t vhost_get_log_size(struct vhost_dev *dev)
}
return log_size;
}
-static struct vhost_log *vhost_log_alloc(uint64_t size)
+
+static struct vhost_log *vhost_log_alloc(uint64_t size, bool share)
{
- struct vhost_log *log = g_malloc0(sizeof *log + size * sizeof(*(log->log)));
+ struct vhost_log *log;
+ uint64_t logsize = size * sizeof(*(log->log));
+ int fd = -1;
+
+ log = g_new0(struct vhost_log, 1);
+ if (share) {
+ log->log = qemu_memfd_alloc("vhost-log", logsize,
+ F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL,
+ &fd);
+ memset(log->log, 0, logsize);
+ } else {
+ log->log = g_malloc0(logsize);
+ }
log->size = size;
log->refcnt = 1;
+ log->fd = fd;
return log;
}
-static struct vhost_log *vhost_log_get(uint64_t size)
+static struct vhost_log *vhost_log_get(uint64_t size, bool share)
{
- if (!vhost_log || vhost_log->size != size) {
- vhost_log = vhost_log_alloc(size);
+ struct vhost_log *log = share ? vhost_log_shm : vhost_log;
+
+ if (!log || log->size != size) {
+ log = vhost_log_alloc(size, share);
+ if (share) {
+ vhost_log_shm = log;
+ } else {
+ vhost_log = log;
+ }
} else {
- ++vhost_log->refcnt;
+ ++log->refcnt;
}
- return vhost_log;
+ return log;
}
static void vhost_log_put(struct vhost_dev *dev, bool sync)
@@ -321,20 +367,35 @@ static void vhost_log_put(struct vhost_dev *dev, bool sync)
if (dev->log_size && sync) {
vhost_log_sync_range(dev, 0, dev->log_size * VHOST_LOG_CHUNK - 1);
}
+
if (vhost_log == log) {
+ g_free(log->log);
vhost_log = NULL;
+ } else if (vhost_log_shm == log) {
+ qemu_memfd_free(log->log, log->size * sizeof(*(log->log)),
+ log->fd);
+ vhost_log_shm = NULL;
}
+
g_free(log);
}
}
-static inline void vhost_dev_log_resize(struct vhost_dev* dev, uint64_t size)
+static bool vhost_dev_log_is_shared(struct vhost_dev *dev)
+{
+ return dev->vhost_ops->vhost_requires_shm_log &&
+ dev->vhost_ops->vhost_requires_shm_log(dev);
+}
+
+static inline void vhost_dev_log_resize(struct vhost_dev *dev, uint64_t size)
{
- struct vhost_log *log = vhost_log_get(size);
+ struct vhost_log *log = vhost_log_get(size, vhost_dev_log_is_shared(dev));
uint64_t log_base = (uintptr_t)log->log;
int r;
- r = dev->vhost_ops->vhost_call(dev, VHOST_SET_LOG_BASE, &log_base);
+ /* inform backend of log switching, this must be done before
+ releasing the current log, to ensure no logging is lost */
+ r = dev->vhost_ops->vhost_set_log_base(dev, log_base, log);
assert(r >= 0);
vhost_log_put(dev, true);
dev->log = log;
@@ -457,6 +518,7 @@ static void vhost_set_memory(MemoryListener *listener,
dev->mem_changed_start_addr = MIN(dev->mem_changed_start_addr, start_addr);
dev->mem_changed_end_addr = MAX(dev->mem_changed_end_addr, start_addr + size - 1);
dev->memory_changed = true;
+ used_memslots = dev->mem->nregions;
}
static bool vhost_section(MemoryRegionSection *section)
@@ -500,7 +562,7 @@ static void vhost_commit(MemoryListener *listener)
}
if (!dev->log_enabled) {
- r = dev->vhost_ops->vhost_call(dev, VHOST_SET_MEM_TABLE, dev->mem);
+ r = dev->vhost_ops->vhost_set_mem_table(dev, dev->mem);
assert(r >= 0);
dev->memory_changed = false;
return;
@@ -513,7 +575,7 @@ static void vhost_commit(MemoryListener *listener)
if (dev->log_size < log_size) {
vhost_dev_log_resize(dev, log_size + VHOST_LOG_BUFFER);
}
- r = dev->vhost_ops->vhost_call(dev, VHOST_SET_MEM_TABLE, dev->mem);
+ r = dev->vhost_ops->vhost_set_mem_table(dev, dev->mem);
assert(r >= 0);
/* To log less, can only decrease log size after table update. */
if (dev->log_size > log_size + VHOST_LOG_BUFFER) {
@@ -581,7 +643,7 @@ static int vhost_virtqueue_set_addr(struct vhost_dev *dev,
.log_guest_addr = vq->used_phys,
.flags = enable_log ? (1 << VHOST_VRING_F_LOG) : 0,
};
- int r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_ADDR, &addr);
+ int r = dev->vhost_ops->vhost_set_vring_addr(dev, &addr);
if (r < 0) {
return -errno;
}
@@ -595,19 +657,20 @@ static int vhost_dev_set_features(struct vhost_dev *dev, bool enable_log)
if (enable_log) {
features |= 0x1ULL << VHOST_F_LOG_ALL;
}
- r = dev->vhost_ops->vhost_call(dev, VHOST_SET_FEATURES, &features);
+ r = dev->vhost_ops->vhost_set_features(dev, features);
return r < 0 ? -errno : 0;
}
static int vhost_dev_set_log(struct vhost_dev *dev, bool enable_log)
{
- int r, t, i;
+ int r, t, i, idx;
r = vhost_dev_set_features(dev, enable_log);
if (r < 0) {
goto err_features;
}
for (i = 0; i < dev->nvqs; ++i) {
- r = vhost_virtqueue_set_addr(dev, dev->vqs + i, i,
+ idx = dev->vhost_ops->vhost_get_vq_index(dev, dev->vq_index + i);
+ r = vhost_virtqueue_set_addr(dev, dev->vqs + i, idx,
enable_log);
if (r < 0) {
goto err_vq;
@@ -616,7 +679,8 @@ static int vhost_dev_set_log(struct vhost_dev *dev, bool enable_log)
return 0;
err_vq:
for (; i >= 0; --i) {
- t = vhost_virtqueue_set_addr(dev, dev->vqs + i, i,
+ idx = dev->vhost_ops->vhost_get_vq_index(dev, dev->vq_index + i);
+ t = vhost_virtqueue_set_addr(dev, dev->vqs + i, idx,
dev->log_enabled);
assert(t >= 0);
}
@@ -700,7 +764,7 @@ static int vhost_virtqueue_set_vring_endian_legacy(struct vhost_dev *dev,
.num = is_big_endian
};
- if (!dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_ENDIAN, &s)) {
+ if (!dev->vhost_ops->vhost_set_vring_endian(dev, &s)) {
return 0;
}
@@ -719,7 +783,7 @@ static int vhost_virtqueue_start(struct vhost_dev *dev,
{
hwaddr s, l, a;
int r;
- int vhost_vq_index = idx - dev->vq_index;
+ int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, idx);
struct vhost_vring_file file = {
.index = vhost_vq_index
};
@@ -728,16 +792,15 @@ static int vhost_virtqueue_start(struct vhost_dev *dev,
};
struct VirtQueue *vvq = virtio_get_queue(vdev, idx);
- assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs);
vq->num = state.num = virtio_queue_get_num(vdev, idx);
- r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_NUM, &state);
+ r = dev->vhost_ops->vhost_set_vring_num(dev, &state);
if (r) {
return -errno;
}
state.num = virtio_queue_get_last_avail_idx(vdev, idx);
- r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_BASE, &state);
+ r = dev->vhost_ops->vhost_set_vring_base(dev, &state);
if (r) {
return -errno;
}
@@ -789,7 +852,7 @@ static int vhost_virtqueue_start(struct vhost_dev *dev,
}
file.fd = event_notifier_get_fd(virtio_queue_get_host_notifier(vvq));
- r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_KICK, &file);
+ r = dev->vhost_ops->vhost_set_vring_kick(dev, &file);
if (r) {
r = -errno;
goto fail_kick;
@@ -822,13 +885,13 @@ static void vhost_virtqueue_stop(struct vhost_dev *dev,
struct vhost_virtqueue *vq,
unsigned idx)
{
- int vhost_vq_index = idx - dev->vq_index;
+ int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, idx);
struct vhost_vring_state state = {
.index = vhost_vq_index,
};
int r;
- assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs);
- r = dev->vhost_ops->vhost_call(dev, VHOST_GET_VRING_BASE, &state);
+
+ r = dev->vhost_ops->vhost_get_vring_base(dev, &state);
if (r < 0) {
fprintf(stderr, "vhost VQ %d ring restore failed: %d\n", idx, r);
fflush(stderr);
@@ -875,8 +938,9 @@ static void vhost_eventfd_del(MemoryListener *listener,
static int vhost_virtqueue_init(struct vhost_dev *dev,
struct vhost_virtqueue *vq, int n)
{
+ int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, n);
struct vhost_vring_file file = {
- .index = n,
+ .index = vhost_vq_index,
};
int r = event_notifier_init(&vq->masked_notifier, 0);
if (r < 0) {
@@ -884,7 +948,7 @@ static int vhost_virtqueue_init(struct vhost_dev *dev,
}
file.fd = event_notifier_get_fd(&vq->masked_notifier);
- r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_CALL, &file);
+ r = dev->vhost_ops->vhost_set_vring_call(dev, &file);
if (r) {
r = -errno;
goto fail_call;
@@ -906,6 +970,8 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
uint64_t features;
int i, r;
+ hdev->migration_blocker = NULL;
+
if (vhost_set_backend_type(hdev, backend_type) < 0) {
close((uintptr_t)opaque);
return -1;
@@ -916,18 +982,26 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
return -errno;
}
- r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_OWNER, NULL);
+ if (used_memslots > hdev->vhost_ops->vhost_backend_memslots_limit(hdev)) {
+ fprintf(stderr, "vhost backend memory slots limit is less"
+ " than current number of present memory slots\n");
+ close((uintptr_t)opaque);
+ return -1;
+ }
+ QLIST_INSERT_HEAD(&vhost_devices, hdev, entry);
+
+ r = hdev->vhost_ops->vhost_set_owner(hdev);
if (r < 0) {
goto fail;
}
- r = hdev->vhost_ops->vhost_call(hdev, VHOST_GET_FEATURES, &features);
+ r = hdev->vhost_ops->vhost_get_features(hdev, &features);
if (r < 0) {
goto fail;
}
for (i = 0; i < hdev->nvqs; ++i) {
- r = vhost_virtqueue_init(hdev, hdev->vqs + i, i);
+ r = vhost_virtqueue_init(hdev, hdev->vqs + i, hdev->vq_index + i);
if (r < 0) {
goto fail_vq;
}
@@ -949,12 +1023,21 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
.eventfd_del = vhost_eventfd_del,
.priority = 10
};
- hdev->migration_blocker = NULL;
- if (!(hdev->features & (0x1ULL << VHOST_F_LOG_ALL))) {
- error_setg(&hdev->migration_blocker,
- "Migration disabled: vhost lacks VHOST_F_LOG_ALL feature.");
+
+ if (hdev->migration_blocker == NULL) {
+ if (!(hdev->features & (0x1ULL << VHOST_F_LOG_ALL))) {
+ error_setg(&hdev->migration_blocker,
+ "Migration disabled: vhost lacks VHOST_F_LOG_ALL feature.");
+ } else if (!qemu_memfd_check()) {
+ error_setg(&hdev->migration_blocker,
+ "Migration disabled: failed to allocate shared memory");
+ }
+ }
+
+ if (hdev->migration_blocker != NULL) {
migrate_add_blocker(hdev->migration_blocker);
}
+
hdev->mem = g_malloc0(offsetof(struct vhost_memory, regions));
hdev->n_mem_sections = 0;
hdev->mem_sections = NULL;
@@ -972,6 +1055,7 @@ fail_vq:
fail:
r = -errno;
hdev->vhost_ops->vhost_backend_cleanup(hdev);
+ QLIST_REMOVE(hdev, entry);
return r;
}
@@ -989,6 +1073,7 @@ void vhost_dev_cleanup(struct vhost_dev *hdev)
g_free(hdev->mem);
g_free(hdev->mem_sections);
hdev->vhost_ops->vhost_backend_cleanup(hdev);
+ QLIST_REMOVE(hdev, entry);
}
/* Stop processing guest IO notifications in qemu.
@@ -1066,18 +1151,16 @@ void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n,
{
struct VirtQueue *vvq = virtio_get_queue(vdev, n);
int r, index = n - hdev->vq_index;
+ struct vhost_vring_file file;
- assert(n >= hdev->vq_index && n < hdev->vq_index + hdev->nvqs);
-
- struct vhost_vring_file file = {
- .index = index
- };
if (mask) {
file.fd = event_notifier_get_fd(&hdev->vqs[index].masked_notifier);
} else {
file.fd = event_notifier_get_fd(virtio_queue_get_guest_notifier(vvq));
}
- r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_VRING_CALL, &file);
+
+ file.index = hdev->vhost_ops->vhost_get_vq_index(hdev, n);
+ r = hdev->vhost_ops->vhost_set_vring_call(hdev, &file);
assert(r >= 0);
}
@@ -1119,7 +1202,7 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
if (r < 0) {
goto fail_features;
}
- r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_MEM_TABLE, hdev->mem);
+ r = hdev->vhost_ops->vhost_set_mem_table(hdev, hdev->mem);
if (r < 0) {
r = -errno;
goto fail_mem;
@@ -1138,10 +1221,12 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
uint64_t log_base;
hdev->log_size = vhost_get_log_size(hdev);
- hdev->log = vhost_log_get(hdev->log_size);
+ hdev->log = vhost_log_get(hdev->log_size,
+ vhost_dev_log_is_shared(hdev));
log_base = (uintptr_t)hdev->log->log;
- r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_LOG_BASE,
- hdev->log_size ? &log_base : NULL);
+ r = hdev->vhost_ops->vhost_set_log_base(hdev,
+ hdev->log_size ? log_base : 0,
+ hdev->log);
if (r < 0) {
r = -errno;
goto fail_log;
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index c419b1714..9671635e6 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -37,9 +37,11 @@
static void balloon_page(void *addr, int deflate)
{
#if defined(__linux__)
- if (!kvm_enabled() || kvm_has_sync_mmu())
+ if (!qemu_balloon_is_inhibited() && (!kvm_enabled() ||
+ kvm_has_sync_mmu())) {
qemu_madvise(addr, TARGET_PAGE_SIZE,
deflate ? QEMU_MADV_WILLNEED : QEMU_MADV_DONTNEED);
+ }
#endif
}
diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c
index febda76b9..81c7cdd50 100644
--- a/hw/virtio/virtio-bus.c
+++ b/hw/virtio/virtio-bus.c
@@ -56,6 +56,9 @@ void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp)
assert(vdc->get_features != NULL);
vdev->host_features = vdc->get_features(vdev, vdev->host_features,
errp);
+ if (klass->post_plugged != NULL) {
+ klass->post_plugged(qbus->parent, errp);
+ }
}
/* Reset the virtio_bus */
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index c024161f5..94667e625 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -86,6 +86,129 @@ static void virtio_pci_save_config(DeviceState *d, QEMUFile *f)
qemu_put_be16(f, vdev->config_vector);
}
+static void virtio_pci_load_modern_queue_state(VirtIOPCIQueue *vq,
+ QEMUFile *f)
+{
+ vq->num = qemu_get_be16(f);
+ vq->enabled = qemu_get_be16(f);
+ vq->desc[0] = qemu_get_be32(f);
+ vq->desc[1] = qemu_get_be32(f);
+ vq->avail[0] = qemu_get_be32(f);
+ vq->avail[1] = qemu_get_be32(f);
+ vq->used[0] = qemu_get_be32(f);
+ vq->used[1] = qemu_get_be32(f);
+}
+
+static bool virtio_pci_has_extra_state(DeviceState *d)
+{
+ VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+
+ return proxy->flags & VIRTIO_PCI_FLAG_MIGRATE_EXTRA;
+}
+
+static int get_virtio_pci_modern_state(QEMUFile *f, void *pv, size_t size)
+{
+ VirtIOPCIProxy *proxy = pv;
+ int i;
+
+ proxy->dfselect = qemu_get_be32(f);
+ proxy->gfselect = qemu_get_be32(f);
+ proxy->guest_features[0] = qemu_get_be32(f);
+ proxy->guest_features[1] = qemu_get_be32(f);
+ for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
+ virtio_pci_load_modern_queue_state(&proxy->vqs[i], f);
+ }
+
+ return 0;
+}
+
+static void virtio_pci_save_modern_queue_state(VirtIOPCIQueue *vq,
+ QEMUFile *f)
+{
+ qemu_put_be16(f, vq->num);
+ qemu_put_be16(f, vq->enabled);
+ qemu_put_be32(f, vq->desc[0]);
+ qemu_put_be32(f, vq->desc[1]);
+ qemu_put_be32(f, vq->avail[0]);
+ qemu_put_be32(f, vq->avail[1]);
+ qemu_put_be32(f, vq->used[0]);
+ qemu_put_be32(f, vq->used[1]);
+}
+
+static void put_virtio_pci_modern_state(QEMUFile *f, void *pv, size_t size)
+{
+ VirtIOPCIProxy *proxy = pv;
+ int i;
+
+ qemu_put_be32(f, proxy->dfselect);
+ qemu_put_be32(f, proxy->gfselect);
+ qemu_put_be32(f, proxy->guest_features[0]);
+ qemu_put_be32(f, proxy->guest_features[1]);
+ for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
+ virtio_pci_save_modern_queue_state(&proxy->vqs[i], f);
+ }
+}
+
+static const VMStateInfo vmstate_info_virtio_pci_modern_state = {
+ .name = "virtqueue_state",
+ .get = get_virtio_pci_modern_state,
+ .put = put_virtio_pci_modern_state,
+};
+
+static bool virtio_pci_modern_state_needed(void *opaque)
+{
+ VirtIOPCIProxy *proxy = opaque;
+
+ return !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN);
+}
+
+static const VMStateDescription vmstate_virtio_pci_modern_state = {
+ .name = "virtio_pci/modern_state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = &virtio_pci_modern_state_needed,
+ .fields = (VMStateField[]) {
+ {
+ .name = "modern_state",
+ .version_id = 0,
+ .field_exists = NULL,
+ .size = 0,
+ .info = &vmstate_info_virtio_pci_modern_state,
+ .flags = VMS_SINGLE,
+ .offset = 0,
+ },
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_virtio_pci = {
+ .name = "virtio_pci",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_END_OF_LIST()
+ },
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_virtio_pci_modern_state,
+ NULL
+ }
+};
+
+static void virtio_pci_save_extra_state(DeviceState *d, QEMUFile *f)
+{
+ VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+
+ vmstate_save_state(f, &vmstate_virtio_pci, proxy, NULL);
+}
+
+static int virtio_pci_load_extra_state(DeviceState *d, QEMUFile *f)
+{
+ VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+
+ return vmstate_load_state(f, &vmstate_virtio_pci, proxy, 1);
+}
+
static void virtio_pci_save_queue(DeviceState *d, int n, QEMUFile *f)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
@@ -133,6 +256,7 @@ static int virtio_pci_load_queue(DeviceState *d, int n, QEMUFile *f)
if (vector != VIRTIO_NO_VECTOR) {
return msix_vector_use(&proxy->pci_dev, vector);
}
+
return 0;
}
@@ -146,7 +270,10 @@ static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
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);
+ bool fast_mmio = kvm_ioeventfd_any_length_enabled();
+ bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY;
MemoryRegion *modern_mr = &proxy->notify.mr;
+ MemoryRegion *modern_notify_mr = &proxy->notify_pio.mr;
MemoryRegion *legacy_mr = &proxy->bar;
hwaddr modern_addr = QEMU_VIRTIO_PCI_QUEUE_MEM_MULT *
virtio_get_queue_index(vq);
@@ -162,8 +289,17 @@ static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
}
virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler);
if (modern) {
- memory_region_add_eventfd(modern_mr, modern_addr, 2,
- true, n, notifier);
+ if (fast_mmio) {
+ memory_region_add_eventfd(modern_mr, modern_addr, 0,
+ false, n, notifier);
+ } else {
+ memory_region_add_eventfd(modern_mr, modern_addr, 2,
+ false, n, notifier);
+ }
+ if (modern_pio) {
+ memory_region_add_eventfd(modern_notify_mr, 0, 2,
+ true, n, notifier);
+ }
}
if (legacy) {
memory_region_add_eventfd(legacy_mr, legacy_addr, 2,
@@ -171,8 +307,17 @@ static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
}
} else {
if (modern) {
- memory_region_del_eventfd(modern_mr, modern_addr, 2,
- true, n, notifier);
+ if (fast_mmio) {
+ memory_region_del_eventfd(modern_mr, modern_addr, 0,
+ false, n, notifier);
+ } else {
+ memory_region_del_eventfd(modern_mr, modern_addr, 2,
+ false, n, notifier);
+ }
+ if (modern_pio) {
+ memory_region_del_eventfd(modern_notify_mr, 0, 2,
+ true, n, notifier);
+ }
}
if (legacy) {
memory_region_del_eventfd(legacy_mr, legacy_addr, 2,
@@ -590,7 +735,7 @@ static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy,
int ret;
if (irqfd->users == 0) {
- ret = kvm_irqchip_add_msi_route(kvm_state, msg);
+ ret = kvm_irqchip_add_msi_route(kvm_state, msg, &proxy->pci_dev);
if (ret < 0) {
return ret;
}
@@ -726,7 +871,8 @@ static int virtio_pci_vq_vector_unmask(VirtIOPCIProxy *proxy,
if (proxy->vector_irqfd) {
irqfd = &proxy->vector_irqfd[vector];
if (irqfd->msg.data != msg.data || irqfd->msg.address != msg.address) {
- ret = kvm_irqchip_update_msi_route(kvm_state, irqfd->virq, msg);
+ ret = kvm_irqchip_update_msi_route(kvm_state, irqfd->virq, msg,
+ &proxy->pci_dev);
if (ret < 0) {
return ret;
}
@@ -1238,6 +1384,7 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr,
proxy->vqs[vdev->queue_sel].avail[0],
((uint64_t)proxy->vqs[vdev->queue_sel].used[1]) << 32 |
proxy->vqs[vdev->queue_sel].used[0]);
+ proxy->vqs[vdev->queue_sel].enabled = 1;
break;
case VIRTIO_PCI_COMMON_Q_DESCLO:
proxy->vqs[vdev->queue_sel].desc[0] = val;
@@ -1280,6 +1427,17 @@ static void virtio_pci_notify_write(void *opaque, hwaddr addr,
}
}
+static void virtio_pci_notify_write_pio(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ VirtIODevice *vdev = opaque;
+ unsigned queue = val;
+
+ if (queue < VIRTIO_QUEUE_MAX) {
+ virtio_queue_notify(vdev, queue);
+ }
+}
+
static uint64_t virtio_pci_isr_read(void *opaque, hwaddr addr,
unsigned size)
{
@@ -1373,6 +1531,16 @@ static void virtio_pci_modern_regions_init(VirtIOPCIProxy *proxy)
},
.endianness = DEVICE_LITTLE_ENDIAN,
};
+ static const MemoryRegionOps notify_pio_ops = {
+ .read = virtio_pci_notify_read,
+ .write = virtio_pci_notify_write_pio,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ };
+
memory_region_init_io(&proxy->common.mr, OBJECT(proxy),
&common_ops,
@@ -1397,30 +1565,60 @@ static void virtio_pci_modern_regions_init(VirtIOPCIProxy *proxy)
virtio_bus_get_device(&proxy->bus),
"virtio-pci-notify",
proxy->notify.size);
+
+ memory_region_init_io(&proxy->notify_pio.mr, OBJECT(proxy),
+ &notify_pio_ops,
+ virtio_bus_get_device(&proxy->bus),
+ "virtio-pci-notify-pio",
+ proxy->notify.size);
}
static void virtio_pci_modern_region_map(VirtIOPCIProxy *proxy,
VirtIOPCIRegion *region,
- struct virtio_pci_cap *cap)
+ struct virtio_pci_cap *cap,
+ MemoryRegion *mr,
+ uint8_t bar)
{
- memory_region_add_subregion(&proxy->modern_bar,
- region->offset,
- &region->mr);
+ memory_region_add_subregion(mr, region->offset, &region->mr);
cap->cfg_type = region->type;
- cap->bar = proxy->modern_mem_bar;
+ cap->bar = 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_mem_region_map(VirtIOPCIProxy *proxy,
+ VirtIOPCIRegion *region,
+ struct virtio_pci_cap *cap)
+{
+ virtio_pci_modern_region_map(proxy, region, cap,
+ &proxy->modern_bar, proxy->modern_mem_bar);
+}
+
+static void virtio_pci_modern_io_region_map(VirtIOPCIProxy *proxy,
+ VirtIOPCIRegion *region,
+ struct virtio_pci_cap *cap)
+{
+ virtio_pci_modern_region_map(proxy, region, cap,
+ &proxy->io_bar, proxy->modern_io_bar);
}
-static void virtio_pci_modern_region_unmap(VirtIOPCIProxy *proxy,
- VirtIOPCIRegion *region)
+static void virtio_pci_modern_mem_region_unmap(VirtIOPCIProxy *proxy,
+ VirtIOPCIRegion *region)
{
memory_region_del_subregion(&proxy->modern_bar,
&region->mr);
}
+static void virtio_pci_modern_io_region_unmap(VirtIOPCIProxy *proxy,
+ VirtIOPCIRegion *region)
+{
+ memory_region_del_subregion(&proxy->io_bar,
+ &region->mr);
+}
+
/* This is called by virtio-bus just after the device is plugged. */
static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
{
@@ -1428,6 +1626,7 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
VirtioBusState *bus = &proxy->bus;
bool legacy = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_LEGACY);
bool modern = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN);
+ bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY;
uint8_t *config;
uint32_t size;
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
@@ -1466,16 +1665,31 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
.cap.cap_len = sizeof cfg,
.cap.cfg_type = VIRTIO_PCI_CAP_PCI_CFG,
};
- struct virtio_pci_cfg_cap *cfg_mask;
+ struct virtio_pci_notify_cap notify_pio = {
+ .cap.cap_len = sizeof notify,
+ .notify_off_multiplier = cpu_to_le32(0x0),
+ };
- /* TODO: add io access for speed */
+ struct virtio_pci_cfg_cap *cfg_mask;
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);
+
+ virtio_pci_modern_mem_region_map(proxy, &proxy->common, &cap);
+ virtio_pci_modern_mem_region_map(proxy, &proxy->isr, &cap);
+ virtio_pci_modern_mem_region_map(proxy, &proxy->device, &cap);
+ virtio_pci_modern_mem_region_map(proxy, &proxy->notify, &notify.cap);
+
+ if (modern_pio) {
+ memory_region_init(&proxy->io_bar, OBJECT(proxy),
+ "virtio-pci-io", 0x4);
+
+ pci_register_bar(&proxy->pci_dev, proxy->modern_io_bar,
+ PCI_BASE_ADDRESS_SPACE_IO, &proxy->io_bar);
+
+ virtio_pci_modern_io_region_map(proxy, &proxy->notify_pio,
+ &notify_pio.cap);
+ }
pci_register_bar(&proxy->pci_dev, proxy->modern_mem_bar,
PCI_BASE_ADDRESS_SPACE_MEMORY |
@@ -1491,12 +1705,17 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
pci_set_long(cfg_mask->pci_cfg_data, ~0x0);
}
- if (proxy->nvectors &&
- 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;
+ if (proxy->nvectors) {
+ int err = msix_init_exclusive_bar(&proxy->pci_dev, proxy->nvectors,
+ proxy->msix_bar);
+ if (err) {
+ /* Notice when a system that supports MSIx can't initialize it. */
+ if (err != -ENOTSUP) {
+ error_report("unable to init msix vectors to %" PRIu32,
+ proxy->nvectors);
+ }
+ proxy->nvectors = 0;
+ }
}
proxy->pci_dev.config_write = virtio_write_config;
@@ -1505,9 +1724,7 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
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);
- }
+ size = pow2ceil(size);
memory_region_init_io(&proxy->bar, OBJECT(proxy),
&virtio_pci_config_ops,
@@ -1528,14 +1745,18 @@ static void virtio_pci_device_unplugged(DeviceState *d)
{
VirtIOPCIProxy *proxy = VIRTIO_PCI(d);
bool modern = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN);
+ bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY;
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);
+ virtio_pci_modern_mem_region_unmap(proxy, &proxy->common);
+ virtio_pci_modern_mem_region_unmap(proxy, &proxy->isr);
+ virtio_pci_modern_mem_region_unmap(proxy, &proxy->device);
+ virtio_pci_modern_mem_region_unmap(proxy, &proxy->notify);
+ if (modern_pio) {
+ virtio_pci_modern_io_region_unmap(proxy, &proxy->notify_pio);
+ }
}
}
@@ -1555,6 +1776,7 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
*/
proxy->legacy_io_bar = 0;
proxy->msix_bar = 1;
+ proxy->modern_io_bar = 2;
proxy->modern_mem_bar = 4;
proxy->common.offset = 0x0;
@@ -1574,6 +1796,10 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
QEMU_VIRTIO_PCI_QUEUE_MEM_MULT * VIRTIO_QUEUE_MAX;
proxy->notify.type = VIRTIO_PCI_CAP_NOTIFY_CFG;
+ proxy->notify_pio.offset = 0x0;
+ proxy->notify_pio.size = 0x4;
+ proxy->notify_pio.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 *
@@ -1588,6 +1814,29 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
address_space_init(&proxy->modern_as, &proxy->modern_cfg, "virtio-pci-cfg-as");
+ if (pci_is_express(pci_dev) && pci_bus_is_express(pci_dev->bus) &&
+ !pci_bus_is_root(pci_dev->bus)) {
+ int pos;
+
+ pos = pcie_endpoint_cap_init(pci_dev, 0);
+ assert(pos > 0);
+
+ pos = pci_add_capability(pci_dev, PCI_CAP_ID_PM, 0, PCI_PM_SIZEOF);
+ assert(pos > 0);
+
+ /*
+ * Indicates that this function complies with revision 1.2 of the
+ * PCI Power Management Interface Specification.
+ */
+ pci_set_word(pci_dev->config + pos + PCI_PM_PMC, 0x3);
+ } else {
+ /*
+ * make future invocations of pci_is_express() return false
+ * and pci_config_size() return PCI_CONFIG_SPACE_SIZE.
+ */
+ pci_dev->cap_present &= ~QEMU_PCI_CAP_EXPRESS;
+ }
+
virtio_pci_bus_new(&proxy->bus, sizeof(proxy->bus), proxy);
if (k->realize) {
k->realize(proxy, errp);
@@ -1606,9 +1855,15 @@ static void virtio_pci_reset(DeviceState *qdev)
{
VirtIOPCIProxy *proxy = VIRTIO_PCI(qdev);
VirtioBusState *bus = VIRTIO_BUS(&proxy->bus);
+ int i;
+
virtio_pci_stop_ioeventfd(proxy);
virtio_bus_reset(bus);
msix_unuse_all_vectors(&proxy->pci_dev);
+
+ for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
+ proxy->vqs[i].enabled = 0;
+ }
}
static Property virtio_pci_properties[] = {
@@ -1618,13 +1873,34 @@ static Property virtio_pci_properties[] = {
VIRTIO_PCI_FLAG_DISABLE_LEGACY_BIT, false),
DEFINE_PROP_BIT("disable-modern", VirtIOPCIProxy, flags,
VIRTIO_PCI_FLAG_DISABLE_MODERN_BIT, true),
+ DEFINE_PROP_BIT("migrate-extra", VirtIOPCIProxy, flags,
+ VIRTIO_PCI_FLAG_MIGRATE_EXTRA_BIT, true),
+ DEFINE_PROP_BIT("modern-pio-notify", VirtIOPCIProxy, flags,
+ VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY_BIT, false),
+ DEFINE_PROP_BIT("x-disable-pcie", VirtIOPCIProxy, flags,
+ VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT, false),
DEFINE_PROP_END_OF_LIST(),
};
+static void virtio_pci_dc_realize(DeviceState *qdev, Error **errp)
+{
+ VirtioPCIClass *vpciklass = VIRTIO_PCI_GET_CLASS(qdev);
+ VirtIOPCIProxy *proxy = VIRTIO_PCI(qdev);
+ PCIDevice *pci_dev = &proxy->pci_dev;
+
+ if (!(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_PCIE) &&
+ !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN)) {
+ pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
+ }
+
+ vpciklass->parent_dc_realize(qdev, errp);
+}
+
static void virtio_pci_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ VirtioPCIClass *vpciklass = VIRTIO_PCI_CLASS(klass);
dc->props = virtio_pci_properties;
k->realize = virtio_pci_realize;
@@ -1632,6 +1908,8 @@ static void virtio_pci_class_init(ObjectClass *klass, void *data)
k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
k->revision = VIRTIO_PCI_ABI_VERSION;
k->class_id = PCI_CLASS_OTHERS;
+ vpciklass->parent_dc_realize = dc->realize;
+ dc->realize = virtio_pci_dc_realize;
dc->reset = virtio_pci_reset;
}
@@ -2009,10 +2287,6 @@ static const TypeInfo virtio_net_pci_info = {
/* virtio-rng-pci */
-static Property virtio_rng_pci_properties[] = {
- DEFINE_PROP_END_OF_LIST(),
-};
-
static void virtio_rng_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
{
VirtIORngPCI *vrng = VIRTIO_RNG_PCI(vpci_dev);
@@ -2039,7 +2313,6 @@ static void virtio_rng_pci_class_init(ObjectClass *klass, void *data)
k->realize = virtio_rng_pci_realize;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
- dc->props = virtio_rng_pci_properties;
pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_RNG;
@@ -2136,14 +2409,6 @@ static void virtio_tablet_initfn(Object *obj)
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,
@@ -2182,12 +2447,22 @@ static const TypeInfo virtio_tablet_pci_info = {
.instance_init = virtio_tablet_initfn,
};
+#ifdef CONFIG_LINUX
+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_host_pci_info = {
.name = TYPE_VIRTIO_INPUT_HOST_PCI,
.parent = TYPE_VIRTIO_INPUT_PCI,
.instance_size = sizeof(VirtIOInputHostPCI),
.instance_init = virtio_host_initfn,
};
+#endif
/* virtio-pci-bus */
@@ -2211,6 +2486,9 @@ 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->save_extra_state = virtio_pci_save_extra_state;
+ k->load_extra_state = virtio_pci_load_extra_state;
+ k->has_extra_state = virtio_pci_has_extra_state;
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;
@@ -2235,7 +2513,9 @@ static void virtio_pci_register_types(void)
type_register_static(&virtio_keyboard_pci_info);
type_register_static(&virtio_mouse_pci_info);
type_register_static(&virtio_tablet_pci_info);
+#ifdef CONFIG_LINUX
type_register_static(&virtio_host_pci_info);
+#endif
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 b6c442f52..c8f9cfdfc 100644
--- a/hw/virtio/virtio-pci.h
+++ b/hw/virtio/virtio-pci.h
@@ -59,21 +59,35 @@ typedef struct VirtioBusClass VirtioPCIBusClass;
#define VIRTIO_PCI_BUS_CLASS(klass) \
OBJECT_CLASS_CHECK(VirtioPCIBusClass, klass, TYPE_VIRTIO_PCI_BUS)
+enum {
+ VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT,
+ VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT,
+ VIRTIO_PCI_FLAG_DISABLE_LEGACY_BIT,
+ VIRTIO_PCI_FLAG_DISABLE_MODERN_BIT,
+ VIRTIO_PCI_FLAG_MIGRATE_EXTRA_BIT,
+ VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY_BIT,
+ VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT,
+};
+
/* Need to activate work-arounds for buggy guests at vmstate load. */
-#define VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT 0
#define VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION \
(1 << VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT)
/* Performance improves when virtqueue kick processing is decoupled from the
* vcpu thread using ioeventfd for some devices. */
-#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)
+#define VIRTIO_PCI_FLAG_DISABLE_PCIE (1 << VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT)
+
+/* migrate extra state */
+#define VIRTIO_PCI_FLAG_MIGRATE_EXTRA (1 << VIRTIO_PCI_FLAG_MIGRATE_EXTRA_BIT)
+
+/* have pio notification for modern device ? */
+#define VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY \
+ (1 << VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY_BIT)
typedef struct {
MSIMessage msg;
@@ -94,6 +108,7 @@ typedef struct {
typedef struct VirtioPCIClass {
PCIDeviceClass parent_class;
+ DeviceRealize parent_dc_realize;
void (*realize)(VirtIOPCIProxy *vpci_dev, Error **errp);
} VirtioPCIClass;
@@ -104,6 +119,14 @@ typedef struct VirtIOPCIRegion {
uint32_t type;
} VirtIOPCIRegion;
+typedef struct VirtIOPCIQueue {
+ uint16_t num;
+ bool enabled;
+ uint32_t desc[2];
+ uint32_t avail[2];
+ uint32_t used[2];
+} VirtIOPCIQueue;
+
struct VirtIOPCIProxy {
PCIDevice pci_dev;
MemoryRegion bar;
@@ -111,11 +134,14 @@ struct VirtIOPCIProxy {
VirtIOPCIRegion isr;
VirtIOPCIRegion device;
VirtIOPCIRegion notify;
+ VirtIOPCIRegion notify_pio;
MemoryRegion modern_bar;
+ MemoryRegion io_bar;
MemoryRegion modern_cfg;
AddressSpace modern_as;
uint32_t legacy_io_bar;
uint32_t msix_bar;
+ uint32_t modern_io_bar;
uint32_t modern_mem_bar;
int config_cap;
uint32_t flags;
@@ -124,13 +150,7 @@ struct VirtIOPCIProxy {
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];
+ VirtIOPCIQueue vqs[VIRTIO_QUEUE_MAX];
bool ioeventfd_disabled;
bool ioeventfd_started;
@@ -267,6 +287,8 @@ struct VirtIOInputHIDPCI {
VirtIOInputHID vdev;
};
+#ifdef CONFIG_LINUX
+
#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)
@@ -276,6 +298,8 @@ struct VirtIOInputHostPCI {
VirtIOInputHost vdev;
};
+#endif
+
/*
* virtio-gpu-pci: This extends VirtioPCIProxy.
*/
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index d24f77551..1edef5945 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -60,6 +60,7 @@ typedef struct VRingUsed
typedef struct VRing
{
unsigned int num;
+ unsigned int num_default;
unsigned int align;
hwaddr desc;
hwaddr avail;
@@ -447,28 +448,59 @@ int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes,
return in_bytes <= in_total && out_bytes <= out_total;
}
-void virtqueue_map_sg(struct iovec *sg, hwaddr *addr,
- size_t num_sg, int is_write)
+static void virtqueue_map_iovec(struct iovec *sg, hwaddr *addr,
+ unsigned int *num_sg, unsigned int max_size,
+ int is_write)
{
unsigned int i;
hwaddr len;
- if (num_sg > VIRTQUEUE_MAX_SIZE) {
- error_report("virtio: map attempt out of bounds: %zd > %d",
- num_sg, VIRTQUEUE_MAX_SIZE);
- exit(1);
- }
+ /* Note: this function MUST validate input, some callers
+ * are passing in num_sg values received over the network.
+ */
+ /* TODO: teach all callers that this can fail, and return failure instead
+ * of asserting here.
+ * When we do, we might be able to re-enable NDEBUG below.
+ */
+#ifdef NDEBUG
+#error building with NDEBUG is not supported
+#endif
+ assert(*num_sg <= max_size);
- for (i = 0; i < num_sg; i++) {
+ for (i = 0; i < *num_sg; i++) {
len = sg[i].iov_len;
sg[i].iov_base = cpu_physical_memory_map(addr[i], &len, is_write);
- if (sg[i].iov_base == NULL || len != sg[i].iov_len) {
+ if (!sg[i].iov_base) {
error_report("virtio: error trying to map MMIO memory");
exit(1);
}
+ if (len == sg[i].iov_len) {
+ continue;
+ }
+ if (*num_sg >= max_size) {
+ error_report("virtio: memory split makes iovec too large");
+ exit(1);
+ }
+ memmove(sg + i + 1, sg + i, sizeof(*sg) * (*num_sg - i));
+ memmove(addr + i + 1, addr + i, sizeof(*addr) * (*num_sg - i));
+ assert(len < sg[i + 1].iov_len);
+ sg[i].iov_len = len;
+ addr[i + 1] += len;
+ sg[i + 1].iov_len -= len;
+ ++*num_sg;
}
}
+void virtqueue_map(VirtQueueElement *elem)
+{
+ virtqueue_map_iovec(elem->in_sg, elem->in_addr, &elem->in_num,
+ MIN(ARRAY_SIZE(elem->in_sg), ARRAY_SIZE(elem->in_addr)),
+ 1);
+ virtqueue_map_iovec(elem->out_sg, elem->out_addr, &elem->out_num,
+ MIN(ARRAY_SIZE(elem->out_sg), ARRAY_SIZE(elem->out_addr)),
+ 0);
+}
+
int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
{
unsigned int i, head, max;
@@ -530,8 +562,7 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
} while ((i = virtqueue_next_desc(vdev, desc_pa, i, max)) != max);
/* Now map what we have collected */
- virtqueue_map_sg(elem->in_sg, elem->in_addr, elem->in_num, 1);
- virtqueue_map_sg(elem->out_sg, elem->out_addr, elem->out_num, 0);
+ virtqueue_map(elem);
elem->index = head;
@@ -646,6 +677,7 @@ void virtio_reset(void *opaque)
vdev->vq[i].signalled_used = 0;
vdev->vq[i].signalled_used_valid = false;
vdev->vq[i].notification = true;
+ vdev->vq[i].vring.num = vdev->vq[i].vring.num_default;
}
}
@@ -977,6 +1009,7 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size,
abort();
vdev->vq[i].vring.num = queue_size;
+ vdev->vq[i].vring.num_default = queue_size;
vdev->vq[i].vring.align = VIRTIO_PCI_VRING_ALIGN;
vdev->vq[i].handle_output = handle_output;
@@ -990,6 +1023,7 @@ void virtio_del_queue(VirtIODevice *vdev, int n)
}
vdev->vq[n].vring.num = 0;
+ vdev->vq[n].vring.num_default = 0;
}
void virtio_irq(VirtQueue *vq)
@@ -1069,6 +1103,29 @@ static bool virtio_virtqueue_needed(void *opaque)
return virtio_host_has_feature(vdev, VIRTIO_F_VERSION_1);
}
+static bool virtio_ringsize_needed(void *opaque)
+{
+ VirtIODevice *vdev = opaque;
+ int i;
+
+ for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
+ if (vdev->vq[i].vring.num != vdev->vq[i].vring.num_default) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool virtio_extra_state_needed(void *opaque)
+{
+ VirtIODevice *vdev = opaque;
+ BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
+
+ return k->has_extra_state &&
+ k->has_extra_state(qbus->parent);
+}
+
static void put_virtqueue_state(QEMUFile *f, void *pv, size_t size)
{
VirtIODevice *vdev = pv;
@@ -1117,6 +1174,99 @@ static const VMStateDescription vmstate_virtio_virtqueues = {
}
};
+static void put_ringsize_state(QEMUFile *f, void *pv, size_t size)
+{
+ VirtIODevice *vdev = pv;
+ int i;
+
+ for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
+ qemu_put_be32(f, vdev->vq[i].vring.num_default);
+ }
+}
+
+static int get_ringsize_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.num_default = qemu_get_be32(f);
+ }
+ return 0;
+}
+
+static VMStateInfo vmstate_info_ringsize = {
+ .name = "ringsize_state",
+ .get = get_ringsize_state,
+ .put = put_ringsize_state,
+};
+
+static const VMStateDescription vmstate_virtio_ringsize = {
+ .name = "virtio/ringsize",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = &virtio_ringsize_needed,
+ .fields = (VMStateField[]) {
+ {
+ .name = "ringsize",
+ .version_id = 0,
+ .field_exists = NULL,
+ .size = 0,
+ .info = &vmstate_info_ringsize,
+ .flags = VMS_SINGLE,
+ .offset = 0,
+ },
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int get_extra_state(QEMUFile *f, void *pv, size_t size)
+{
+ VirtIODevice *vdev = pv;
+ BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
+
+ if (!k->load_extra_state) {
+ return -1;
+ } else {
+ return k->load_extra_state(qbus->parent, f);
+ }
+}
+
+static void put_extra_state(QEMUFile *f, void *pv, size_t size)
+{
+ VirtIODevice *vdev = pv;
+ BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
+
+ k->save_extra_state(qbus->parent, f);
+}
+
+static const VMStateInfo vmstate_info_extra_state = {
+ .name = "virtqueue_extra_state",
+ .get = get_extra_state,
+ .put = put_extra_state,
+};
+
+static const VMStateDescription vmstate_virtio_extra_state = {
+ .name = "virtio/extra_state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = &virtio_extra_state_needed,
+ .fields = (VMStateField[]) {
+ {
+ .name = "extra_state",
+ .version_id = 0,
+ .field_exists = NULL,
+ .size = 0,
+ .info = &vmstate_info_extra_state,
+ .flags = VMS_SINGLE,
+ .offset = 0,
+ },
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_virtio_device_endian = {
.name = "virtio/device_endian",
.version_id = 1,
@@ -1151,6 +1301,8 @@ static const VMStateDescription vmstate_virtio = {
&vmstate_virtio_device_endian,
&vmstate_virtio_64bit_features,
&vmstate_virtio_virtqueues,
+ &vmstate_virtio_ringsize,
+ &vmstate_virtio_extra_state,
NULL
}
};
@@ -1473,7 +1625,7 @@ hwaddr virtio_queue_get_desc_size(VirtIODevice *vdev, int n)
hwaddr virtio_queue_get_avail_size(VirtIODevice *vdev, int n)
{
return offsetof(VRingAvail, ring) +
- sizeof(uint64_t) * vdev->vq[n].vring.num;
+ sizeof(uint16_t) * vdev->vq[n].vring.num;
}
hwaddr virtio_queue_get_used_size(VirtIODevice *vdev, int n)
diff --git a/hw/watchdog/wdt_i6300esb.c b/hw/watchdog/wdt_i6300esb.c
index cfa2b1be1..a91c8fdad 100644
--- a/hw/watchdog/wdt_i6300esb.c
+++ b/hw/watchdog/wdt_i6300esb.c
@@ -129,14 +129,9 @@ static void i6300esb_restart_timer(I6300State *d, int stage)
else
timeout <<= 5;
- /* Get the timeout in units of ticks_per_sec.
- *
- * ticks_per_sec is typically 10^9 == 0x3B9ACA00 (30 bits), with
- * 20 bits of user supplied preload, and 15 bits of scale, the
- * multiply here can exceed 64-bits, before we divide by 33MHz, so
- * we use a higher-precision intermediate result.
- */
- timeout = muldiv64(get_ticks_per_sec(), timeout, 33000000);
+ /* Get the timeout in nanoseconds. */
+
+ timeout = timeout * 30; /* on a PCI bus, 1 tick is 30 ns*/
i6300esb_debug("stage %d, timeout %" PRIi64 "\n", d->stage, timeout);
diff --git a/hw/xen/Makefile.objs b/hw/xen/Makefile.objs
index a0ca0aa3d..a9ad7e70f 100644
--- a/hw/xen/Makefile.objs
+++ b/hw/xen/Makefile.objs
@@ -3,3 +3,4 @@ common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o
obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o
obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o
+obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o xen_pt_graphics.o
diff --git a/hw/xen/xen-host-pci-device.c b/hw/xen/xen-host-pci-device.c
index 743b37b99..7d8a0237c 100644
--- a/hw/xen/xen-host-pci-device.c
+++ b/hw/xen/xen-host-pci-device.c
@@ -40,7 +40,7 @@ static int xen_host_pci_sysfs_path(const XenHostPCIDevice *d,
d->domain, d->bus, d->dev, d->func, name);
if (rc >= size || rc < 0) {
- /* The ouput is truncated or an other error is encountered */
+ /* The output is truncated, or some other error was encountered */
return -ENODEV;
}
return 0;
@@ -376,6 +376,11 @@ int xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain,
goto error;
}
d->irq = v;
+ rc = xen_host_pci_get_hex_value(d, "class", &v);
+ if (rc) {
+ goto error;
+ }
+ d->class_code = v;
d->is_virtfn = xen_host_pci_dev_is_virtfn(d);
return 0;
@@ -387,6 +392,11 @@ error:
return rc;
}
+bool xen_host_pci_device_closed(XenHostPCIDevice *d)
+{
+ return d->config_fd == -1;
+}
+
void xen_host_pci_device_put(XenHostPCIDevice *d)
{
if (d->config_fd >= 0) {
diff --git a/hw/xen/xen-host-pci-device.h b/hw/xen/xen-host-pci-device.h
index c2486f0c1..3d44e044f 100644
--- a/hw/xen/xen-host-pci-device.h
+++ b/hw/xen/xen-host-pci-device.h
@@ -25,6 +25,7 @@ typedef struct XenHostPCIDevice {
uint16_t vendor_id;
uint16_t device_id;
+ uint32_t class_code;
int irq;
XenHostPCIIORegion io_regions[PCI_NUM_REGIONS - 1];
@@ -38,6 +39,7 @@ typedef struct XenHostPCIDevice {
int xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain,
uint8_t bus, uint8_t dev, uint8_t func);
void xen_host_pci_device_put(XenHostPCIDevice *pci_dev);
+bool xen_host_pci_device_closed(XenHostPCIDevice *d);
int xen_host_pci_get_byte(XenHostPCIDevice *d, int pos, uint8_t *p);
int xen_host_pci_get_word(XenHostPCIDevice *d, int pos, uint16_t *p);
diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c
index ed5fcaec0..aa9628823 100644
--- a/hw/xen/xen_pt.c
+++ b/hw/xen/xen_pt.c
@@ -56,6 +56,7 @@
#include "hw/pci/pci.h"
#include "hw/xen/xen.h"
+#include "hw/i386/pc.h"
#include "hw/xen/xen_backend.h"
#include "xen_pt.h"
#include "qemu/range.h"
@@ -378,7 +379,7 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr,
}
}
- /* need to shift back before passing them to xen_host_pci_device */
+ /* need to shift back before passing them to xen_host_pci_set_block. */
val >>= (addr & 3) << 3;
memory_region_transaction_commit();
@@ -406,7 +407,7 @@ out:
(uint8_t *)&val + index, len);
if (rc < 0) {
- XEN_PT_ERR(d, "pci_write_block failed. return value: %d.\n", rc);
+ XEN_PT_ERR(d, "xen_host_pci_set_block failed. return value: %d.\n", rc);
}
}
}
@@ -502,6 +503,7 @@ static int xen_pt_register_regions(XenPCIPassthroughState *s, uint16_t *cmd)
d->rom.size, d->rom.base_addr);
}
+ xen_pt_register_vga_regions(d);
return 0;
}
@@ -683,13 +685,86 @@ static const MemoryListener xen_pt_io_listener = {
.priority = 10,
};
+static void
+xen_igd_passthrough_isa_bridge_create(XenPCIPassthroughState *s,
+ XenHostPCIDevice *dev)
+{
+ uint16_t gpu_dev_id;
+ PCIDevice *d = &s->dev;
+
+ gpu_dev_id = dev->device_id;
+ igd_passthrough_isa_bridge_create(d->bus, gpu_dev_id);
+}
+
+/* destroy. */
+static void xen_pt_destroy(PCIDevice *d) {
+
+ XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
+ XenHostPCIDevice *host_dev = &s->real_device;
+ uint8_t machine_irq = s->machine_irq;
+ uint8_t intx;
+ int rc;
+
+ if (machine_irq && !xen_host_pci_device_closed(&s->real_device)) {
+ intx = xen_pt_pci_intx(s);
+ rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq,
+ PT_IRQ_TYPE_PCI,
+ pci_bus_num(d->bus),
+ PCI_SLOT(s->dev.devfn),
+ intx,
+ 0 /* isa_irq */);
+ if (rc < 0) {
+ XEN_PT_ERR(d, "unbinding of interrupt INT%c failed."
+ " (machine irq: %i, err: %d)"
+ " But bravely continuing on..\n",
+ 'a' + intx, machine_irq, errno);
+ }
+ }
+
+ /* N.B. xen_pt_config_delete takes care of freeing them. */
+ if (s->msi) {
+ xen_pt_msi_disable(s);
+ }
+ if (s->msix) {
+ xen_pt_msix_disable(s);
+ }
+
+ if (machine_irq) {
+ xen_pt_mapped_machine_irq[machine_irq]--;
+
+ if (xen_pt_mapped_machine_irq[machine_irq] == 0) {
+ rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq);
+
+ if (rc < 0) {
+ XEN_PT_ERR(d, "unmapping of interrupt %i failed. (err: %d)"
+ " But bravely continuing on..\n",
+ machine_irq, errno);
+ }
+ }
+ s->machine_irq = 0;
+ }
+
+ /* delete all emulated config registers */
+ xen_pt_config_delete(s);
+
+ xen_pt_unregister_vga_regions(host_dev);
+
+ if (s->listener_set) {
+ memory_listener_unregister(&s->memory_listener);
+ memory_listener_unregister(&s->io_listener);
+ s->listener_set = false;
+ }
+ if (!xen_host_pci_device_closed(&s->real_device)) {
+ xen_host_pci_device_put(&s->real_device);
+ }
+}
/* init */
static int xen_pt_initfn(PCIDevice *d)
{
XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
int rc = 0;
- uint8_t machine_irq = 0;
+ uint8_t machine_irq = 0, scratch;
uint16_t cmd = 0;
int pirq = XEN_PT_UNASSIGNED_PIRQ;
@@ -715,27 +790,48 @@ static int xen_pt_initfn(PCIDevice *d)
}
/* Initialize virtualized PCI configuration (Extended 256 Bytes) */
- if (xen_host_pci_get_block(&s->real_device, 0, d->config,
- PCI_CONFIG_SPACE_SIZE) == -1) {
- xen_host_pci_device_put(&s->real_device);
- return -1;
- }
+ memset(d->config, 0, PCI_CONFIG_SPACE_SIZE);
s->memory_listener = xen_pt_memory_listener;
s->io_listener = xen_pt_io_listener;
+ /* Setup VGA bios for passthrough GFX */
+ if ((s->real_device.domain == 0) && (s->real_device.bus == 0) &&
+ (s->real_device.dev == 2) && (s->real_device.func == 0)) {
+ if (!is_igd_vga_passthrough(&s->real_device)) {
+ XEN_PT_ERR(d, "Need to enable igd-passthru if you're trying"
+ " to passthrough IGD GFX.\n");
+ xen_host_pci_device_put(&s->real_device);
+ return -1;
+ }
+
+ if (xen_pt_setup_vga(s, &s->real_device) < 0) {
+ XEN_PT_ERR(d, "Setup VGA BIOS of passthrough GFX failed!\n");
+ xen_host_pci_device_put(&s->real_device);
+ return -1;
+ }
+
+ /* Register ISA bridge for passthrough GFX. */
+ xen_igd_passthrough_isa_bridge_create(s, &s->real_device);
+ }
+
/* Handle real device's MMIO/PIO BARs */
xen_pt_register_regions(s, &cmd);
/* reinitialize each config register to be emulated */
- if (xen_pt_config_init(s)) {
+ rc = xen_pt_config_init(s);
+ if (rc) {
XEN_PT_ERR(d, "PCI Config space initialisation failed.\n");
- xen_host_pci_device_put(&s->real_device);
- return -1;
+ goto err_out;
}
/* Bind interrupt */
- if (!s->dev.config[PCI_INTERRUPT_PIN]) {
+ rc = xen_host_pci_get_byte(&s->real_device, PCI_INTERRUPT_PIN, &scratch);
+ if (rc) {
+ XEN_PT_ERR(d, "Failed to read PCI_INTERRUPT_PIN! (rc:%d)\n", rc);
+ goto err_out;
+ }
+ if (!scratch) {
XEN_PT_LOG(d, "no pin interrupt\n");
goto out;
}
@@ -785,69 +881,41 @@ static int xen_pt_initfn(PCIDevice *d)
out:
if (cmd) {
- xen_host_pci_set_word(&s->real_device, PCI_COMMAND,
- pci_get_word(d->config + PCI_COMMAND) | cmd);
+ uint16_t val;
+
+ rc = xen_host_pci_get_word(&s->real_device, PCI_COMMAND, &val);
+ if (rc) {
+ XEN_PT_ERR(d, "Failed to read PCI_COMMAND! (rc: %d)\n", rc);
+ goto err_out;
+ } else {
+ val |= cmd;
+ rc = xen_host_pci_set_word(&s->real_device, PCI_COMMAND, val);
+ if (rc) {
+ XEN_PT_ERR(d, "Failed to write PCI_COMMAND val=0x%x!(rc: %d)\n",
+ val, rc);
+ goto err_out;
+ }
+ }
}
memory_listener_register(&s->memory_listener, &s->dev.bus_master_as);
memory_listener_register(&s->io_listener, &address_space_io);
+ s->listener_set = true;
XEN_PT_LOG(d,
"Real physical device %02x:%02x.%d registered successfully!\n",
s->hostaddr.bus, s->hostaddr.slot, s->hostaddr.function);
return 0;
+
+err_out:
+ xen_pt_destroy(d);
+ assert(rc);
+ return rc;
}
static void xen_pt_unregister_device(PCIDevice *d)
{
- XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
- uint8_t machine_irq = s->machine_irq;
- uint8_t intx = xen_pt_pci_intx(s);
- int rc;
-
- if (machine_irq) {
- rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq,
- PT_IRQ_TYPE_PCI,
- pci_bus_num(d->bus),
- PCI_SLOT(s->dev.devfn),
- intx,
- 0 /* isa_irq */);
- if (rc < 0) {
- XEN_PT_ERR(d, "unbinding of interrupt INT%c failed."
- " (machine irq: %i, err: %d)"
- " But bravely continuing on..\n",
- 'a' + intx, machine_irq, errno);
- }
- }
-
- if (s->msi) {
- xen_pt_msi_disable(s);
- }
- if (s->msix) {
- xen_pt_msix_disable(s);
- }
-
- if (machine_irq) {
- xen_pt_mapped_machine_irq[machine_irq]--;
-
- if (xen_pt_mapped_machine_irq[machine_irq] == 0) {
- rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq);
-
- if (rc < 0) {
- XEN_PT_ERR(d, "unmapping of interrupt %i failed. (err: %d)"
- " But bravely continuing on..\n",
- machine_irq, errno);
- }
- }
- }
-
- /* delete all emulated config registers */
- xen_pt_config_delete(s);
-
- memory_listener_unregister(&s->memory_listener);
- memory_listener_unregister(&s->io_listener);
-
- xen_host_pci_device_put(&s->real_device);
+ xen_pt_destroy(d);
}
static Property xen_pci_passthrough_properties[] = {
@@ -870,10 +938,18 @@ static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data)
dc->props = xen_pci_passthrough_properties;
};
+static void xen_pci_passthrough_finalize(Object *obj)
+{
+ XenPCIPassthroughState *s = XEN_PT_DEVICE(obj);
+
+ xen_pt_msix_delete(s);
+}
+
static const TypeInfo xen_pci_passthrough_info = {
.name = TYPE_XEN_PT_DEVICE,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(XenPCIPassthroughState),
+ .instance_finalize = xen_pci_passthrough_finalize,
.class_init = xen_pci_passthrough_class_init,
};
diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h
index 393f36ccb..c54528008 100644
--- a/hw/xen/xen_pt.h
+++ b/hw/xen/xen_pt.h
@@ -40,6 +40,9 @@ typedef struct XenPCIPassthroughState XenPCIPassthroughState;
#define XEN_PT_DEVICE(obj) \
OBJECT_CHECK(XenPCIPassthroughState, (obj), TYPE_XEN_PT_DEVICE)
+uint32_t igd_read_opregion(XenPCIPassthroughState *s);
+void igd_write_opregion(XenPCIPassthroughState *s, uint32_t val);
+
/* function type for config reg */
typedef int (*xen_pt_conf_reg_init)
(XenPCIPassthroughState *, XenPTRegInfo *, uint32_t real_offset,
@@ -66,8 +69,9 @@ typedef int (*xen_pt_conf_byte_read)
#define XEN_PT_BAR_ALLF 0xFFFFFFFF
#define XEN_PT_BAR_UNMAPPED (-1)
-#define PCI_CAP_MAX 48
+#define XEN_PCI_CAP_MAX 48
+#define XEN_PCI_INTEL_OPREGION 0xfc
typedef enum {
XEN_PT_GRP_TYPE_HARDWIRED = 0, /* 0 Hardwired reg group */
@@ -134,7 +138,11 @@ struct XenPTRegInfo {
struct XenPTReg {
QLIST_ENTRY(XenPTReg) entries;
XenPTRegInfo *reg;
- uint32_t data; /* emulated value */
+ union {
+ uint8_t *byte;
+ uint16_t *half_word;
+ uint32_t *word;
+ } ptr; /* pointer to dev.config. */
};
typedef const struct XenPTRegGroupInfo XenPTRegGroupInfo;
@@ -217,6 +225,7 @@ struct XenPCIPassthroughState {
MemoryListener memory_listener;
MemoryListener io_listener;
+ bool listener_set;
};
int xen_pt_config_init(XenPCIPassthroughState *s);
@@ -282,6 +291,7 @@ static inline uint8_t xen_pt_pci_intx(XenPCIPassthroughState *s)
" value=%i, acceptable range is 1 - 4\n", r_val);
r_val = 0;
} else {
+ /* Note that if s.real_device.config_fd is closed we make 0xff. */
r_val -= 1;
}
@@ -289,13 +299,13 @@ static inline uint8_t xen_pt_pci_intx(XenPCIPassthroughState *s)
}
/* MSI/MSI-X */
-int xen_pt_msi_set_enable(XenPCIPassthroughState *s, bool en);
int xen_pt_msi_setup(XenPCIPassthroughState *s);
int xen_pt_msi_update(XenPCIPassthroughState *d);
void xen_pt_msi_disable(XenPCIPassthroughState *s);
int xen_pt_msix_init(XenPCIPassthroughState *s, uint32_t base);
void xen_pt_msix_delete(XenPCIPassthroughState *s);
+void xen_pt_msix_unmap(XenPCIPassthroughState *s);
int xen_pt_msix_update(XenPCIPassthroughState *s);
int xen_pt_msix_update_remap(XenPCIPassthroughState *s, int bar_index);
void xen_pt_msix_disable(XenPCIPassthroughState *s);
@@ -305,5 +315,18 @@ static inline bool xen_pt_has_msix_mapping(XenPCIPassthroughState *s, int bar)
return s->msix && s->msix->bar_index == bar;
}
-
+extern void *pci_assign_dev_load_option_rom(PCIDevice *dev,
+ struct Object *owner, int *size,
+ unsigned int domain,
+ unsigned int bus, unsigned int slot,
+ unsigned int function);
+extern bool has_igd_gfx_passthru;
+static inline bool is_igd_vga_passthrough(XenHostPCIDevice *dev)
+{
+ return (has_igd_gfx_passthru
+ && ((dev->class_code >> 0x8) == PCI_CLASS_DISPLAY_VGA));
+}
+int xen_pt_register_vga_regions(XenHostPCIDevice *dev);
+int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev);
+int xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev);
#endif /* !XEN_PT_H */
diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c
index dd37be38a..3d8686d55 100644
--- a/hw/xen/xen_pt_config_init.c
+++ b/hw/xen/xen_pt_config_init.c
@@ -128,10 +128,11 @@ static int xen_pt_byte_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint8_t valid_emu_mask = 0;
+ uint8_t *data = cfg_entry->ptr.byte;
/* emulate byte register */
valid_emu_mask = reg->emu_mask & valid_mask;
- *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask);
+ *value = XEN_PT_MERGE_VALUE(*value, *data, ~valid_emu_mask);
return 0;
}
@@ -140,10 +141,11 @@ static int xen_pt_word_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint16_t valid_emu_mask = 0;
+ uint16_t *data = cfg_entry->ptr.half_word;
/* emulate word register */
valid_emu_mask = reg->emu_mask & valid_mask;
- *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask);
+ *value = XEN_PT_MERGE_VALUE(*value, *data, ~valid_emu_mask);
return 0;
}
@@ -152,10 +154,11 @@ static int xen_pt_long_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint32_t valid_emu_mask = 0;
+ uint32_t *data = cfg_entry->ptr.word;
/* emulate long register */
valid_emu_mask = reg->emu_mask & valid_mask;
- *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask);
+ *value = XEN_PT_MERGE_VALUE(*value, *data, ~valid_emu_mask);
return 0;
}
@@ -169,10 +172,11 @@ 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 = get_throughable_mask(s, reg, valid_mask);
+ uint8_t *data = cfg_entry->ptr.byte;
/* 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);
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
/* create value for writing to I/O device register */
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
@@ -186,10 +190,11 @@ 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 = get_throughable_mask(s, reg, valid_mask);
+ uint16_t *data = cfg_entry->ptr.half_word;
/* 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);
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
/* create value for writing to I/O device register */
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
@@ -203,10 +208,11 @@ 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 = get_throughable_mask(s, reg, valid_mask);
+ uint32_t *data = cfg_entry->ptr.word;
/* 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);
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
/* create value for writing to I/O device register */
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
@@ -255,7 +261,7 @@ static int xen_pt_status_reg_init(XenPCIPassthroughState *s,
reg_entry = xen_pt_find_reg(reg_grp_entry, PCI_CAPABILITY_LIST);
if (reg_entry) {
/* check Capabilities Pointer register */
- if (reg_entry->data) {
+ if (*reg_entry->ptr.half_word) {
reg_field |= PCI_STATUS_CAP_LIST;
} else {
reg_field &= ~PCI_STATUS_CAP_LIST;
@@ -301,10 +307,11 @@ 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 = get_throughable_mask(s, reg, valid_mask);
+ uint16_t *data = cfg_entry->ptr.half_word;
/* modify emulate register */
writable_mask = ~reg->ro_mask & valid_mask;
- cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
/* create value for writing to I/O device register */
if (*val & PCI_COMMAND_INTX_DISABLE) {
@@ -447,7 +454,7 @@ static int xen_pt_bar_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
/* emulate BAR */
valid_emu_mask = bar_emu_mask & valid_mask;
- *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask);
+ *value = XEN_PT_MERGE_VALUE(*value, *cfg_entry->ptr.word, ~valid_emu_mask);
return 0;
}
@@ -464,6 +471,7 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
uint32_t bar_ro_mask = 0;
uint32_t r_size = 0;
int index = 0;
+ uint32_t *data = cfg_entry->ptr.word;
index = xen_pt_bar_offset_to_index(reg->offset);
if (index < 0 || index >= PCI_NUM_REGIONS) {
@@ -500,7 +508,7 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
/* modify emulate register */
writable_mask = bar_emu_mask & ~bar_ro_mask & valid_mask;
- cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
/* check whether we need to update the virtual region address or not */
switch (s->bases[index].bar_flag) {
@@ -533,6 +541,7 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s,
uint32_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
pcibus_t r_size = 0;
uint32_t bar_ro_mask = 0;
+ uint32_t *data = cfg_entry->ptr.word;
r_size = d->io_regions[PCI_ROM_SLOT].size;
base = &s->bases[PCI_ROM_SLOT];
@@ -544,7 +553,7 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s,
/* modify emulate register */
writable_mask = ~bar_ro_mask & valid_mask;
- cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
/* create value for writing to I/O device register */
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
@@ -552,6 +561,22 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s,
return 0;
}
+static int xen_pt_intel_opregion_read(XenPCIPassthroughState *s,
+ XenPTReg *cfg_entry,
+ uint32_t *value, uint32_t valid_mask)
+{
+ *value = igd_read_opregion(s);
+ return 0;
+}
+
+static int xen_pt_intel_opregion_write(XenPCIPassthroughState *s,
+ XenPTReg *cfg_entry, uint32_t *value,
+ uint32_t dev_value, uint32_t valid_mask)
+{
+ igd_write_opregion(s, *value);
+ return 0;
+}
+
/* Header Type0 reg static information table */
static XenPTRegInfo xen_pt_emu_reg_header0[] = {
/* Vendor ID reg */
@@ -800,15 +825,21 @@ static XenPTRegInfo xen_pt_emu_reg_vendor[] = {
static inline uint8_t get_capability_version(XenPCIPassthroughState *s,
uint32_t offset)
{
- uint8_t flags = pci_get_byte(s->dev.config + offset + PCI_EXP_FLAGS);
- return flags & PCI_EXP_FLAGS_VERS;
+ uint8_t flag;
+ if (xen_host_pci_get_byte(&s->real_device, offset + PCI_EXP_FLAGS, &flag)) {
+ return 0;
+ }
+ return flag & PCI_EXP_FLAGS_VERS;
}
static inline uint8_t get_device_type(XenPCIPassthroughState *s,
uint32_t offset)
{
- uint8_t flags = pci_get_byte(s->dev.config + offset + PCI_EXP_FLAGS);
- return (flags & PCI_EXP_FLAGS_TYPE) >> 4;
+ uint8_t flag;
+ if (xen_host_pci_get_byte(&s->real_device, offset + PCI_EXP_FLAGS, &flag)) {
+ return 0;
+ }
+ return (flag & PCI_EXP_FLAGS_TYPE) >> 4;
}
/* initialize Link Control register */
@@ -857,8 +888,14 @@ static int xen_pt_linkctrl2_reg_init(XenPCIPassthroughState *s,
reg_field = XEN_PT_INVALID_REG;
} else {
/* set Supported Link Speed */
- uint8_t lnkcap = pci_get_byte(s->dev.config + real_offset - reg->offset
- + PCI_EXP_LNKCAP);
+ uint8_t lnkcap;
+ int rc;
+ rc = xen_host_pci_get_byte(&s->real_device,
+ real_offset - reg->offset + PCI_EXP_LNKCAP,
+ &lnkcap);
+ if (rc) {
+ return rc;
+ }
reg_field |= PCI_EXP_LNKCAP_SLS & lnkcap;
}
@@ -971,10 +1008,11 @@ static int xen_pt_pmcsr_reg_write(XenPCIPassthroughState *s,
XenPTRegInfo *reg = cfg_entry->reg;
uint16_t writable_mask = 0;
uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
+ uint16_t *data = cfg_entry->ptr.half_word;
/* 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);
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
/* create value for writing to I/O device register */
*val = XEN_PT_MERGE_VALUE(*val, dev_value & ~PCI_PM_CTRL_PME_STATUS,
@@ -1039,13 +1077,15 @@ static int xen_pt_msgctrl_reg_init(XenPCIPassthroughState *s,
XenPTRegInfo *reg, uint32_t real_offset,
uint32_t *data)
{
- PCIDevice *d = &s->dev;
XenPTMSI *msi = s->msi;
- uint16_t reg_field = 0;
+ uint16_t reg_field;
+ int rc;
/* use I/O device register's value as initial value */
- reg_field = pci_get_word(d->config + real_offset);
-
+ rc = xen_host_pci_get_word(&s->real_device, real_offset, &reg_field);
+ if (rc) {
+ return rc;
+ }
if (reg_field & PCI_MSI_FLAGS_ENABLE) {
XEN_PT_LOG(&s->dev, "MSI already enabled, disabling it first\n");
xen_host_pci_set_word(&s->real_device, real_offset,
@@ -1067,6 +1107,7 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s,
XenPTMSI *msi = s->msi;
uint16_t writable_mask = 0;
uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
+ uint16_t *data = cfg_entry->ptr.half_word;
/* Currently no support for multi-vector */
if (*val & PCI_MSI_FLAGS_QSIZE) {
@@ -1075,8 +1116,8 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s,
/* 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);
- msi->flags |= cfg_entry->data & ~PCI_MSI_FLAGS_ENABLE;
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
+ msi->flags |= *data & ~PCI_MSI_FLAGS_ENABLE;
/* create value for writing to I/O device register */
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
@@ -1086,7 +1127,7 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s,
/* setup MSI pirq for the first time */
if (!msi->initialized) {
/* Init physical one */
- XEN_PT_LOG(&s->dev, "setup MSI\n");
+ XEN_PT_LOG(&s->dev, "setup MSI (register: %x).\n", *val);
if (xen_pt_msi_setup(s)) {
/* We do not broadcast the error to the framework code, so
* that MSI errors are contained in MSI emulation code and
@@ -1094,12 +1135,12 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s,
* Guest MSI would be actually not working.
*/
*val &= ~PCI_MSI_FLAGS_ENABLE;
- XEN_PT_WARN(&s->dev, "Can not map MSI.\n");
+ XEN_PT_WARN(&s->dev, "Can not map MSI (register: %x)!\n", *val);
return 0;
}
if (xen_pt_msi_update(s)) {
*val &= ~PCI_MSI_FLAGS_ENABLE;
- XEN_PT_WARN(&s->dev, "Can not bind MSI\n");
+ XEN_PT_WARN(&s->dev, "Can not bind MSI (register: %x)!\n", *val);
return 0;
}
msi->initialized = true;
@@ -1190,18 +1231,19 @@ static int xen_pt_msgaddr32_reg_write(XenPCIPassthroughState *s,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint32_t writable_mask = 0;
- uint32_t old_addr = cfg_entry->data;
+ uint32_t old_addr = *cfg_entry->ptr.word;
+ uint32_t *data = cfg_entry->ptr.word;
/* 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);
- s->msi->addr_lo = cfg_entry->data;
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
+ s->msi->addr_lo = *data;
/* create value for writing to I/O device register */
*val = XEN_PT_MERGE_VALUE(*val, dev_value, 0);
/* update MSI */
- if (cfg_entry->data != old_addr) {
+ if (*data != old_addr) {
if (s->msi->mapped) {
xen_pt_msi_update(s);
}
@@ -1216,7 +1258,8 @@ static int xen_pt_msgaddr64_reg_write(XenPCIPassthroughState *s,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint32_t writable_mask = 0;
- uint32_t old_addr = cfg_entry->data;
+ uint32_t old_addr = *cfg_entry->ptr.word;
+ uint32_t *data = cfg_entry->ptr.word;
/* check whether the type is 64 bit or not */
if (!(s->msi->flags & PCI_MSI_FLAGS_64BIT)) {
@@ -1227,15 +1270,15 @@ static int xen_pt_msgaddr64_reg_write(XenPCIPassthroughState *s,
/* 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);
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
/* update the msi_info too */
- s->msi->addr_hi = cfg_entry->data;
+ s->msi->addr_hi = *data;
/* create value for writing to I/O device register */
*val = XEN_PT_MERGE_VALUE(*val, dev_value, 0);
/* update MSI */
- if (cfg_entry->data != old_addr) {
+ if (*data != old_addr) {
if (s->msi->mapped) {
xen_pt_msi_update(s);
}
@@ -1254,8 +1297,9 @@ 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 old_data = cfg_entry->data;
+ uint16_t old_data = *cfg_entry->ptr.half_word;
uint32_t offset = reg->offset;
+ uint16_t *data = cfg_entry->ptr.half_word;
/* check the offset whether matches the type or not */
if (!xen_pt_msi_check_type(offset, msi->flags, DATA)) {
@@ -1266,15 +1310,15 @@ static int xen_pt_msgdata_reg_write(XenPCIPassthroughState *s,
/* 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);
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
/* update the msi_info too */
- msi->data = cfg_entry->data;
+ msi->data = *data;
/* create value for writing to I/O device register */
*val = XEN_PT_MERGE_VALUE(*val, dev_value, 0);
/* update MSI */
- if (cfg_entry->data != old_data) {
+ if (*data != old_data) {
if (msi->mapped) {
xen_pt_msi_update(s);
}
@@ -1411,14 +1455,16 @@ static int xen_pt_msixctrl_reg_init(XenPCIPassthroughState *s,
XenPTRegInfo *reg, uint32_t real_offset,
uint32_t *data)
{
- PCIDevice *d = &s->dev;
- uint16_t reg_field = 0;
+ uint16_t reg_field;
+ int rc;
/* use I/O device register's value as initial value */
- reg_field = pci_get_word(d->config + real_offset);
-
+ rc = xen_host_pci_get_word(&s->real_device, real_offset, &reg_field);
+ if (rc) {
+ return rc;
+ }
if (reg_field & PCI_MSIX_FLAGS_ENABLE) {
- XEN_PT_LOG(d, "MSIX already enabled, disabling it first\n");
+ XEN_PT_LOG(&s->dev, "MSIX already enabled, disabling it first\n");
xen_host_pci_set_word(&s->real_device, real_offset,
reg_field & ~PCI_MSIX_FLAGS_ENABLE);
}
@@ -1436,10 +1482,11 @@ static int xen_pt_msixctrl_reg_write(XenPCIPassthroughState *s,
uint16_t writable_mask = 0;
uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
int debug_msix_enabled_old;
+ uint16_t *data = cfg_entry->ptr.half_word;
/* 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);
+ *data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
/* create value for writing to I/O device register */
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
@@ -1492,6 +1539,19 @@ static XenPTRegInfo xen_pt_emu_reg_msix[] = {
},
};
+static XenPTRegInfo xen_pt_emu_reg_igd_opregion[] = {
+ /* Intel IGFX OpRegion reg */
+ {
+ .offset = 0x0,
+ .size = 4,
+ .init_val = 0,
+ .u.dw.read = xen_pt_intel_opregion_read,
+ .u.dw.write = xen_pt_intel_opregion_write,
+ },
+ {
+ .size = 0,
+ },
+};
/****************************
* Capabilities
@@ -1511,8 +1571,7 @@ static int xen_pt_vendor_size_init(XenPCIPassthroughState *s,
const XenPTRegGroupInfo *grp_reg,
uint32_t base_offset, uint8_t *size)
{
- *size = pci_get_byte(s->dev.config + base_offset + 0x02);
- return 0;
+ return xen_host_pci_get_byte(&s->real_device, base_offset + 0x02, size);
}
/* get PCI Express Capability Structure register group size */
static int xen_pt_pcie_size_init(XenPCIPassthroughState *s,
@@ -1591,12 +1650,15 @@ static int xen_pt_msi_size_init(XenPCIPassthroughState *s,
const XenPTRegGroupInfo *grp_reg,
uint32_t base_offset, uint8_t *size)
{
- PCIDevice *d = &s->dev;
uint16_t msg_ctrl = 0;
uint8_t msi_size = 0xa;
+ int rc;
- msg_ctrl = pci_get_word(d->config + (base_offset + PCI_MSI_FLAGS));
-
+ rc = xen_host_pci_get_word(&s->real_device, base_offset + PCI_MSI_FLAGS,
+ &msg_ctrl);
+ if (rc) {
+ return rc;
+ }
/* check if 64-bit address is capable of per-vector masking */
if (msg_ctrl & PCI_MSI_FLAGS_64BIT) {
msi_size += 4;
@@ -1729,6 +1791,14 @@ static const XenPTRegGroupInfo xen_pt_emu_reg_grps[] = {
.size_init = xen_pt_msix_size_init,
.emu_regs = xen_pt_emu_reg_msix,
},
+ /* Intel IGD Opregion group */
+ {
+ .grp_id = XEN_PCI_INTEL_OPREGION,
+ .grp_type = XEN_PT_GRP_TYPE_EMU,
+ .grp_size = 0x4,
+ .size_init = xen_pt_reg_grp_size_init,
+ .emu_regs = xen_pt_emu_reg_igd_opregion,
+ },
{
.grp_size = 0,
},
@@ -1739,11 +1809,14 @@ static int xen_pt_ptr_reg_init(XenPCIPassthroughState *s,
XenPTRegInfo *reg, uint32_t real_offset,
uint32_t *data)
{
- int i;
- uint8_t *config = s->dev.config;
- uint32_t reg_field = pci_get_byte(config + real_offset);
+ int i, rc;
+ uint8_t reg_field;
uint8_t cap_id = 0;
+ rc = xen_host_pci_get_byte(&s->real_device, real_offset, &reg_field);
+ if (rc) {
+ return rc;
+ }
/* find capability offset */
while (reg_field) {
for (i = 0; xen_pt_emu_reg_grps[i].grp_size != 0; i++) {
@@ -1752,7 +1825,13 @@ static int xen_pt_ptr_reg_init(XenPCIPassthroughState *s,
continue;
}
- cap_id = pci_get_byte(config + reg_field + PCI_CAP_LIST_ID);
+ rc = xen_host_pci_get_byte(&s->real_device,
+ reg_field + PCI_CAP_LIST_ID, &cap_id);
+ if (rc) {
+ XEN_PT_ERR(&s->dev, "Failed to read capability @0x%x (rc:%d)\n",
+ reg_field + PCI_CAP_LIST_ID, rc);
+ return rc;
+ }
if (xen_pt_emu_reg_grps[i].grp_id == cap_id) {
if (xen_pt_emu_reg_grps[i].grp_type == XEN_PT_GRP_TYPE_EMU) {
goto out;
@@ -1763,7 +1842,11 @@ static int xen_pt_ptr_reg_init(XenPCIPassthroughState *s,
}
/* next capability */
- reg_field = pci_get_byte(config + reg_field + PCI_CAP_LIST_NEXT);
+ rc = xen_host_pci_get_byte(&s->real_device,
+ reg_field + PCI_CAP_LIST_NEXT, &reg_field);
+ if (rc) {
+ return rc;
+ }
}
out:
@@ -1779,7 +1862,7 @@ out:
static uint8_t find_cap_offset(XenPCIPassthroughState *s, uint8_t cap)
{
uint8_t id;
- unsigned max_cap = PCI_CAP_MAX;
+ unsigned max_cap = XEN_PCI_CAP_MAX;
uint8_t pos = PCI_CAPABILITY_LIST;
uint8_t status = 0;
@@ -1827,6 +1910,10 @@ static int xen_pt_config_reg_init(XenPCIPassthroughState *s,
reg_entry->reg = reg;
if (reg->init) {
+ uint32_t host_mask, size_mask;
+ unsigned int offset;
+ uint32_t val;
+
/* initialize emulate register */
rc = reg->init(s, reg_entry->reg,
reg_grp->base_offset + reg->offset, &data);
@@ -1839,8 +1926,67 @@ static int xen_pt_config_reg_init(XenPCIPassthroughState *s,
g_free(reg_entry);
return 0;
}
- /* set register value */
- reg_entry->data = data;
+ /* Sync up the data to dev.config */
+ offset = reg_grp->base_offset + reg->offset;
+ size_mask = 0xFFFFFFFF >> ((4 - reg->size) << 3);
+
+ switch (reg->size) {
+ case 1: rc = xen_host_pci_get_byte(&s->real_device, offset, (uint8_t *)&val);
+ break;
+ case 2: rc = xen_host_pci_get_word(&s->real_device, offset, (uint16_t *)&val);
+ break;
+ case 4: rc = xen_host_pci_get_long(&s->real_device, offset, &val);
+ break;
+ default: abort();
+ }
+ if (rc) {
+ /* Serious issues when we cannot read the host values! */
+ g_free(reg_entry);
+ return rc;
+ }
+ /* Set bits in emu_mask are the ones we emulate. The dev.config shall
+ * contain the emulated view of the guest - therefore we flip the mask
+ * to mask out the host values (which dev.config initially has) . */
+ host_mask = size_mask & ~reg->emu_mask;
+
+ if ((data & host_mask) != (val & host_mask)) {
+ uint32_t new_val;
+
+ /* Mask out host (including past size). */
+ new_val = val & host_mask;
+ /* Merge emulated ones (excluding the non-emulated ones). */
+ new_val |= data & host_mask;
+ /* Leave intact host and emulated values past the size - even though
+ * we do not care as we write per reg->size granularity, but for the
+ * logging below lets have the proper value. */
+ new_val |= ((val | data)) & ~size_mask;
+ XEN_PT_LOG(&s->dev,"Offset 0x%04x mismatch! Emulated=0x%04x, host=0x%04x, syncing to 0x%04x.\n",
+ offset, data, val, new_val);
+ val = new_val;
+ } else
+ val = data;
+
+ if (val & ~size_mask) {
+ XEN_PT_ERR(&s->dev,"Offset 0x%04x:0x%04x expands past register size(%d)!\n",
+ offset, val, reg->size);
+ g_free(reg_entry);
+ return -ENXIO;
+ }
+ /* This could be just pci_set_long as we don't modify the bits
+ * past reg->size, but in case this routine is run in parallel or the
+ * init value is larger, we do not want to over-write registers. */
+ switch (reg->size) {
+ case 1: pci_set_byte(s->dev.config + offset, (uint8_t)val);
+ break;
+ case 2: pci_set_word(s->dev.config + offset, (uint16_t)val);
+ break;
+ case 4: pci_set_long(s->dev.config + offset, val);
+ break;
+ default: abort();
+ }
+ /* set register value pointer to the data. */
+ reg_entry->ptr.byte = s->dev.config + offset;
+
}
/* list add register entry */
QLIST_INSERT_HEAD(&reg_grp->reg_tbl_list, reg_entry, entries);
@@ -1858,7 +2004,8 @@ int xen_pt_config_init(XenPCIPassthroughState *s)
uint32_t reg_grp_offset = 0;
XenPTRegGroup *reg_grp_entry = NULL;
- if (xen_pt_emu_reg_grps[i].grp_id != 0xFF) {
+ if (xen_pt_emu_reg_grps[i].grp_id != 0xFF
+ && xen_pt_emu_reg_grps[i].grp_id != XEN_PCI_INTEL_OPREGION) {
if (xen_pt_hide_dev_cap(&s->real_device,
xen_pt_emu_reg_grps[i].grp_id)) {
continue;
@@ -1871,6 +2018,15 @@ int xen_pt_config_init(XenPCIPassthroughState *s)
}
}
+ /*
+ * By default we will trap up to 0x40 in the cfg space.
+ * If an intel device is pass through we need to trap 0xfc,
+ * therefore the size should be 0xff.
+ */
+ if (xen_pt_emu_reg_grps[i].grp_id == XEN_PCI_INTEL_OPREGION) {
+ reg_grp_offset = XEN_PCI_INTEL_OPREGION;
+ }
+
reg_grp_entry = g_new0(XenPTRegGroup, 1);
QLIST_INIT(&reg_grp_entry->reg_tbl_list);
QLIST_INSERT_HEAD(&s->reg_grps, reg_grp_entry, entries);
@@ -1883,6 +2039,9 @@ int xen_pt_config_init(XenPCIPassthroughState *s)
reg_grp_offset,
&reg_grp_entry->size);
if (rc < 0) {
+ XEN_PT_LOG(&s->dev, "Failed to initialize %d/%ld, type=0x%x, rc:%d\n",
+ i, ARRAY_SIZE(xen_pt_emu_reg_grps),
+ xen_pt_emu_reg_grps[i].grp_type, rc);
xen_pt_config_delete(s);
return rc;
}
@@ -1897,6 +2056,10 @@ int xen_pt_config_init(XenPCIPassthroughState *s)
/* initialize capability register */
rc = xen_pt_config_reg_init(s, reg_grp_entry, regs);
if (rc < 0) {
+ XEN_PT_LOG(&s->dev, "Failed to initialize %d/%ld reg 0x%x in grp_type=0x%x (%d/%ld), rc=%d\n",
+ j, ARRAY_SIZE(xen_pt_emu_reg_grps[i].emu_regs),
+ regs->offset, xen_pt_emu_reg_grps[i].grp_type,
+ i, ARRAY_SIZE(xen_pt_emu_reg_grps), rc);
xen_pt_config_delete(s);
return rc;
}
@@ -1916,11 +2079,9 @@ void xen_pt_config_delete(XenPCIPassthroughState *s)
/* free MSI/MSI-X info table */
if (s->msix) {
- xen_pt_msix_delete(s);
- }
- if (s->msi) {
- g_free(s->msi);
+ xen_pt_msix_unmap(s);
}
+ g_free(s->msi);
/* free all register group entry */
QLIST_FOREACH_SAFE(reg_group, &s->reg_grps, entries, next_grp) {
diff --git a/hw/xen/xen_pt_graphics.c b/hw/xen/xen_pt_graphics.c
new file mode 100644
index 000000000..df6069bf6
--- /dev/null
+++ b/hw/xen/xen_pt_graphics.c
@@ -0,0 +1,272 @@
+/*
+ * graphics passthrough
+ */
+#include "xen_pt.h"
+#include "xen-host-pci-device.h"
+#include "hw/xen/xen_backend.h"
+
+static unsigned long igd_guest_opregion;
+static unsigned long igd_host_opregion;
+
+#define XEN_PCI_INTEL_OPREGION_MASK 0xfff
+
+typedef struct VGARegion {
+ int type; /* Memory or port I/O */
+ uint64_t guest_base_addr;
+ uint64_t machine_base_addr;
+ uint64_t size; /* size of the region */
+ int rc;
+} VGARegion;
+
+#define IORESOURCE_IO 0x00000100
+#define IORESOURCE_MEM 0x00000200
+
+static struct VGARegion vga_args[] = {
+ {
+ .type = IORESOURCE_IO,
+ .guest_base_addr = 0x3B0,
+ .machine_base_addr = 0x3B0,
+ .size = 0xC,
+ .rc = -1,
+ },
+ {
+ .type = IORESOURCE_IO,
+ .guest_base_addr = 0x3C0,
+ .machine_base_addr = 0x3C0,
+ .size = 0x20,
+ .rc = -1,
+ },
+ {
+ .type = IORESOURCE_MEM,
+ .guest_base_addr = 0xa0000 >> XC_PAGE_SHIFT,
+ .machine_base_addr = 0xa0000 >> XC_PAGE_SHIFT,
+ .size = 0x20,
+ .rc = -1,
+ },
+};
+
+/*
+ * register VGA resources for the domain with assigned gfx
+ */
+int xen_pt_register_vga_regions(XenHostPCIDevice *dev)
+{
+ int i = 0;
+
+ if (!is_igd_vga_passthrough(dev)) {
+ return 0;
+ }
+
+ for (i = 0 ; i < ARRAY_SIZE(vga_args); i++) {
+ if (vga_args[i].type == IORESOURCE_IO) {
+ vga_args[i].rc = xc_domain_ioport_mapping(xen_xc, xen_domid,
+ vga_args[i].guest_base_addr,
+ vga_args[i].machine_base_addr,
+ vga_args[i].size, DPCI_ADD_MAPPING);
+ } else {
+ vga_args[i].rc = xc_domain_memory_mapping(xen_xc, xen_domid,
+ vga_args[i].guest_base_addr,
+ vga_args[i].machine_base_addr,
+ vga_args[i].size, DPCI_ADD_MAPPING);
+ }
+
+ if (vga_args[i].rc) {
+ XEN_PT_ERR(NULL, "VGA %s mapping failed! (rc: %i)\n",
+ vga_args[i].type == IORESOURCE_IO ? "ioport" : "memory",
+ vga_args[i].rc);
+ return vga_args[i].rc;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * unregister VGA resources for the domain with assigned gfx
+ */
+int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev)
+{
+ int i = 0;
+ int ret = 0;
+
+ if (!is_igd_vga_passthrough(dev)) {
+ return 0;
+ }
+
+ for (i = 0 ; i < ARRAY_SIZE(vga_args); i++) {
+ if (vga_args[i].type == IORESOURCE_IO) {
+ vga_args[i].rc = xc_domain_ioport_mapping(xen_xc, xen_domid,
+ vga_args[i].guest_base_addr,
+ vga_args[i].machine_base_addr,
+ vga_args[i].size, DPCI_REMOVE_MAPPING);
+ } else {
+ vga_args[i].rc = xc_domain_memory_mapping(xen_xc, xen_domid,
+ vga_args[i].guest_base_addr,
+ vga_args[i].machine_base_addr,
+ vga_args[i].size, DPCI_REMOVE_MAPPING);
+ }
+
+ if (vga_args[i].rc) {
+ XEN_PT_ERR(NULL, "VGA %s unmapping failed! (rc: %i)\n",
+ vga_args[i].type == IORESOURCE_IO ? "ioport" : "memory",
+ vga_args[i].rc);
+ return vga_args[i].rc;
+ }
+ }
+
+ if (igd_guest_opregion) {
+ ret = xc_domain_memory_mapping(xen_xc, xen_domid,
+ (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT),
+ (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
+ 3,
+ DPCI_REMOVE_MAPPING);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void *get_vgabios(XenPCIPassthroughState *s, int *size,
+ XenHostPCIDevice *dev)
+{
+ return pci_assign_dev_load_option_rom(&s->dev, OBJECT(&s->dev), size,
+ dev->domain, dev->bus,
+ dev->dev, dev->func);
+}
+
+/* Refer to Seabios. */
+struct rom_header {
+ uint16_t signature;
+ uint8_t size;
+ uint8_t initVector[4];
+ uint8_t reserved[17];
+ uint16_t pcioffset;
+ uint16_t pnpoffset;
+} __attribute__((packed));
+
+struct pci_data {
+ uint32_t signature;
+ uint16_t vendor;
+ uint16_t device;
+ uint16_t vitaldata;
+ uint16_t dlen;
+ uint8_t drevision;
+ uint8_t class_lo;
+ uint16_t class_hi;
+ uint16_t ilen;
+ uint16_t irevision;
+ uint8_t type;
+ uint8_t indicator;
+ uint16_t reserved;
+} __attribute__((packed));
+
+int xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev)
+{
+ unsigned char *bios = NULL;
+ struct rom_header *rom;
+ int bios_size;
+ char *c = NULL;
+ char checksum = 0;
+ uint32_t len = 0;
+ struct pci_data *pd = NULL;
+
+ if (!is_igd_vga_passthrough(dev)) {
+ return -1;
+ }
+
+ bios = get_vgabios(s, &bios_size, dev);
+ if (!bios) {
+ XEN_PT_ERR(&s->dev, "VGA: Can't getting VBIOS!\n");
+ return -1;
+ }
+
+ /* Currently we fixed this address as a primary. */
+ rom = (struct rom_header *)bios;
+ pd = (void *)(bios + (unsigned char)rom->pcioffset);
+
+ /* We may need to fixup Device Identification. */
+ if (pd->device != s->real_device.device_id) {
+ pd->device = s->real_device.device_id;
+
+ len = rom->size * 512;
+ /* Then adjust the bios checksum */
+ for (c = (char *)bios; c < ((char *)bios + len); c++) {
+ checksum += *c;
+ }
+ if (checksum) {
+ bios[len - 1] -= checksum;
+ XEN_PT_LOG(&s->dev, "vga bios checksum is adjusted %x!\n",
+ checksum);
+ }
+ }
+
+ /* Currently we fixed this address as a primary for legacy BIOS. */
+ cpu_physical_memory_rw(0xc0000, bios, bios_size, 1);
+ return 0;
+}
+
+uint32_t igd_read_opregion(XenPCIPassthroughState *s)
+{
+ uint32_t val = 0;
+
+ if (!igd_guest_opregion) {
+ return val;
+ }
+
+ val = igd_guest_opregion;
+
+ XEN_PT_LOG(&s->dev, "Read opregion val=%x\n", val);
+ return val;
+}
+
+#define XEN_PCI_INTEL_OPREGION_PAGES 0x3
+#define XEN_PCI_INTEL_OPREGION_ENABLE_ACCESSED 0x1
+void igd_write_opregion(XenPCIPassthroughState *s, uint32_t val)
+{
+ int ret;
+
+ if (igd_guest_opregion) {
+ XEN_PT_LOG(&s->dev, "opregion register already been set, ignoring %x\n",
+ val);
+ return;
+ }
+
+ /* We just work with LE. */
+ xen_host_pci_get_block(&s->real_device, XEN_PCI_INTEL_OPREGION,
+ (uint8_t *)&igd_host_opregion, 4);
+ igd_guest_opregion = (unsigned long)(val & ~XEN_PCI_INTEL_OPREGION_MASK)
+ | (igd_host_opregion & XEN_PCI_INTEL_OPREGION_MASK);
+
+ ret = xc_domain_iomem_permission(xen_xc, xen_domid,
+ (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
+ XEN_PCI_INTEL_OPREGION_PAGES,
+ XEN_PCI_INTEL_OPREGION_ENABLE_ACCESSED);
+
+ if (ret) {
+ XEN_PT_ERR(&s->dev, "[%d]:Can't enable to access IGD host opregion:"
+ " 0x%lx.\n", ret,
+ (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT)),
+ igd_guest_opregion = 0;
+ return;
+ }
+
+ ret = xc_domain_memory_mapping(xen_xc, xen_domid,
+ (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT),
+ (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
+ XEN_PCI_INTEL_OPREGION_PAGES,
+ DPCI_ADD_MAPPING);
+
+ if (ret) {
+ XEN_PT_ERR(&s->dev, "[%d]:Can't map IGD host opregion:0x%lx to"
+ " guest opregion:0x%lx.\n", ret,
+ (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
+ (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT));
+ igd_guest_opregion = 0;
+ return;
+ }
+
+ XEN_PT_LOG(&s->dev, "Map OpRegion: 0x%lx -> 0x%lx\n",
+ (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
+ (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT));
+}
diff --git a/hw/xen/xen_pt_msi.c b/hw/xen/xen_pt_msi.c
index 263e0514a..82de2bced 100644
--- a/hw/xen/xen_pt_msi.c
+++ b/hw/xen/xen_pt_msi.c
@@ -75,19 +75,29 @@ static int msi_msix_enable(XenPCIPassthroughState *s,
bool enable)
{
uint16_t val = 0;
+ int rc;
if (!address) {
return -1;
}
- xen_host_pci_get_word(&s->real_device, address, &val);
+ rc = xen_host_pci_get_word(&s->real_device, address, &val);
+ if (rc) {
+ XEN_PT_ERR(&s->dev, "Failed to read MSI/MSI-X register (0x%x), rc:%d\n",
+ address, rc);
+ return rc;
+ }
if (enable) {
val |= flag;
} else {
val &= ~flag;
}
- xen_host_pci_set_word(&s->real_device, address, val);
- return 0;
+ rc = xen_host_pci_set_word(&s->real_device, address, val);
+ if (rc) {
+ XEN_PT_ERR(&s->dev, "Failed to write MSI/MSI-X register (0x%x), rc:%d\n",
+ address, rc);
+ }
+ return rc;
}
static int msi_msix_setup(XenPCIPassthroughState *s,
@@ -220,7 +230,7 @@ static int msi_msix_disable(XenPCIPassthroughState *s,
* MSI virtualization functions
*/
-int xen_pt_msi_set_enable(XenPCIPassthroughState *s, bool enable)
+static int xen_pt_msi_set_enable(XenPCIPassthroughState *s, bool enable)
{
XEN_PT_LOG(&s->dev, "%s MSI.\n", enable ? "enabling" : "disabling");
@@ -276,7 +286,7 @@ void xen_pt_msi_disable(XenPCIPassthroughState *s)
return;
}
- xen_pt_msi_set_enable(s, false);
+ (void)xen_pt_msi_set_enable(s, false);
msi_msix_disable(s, msi_addr64(msi), msi->data, msi->pirq, false,
msi->initialized);
@@ -600,7 +610,7 @@ error_out:
return rc;
}
-void xen_pt_msix_delete(XenPCIPassthroughState *s)
+void xen_pt_msix_unmap(XenPCIPassthroughState *s)
{
XenPTMSIX *msix = s->msix;
@@ -617,6 +627,17 @@ void xen_pt_msix_delete(XenPCIPassthroughState *s)
}
memory_region_del_subregion(&s->bar[msix->bar_index], &msix->mmio);
+}
+
+void xen_pt_msix_delete(XenPCIPassthroughState *s)
+{
+ XenPTMSIX *msix = s->msix;
+
+ if (!msix) {
+ return;
+ }
+
+ object_unparent(OBJECT(&msix->mmio));
g_free(s->msix);
s->msix = NULL;
diff --git a/hw/xenpv/xen_domainbuild.c b/hw/xenpv/xen_domainbuild.c
index c0ab7537d..ac0e5ac9f 100644
--- a/hw/xenpv/xen_domainbuild.c
+++ b/hw/xenpv/xen_domainbuild.c
@@ -234,7 +234,7 @@ int xen_domain_build_pv(const char *kernel, const char *ramdisk,
int rc;
memcpy(uuid, qemu_uuid, sizeof(uuid));
- rc = xc_domain_create(xen_xc, ssidref, uuid, flags, &xen_domid);
+ rc = xen_domain_create(xen_xc, ssidref, uuid, flags, &xen_domid);
if (rc < 0) {
fprintf(stderr, "xen: xc_domain_create() failed\n");
goto err;
diff --git a/hw/xenpv/xen_machine_pv.c b/hw/xenpv/xen_machine_pv.c
index 2e545d241..23d6ef07e 100644
--- a/hw/xenpv/xen_machine_pv.c
+++ b/hw/xenpv/xen_machine_pv.c
@@ -93,17 +93,12 @@ static void xen_init_pv(MachineState *machine)
xen_init_display(xen_domid);
}
-static QEMUMachine xenpv_machine = {
- .name = "xenpv",
- .desc = "Xen Para-virtualized PC",
- .init = xen_init_pv,
- .max_cpus = 1,
- .default_machine_opts = "accel=xen",
-};
-
-static void xenpv_machine_init(void)
+static void xenpv_machine_init(MachineClass *mc)
{
- qemu_register_machine(&xenpv_machine);
+ mc->desc = "Xen Para-virtualized PC";
+ mc->init = xen_init_pv;
+ mc->max_cpus = 1;
+ mc->default_machine_opts = "accel=xen";
}
-machine_init(xenpv_machine_init);
+DEFINE_MACHINE("xenpv", xenpv_machine_init)
diff --git a/hw/xtensa/sim.c b/hw/xtensa/sim.c
index 328d20975..6266b8d44 100644
--- a/hw/xtensa/sim.c
+++ b/hw/xtensa/sim.c
@@ -79,12 +79,12 @@ static void xtensa_sim_init(MachineState *machine)
}
ram = g_malloc(sizeof(*ram));
- memory_region_init_ram(ram, NULL, "xtensa.sram", ram_size, &error_abort);
+ memory_region_init_ram(ram, NULL, "xtensa.sram", ram_size, &error_fatal);
vmstate_register_ram_global(ram);
memory_region_add_subregion(get_system_memory(), 0, ram);
rom = g_malloc(sizeof(*rom));
- memory_region_init_ram(rom, NULL, "xtensa.rom", 0x1000, &error_abort);
+ memory_region_init_ram(rom, NULL, "xtensa.rom", 0x1000, &error_fatal);
vmstate_register_ram_global(rom);
memory_region_add_subregion(get_system_memory(), 0xfe000000, rom);
@@ -93,10 +93,10 @@ static void xtensa_sim_init(MachineState *machine)
uint64_t elf_lowaddr;
#ifdef TARGET_WORDS_BIGENDIAN
int success = load_elf(kernel_filename, translate_phys_addr, cpu,
- &elf_entry, &elf_lowaddr, NULL, 1, ELF_MACHINE, 0);
+ &elf_entry, &elf_lowaddr, NULL, 1, EM_XTENSA, 0);
#else
int success = load_elf(kernel_filename, translate_phys_addr, cpu,
- &elf_entry, &elf_lowaddr, NULL, 0, ELF_MACHINE, 0);
+ &elf_entry, &elf_lowaddr, NULL, 0, EM_XTENSA, 0);
#endif
if (success > 0) {
env->pc = elf_entry;
@@ -104,17 +104,12 @@ static void xtensa_sim_init(MachineState *machine)
}
}
-static QEMUMachine xtensa_sim_machine = {
- .name = "sim",
- .desc = "sim machine (" XTENSA_DEFAULT_CPU_MODEL ")",
- .is_default = true,
- .init = xtensa_sim_init,
- .max_cpus = 4,
-};
-
-static void xtensa_sim_machine_init(void)
+static void xtensa_sim_machine_init(MachineClass *mc)
{
- qemu_register_machine(&xtensa_sim_machine);
+ mc->desc = "sim machine (" XTENSA_DEFAULT_CPU_MODEL ")";
+ mc->is_default = true;
+ mc->init = xtensa_sim_init;
+ mc->max_cpus = 4;
}
-machine_init(xtensa_sim_machine_init);
+DEFINE_MACHINE("sim", xtensa_sim_machine_init)
diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c
index ab4d0e412..c1bc5aef5 100644
--- a/hw/xtensa/xtfpga.c
+++ b/hw/xtensa/xtfpga.c
@@ -143,11 +143,34 @@ static void lx60_net_init(MemoryRegion *address_space,
sysbus_mmio_get_region(s, 1));
ram = g_malloc(sizeof(*ram));
- memory_region_init_ram(ram, OBJECT(s), "open_eth.ram", 16384, &error_abort);
+ memory_region_init_ram(ram, OBJECT(s), "open_eth.ram", 16384,
+ &error_fatal);
vmstate_register_ram_global(ram);
memory_region_add_subregion(address_space, buffers, ram);
}
+static pflash_t *xtfpga_flash_init(MemoryRegion *address_space,
+ const LxBoardDesc *board,
+ DriveInfo *dinfo, int be)
+{
+ SysBusDevice *s;
+ DeviceState *dev = qdev_create(NULL, "cfi.pflash01");
+
+ qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo),
+ &error_abort);
+ qdev_prop_set_uint32(dev, "num-blocks",
+ board->flash_size / board->flash_sector_size);
+ qdev_prop_set_uint64(dev, "sector-length", board->flash_sector_size);
+ qdev_prop_set_uint8(dev, "width", 4);
+ qdev_prop_set_bit(dev, "big-endian", be);
+ qdev_prop_set_string(dev, "name", "lx60.io.flash");
+ qdev_init_nofail(dev);
+ s = SYS_BUS_DEVICE(dev);
+ memory_region_add_subregion(address_space, board->flash_base,
+ sysbus_mmio_get_region(s, 0));
+ return OBJECT_CHECK(pflash_t, (dev), "cfi.pflash01");
+}
+
static uint64_t translate_phys_addr(void *opaque, uint64_t addr)
{
XtensaCPU *cpu = opaque;
@@ -223,7 +246,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine)
ram = g_malloc(sizeof(*ram));
memory_region_init_ram(ram, NULL, "lx60.dram", machine->ram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(ram);
memory_region_add_subregion(system_memory, 0, ram);
@@ -246,16 +269,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine)
dinfo = drive_get(IF_PFLASH, 0, 0);
if (dinfo) {
- flash = pflash_cfi01_register(board->flash_base,
- NULL, "lx60.io.flash", board->flash_size,
- blk_by_legacy_dinfo(dinfo),
- board->flash_sector_size,
- board->flash_size / board->flash_sector_size,
- 4, 0x0000, 0x0000, 0x0000, 0x0000, be);
- if (flash == NULL) {
- error_report("unable to mount pflash");
- exit(EXIT_FAILURE);
- }
+ flash = xtfpga_flash_init(system_io, board, dinfo, be);
}
/* Use presence of kernel file name as 'boot from SRAM' switch. */
@@ -275,7 +289,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine)
rom = g_malloc(sizeof(*rom));
memory_region_init_ram(rom, NULL, "lx60.sram", board->sram_size,
- &error_abort);
+ &error_fatal);
vmstate_register_ram_global(rom);
memory_region_add_subregion(system_memory, 0xfe000000, rom);
@@ -340,7 +354,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine)
uint64_t elf_entry;
uint64_t elf_lowaddr;
int success = load_elf(kernel_filename, translate_phys_addr, cpu,
- &elf_entry, &elf_lowaddr, NULL, be, ELF_MACHINE, 0);
+ &elf_entry, &elf_lowaddr, NULL, be, EM_XTENSA, 0);
if (success > 0) {
entry_point = elf_entry;
} else {
@@ -385,7 +399,7 @@ static void lx_init(const LxBoardDesc *board, MachineState *machine)
static void xtensa_lx60_init(MachineState *machine)
{
static const LxBoardDesc lx60_board = {
- .flash_base = 0xf8000000,
+ .flash_base = 0x08000000,
.flash_size = 0x00400000,
.flash_sector_size = 0x10000,
.sram_size = 0x20000,
@@ -396,7 +410,7 @@ static void xtensa_lx60_init(MachineState *machine)
static void xtensa_lx200_init(MachineState *machine)
{
static const LxBoardDesc lx200_board = {
- .flash_base = 0xf8000000,
+ .flash_base = 0x08000000,
.flash_size = 0x01000000,
.flash_sector_size = 0x20000,
.sram_size = 0x2000000,
@@ -407,7 +421,7 @@ static void xtensa_lx200_init(MachineState *machine)
static void xtensa_ml605_init(MachineState *machine)
{
static const LxBoardDesc ml605_board = {
- .flash_base = 0xf8000000,
+ .flash_base = 0x08000000,
.flash_size = 0x01000000,
.flash_sector_size = 0x20000,
.sram_size = 0x2000000,
@@ -418,7 +432,7 @@ static void xtensa_ml605_init(MachineState *machine)
static void xtensa_kc705_init(MachineState *machine)
{
static const LxBoardDesc kc705_board = {
- .flash_base = 0xf0000000,
+ .flash_base = 0x00000000,
.flash_size = 0x08000000,
.flash_boot_base = 0x06000000,
.flash_sector_size = 0x20000,
@@ -427,40 +441,72 @@ static void xtensa_kc705_init(MachineState *machine)
lx_init(&kc705_board, machine);
}
-static QEMUMachine xtensa_lx60_machine = {
- .name = "lx60",
- .desc = "lx60 EVB (" XTENSA_DEFAULT_CPU_MODEL ")",
- .init = xtensa_lx60_init,
- .max_cpus = 4,
+static void xtensa_lx60_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "lx60 EVB (" XTENSA_DEFAULT_CPU_MODEL ")";
+ mc->init = xtensa_lx60_init;
+ mc->max_cpus = 4;
+}
+
+static const TypeInfo xtensa_lx60_type = {
+ .name = MACHINE_TYPE_NAME("lx60"),
+ .parent = TYPE_MACHINE,
+ .class_init = xtensa_lx60_class_init,
};
-static QEMUMachine xtensa_lx200_machine = {
- .name = "lx200",
- .desc = "lx200 EVB (" XTENSA_DEFAULT_CPU_MODEL ")",
- .init = xtensa_lx200_init,
- .max_cpus = 4,
+static void xtensa_lx200_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "lx200 EVB (" XTENSA_DEFAULT_CPU_MODEL ")";
+ mc->init = xtensa_lx200_init;
+ mc->max_cpus = 4;
+}
+
+static const TypeInfo xtensa_lx200_type = {
+ .name = MACHINE_TYPE_NAME("lx200"),
+ .parent = TYPE_MACHINE,
+ .class_init = xtensa_lx200_class_init,
};
-static QEMUMachine xtensa_ml605_machine = {
- .name = "ml605",
- .desc = "ml605 EVB (" XTENSA_DEFAULT_CPU_MODEL ")",
- .init = xtensa_ml605_init,
- .max_cpus = 4,
+static void xtensa_ml605_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "ml605 EVB (" XTENSA_DEFAULT_CPU_MODEL ")";
+ mc->init = xtensa_ml605_init;
+ mc->max_cpus = 4;
+}
+
+static const TypeInfo xtensa_ml605_type = {
+ .name = MACHINE_TYPE_NAME("ml605"),
+ .parent = TYPE_MACHINE,
+ .class_init = xtensa_ml605_class_init,
};
-static QEMUMachine xtensa_kc705_machine = {
- .name = "kc705",
- .desc = "kc705 EVB (" XTENSA_DEFAULT_CPU_MODEL ")",
- .init = xtensa_kc705_init,
- .max_cpus = 4,
+static void xtensa_kc705_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "kc705 EVB (" XTENSA_DEFAULT_CPU_MODEL ")";
+ mc->init = xtensa_kc705_init;
+ mc->max_cpus = 4;
+}
+
+static const TypeInfo xtensa_kc705_type = {
+ .name = MACHINE_TYPE_NAME("kc705"),
+ .parent = TYPE_MACHINE,
+ .class_init = xtensa_kc705_class_init,
};
static void xtensa_lx_machines_init(void)
{
- qemu_register_machine(&xtensa_lx60_machine);
- qemu_register_machine(&xtensa_lx200_machine);
- qemu_register_machine(&xtensa_ml605_machine);
- qemu_register_machine(&xtensa_kc705_machine);
+ type_register_static(&xtensa_lx60_type);
+ type_register_static(&xtensa_lx200_type);
+ type_register_static(&xtensa_ml605_type);
+ type_register_static(&xtensa_kc705_type);
}
-machine_init(xtensa_lx_machines_init);
+machine_init(xtensa_lx_machines_init)