summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS17
-rw-r--r--Makefile9
-rw-r--r--Makefile.objs2
-rw-r--r--Makefile.target6
-rw-r--r--arch_init.c1654
-rw-r--r--async.c8
-rw-r--r--audio/Makefile.objs4
-rw-r--r--audio/alsaaudio.c195
-rw-r--r--audio/audio.c35
-rw-r--r--audio/audio_int.h7
-rw-r--r--audio/audio_template.h43
-rw-r--r--audio/coreaudio.c46
-rw-r--r--audio/dsound_template.h51
-rw-r--r--audio/dsoundaudio.c238
-rw-r--r--audio/esdaudio.c557
-rw-r--r--audio/fmodaudio.c685
-rw-r--r--audio/noaudio.c4
-rw-r--r--audio/ossaudio.c140
-rw-r--r--audio/paaudio.c105
-rw-r--r--audio/sdlaudio.c10
-rw-r--r--audio/spiceaudio.c5
-rw-r--r--audio/wavaudio.c42
-rw-r--r--audio/winwaveaudio.c717
-rw-r--r--block.c275
-rw-r--r--block/Makefile.objs1
-rw-r--r--block/blkdebug.c14
-rw-r--r--block/blkverify.c4
-rw-r--r--block/io.c71
-rw-r--r--block/iscsi.c7
-rw-r--r--block/qapi.c8
-rw-r--r--block/qcow2.c11
-rw-r--r--block/qcow2.h5
-rw-r--r--block/quorum.c51
-rw-r--r--block/raw-posix.c5
-rw-r--r--block/throttle-groups.c496
-rw-r--r--block/vmdk.c84
-rw-r--r--blockdev-nbd.c4
-rw-r--r--blockdev.c73
-rw-r--r--bsd-user/main.c4
-rwxr-xr-xconfigure76
-rw-r--r--cpus.c95
-rw-r--r--cputlb.c7
-rw-r--r--default-configs/mips-softmmu.mak5
-rw-r--r--default-configs/mips64-softmmu.mak1
-rw-r--r--default-configs/mips64el-softmmu.mak1
-rw-r--r--default-configs/mipsel-softmmu.mak5
-rw-r--r--default-configs/s390x-softmmu.mak1
-rw-r--r--default-configs/x86_64-softmmu.mak1
-rw-r--r--device_tree.c1
-rw-r--r--disas/mips.c3
-rw-r--r--docs/migration.txt11
-rw-r--r--docs/qapi-code-gen.txt2
-rw-r--r--docs/specs/fw_cfg.txt21
-rw-r--r--docs/specs/ppc-spapr-hotplug.txt287
-rw-r--r--docs/specs/rocker.txt1
-rw-r--r--docs/writing-qmp-commands.txt2
m---------dtc0
-rw-r--r--exec.c144
-rw-r--r--fpu/softfloat-specialize.h8
-rw-r--r--fsdev/virtfs-proxy-helper.c8
-rw-r--r--hmp-commands.hx44
-rw-r--r--hmp.c336
-rw-r--r--hmp.h6
-rw-r--r--hw/acpi/ich9.c14
-rw-r--r--hw/acpi/piix4.c10
-rw-r--r--hw/alpha/dp264.c7
-rw-r--r--hw/alpha/typhoon.c8
-rw-r--r--hw/arm/Makefile.objs1
-rw-r--r--hw/arm/boot.c34
-rw-r--r--hw/arm/nseries.c5
-rw-r--r--hw/arm/omap_sx1.c2
-rw-r--r--hw/arm/pxa2xx.c248
-rw-r--r--hw/arm/pxa2xx_pic.c2
-rw-r--r--hw/arm/sysbus-fdt.c174
-rw-r--r--hw/arm/vexpress.c2
-rw-r--r--hw/arm/virt-acpi-build.c43
-rw-r--r--hw/arm/virt.c166
-rw-r--r--hw/block/fdc.c332
-rw-r--r--hw/block/pflash_cfi01.c204
-rw-r--r--hw/char/parallel.c25
-rw-r--r--hw/char/serial.c41
-rw-r--r--hw/core/machine.c9
-rw-r--r--hw/core/nmi.c20
-rw-r--r--hw/core/qdev-properties-system.c5
-rw-r--r--hw/display/Makefile.objs4
-rw-r--r--hw/display/cg3.c2
-rw-r--r--hw/display/exynos4210_fimd.c22
-rw-r--r--hw/display/framebuffer.c4
-rw-r--r--hw/display/g364fb.c3
-rw-r--r--hw/display/qxl.c11
-rw-r--r--hw/display/sm501.c2
-rw-r--r--hw/display/tc6393xb.c2
-rw-r--r--hw/display/tcx.c3
-rw-r--r--hw/display/vga-pci.c97
-rw-r--r--hw/display/vga.c11
-rw-r--r--hw/display/vga_int.h6
-rw-r--r--hw/display/virtio-gpu-pci.c68
-rw-r--r--hw/display/virtio-gpu.c918
-rw-r--r--hw/display/virtio-vga.c175
-rw-r--r--hw/display/vmware_vga.c2
-rw-r--r--hw/dma/rc4030.c462
-rw-r--r--hw/gpio/pl061.c2
-rw-r--r--hw/i386/acpi-build.c1
-rw-r--r--hw/i386/pc.c36
-rw-r--r--hw/i386/pc_piix.c13
-rw-r--r--hw/i386/pc_q35.c7
-rw-r--r--hw/ide/core.c32
-rw-r--r--hw/ide/macio.c271
-rw-r--r--hw/ide/pci.c21
-rw-r--r--hw/input/pckbd.c22
-rw-r--r--hw/input/ps2.c11
-rw-r--r--hw/intc/Makefile.objs1
-rw-r--r--hw/intc/apic.c9
-rw-r--r--hw/intc/apic_common.c10
-rw-r--r--hw/intc/arm_gic.c2
-rw-r--r--hw/intc/arm_gicv2m.c192
-rw-r--r--hw/intc/exynos4210_gic.c7
-rw-r--r--hw/isa/i82378.c7
-rw-r--r--hw/isa/isa-bus.c29
-rw-r--r--hw/isa/lpc_ich9.c34
-rw-r--r--hw/lm32/lm32_boards.c10
-rw-r--r--hw/lm32/milkymist.c5
-rw-r--r--hw/mips/Makefile.objs3
-rw-r--r--hw/mips/mips_jazz.c53
-rw-r--r--hw/mips/mips_malta.c15
-rw-r--r--hw/misc/macio/macio.c71
-rw-r--r--hw/net/Makefile.objs1
-rw-r--r--hw/net/cadence_gem.c2
-rw-r--r--hw/net/dp8393x.c369
-rw-r--r--hw/net/e1000.c11
-rw-r--r--hw/net/pcnet.c8
-rw-r--r--hw/net/rocker/qmp-norocker.c50
-rw-r--r--hw/net/rocker/rocker.c68
-rw-r--r--hw/net/rocker/rocker_fp.c29
-rw-r--r--hw/net/rocker/rocker_fp.h2
-rw-r--r--hw/net/rocker/rocker_hw.h1
-rw-r--r--hw/net/rocker/rocker_of_dpa.c312
-rw-r--r--hw/net/rtl8139.c11
-rw-r--r--hw/net/vmxnet3.c12
-rw-r--r--hw/nvram/fw_cfg.c55
-rw-r--r--hw/pci-host/pam.c20
-rw-r--r--hw/pci-host/piix.c54
-rw-r--r--hw/pci-host/q35.c142
-rw-r--r--hw/pci/msi.c4
-rw-r--r--hw/pci/pci-stub.c14
-rw-r--r--hw/pci/pci.c4
-rw-r--r--hw/pci/pcie_aer.c39
-rw-r--r--hw/ppc/Makefile.objs2
-rw-r--r--hw/ppc/e500.c1
-rw-r--r--hw/ppc/mac_newworld.c2
-rw-r--r--hw/ppc/mac_oldworld.c2
-rw-r--r--hw/ppc/prep.c5
-rw-r--r--hw/ppc/spapr.c49
-rw-r--r--hw/ppc/spapr_drc.c744
-rw-r--r--hw/ppc/spapr_events.c338
-rw-r--r--hw/ppc/spapr_iommu.c46
-rw-r--r--hw/ppc/spapr_pci.c513
-rw-r--r--hw/ppc/spapr_rtas.c361
-rw-r--r--hw/ppc/spapr_vio.c2
-rw-r--r--hw/s390x/s390-virtio-ccw.c1
-rw-r--r--hw/s390x/s390-virtio.c1
-rw-r--r--hw/s390x/virtio-ccw.c64
-rw-r--r--hw/s390x/virtio-ccw.h18
-rw-r--r--hw/scsi/scsi-bus.c11
-rw-r--r--hw/sd/pxa2xx_mmci.c68
-rw-r--r--hw/sh4/r2d.c12
-rw-r--r--hw/sparc/sun4m.c8
-rw-r--r--hw/sparc64/sun4u.c2
-rw-r--r--hw/timer/arm_timer.c6
-rw-r--r--hw/timer/hpet.c11
-rw-r--r--hw/timer/mc146818rtc.c23
-rw-r--r--hw/unicore32/puv3.c8
-rw-r--r--hw/usb/hcd-ohci.c11
-rw-r--r--hw/usb/redirect.c34
-rw-r--r--hw/vfio/Makefile.objs2
-rw-r--r--hw/vfio/calxeda-xgmac.c55
-rw-r--r--hw/vfio/platform.c615
-rw-r--r--hw/virtio/dataplane/vring.c2
-rw-r--r--hw/virtio/vhost.c9
-rw-r--r--hw/virtio/virtio-pci.h14
-rw-r--r--hw/virtio/virtio.c16
-rw-r--r--hw/watchdog/Makefile.objs1
-rw-r--r--hw/watchdog/watchdog.c10
-rw-r--r--hw/watchdog/wdt_diag288.c122
-rw-r--r--hw/xen/xen_backend.c4
-rw-r--r--hw/xen/xen_pt.c51
-rw-r--r--hw/xen/xen_pt.h7
-rw-r--r--hw/xen/xen_pt_config_init.c235
-rw-r--r--hw/xen/xen_pt_msi.c12
-rw-r--r--include/block/aio.h2
-rw-r--r--include/block/block.h15
-rw-r--r--include/block/block_int.h26
-rw-r--r--include/block/throttle-groups.h46
-rw-r--r--include/exec/cpu-common.h4
-rw-r--r--include/exec/cpu-defs.h35
-rw-r--r--include/exec/cpu_ldst.h112
-rw-r--r--include/exec/cputlb.h3
-rw-r--r--include/exec/exec-all.h8
-rw-r--r--include/exec/memattrs.h4
-rw-r--r--include/exec/memory.h25
-rw-r--r--include/exec/ram_addr.h138
-rw-r--r--include/exec/user/thunk.h4
-rw-r--r--include/hw/acpi/acpi-defs.h32
-rw-r--r--include/hw/acpi/ich9.h1
-rw-r--r--include/hw/arm/arm.h28
-rw-r--r--include/hw/arm/sysbus-fdt.h60
-rw-r--r--include/hw/arm/virt.h3
-rw-r--r--include/hw/boards.h1
-rw-r--r--include/hw/i386/ich9.h6
-rw-r--r--include/hw/i386/pc.h3
-rw-r--r--include/hw/mips/mips.h11
-rw-r--r--include/hw/nmi.h1
-rw-r--r--include/hw/nvram/fw_cfg.h3
-rw-r--r--include/hw/pci-host/pam.h4
-rw-r--r--include/hw/pci-host/q35.h36
-rw-r--r--include/hw/pci-host/spapr.h7
-rw-r--r--include/hw/pci/pci.h6
-rw-r--r--include/hw/pci/pci_regs.h2
-rw-r--r--include/hw/ppc/mac_dbdma.h4
-rw-r--r--include/hw/ppc/spapr.h59
-rw-r--r--include/hw/ppc/spapr_drc.h201
-rw-r--r--include/hw/vfio/vfio-calxeda-xgmac.h46
-rw-r--r--include/hw/vfio/vfio-common.h1
-rw-r--r--include/hw/vfio/vfio-platform.h75
-rw-r--r--include/hw/virtio/virtio-gpu.h145
-rw-r--r--include/hw/virtio/virtio-input.h18
-rw-r--r--include/hw/watchdog/wdt_diag288.h36
-rw-r--r--include/migration/migration.h17
-rw-r--r--include/migration/qemu-file.h5
-rw-r--r--include/migration/vmstate.h10
-rw-r--r--include/monitor/monitor.h8
-rw-r--r--include/qapi/qmp/qdict.h4
-rw-r--r--include/qemu-common.h6
-rw-r--r--include/qemu/atomic.h12
-rw-r--r--include/qemu/bitmap.h4
-rw-r--r--include/qemu/bitops.h14
-rw-r--r--include/qemu/main-loop.h57
-rw-r--r--include/qemu/option.h12
-rw-r--r--include/qemu/queue.h6
-rw-r--r--include/qemu/throttle.h46
-rw-r--r--include/qemu/typedefs.h2
-rw-r--r--include/qom/object.h18
-rw-r--r--include/standard-headers/linux/virtio_gpu.h204
-rw-r--r--include/standard-headers/linux/virtio_ids.h1
-rw-r--r--include/standard-headers/linux/virtio_ring.h2
-rw-r--r--include/sysemu/arch_init.h1
-rw-r--r--include/sysemu/blockdev.h2
-rw-r--r--include/sysemu/kvm.h2
-rw-r--r--include/sysemu/sysemu.h7
-rw-r--r--include/ui/console.h6
-rw-r--r--iohandler.c21
-rw-r--r--iothread.c11
-rw-r--r--kvm-all.c87
-rw-r--r--linux-headers/asm-x86/kvm.h14
-rw-r--r--linux-headers/linux/kvm.h7
-rw-r--r--linux-user/elfload.c32
-rw-r--r--linux-user/main.c10
-rw-r--r--linux-user/mmap.c7
-rw-r--r--linux-user/syscall.c108
-rw-r--r--main-loop.c3
-rw-r--r--memory.c81
-rw-r--r--migration/exec.c6
-rw-r--r--migration/fd.c4
-rw-r--r--migration/migration.c34
-rw-r--r--migration/qemu-file.c29
-rw-r--r--migration/ram.c1628
-rw-r--r--migration/rdma.c85
-rw-r--r--migration/savevm.c (renamed from savevm.c)257
-rw-r--r--migration/tcp.c6
-rw-r--r--migration/unix.c6
-rw-r--r--migration/vmstate.c21
-rw-r--r--monitor.c429
-rw-r--r--net/l2tpv3.c17
-rw-r--r--net/net.c10
-rw-r--r--net/netmap.c20
-rw-r--r--net/slirp.c7
-rw-r--r--net/socket.c37
-rw-r--r--net/tap.c19
-rw-r--r--net/vhost-user.c32
-rw-r--r--numa.c5
-rw-r--r--pc-bios/bios-256k.binbin262144 -> 262144 bytes
-rw-r--r--pc-bios/bios.binbin131072 -> 131072 bytes
-rw-r--r--pc-bios/openbios-ppcbin746588 -> 746588 bytes
-rw-r--r--pc-bios/openbios-sparc32bin381512 -> 381512 bytes
-rw-r--r--pc-bios/openbios-sparc64bin1616768 -> 1616768 bytes
-rw-r--r--pc-bios/s390-ccw/Makefile2
-rw-r--r--pc-bios/vgabios-cirrus.binbin37888 -> 38400 bytes
-rw-r--r--pc-bios/vgabios-qxl.binbin38400 -> 38400 bytes
-rw-r--r--pc-bios/vgabios-stdvga.binbin38400 -> 38400 bytes
-rw-r--r--pc-bios/vgabios-virtio.binbin0 -> 38400 bytes
-rw-r--r--pc-bios/vgabios-vmware.binbin38400 -> 38400 bytes
-rw-r--r--pc-bios/vgabios.binbin38400 -> 38400 bytes
-rw-r--r--qapi-schema.json28
-rw-r--r--qapi/block-core.json29
-rw-r--r--qapi/rocker.json286
-rw-r--r--qdev-monitor.c29
-rw-r--r--qemu-nbd.c21
-rw-r--r--qemu-options.hx73
-rw-r--r--qmp-commands.hx122
-rw-r--r--qobject/qdict.c111
-rw-r--r--qom/object.c16
-rw-r--r--roms/Makefile7
-rw-r--r--roms/config.vga-virtio6
m---------roms/openbios0
m---------roms/seabios0
-rwxr-xr-xscripts/analyze-migration.py5
-rw-r--r--scripts/qapi-types.py31
-rw-r--r--scripts/qapi.py346
-rw-r--r--softmmu_template.h22
-rw-r--r--stubs/mon-is-qmp.c4
-rw-r--r--stubs/set-fd-handler.c3
-rw-r--r--target-arm/cpu-qom.h8
-rw-r--r--target-arm/cpu.c29
-rw-r--r--target-arm/cpu.h2
-rw-r--r--target-arm/cpu64.c3
-rw-r--r--target-arm/helper.c333
-rw-r--r--target-arm/internals.h3
-rw-r--r--target-arm/kvm-consts.h4
-rw-r--r--target-arm/kvm.c5
-rw-r--r--target-arm/kvm32.c15
-rw-r--r--target-arm/kvm64.c15
-rw-r--r--target-arm/machine.c26
-rw-r--r--target-arm/op_helper.c13
-rw-r--r--target-arm/psci.c19
-rw-r--r--target-arm/translate.c114
-rw-r--r--target-i386/Makefile.objs2
-rw-r--r--target-i386/cpu-qom.h3
-rw-r--r--target-i386/cpu.c169
-rw-r--r--target-i386/cpu.h42
-rw-r--r--target-i386/helper.c135
-rw-r--r--target-i386/helper.h12
-rw-r--r--target-i386/ioport-user.c60
-rw-r--r--target-i386/kvm.c7
-rw-r--r--target-i386/machine.c84
-rw-r--r--target-i386/misc_helper.c59
-rw-r--r--target-i386/seg_helper.c12
-rw-r--r--target-i386/smm_helper.c333
-rw-r--r--target-i386/svm_helper.c230
-rw-r--r--target-i386/translate.c12
-rw-r--r--target-microblaze/cpu.h1
-rw-r--r--target-mips/cpu.h52
-rw-r--r--target-mips/helper.h11
-rw-r--r--target-mips/kvm.c5
-rw-r--r--target-mips/machine.c21
-rw-r--r--target-mips/mips-defs.h4
-rw-r--r--target-mips/op_helper.c244
-rw-r--r--target-mips/translate.c802
-rw-r--r--target-mips/translate_init.c37
-rw-r--r--target-ppc/kvm.c22
-rw-r--r--target-ppc/kvm_ppc.h5
-rw-r--r--target-ppc/machine.c62
-rw-r--r--target-s390x/cc_helper.c4
-rw-r--r--target-s390x/cpu-qom.h1
-rw-r--r--target-s390x/cpu.c27
-rw-r--r--target-s390x/cpu.h102
-rw-r--r--target-s390x/fpu_helper.c43
-rw-r--r--target-s390x/helper.c132
-rw-r--r--target-s390x/helper.h25
-rw-r--r--target-s390x/insn-data.def115
-rw-r--r--target-s390x/int_helper.c42
-rw-r--r--target-s390x/ioinst.c6
-rw-r--r--target-s390x/ioinst.h2
-rw-r--r--target-s390x/kvm.c30
-rw-r--r--target-s390x/machine.c29
-rw-r--r--target-s390x/mem_helper.c310
-rw-r--r--target-s390x/misc_helper.c190
-rw-r--r--target-s390x/mmu_helper.c2
-rw-r--r--target-s390x/translate.c556
-rw-r--r--target-sh4/cpu.c3
-rw-r--r--target-sh4/cpu.h50
-rw-r--r--target-sh4/gdbstub.c8
-rw-r--r--target-sh4/helper.c29
-rw-r--r--target-sh4/helper.h1
-rw-r--r--target-sh4/op_helper.c148
-rw-r--r--target-sh4/translate.c327
-rw-r--r--tcg/aarch64/tcg-target.c4
-rw-r--r--tcg/aarch64/tcg-target.h1
-rw-r--r--tcg/arm/tcg-target.c6
-rw-r--r--tcg/arm/tcg-target.h1
-rw-r--r--tcg/i386/tcg-target.c4
-rw-r--r--tcg/i386/tcg-target.h1
-rw-r--r--tcg/ia64/tcg-target.h2
-rw-r--r--tcg/mips/tcg-target.c4
-rw-r--r--tcg/mips/tcg-target.h1
-rw-r--r--tcg/optimize.c149
-rw-r--r--tcg/ppc/tcg-target.c8
-rw-r--r--tcg/ppc/tcg-target.h1
-rw-r--r--tcg/s390/tcg-target.c4
-rw-r--r--tcg/s390/tcg-target.h1
-rw-r--r--tcg/sparc/tcg-target.c30
-rw-r--r--tcg/sparc/tcg-target.h1
-rw-r--r--tcg/tcg.c39
-rw-r--r--tcg/tcg.h4
-rw-r--r--tcg/tci/tcg-target.h4
-rw-r--r--tci.c8
-rw-r--r--tests/Makefile6
-rw-r--r--tests/bios-tables-test.c76
-rw-r--r--tests/check-qdict.c67
-rw-r--r--tests/endianness-test.c2
-rw-r--r--tests/fdc-test.c34
-rw-r--r--tests/q35-test.c91
-rw-r--r--tests/qapi-schema/flat-union-array-branch.err1
-rw-r--r--tests/qapi-schema/flat-union-array-branch.exit1
-rw-r--r--tests/qapi-schema/flat-union-array-branch.json12
-rw-r--r--tests/qapi-schema/flat-union-array-branch.out (renamed from sysconfigs/target/target-x86_64.conf)0
-rw-r--r--tests/qapi-schema/include-cycle.err4
-rw-r--r--tests/qapi-schema/include-nested-err.err2
-rwxr-xr-xtests/qemu-iotests/0511
-rw-r--r--tests/qemu-iotests/051.out3
-rwxr-xr-xtests/qemu-iotests/09393
-rwxr-xr-xtests/qemu-iotests/10310
-rw-r--r--tests/qemu-iotests/103.out5
-rwxr-xr-xtests/qemu-iotests/11960
-rw-r--r--tests/qemu-iotests/119.out11
-rwxr-xr-xtests/qemu-iotests/12065
-rw-r--r--tests/qemu-iotests/120.out15
-rw-r--r--tests/qemu-iotests/1242
-rwxr-xr-xtests/qemu-iotests/1284
-rw-r--r--tests/qemu-iotests/group2
-rwxr-xr-xtests/rocker/bridge25
-rwxr-xr-xtests/rocker/bridge-stp25
-rwxr-xr-xtests/rocker/bridge-vlan37
-rwxr-xr-xtests/rocker/bridge-vlan-stp37
-rwxr-xr-xtests/rocker/port8
-rw-r--r--tests/test-aio.c19
-rw-r--r--tests/test-throttle.c163
-rw-r--r--thunk.c16
-rw-r--r--tpm.c5
-rw-r--r--trace-events59
-rw-r--r--translate-all.c40
-rw-r--r--translate-all.h8
-rw-r--r--ui/console.c61
-rw-r--r--ui/gtk.c13
-rw-r--r--ui/sdl2.c4
-rw-r--r--ui/spice-core.c5
-rw-r--r--ui/spice-display.c26
-rw-r--r--ui/vnc-auth-sasl.c2
-rw-r--r--ui/vnc-auth-vencrypt.c2
-rw-r--r--ui/vnc-ws.c6
-rw-r--r--ui/vnc.c29
-rw-r--r--user-exec.c1
-rw-r--r--util/bitmap.c83
-rw-r--r--util/event_notifier-posix.c3
-rw-r--r--util/qemu-config.c11
-rw-r--r--util/qemu-option.c43
-rw-r--r--util/qemu-sockets.c14
-rw-r--r--util/throttle.c81
-rw-r--r--vl.c202
-rw-r--r--xen-hvm.c22
449 files changed, 18489 insertions, 9902 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 7a13d68a36..106e2e478f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -356,6 +356,13 @@ F: hw/misc/zynq_slcr.c
F: hw/*/cadence_*
F: hw/ssi/xilinx_spips.c
+ARM ACPI Subsystem
+M: Shannon Zhao <zhaoshenglong@huawei.com>
+M: Shannon Zhao <shannon.zhao@linaro.org>
+S: Maintained
+F: hw/arm/virt-acpi-build.c
+F: include/hw/arm/virt-acpi-build.h
+
CRIS Machines
-------------
Axis Dev88
@@ -486,7 +493,8 @@ F: hw/ppc/prep.c
F: hw/pci-host/prep.[hc]
F: hw/isa/pc87312.[hc]
-sPAPR
+sPAPR (pseries)
+M: David Gibson <david@gibson.dropbear.id.au>
M: Alexander Graf <agraf@suse.de>
L: qemu-ppc@nongnu.org
S: Supported
@@ -770,7 +778,6 @@ F: hw/net/rocker/
Subsystems
----------
Audio
-M: Vassili Karpov (malc) <av1474@comtv.ru>
M: Gerd Hoffmann <kraxel@redhat.com>
S: Maintained
F: audio/
@@ -946,7 +953,10 @@ M: Markus Armbruster <armbru@redhat.com>
M: Michael Roth <mdroth@linux.vnet.ibm.com>
S: Supported
F: qapi/
+X: qapi/*.json
F: tests/qapi-schema/
+F: scripts/qapi*
+F: docs/qapi*
T: git git://repo.or.cz/qemu/armbru.git qapi-next
QAPI Schema
@@ -954,6 +964,7 @@ M: Eric Blake <eblake@redhat.com>
M: Markus Armbruster <armbru@redhat.com>
S: Supported
F: qapi-schema.json
+F: qapi/*.json
T: git git://repo.or.cz/qemu/armbru.git qapi-next
QObject
@@ -1014,8 +1025,6 @@ M: Amit Shah <amit.shah@redhat.com>
S: Maintained
F: include/migration/
F: migration/
-F: savevm.c
-F: arch_init.c
F: scripts/vmstate-static-checker.py
F: tests/vmstate-static-checker-data/
diff --git a/Makefile b/Makefile
index d94580404c..3f97904a3d 100644
--- a/Makefile
+++ b/Makefile
@@ -342,7 +342,7 @@ bepo cz
ifdef INSTALL_BLOBS
BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
-vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin \
+vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \
acpi-dsdt.aml q35-acpi-dsdt.aml \
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
@@ -389,13 +389,8 @@ ifneq (,$(findstring qemu-ga,$(TOOLS)))
endif
endif
-install-confdir:
- $(INSTALL_DIR) "$(DESTDIR)$(qemu_confdir)"
-install-sysconfig: install-datadir install-confdir
- $(INSTALL_DATA) $(SRC_PATH)/sysconfigs/target/target-x86_64.conf "$(DESTDIR)$(qemu_confdir)"
-
-install: all $(if $(BUILD_DOCS),install-doc) install-sysconfig \
+install: all $(if $(BUILD_DOCS),install-doc) \
install-datadir install-localstatedir
ifneq ($(TOOLS),)
$(call install-prog,$(TOOLS),$(DESTDIR)$(bindir))
diff --git a/Makefile.objs b/Makefile.objs
index 28999d39c4..4881d2c2a6 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -76,6 +76,8 @@ common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o
common-obj-$(CONFIG_SMARTCARD_NSS) += $(libcacard-y)
+common-obj-$(CONFIG_FDT) += device_tree.o
+
######################################################################
# qapi
diff --git a/Makefile.target b/Makefile.target
index be01dd39c1..3e7aafd72d 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -1,5 +1,7 @@
# -*- Mode: makefile -*-
+BUILD_DIR?=$(CURDIR)/..
+
include ../config-host.mak
include config-target.mak
include config-devices.mak
@@ -129,11 +131,11 @@ ifdef CONFIG_SOFTMMU
obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
obj-y += qtest.o bootdevice.o
obj-y += hw/
-obj-$(CONFIG_FDT) += device_tree.o
obj-$(CONFIG_KVM) += kvm-all.o
-obj-y += memory.o savevm.o cputlb.o
+obj-y += memory.o cputlb.o
obj-y += memory_mapping.o
obj-y += dump.o
+obj-y += migration/ram.o migration/savevm.o
LIBS := $(libs_softmmu) $(LIBS)
# xen support
diff --git a/arch_init.c b/arch_init.c
index 23d3feba44..725c638ece 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -22,46 +22,15 @@
* THE SOFTWARE.
*/
#include <stdint.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <zlib.h>
-#ifndef _WIN32
-#include <sys/types.h>
-#include <sys/mman.h>
-#endif
-#include "config.h"
-#include "monitor/monitor.h"
#include "sysemu/sysemu.h"
-#include "qemu/bitops.h"
-#include "qemu/bitmap.h"
#include "sysemu/arch_init.h"
-#include "audio/audio.h"
-#include "hw/i386/pc.h"
#include "hw/pci/pci.h"
#include "hw/audio/audio.h"
-#include "sysemu/kvm.h"
-#include "migration/migration.h"
#include "hw/i386/smbios.h"
-#include "exec/address-spaces.h"
-#include "hw/audio/pcspk.h"
-#include "migration/page_cache.h"
#include "qemu/config-file.h"
#include "qemu/error-report.h"
#include "qmp-commands.h"
-#include "trace.h"
-#include "exec/cpu-all.h"
-#include "exec/ram_addr.h"
#include "hw/acpi/acpi.h"
-#include "qemu/host-utils.h"
-#include "qemu/rcu_queue.h"
-
-#ifdef DEBUG_ARCH_INIT
-#define DPRINTF(fmt, ...) \
- do { fprintf(stdout, "arch_init: " fmt, ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) \
- do { } while (0)
-#endif
#ifdef TARGET_SPARC
int graphic_width = 1024;
@@ -111,24 +80,6 @@ int graphic_depth = 32;
#endif
const uint32_t arch_type = QEMU_ARCH;
-static bool mig_throttle_on;
-static int dirty_rate_high_cnt;
-static void check_guest_throttling(void);
-
-static uint64_t bitmap_sync_count;
-
-/***********************************************************/
-/* ram save/restore */
-
-#define RAM_SAVE_FLAG_FULL 0x01 /* Obsolete, not used anymore */
-#define RAM_SAVE_FLAG_COMPRESS 0x02
-#define RAM_SAVE_FLAG_MEM_SIZE 0x04
-#define RAM_SAVE_FLAG_PAGE 0x08
-#define RAM_SAVE_FLAG_EOS 0x10
-#define RAM_SAVE_FLAG_CONTINUE 0x20
-#define RAM_SAVE_FLAG_XBZRLE 0x40
-/* 0x80 is reserved in migration.h start with 0x100 next */
-#define RAM_SAVE_FLAG_COMPRESS_PAGE 0x100
static struct defconfig_file {
const char *filename;
@@ -136,12 +87,9 @@ static struct defconfig_file {
bool userconfig;
} default_config_files[] = {
{ CONFIG_QEMU_CONFDIR "/qemu.conf", true },
- { CONFIG_QEMU_CONFDIR "/target-" TARGET_NAME ".conf", true },
{ NULL }, /* end of list */
};
-static const uint8_t ZERO_TARGET_PAGE[TARGET_PAGE_SIZE];
-
int qemu_read_default_config_files(bool userconfig)
{
int ret;
@@ -160,1559 +108,6 @@ int qemu_read_default_config_files(bool userconfig)
return 0;
}
-static inline bool is_zero_range(uint8_t *p, uint64_t size)
-{
- return buffer_find_nonzero_offset(p, size) == size;
-}
-
-/* struct contains XBZRLE cache and a static page
- used by the compression */
-static struct {
- /* buffer used for XBZRLE encoding */
- uint8_t *encoded_buf;
- /* buffer for storing page content */
- uint8_t *current_buf;
- /* Cache for XBZRLE, Protected by lock. */
- PageCache *cache;
- QemuMutex lock;
-} XBZRLE;
-
-/* buffer used for XBZRLE decoding */
-static uint8_t *xbzrle_decoded_buf;
-
-static void XBZRLE_cache_lock(void)
-{
- if (migrate_use_xbzrle())
- qemu_mutex_lock(&XBZRLE.lock);
-}
-
-static void XBZRLE_cache_unlock(void)
-{
- if (migrate_use_xbzrle())
- qemu_mutex_unlock(&XBZRLE.lock);
-}
-
-/*
- * called from qmp_migrate_set_cache_size in main thread, possibly while
- * a migration is in progress.
- * A running migration maybe using the cache and might finish during this
- * call, hence changes to the cache are protected by XBZRLE.lock().
- */
-int64_t xbzrle_cache_resize(int64_t new_size)
-{
- PageCache *new_cache;
- int64_t ret;
-
- if (new_size < TARGET_PAGE_SIZE) {
- return -1;
- }
-
- XBZRLE_cache_lock();
-
- if (XBZRLE.cache != NULL) {
- if (pow2floor(new_size) == migrate_xbzrle_cache_size()) {
- goto out_new_size;
- }
- new_cache = cache_init(new_size / TARGET_PAGE_SIZE,
- TARGET_PAGE_SIZE);
- if (!new_cache) {
- error_report("Error creating cache");
- ret = -1;
- goto out;
- }
-
- cache_fini(XBZRLE.cache);
- XBZRLE.cache = new_cache;
- }
-
-out_new_size:
- ret = pow2floor(new_size);
-out:
- XBZRLE_cache_unlock();
- return ret;
-}
-
-/* accounting for migration statistics */
-typedef struct AccountingInfo {
- uint64_t dup_pages;
- uint64_t skipped_pages;
- uint64_t norm_pages;
- uint64_t iterations;
- uint64_t xbzrle_bytes;
- uint64_t xbzrle_pages;
- uint64_t xbzrle_cache_miss;
- double xbzrle_cache_miss_rate;
- uint64_t xbzrle_overflows;
-} AccountingInfo;
-
-static AccountingInfo acct_info;
-
-static void acct_clear(void)
-{
- memset(&acct_info, 0, sizeof(acct_info));
-}
-
-uint64_t dup_mig_bytes_transferred(void)
-{
- return acct_info.dup_pages * TARGET_PAGE_SIZE;
-}
-
-uint64_t dup_mig_pages_transferred(void)
-{
- return acct_info.dup_pages;
-}
-
-uint64_t skipped_mig_bytes_transferred(void)
-{
- return acct_info.skipped_pages * TARGET_PAGE_SIZE;
-}
-
-uint64_t skipped_mig_pages_transferred(void)
-{
- return acct_info.skipped_pages;
-}
-
-uint64_t norm_mig_bytes_transferred(void)
-{
- return acct_info.norm_pages * TARGET_PAGE_SIZE;
-}
-
-uint64_t norm_mig_pages_transferred(void)
-{
- return acct_info.norm_pages;
-}
-
-uint64_t xbzrle_mig_bytes_transferred(void)
-{
- return acct_info.xbzrle_bytes;
-}
-
-uint64_t xbzrle_mig_pages_transferred(void)
-{
- return acct_info.xbzrle_pages;
-}
-
-uint64_t xbzrle_mig_pages_cache_miss(void)
-{
- return acct_info.xbzrle_cache_miss;
-}
-
-double xbzrle_mig_cache_miss_rate(void)
-{
- return acct_info.xbzrle_cache_miss_rate;
-}
-
-uint64_t xbzrle_mig_pages_overflow(void)
-{
- return acct_info.xbzrle_overflows;
-}
-
-/* This is the last block that we have visited serching for dirty pages
- */
-static RAMBlock *last_seen_block;
-/* This is the last block from where we have sent data */
-static RAMBlock *last_sent_block;
-static ram_addr_t last_offset;
-static unsigned long *migration_bitmap;
-static uint64_t migration_dirty_pages;
-static uint32_t last_version;
-static bool ram_bulk_stage;
-
-struct CompressParam {
- bool start;
- bool done;
- QEMUFile *file;
- QemuMutex mutex;
- QemuCond cond;
- RAMBlock *block;
- ram_addr_t offset;
-};
-typedef struct CompressParam CompressParam;
-
-struct DecompressParam {
- bool start;
- QemuMutex mutex;
- QemuCond cond;
- void *des;
- uint8 *compbuf;
- int len;
-};
-typedef struct DecompressParam DecompressParam;
-
-static CompressParam *comp_param;
-static QemuThread *compress_threads;
-/* comp_done_cond is used to wake up the migration thread when
- * one of the compression threads has finished the compression.
- * comp_done_lock is used to co-work with comp_done_cond.
- */
-static QemuMutex *comp_done_lock;
-static QemuCond *comp_done_cond;
-/* The empty QEMUFileOps will be used by file in CompressParam */
-static const QEMUFileOps empty_ops = { };
-
-static bool compression_switch;
-static bool quit_comp_thread;
-static bool quit_decomp_thread;
-static DecompressParam *decomp_param;
-static QemuThread *decompress_threads;
-static uint8_t *compressed_data_buf;
-
-static int do_compress_ram_page(CompressParam *param);
-
-static void *do_data_compress(void *opaque)
-{
- CompressParam *param = opaque;
-
- while (!quit_comp_thread) {
- qemu_mutex_lock(&param->mutex);
- /* Re-check the quit_comp_thread in case of
- * terminate_compression_threads is called just before
- * qemu_mutex_lock(&param->mutex) and after
- * while(!quit_comp_thread), re-check it here can make
- * sure the compression thread terminate as expected.
- */
- while (!param->start && !quit_comp_thread) {
- qemu_cond_wait(&param->cond, &param->mutex);
- }
- if (!quit_comp_thread) {
- do_compress_ram_page(param);
- }
- param->start = false;
- qemu_mutex_unlock(&param->mutex);
-
- qemu_mutex_lock(comp_done_lock);
- param->done = true;
- qemu_cond_signal(comp_done_cond);
- qemu_mutex_unlock(comp_done_lock);
- }
-
- return NULL;
-}
-
-static inline void terminate_compression_threads(void)
-{
- int idx, thread_count;
-
- thread_count = migrate_compress_threads();
- quit_comp_thread = true;
- for (idx = 0; idx < thread_count; idx++) {
- qemu_mutex_lock(&comp_param[idx].mutex);
- qemu_cond_signal(&comp_param[idx].cond);
- qemu_mutex_unlock(&comp_param[idx].mutex);
- }
-}
-
-void migrate_compress_threads_join(void)
-{
- int i, thread_count;
-
- if (!migrate_use_compression()) {
- return;
- }
- terminate_compression_threads();
- thread_count = migrate_compress_threads();
- for (i = 0; i < thread_count; i++) {
- qemu_thread_join(compress_threads + i);
- qemu_fclose(comp_param[i].file);
- qemu_mutex_destroy(&comp_param[i].mutex);
- qemu_cond_destroy(&comp_param[i].cond);
- }
- qemu_mutex_destroy(comp_done_lock);
- qemu_cond_destroy(comp_done_cond);
- g_free(compress_threads);
- g_free(comp_param);
- g_free(comp_done_cond);
- g_free(comp_done_lock);
- compress_threads = NULL;
- comp_param = NULL;
- comp_done_cond = NULL;
- comp_done_lock = NULL;
-}
-
-void migrate_compress_threads_create(void)
-{
- int i, thread_count;
-
- if (!migrate_use_compression()) {
- return;
- }
- quit_comp_thread = false;
- compression_switch = true;
- thread_count = migrate_compress_threads();
- compress_threads = g_new0(QemuThread, thread_count);
- comp_param = g_new0(CompressParam, thread_count);
- comp_done_cond = g_new0(QemuCond, 1);
- comp_done_lock = g_new0(QemuMutex, 1);
- qemu_cond_init(comp_done_cond);
- qemu_mutex_init(comp_done_lock);
- for (i = 0; i < thread_count; i++) {
- /* com_param[i].file is just used as a dummy buffer to save data, set
- * it's ops to empty.
- */
- comp_param[i].file = qemu_fopen_ops(NULL, &empty_ops);
- comp_param[i].done = true;
- qemu_mutex_init(&comp_param[i].mutex);
- qemu_cond_init(&comp_param[i].cond);
- qemu_thread_create(compress_threads + i, "compress",
- do_data_compress, comp_param + i,
- QEMU_THREAD_JOINABLE);
- }
-}
-
-/**
- * save_page_header: Write page header to wire
- *
- * If this is the 1st block, it also writes the block identification
- *
- * Returns: Number of bytes written
- *
- * @f: QEMUFile where to send the data
- * @block: block that contains the page we want to send
- * @offset: offset inside the block for the page
- * in the lower bits, it contains flags
- */
-static size_t save_page_header(QEMUFile *f, RAMBlock *block, ram_addr_t offset)
-{
- size_t size;
-
- qemu_put_be64(f, offset);
- size = 8;
-
- if (!(offset & RAM_SAVE_FLAG_CONTINUE)) {
- qemu_put_byte(f, strlen(block->idstr));
- qemu_put_buffer(f, (uint8_t *)block->idstr,
- strlen(block->idstr));
- size += 1 + strlen(block->idstr);
- }
- return size;
-}
-
-/* Update the xbzrle cache to reflect a page that's been sent as all 0.
- * The important thing is that a stale (not-yet-0'd) page be replaced
- * by the new data.
- * As a bonus, if the page wasn't in the cache it gets added so that
- * when a small write is made into the 0'd page it gets XBZRLE sent
- */
-static void xbzrle_cache_zero_page(ram_addr_t current_addr)
-{
- if (ram_bulk_stage || !migrate_use_xbzrle()) {
- return;
- }
-
- /* We don't care if this fails to allocate a new cache page
- * as long as it updated an old one */
- cache_insert(XBZRLE.cache, current_addr, ZERO_TARGET_PAGE,
- bitmap_sync_count);
-}
-
-#define ENCODING_FLAG_XBZRLE 0x1
-
-/**
- * save_xbzrle_page: compress and send current page
- *
- * Returns: 1 means that we wrote the page
- * 0 means that page is identical to the one already sent
- * -1 means that xbzrle would be longer than normal
- *
- * @f: QEMUFile where to send the data
- * @current_data:
- * @current_addr:
- * @block: block that contains the page we want to send
- * @offset: offset inside the block for the page
- * @last_stage: if we are at the completion stage
- * @bytes_transferred: increase it with the number of transferred bytes
- */
-static int save_xbzrle_page(QEMUFile *f, uint8_t **current_data,
- ram_addr_t current_addr, RAMBlock *block,
- ram_addr_t offset, bool last_stage,
- uint64_t *bytes_transferred)
-{
- int encoded_len = 0, bytes_xbzrle;
- uint8_t *prev_cached_page;
-
- if (!cache_is_cached(XBZRLE.cache, current_addr, bitmap_sync_count)) {
- acct_info.xbzrle_cache_miss++;
- if (!last_stage) {
- if (cache_insert(XBZRLE.cache, current_addr, *current_data,
- bitmap_sync_count) == -1) {
- return -1;
- } else {
- /* update *current_data when the page has been
- inserted into cache */
- *current_data = get_cached_data(XBZRLE.cache, current_addr);
- }
- }
- return -1;
- }
-
- prev_cached_page = get_cached_data(XBZRLE.cache, current_addr);
-
- /* save current buffer into memory */
- memcpy(XBZRLE.current_buf, *current_data, TARGET_PAGE_SIZE);
-
- /* XBZRLE encoding (if there is no overflow) */
- encoded_len = xbzrle_encode_buffer(prev_cached_page, XBZRLE.current_buf,
- TARGET_PAGE_SIZE, XBZRLE.encoded_buf,
- TARGET_PAGE_SIZE);
- if (encoded_len == 0) {
- DPRINTF("Skipping unmodified page\n");
- return 0;
- } else if (encoded_len == -1) {
- DPRINTF("Overflow\n");
- acct_info.xbzrle_overflows++;
- /* update data in the cache */
- if (!last_stage) {
- memcpy(prev_cached_page, *current_data, TARGET_PAGE_SIZE);
- *current_data = prev_cached_page;
- }
- return -1;
- }
-
- /* we need to update the data in the cache, in order to get the same data */
- if (!last_stage) {
- memcpy(prev_cached_page, XBZRLE.current_buf, TARGET_PAGE_SIZE);
- }
-
- /* Send XBZRLE based compressed page */
- bytes_xbzrle = save_page_header(f, block, offset | RAM_SAVE_FLAG_XBZRLE);
- qemu_put_byte(f, ENCODING_FLAG_XBZRLE);
- qemu_put_be16(f, encoded_len);
- qemu_put_buffer(f, XBZRLE.encoded_buf, encoded_len);
- bytes_xbzrle += encoded_len + 1 + 2;
- acct_info.xbzrle_pages++;
- acct_info.xbzrle_bytes += bytes_xbzrle;
- *bytes_transferred += bytes_xbzrle;
-
- return 1;
-}
-
-static inline
-ram_addr_t migration_bitmap_find_and_reset_dirty(MemoryRegion *mr,
- ram_addr_t start)
-{
- unsigned long base = mr->ram_addr >> TARGET_PAGE_BITS;
- unsigned long nr = base + (start >> TARGET_PAGE_BITS);
- uint64_t mr_size = TARGET_PAGE_ALIGN(memory_region_size(mr));
- unsigned long size = base + (mr_size >> TARGET_PAGE_BITS);
-
- unsigned long next;
-
- if (ram_bulk_stage && nr > base) {
- next = nr + 1;
- } else {
- next = find_next_bit(migration_bitmap, size, nr);
- }
-
- if (next < size) {
- clear_bit(next, migration_bitmap);
- migration_dirty_pages--;
- }
- return (next - base) << TARGET_PAGE_BITS;
-}
-
-static inline bool migration_bitmap_set_dirty(ram_addr_t addr)
-{
- bool ret;
- int nr = addr >> TARGET_PAGE_BITS;
-
- ret = test_and_set_bit(nr, migration_bitmap);
-
- if (!ret) {
- migration_dirty_pages++;
- }
- return ret;
-}
-
-static void migration_bitmap_sync_range(ram_addr_t start, ram_addr_t length)
-{
- ram_addr_t addr;
- unsigned long page = BIT_WORD(start >> TARGET_PAGE_BITS);
-
- /* start address is aligned at the start of a word? */
- if (((page * BITS_PER_LONG) << TARGET_PAGE_BITS) == start) {
- int k;
- int nr = BITS_TO_LONGS(length >> TARGET_PAGE_BITS);
- unsigned long *src = ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION];
-
- for (k = page; k < page + nr; k++) {
- if (src[k]) {
- unsigned long new_dirty;
- new_dirty = ~migration_bitmap[k];
- migration_bitmap[k] |= src[k];
- new_dirty &= src[k];
- migration_dirty_pages += ctpopl(new_dirty);
- src[k] = 0;
- }
- }
- } else {
- for (addr = 0; addr < length; addr += TARGET_PAGE_SIZE) {
- if (cpu_physical_memory_get_dirty(start + addr,
- TARGET_PAGE_SIZE,
- DIRTY_MEMORY_MIGRATION)) {
- cpu_physical_memory_reset_dirty(start + addr,
- TARGET_PAGE_SIZE,
- DIRTY_MEMORY_MIGRATION);
- migration_bitmap_set_dirty(start + addr);
- }
- }
- }
-}
-
-
-/* Fix me: there are too many global variables used in migration process. */
-static int64_t start_time;
-static int64_t bytes_xfer_prev;
-static int64_t num_dirty_pages_period;
-static uint64_t xbzrle_cache_miss_prev;
-static uint64_t iterations_prev;
-
-static void migration_bitmap_sync_init(void)
-{
- start_time = 0;
- bytes_xfer_prev = 0;
- num_dirty_pages_period = 0;
- xbzrle_cache_miss_prev = 0;
- iterations_prev = 0;
-}
-
-/* Called with iothread lock held, to protect ram_list.dirty_memory[] */
-static void migration_bitmap_sync(void)
-{
- RAMBlock *block;
- uint64_t num_dirty_pages_init = migration_dirty_pages;
- MigrationState *s = migrate_get_current();
- int64_t end_time;
- int64_t bytes_xfer_now;
-
- bitmap_sync_count++;
-
- if (!bytes_xfer_prev) {
- bytes_xfer_prev = ram_bytes_transferred();
- }
-
- if (!start_time) {
- start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
- }
-
- trace_migration_bitmap_sync_start();
- address_space_sync_dirty_bitmap(&address_space_memory);
-
- rcu_read_lock();
- QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
- migration_bitmap_sync_range(block->mr->ram_addr, block->used_length);
- }
- rcu_read_unlock();
-
- trace_migration_bitmap_sync_end(migration_dirty_pages
- - num_dirty_pages_init);
- num_dirty_pages_period += migration_dirty_pages - num_dirty_pages_init;
- end_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
-
- /* more than 1 second = 1000 millisecons */
- if (end_time > start_time + 1000) {
- if (migrate_auto_converge()) {
- /* The following detection logic can be refined later. For now:
- Check to see if the dirtied bytes is 50% more than the approx.
- amount of bytes that just got transferred since the last time we
- were in this routine. If that happens >N times (for now N==4)
- we turn on the throttle down logic */
- bytes_xfer_now = ram_bytes_transferred();
- if (s->dirty_pages_rate &&
- (num_dirty_pages_period * TARGET_PAGE_SIZE >
- (bytes_xfer_now - bytes_xfer_prev)/2) &&
- (dirty_rate_high_cnt++ > 4)) {
- trace_migration_throttle();
- mig_throttle_on = true;
- dirty_rate_high_cnt = 0;
- }
- bytes_xfer_prev = bytes_xfer_now;
- } else {
- mig_throttle_on = false;
- }
- if (migrate_use_xbzrle()) {
- if (iterations_prev != acct_info.iterations) {
- acct_info.xbzrle_cache_miss_rate =
- (double)(acct_info.xbzrle_cache_miss -
- xbzrle_cache_miss_prev) /
- (acct_info.iterations - iterations_prev);
- }
- iterations_prev = acct_info.iterations;
- xbzrle_cache_miss_prev = acct_info.xbzrle_cache_miss;
- }
- s->dirty_pages_rate = num_dirty_pages_period * 1000
- / (end_time - start_time);
- s->dirty_bytes_rate = s->dirty_pages_rate * TARGET_PAGE_SIZE;
- start_time = end_time;
- num_dirty_pages_period = 0;
- }
- s->dirty_sync_count = bitmap_sync_count;
-}
-
-/**
- * save_zero_page: Send the zero page to the stream
- *
- * Returns: Number of pages written.
- *
- * @f: QEMUFile where to send the data
- * @block: block that contains the page we want to send
- * @offset: offset inside the block for the page
- * @p: pointer to the page
- * @bytes_transferred: increase it with the number of transferred bytes
- */
-static int save_zero_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset,
- uint8_t *p, uint64_t *bytes_transferred)
-{
- int pages = -1;
-
- if (is_zero_range(p, TARGET_PAGE_SIZE)) {
- acct_info.dup_pages++;
- *bytes_transferred += save_page_header(f, block,
- offset | RAM_SAVE_FLAG_COMPRESS);
- qemu_put_byte(f, 0);
- *bytes_transferred += 1;
- pages = 1;
- }
-
- return pages;
-}
-
-/**
- * ram_save_page: Send the given page to the stream
- *
- * Returns: Number of pages written.
- *
- * @f: QEMUFile where to send the data
- * @block: block that contains the page we want to send
- * @offset: offset inside the block for the page
- * @last_stage: if we are at the completion stage
- * @bytes_transferred: increase it with the number of transferred bytes
- */
-static int ram_save_page(QEMUFile *f, RAMBlock* block, ram_addr_t offset,
- bool last_stage, uint64_t *bytes_transferred)
-{
- int pages = -1;
- uint64_t bytes_xmit;
- ram_addr_t current_addr;
- MemoryRegion *mr = block->mr;
- uint8_t *p;
- int ret;
- bool send_async = true;
-
- p = memory_region_get_ram_ptr(mr) + offset;
-
- /* In doubt sent page as normal */
- bytes_xmit = 0;
- ret = ram_control_save_page(f, block->offset,
- offset, TARGET_PAGE_SIZE, &bytes_xmit);
- if (bytes_xmit) {
- *bytes_transferred += bytes_xmit;
- pages = 1;
- }
-
- XBZRLE_cache_lock();
-
- current_addr = block->offset + offset;
-
- if (block == last_sent_block) {
- offset |= RAM_SAVE_FLAG_CONTINUE;
- }
- if (ret != RAM_SAVE_CONTROL_NOT_SUPP) {
- if (ret != RAM_SAVE_CONTROL_DELAYED) {
- if (bytes_xmit > 0) {
- acct_info.norm_pages++;
- } else if (bytes_xmit == 0) {
- acct_info.dup_pages++;
- }
- }
- } else {
- pages = save_zero_page(f, block, offset, p, bytes_transferred);
- if (pages > 0) {
- /* Must let xbzrle know, otherwise a previous (now 0'd) cached
- * page would be stale
- */
- xbzrle_cache_zero_page(current_addr);
- } else if (!ram_bulk_stage && migrate_use_xbzrle()) {
- pages = save_xbzrle_page(f, &p, current_addr, block,
- offset, last_stage, bytes_transferred);
- if (!last_stage) {
- /* Can't send this cached data async, since the cache page
- * might get updated before it gets to the wire
- */
- send_async = false;
- }
- }
- }
-
- /* XBZRLE overflow or normal page */
- if (pages == -1) {
- *bytes_transferred += save_page_header(f, block,
- offset | RAM_SAVE_FLAG_PAGE);
- if (send_async) {
- qemu_put_buffer_async(f, p, TARGET_PAGE_SIZE);
- } else {
- qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
- }
- *bytes_transferred += TARGET_PAGE_SIZE;
- pages = 1;
- acct_info.norm_pages++;
- }
-
- XBZRLE_cache_unlock();
-
- return pages;
-}
-
-static int do_compress_ram_page(CompressParam *param)
-{
- int bytes_sent, blen;
- uint8_t *p;
- RAMBlock *block = param->block;
- ram_addr_t offset = param->offset;
-
- p = memory_region_get_ram_ptr(block->mr) + (offset & TARGET_PAGE_MASK);
-
- bytes_sent = save_page_header(param->file, block, offset |
- RAM_SAVE_FLAG_COMPRESS_PAGE);
- blen = qemu_put_compression_data(param->file, p, TARGET_PAGE_SIZE,
- migrate_compress_level());
- bytes_sent += blen;
-
- return bytes_sent;
-}
-
-static inline void start_compression(CompressParam *param)
-{
- param->done = false;
- qemu_mutex_lock(&param->mutex);
- param->start = true;
- qemu_cond_signal(&param->cond);
- qemu_mutex_unlock(&param->mutex);
-}
-
-static inline void start_decompression(DecompressParam *param)
-{
- qemu_mutex_lock(&param->mutex);
- param->start = true;
- qemu_cond_signal(&param->cond);
- qemu_mutex_unlock(&param->mutex);
-}
-
-static uint64_t bytes_transferred;
-
-static void flush_compressed_data(QEMUFile *f)
-{
- int idx, len, thread_count;
-
- if (!migrate_use_compression()) {
- return;
- }
- thread_count = migrate_compress_threads();
- for (idx = 0; idx < thread_count; idx++) {
- if (!comp_param[idx].done) {
- qemu_mutex_lock(comp_done_lock);
- while (!comp_param[idx].done && !quit_comp_thread) {
- qemu_cond_wait(comp_done_cond, comp_done_lock);
- }
- qemu_mutex_unlock(comp_done_lock);
- }
- if (!quit_comp_thread) {
- len = qemu_put_qemu_file(f, comp_param[idx].file);
- bytes_transferred += len;
- }
- }
-}
-
-static inline void set_compress_params(CompressParam *param, RAMBlock *block,
- ram_addr_t offset)
-{
- param->block = block;
- param->offset = offset;
-}
-
-static int compress_page_with_multi_thread(QEMUFile *f, RAMBlock *block,
- ram_addr_t offset,
- uint64_t *bytes_transferred)
-{
- int idx, thread_count, bytes_xmit = -1, pages = -1;
-
- thread_count = migrate_compress_threads();
- qemu_mutex_lock(comp_done_lock);
- while (true) {
- for (idx = 0; idx < thread_count; idx++) {
- if (comp_param[idx].done) {
- bytes_xmit = qemu_put_qemu_file(f, comp_param[idx].file);
- set_compress_params(&comp_param[idx], block, offset);
- start_compression(&comp_param[idx]);
- pages = 1;
- acct_info.norm_pages++;
- *bytes_transferred += bytes_xmit;
- break;
- }
- }
- if (pages > 0) {
- break;
- } else {
- qemu_cond_wait(comp_done_cond, comp_done_lock);
- }
- }
- qemu_mutex_unlock(comp_done_lock);
-
- return pages;
-}
-
-/**
- * ram_save_compressed_page: compress the given page and send it to the stream
- *
- * Returns: Number of pages written.
- *
- * @f: QEMUFile where to send the data
- * @block: block that contains the page we want to send
- * @offset: offset inside the block for the page
- * @last_stage: if we are at the completion stage
- * @bytes_transferred: increase it with the number of transferred bytes
- */
-static int ram_save_compressed_page(QEMUFile *f, RAMBlock *block,
- ram_addr_t offset, bool last_stage,
- uint64_t *bytes_transferred)
-{
- int pages = -1;
- uint64_t bytes_xmit;
- MemoryRegion *mr = block->mr;
- uint8_t *p;
- int ret;
-
- p = memory_region_get_ram_ptr(mr) + offset;
-
- bytes_xmit = 0;
- ret = ram_control_save_page(f, block->offset,
- offset, TARGET_PAGE_SIZE, &bytes_xmit);
- if (bytes_xmit) {
- *bytes_transferred += bytes_xmit;
- pages = 1;
- }
- if (block == last_sent_block) {
- offset |= RAM_SAVE_FLAG_CONTINUE;
- }
- if (ret != RAM_SAVE_CONTROL_NOT_SUPP) {
- if (ret != RAM_SAVE_CONTROL_DELAYED) {
- if (bytes_xmit > 0) {
- acct_info.norm_pages++;
- } else if (bytes_xmit == 0) {
- acct_info.dup_pages++;
- }
- }
- } else {
- /* When starting the process of a new block, the first page of
- * the block should be sent out before other pages in the same
- * block, and all the pages in last block should have been sent
- * out, keeping this order is important, because the 'cont' flag
- * is used to avoid resending the block name.
- */
- if (block != last_sent_block) {
- flush_compressed_data(f);
- pages = save_zero_page(f, block, offset, p, bytes_transferred);
- if (pages == -1) {
- set_compress_params(&comp_param[0], block, offset);
- /* Use the qemu thread to compress the data to make sure the
- * first page is sent out before other pages
- */
- bytes_xmit = do_compress_ram_page(&comp_param[0]);
- acct_info.norm_pages++;
- qemu_put_qemu_file(f, comp_param[0].file);
- *bytes_transferred += bytes_xmit;
- pages = 1;
- }
- } else {
- pages = save_zero_page(f, block, offset, p, bytes_transferred);
- if (pages == -1) {
- pages = compress_page_with_multi_thread(f, block, offset,
- bytes_transferred);
- }
- }
- }
-
- return pages;
-}
-
-/**
- * ram_find_and_save_block: Finds a dirty page and sends it to f
- *
- * Called within an RCU critical section.
- *
- * Returns: The number of pages written
- * 0 means no dirty pages
- *
- * @f: QEMUFile where to send the data
- * @last_stage: if we are at the completion stage
- * @bytes_transferred: increase it with the number of transferred bytes
- */
-
-static int ram_find_and_save_block(QEMUFile *f, bool last_stage,
- uint64_t *bytes_transferred)
-{
- RAMBlock *block = last_seen_block;
- ram_addr_t offset = last_offset;
- bool complete_round = false;
- int pages = 0;
- MemoryRegion *mr;
-
- if (!block)
- block = QLIST_FIRST_RCU(&ram_list.blocks);
-
- while (true) {
- mr = block->mr;
- offset = migration_bitmap_find_and_reset_dirty(mr, offset);
- if (complete_round && block == last_seen_block &&
- offset >= last_offset) {
- break;
- }
- if (offset >= block->used_length) {
- offset = 0;
- block = QLIST_NEXT_RCU(block, next);
- if (!block) {
- block = QLIST_FIRST_RCU(&ram_list.blocks);
- complete_round = true;
- ram_bulk_stage = false;
- if (migrate_use_xbzrle()) {
- /* If xbzrle is on, stop using the data compression at this
- * point. In theory, xbzrle can do better than compression.
- */
- flush_compressed_data(f);
- compression_switch = false;
- }
- }
- } else {
- if (compression_switch && migrate_use_compression()) {
- pages = ram_save_compressed_page(f, block, offset, last_stage,
- bytes_transferred);
- } else {
- pages = ram_save_page(f, block, offset, last_stage,
- bytes_transferred);
- }
-
- /* if page is unmodified, continue to the next */
- if (pages > 0) {
- last_sent_block = block;
- break;
- }
- }
- }
-
- last_seen_block = block;
- last_offset = offset;
-
- return pages;
-}
-
-void acct_update_position(QEMUFile *f, size_t size, bool zero)
-{
- uint64_t pages = size / TARGET_PAGE_SIZE;
- if (zero) {
- acct_info.dup_pages += pages;
- } else {
- acct_info.norm_pages += pages;
- bytes_transferred += size;
- qemu_update_position(f, size);
- }
-}
-
-static ram_addr_t ram_save_remaining(void)
-{
- return migration_dirty_pages;
-}
-
-uint64_t ram_bytes_remaining(void)
-{
- return ram_save_remaining() * TARGET_PAGE_SIZE;
-}
-
-uint64_t ram_bytes_transferred(void)
-{
- return bytes_transferred;
-}
-
-uint64_t ram_bytes_total(void)
-{
- RAMBlock *block;
- uint64_t total = 0;
-
- rcu_read_lock();
- QLIST_FOREACH_RCU(block, &ram_list.blocks, next)
- total += block->used_length;
- rcu_read_unlock();
- return total;
-}
-
-void free_xbzrle_decoded_buf(void)
-{
- g_free(xbzrle_decoded_buf);
- xbzrle_decoded_buf = NULL;
-}
-
-static void migration_end(void)
-{
- if (migration_bitmap) {
- memory_global_dirty_log_stop();
- g_free(migration_bitmap);
- migration_bitmap = NULL;
- }
-
- XBZRLE_cache_lock();
- if (XBZRLE.cache) {
- cache_fini(XBZRLE.cache);
- g_free(XBZRLE.encoded_buf);
- g_free(XBZRLE.current_buf);
- XBZRLE.cache = NULL;
- XBZRLE.encoded_buf = NULL;
- XBZRLE.current_buf = NULL;
- }
- XBZRLE_cache_unlock();
-}
-
-static void ram_migration_cancel(void *opaque)
-{
- migration_end();
-}
-
-static void reset_ram_globals(void)
-{
- last_seen_block = NULL;
- last_sent_block = NULL;
- last_offset = 0;
- last_version = ram_list.version;
- ram_bulk_stage = true;
-}
-
-#define MAX_WAIT 50 /* ms, half buffered_file limit */
-
-
-/* Each of ram_save_setup, ram_save_iterate and ram_save_complete has
- * long-running RCU critical section. When rcu-reclaims in the code
- * start to become numerous it will be necessary to reduce the
- * granularity of these critical sections.
- */
-
-static int ram_save_setup(QEMUFile *f, void *opaque)
-{
- RAMBlock *block;
- int64_t ram_bitmap_pages; /* Size of bitmap in pages, including gaps */
-
- mig_throttle_on = false;
- dirty_rate_high_cnt = 0;
- bitmap_sync_count = 0;
- migration_bitmap_sync_init();
-
- if (migrate_use_xbzrle()) {
- XBZRLE_cache_lock();
- XBZRLE.cache = cache_init(migrate_xbzrle_cache_size() /
- TARGET_PAGE_SIZE,
- TARGET_PAGE_SIZE);
- if (!XBZRLE.cache) {
- XBZRLE_cache_unlock();
- error_report("Error creating cache");
- return -1;
- }
- XBZRLE_cache_unlock();
-
- /* We prefer not to abort if there is no memory */
- XBZRLE.encoded_buf = g_try_malloc0(TARGET_PAGE_SIZE);
- if (!XBZRLE.encoded_buf) {
- error_report("Error allocating encoded_buf");
- return -1;
- }
-
- XBZRLE.current_buf = g_try_malloc(TARGET_PAGE_SIZE);
- if (!XBZRLE.current_buf) {
- error_report("Error allocating current_buf");
- g_free(XBZRLE.encoded_buf);
- XBZRLE.encoded_buf = NULL;
- return -1;
- }
-
- acct_clear();
- }
-
- /* iothread lock needed for ram_list.dirty_memory[] */
- qemu_mutex_lock_iothread();
- qemu_mutex_lock_ramlist();
- rcu_read_lock();
- bytes_transferred = 0;
- reset_ram_globals();
-
- ram_bitmap_pages = last_ram_offset() >> TARGET_PAGE_BITS;
- migration_bitmap = bitmap_new(ram_bitmap_pages);
- bitmap_set(migration_bitmap, 0, ram_bitmap_pages);
-
- /*
- * Count the total number of pages used by ram blocks not including any
- * gaps due to alignment or unplugs.
- */
- migration_dirty_pages = ram_bytes_total() >> TARGET_PAGE_BITS;
-
- memory_global_dirty_log_start();
- migration_bitmap_sync();
- qemu_mutex_unlock_ramlist();
- qemu_mutex_unlock_iothread();
-
- qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE);
-
- QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
- qemu_put_byte(f, strlen(block->idstr));
- qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr));
- qemu_put_be64(f, block->used_length);
- }
-
- rcu_read_unlock();
-
- ram_control_before_iterate(f, RAM_CONTROL_SETUP);
- ram_control_after_iterate(f, RAM_CONTROL_SETUP);
-
- qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
-
- return 0;
-}
-
-static int ram_save_iterate(QEMUFile *f, void *opaque)
-{
- int ret;
- int i;
- int64_t t0;
- int pages_sent = 0;
-
- rcu_read_lock();
- if (ram_list.version != last_version) {
- reset_ram_globals();
- }
-
- /* Read version before ram_list.blocks */
- smp_rmb();
-
- ram_control_before_iterate(f, RAM_CONTROL_ROUND);
-
- t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
- i = 0;
- while ((ret = qemu_file_rate_limit(f)) == 0) {
- int pages;
-
- pages = ram_find_and_save_block(f, false, &bytes_transferred);
- /* no more pages to sent */
- if (pages == 0) {
- break;
- }
- pages_sent += pages;
- acct_info.iterations++;
- check_guest_throttling();
- /* we want to check in the 1st loop, just in case it was the 1st time
- and we had to sync the dirty bitmap.
- qemu_get_clock_ns() is a bit expensive, so we only check each some
- iterations
- */
- if ((i & 63) == 0) {
- uint64_t t1 = (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - t0) / 1000000;
- if (t1 > MAX_WAIT) {
- DPRINTF("big wait: %" PRIu64 " milliseconds, %d iterations\n",
- t1, i);
- break;
- }
- }
- i++;
- }
- flush_compressed_data(f);
- rcu_read_unlock();
-
- /*
- * Must occur before EOS (or any QEMUFile operation)
- * because of RDMA protocol.
- */
- ram_control_after_iterate(f, RAM_CONTROL_ROUND);
-
- qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
- bytes_transferred += 8;
-
- ret = qemu_file_get_error(f);
- if (ret < 0) {
- return ret;
- }
-
- return pages_sent;
-}
-
-/* Called with iothread lock */
-static int ram_save_complete(QEMUFile *f, void *opaque)
-{
- rcu_read_lock();
-
- migration_bitmap_sync();
-
- ram_control_before_iterate(f, RAM_CONTROL_FINISH);
-
- /* try transferring iterative blocks of memory */
-
- /* flush all remaining blocks regardless of rate limiting */
- while (true) {
- int pages;
-
- pages = ram_find_and_save_block(f, true, &bytes_transferred);
- /* no more blocks to sent */
- if (pages == 0) {
- break;
- }
- }
-
- flush_compressed_data(f);
- ram_control_after_iterate(f, RAM_CONTROL_FINISH);
- migration_end();
-
- rcu_read_unlock();
- qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
-
- return 0;
-}
-
-static uint64_t ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size)
-{
- uint64_t remaining_size;
-
- remaining_size = ram_save_remaining() * TARGET_PAGE_SIZE;
-
- if (remaining_size < max_size) {
- qemu_mutex_lock_iothread();
- rcu_read_lock();
- migration_bitmap_sync();
- rcu_read_unlock();
- qemu_mutex_unlock_iothread();
- remaining_size = ram_save_remaining() * TARGET_PAGE_SIZE;
- }
- return remaining_size;
-}
-
-static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host)
-{
- unsigned int xh_len;
- int xh_flags;
-
- if (!xbzrle_decoded_buf) {
- xbzrle_decoded_buf = g_malloc(TARGET_PAGE_SIZE);
- }
-
- /* extract RLE header */
- xh_flags = qemu_get_byte(f);
- xh_len = qemu_get_be16(f);
-
- if (xh_flags != ENCODING_FLAG_XBZRLE) {
- error_report("Failed to load XBZRLE page - wrong compression!");
- return -1;
- }
-
- if (xh_len > TARGET_PAGE_SIZE) {
- error_report("Failed to load XBZRLE page - len overflow!");
- return -1;
- }
- /* load data and decode */
- qemu_get_buffer(f, xbzrle_decoded_buf, xh_len);
-
- /* decode RLE */
- if (xbzrle_decode_buffer(xbzrle_decoded_buf, xh_len, host,
- TARGET_PAGE_SIZE) == -1) {
- error_report("Failed to load XBZRLE page - decode error!");
- return -1;
- }
-
- return 0;
-}
-
-/* Must be called from within a rcu critical section.
- * Returns a pointer from within the RCU-protected ram_list.
- */
-static inline void *host_from_stream_offset(QEMUFile *f,
- ram_addr_t offset,
- int flags)
-{
- static RAMBlock *block = NULL;
- char id[256];
- uint8_t len;
-
- if (flags & RAM_SAVE_FLAG_CONTINUE) {
- if (!block || block->max_length <= offset) {
- error_report("Ack, bad migration stream!");
- return NULL;
- }
-
- return memory_region_get_ram_ptr(block->mr) + offset;
- }
-
- len = qemu_get_byte(f);
- qemu_get_buffer(f, (uint8_t *)id, len);
- id[len] = 0;
-
- QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
- if (!strncmp(id, block->idstr, sizeof(id)) &&
- block->max_length > offset) {
- return memory_region_get_ram_ptr(block->mr) + offset;
- }
- }
-
- error_report("Can't find block %s!", id);
- return NULL;
-}
-
-/*
- * If a page (or a whole RDMA chunk) has been
- * determined to be zero, then zap it.
- */
-void ram_handle_compressed(void *host, uint8_t ch, uint64_t size)
-{
- if (ch != 0 || !is_zero_range(host, size)) {
- memset(host, ch, size);
- }
-}
-
-static void *do_data_decompress(void *opaque)
-{
- DecompressParam *param = opaque;
- unsigned long pagesize;
-
- while (!quit_decomp_thread) {
- qemu_mutex_lock(&param->mutex);
- while (!param->start && !quit_decomp_thread) {
- qemu_cond_wait(&param->cond, &param->mutex);
- pagesize = TARGET_PAGE_SIZE;
- if (!quit_decomp_thread) {
- /* uncompress() will return failed in some case, especially
- * when the page is dirted when doing the compression, it's
- * not a problem because the dirty page will be retransferred
- * and uncompress() won't break the data in other pages.
- */
- uncompress((Bytef *)param->des, &pagesize,
- (const Bytef *)param->compbuf, param->len);
- }
- param->start = false;
- }
- qemu_mutex_unlock(&param->mutex);
- }
-
- return NULL;
-}
-
-void migrate_decompress_threads_create(void)
-{
- int i, thread_count;
-
- thread_count = migrate_decompress_threads();
- decompress_threads = g_new0(QemuThread, thread_count);
- decomp_param = g_new0(DecompressParam, thread_count);
- compressed_data_buf = g_malloc0(compressBound(TARGET_PAGE_SIZE));
- quit_decomp_thread = false;
- for (i = 0; i < thread_count; i++) {
- qemu_mutex_init(&decomp_param[i].mutex);
- qemu_cond_init(&decomp_param[i].cond);
- decomp_param[i].compbuf = g_malloc0(compressBound(TARGET_PAGE_SIZE));
- qemu_thread_create(decompress_threads + i, "decompress",
- do_data_decompress, decomp_param + i,
- QEMU_THREAD_JOINABLE);
- }
-}
-
-void migrate_decompress_threads_join(void)
-{
- int i, thread_count;
-
- quit_decomp_thread = true;
- thread_count = migrate_decompress_threads();
- for (i = 0; i < thread_count; i++) {
- qemu_mutex_lock(&decomp_param[i].mutex);
- qemu_cond_signal(&decomp_param[i].cond);
- qemu_mutex_unlock(&decomp_param[i].mutex);
- }
- for (i = 0; i < thread_count; i++) {
- qemu_thread_join(decompress_threads + i);
- qemu_mutex_destroy(&decomp_param[i].mutex);
- qemu_cond_destroy(&decomp_param[i].cond);
- g_free(decomp_param[i].compbuf);
- }
- g_free(decompress_threads);
- g_free(decomp_param);
- g_free(compressed_data_buf);
- decompress_threads = NULL;
- decomp_param = NULL;
- compressed_data_buf = NULL;
-}
-
-static void decompress_data_with_multi_threads(uint8_t *compbuf,
- void *host, int len)
-{
- int idx, thread_count;
-
- thread_count = migrate_decompress_threads();
- while (true) {
- for (idx = 0; idx < thread_count; idx++) {
- if (!decomp_param[idx].start) {
- memcpy(decomp_param[idx].compbuf, compbuf, len);
- decomp_param[idx].des = host;
- decomp_param[idx].len = len;
- start_decompression(&decomp_param[idx]);
- break;
- }
- }
- if (idx < thread_count) {
- break;
- }
- }
-}
-
-static int ram_load(QEMUFile *f, void *opaque, int version_id)
-{
- int flags = 0, ret = 0;
- static uint64_t seq_iter;
- int len = 0;
-
- seq_iter++;
-
- if (version_id != 4) {
- ret = -EINVAL;
- }
-
- /* This RCU critical section can be very long running.
- * When RCU reclaims in the code start to become numerous,
- * it will be necessary to reduce the granularity of this
- * critical section.
- */
- rcu_read_lock();
- while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) {
- ram_addr_t addr, total_ram_bytes;
- void *host;
- uint8_t ch;
-
- addr = qemu_get_be64(f);
- flags = addr & ~TARGET_PAGE_MASK;
- addr &= TARGET_PAGE_MASK;
-
- switch (flags & ~RAM_SAVE_FLAG_CONTINUE) {
- case RAM_SAVE_FLAG_MEM_SIZE:
- /* Synchronize RAM block list */
- total_ram_bytes = addr;
- while (!ret && total_ram_bytes) {
- RAMBlock *block;
- uint8_t len;
- char id[256];
- ram_addr_t length;
-
- len = qemu_get_byte(f);
- qemu_get_buffer(f, (uint8_t *)id, len);
- id[len] = 0;
- length = qemu_get_be64(f);
-
- QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
- if (!strncmp(id, block->idstr, sizeof(id))) {
- if (length != block->used_length) {
- Error *local_err = NULL;
-
- ret = qemu_ram_resize(block->offset, length, &local_err);
- if (local_err) {
- error_report_err(local_err);
- }
- }
- break;
- }
- }
-
- if (!block) {
- error_report("Unknown ramblock \"%s\", cannot "
- "accept migration", id);
- ret = -EINVAL;
- }
-
- total_ram_bytes -= length;
- }
- break;
- case RAM_SAVE_FLAG_COMPRESS:
- host = host_from_stream_offset(f, addr, flags);
- if (!host) {
- error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
- ret = -EINVAL;
- break;
- }
- ch = qemu_get_byte(f);
- ram_handle_compressed(host, ch, TARGET_PAGE_SIZE);
- break;
- case RAM_SAVE_FLAG_PAGE:
- host = host_from_stream_offset(f, addr, flags);
- if (!host) {
- error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
- ret = -EINVAL;
- break;
- }
- qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
- break;
- case RAM_SAVE_FLAG_COMPRESS_PAGE:
- host = host_from_stream_offset(f, addr, flags);
- if (!host) {
- error_report("Invalid RAM offset " RAM_ADDR_FMT, addr);
- ret = -EINVAL;
- break;
- }
-
- len = qemu_get_be32(f);
- if (len < 0 || len > compressBound(TARGET_PAGE_SIZE)) {
- error_report("Invalid compressed data length: %d", len);
- ret = -EINVAL;
- break;
- }
- qemu_get_buffer(f, compressed_data_buf, len);
- decompress_data_with_multi_threads(compressed_data_buf, host, len);
- break;
- case RAM_SAVE_FLAG_XBZRLE:
- host = host_from_stream_offset(f, addr, flags);
- if (!host) {
- error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
- ret = -EINVAL;
- break;
- }
- if (load_xbzrle(f, addr, host) < 0) {
- error_report("Failed to decompress XBZRLE page at "
- RAM_ADDR_FMT, addr);
- ret = -EINVAL;
- break;
- }
- break;
- case RAM_SAVE_FLAG_EOS:
- /* normal exit */
- break;
- default:
- if (flags & RAM_SAVE_FLAG_HOOK) {
- ram_control_load_hook(f, flags);
- } else {
- error_report("Unknown combination of migration flags: %#x",
- flags);
- ret = -EINVAL;
- }
- }
- if (!ret) {
- ret = qemu_file_get_error(f);
- }
- }
-
- rcu_read_unlock();
- DPRINTF("Completed load of VM with exit code %d seq iteration "
- "%" PRIu64 "\n", ret, seq_iter);
- return ret;
-}
-
-static SaveVMHandlers savevm_ram_handlers = {
- .save_live_setup = ram_save_setup,
- .save_live_iterate = ram_save_iterate,
- .save_live_complete = ram_save_complete,
- .save_live_pending = ram_save_pending,
- .load_state = ram_load,
- .cancel = ram_migration_cancel,
-};
-
-void ram_mig_init(void)
-{
- qemu_mutex_init(&XBZRLE.lock);
- register_savevm_live(NULL, "ram", 0, 4, &savevm_ram_handlers, NULL);
-}
-
struct soundhw {
const char *name;
const char *descr;
@@ -1912,52 +307,3 @@ TargetInfo *qmp_query_target(Error **errp)
return info;
}
-
-/* Stub function that's gets run on the vcpu when its brought out of the
- VM to run inside qemu via async_run_on_cpu()*/
-static void mig_sleep_cpu(void *opq)
-{
- qemu_mutex_unlock_iothread();
- g_usleep(30*1000);
- qemu_mutex_lock_iothread();
-}
-
-/* To reduce the dirty rate explicitly disallow the VCPUs from spending
- much time in the VM. The migration thread will try to catchup.
- Workload will experience a performance drop.
-*/
-static void mig_throttle_guest_down(void)
-{
- CPUState *cpu;
-
- qemu_mutex_lock_iothread();
- CPU_FOREACH(cpu) {
- async_run_on_cpu(cpu, mig_sleep_cpu, NULL);
- }
- qemu_mutex_unlock_iothread();
-}
-
-static void check_guest_throttling(void)
-{
- static int64_t t0;
- int64_t t1;
-
- if (!mig_throttle_on) {
- return;
- }
-
- if (!t0) {
- t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
- return;
- }
-
- t1 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
-
- /* If it has been more than 40 ms since the last time the guest
- * was throttled then do it again.
- */
- if (40 < (t1-t0)/1000000) {
- mig_throttle_guest_down();
- t0 = t1;
- }
-}
diff --git a/async.c b/async.c
index 46d9e639d7..77d080d6f5 100644
--- a/async.c
+++ b/async.c
@@ -280,6 +280,12 @@ static void aio_timerlist_notify(void *opaque)
aio_notify(opaque);
}
+static void aio_rfifolock_cb(void *opaque)
+{
+ /* Kick owner thread in case they are blocked in aio_poll() */
+ aio_notify(opaque);
+}
+
AioContext *aio_context_new(Error **errp)
{
int ret;
@@ -297,7 +303,7 @@ AioContext *aio_context_new(Error **errp)
event_notifier_test_and_clear);
ctx->thread_pool = NULL;
qemu_mutex_init(&ctx->bh_lock);
- rfifolock_init(&ctx->lock, NULL, NULL);
+ rfifolock_init(&ctx->lock, aio_rfifolock_cb, ctx);
timerlistgroup_init(&ctx->tlg, aio_timerlist_notify, ctx);
return ctx;
diff --git a/audio/Makefile.objs b/audio/Makefile.objs
index 26a0ac9507..481d1aa30e 100644
--- a/audio/Makefile.objs
+++ b/audio/Makefile.objs
@@ -5,13 +5,9 @@ common-obj-$(CONFIG_SPICE) += spiceaudio.o
common-obj-$(CONFIG_COREAUDIO) += coreaudio.o
common-obj-$(CONFIG_ALSA) += alsaaudio.o
common-obj-$(CONFIG_DSOUND) += dsoundaudio.o
-common-obj-$(CONFIG_FMOD) += fmodaudio.o
-common-obj-$(CONFIG_ESD) += esdaudio.o
common-obj-$(CONFIG_PA) += paaudio.o
-common-obj-$(CONFIG_WINWAVE) += winwaveaudio.o
common-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o
common-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
common-obj-y += wavcapture.o
-$(obj)/audio.o $(obj)/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS)
sdlaudio.o-cflags := $(SDL_CFLAGS)
diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
index 74ead97d87..6315b2d746 100644
--- a/audio/alsaaudio.c
+++ b/audio/alsaaudio.c
@@ -25,6 +25,7 @@
#include "qemu-common.h"
#include "qemu/main-loop.h"
#include "audio.h"
+#include "trace.h"
#if QEMU_GNUC_PREREQ(4, 3)
#pragma GCC diagnostic ignored "-Waddress"
@@ -33,9 +34,28 @@
#define AUDIO_CAP "alsa"
#include "audio_int.h"
+typedef struct ALSAConf {
+ int size_in_usec_in;
+ int size_in_usec_out;
+ const char *pcm_name_in;
+ const char *pcm_name_out;
+ unsigned int buffer_size_in;
+ unsigned int period_size_in;
+ unsigned int buffer_size_out;
+ unsigned int period_size_out;
+ unsigned int threshold;
+
+ int buffer_size_in_overridden;
+ int period_size_in_overridden;
+
+ int buffer_size_out_overridden;
+ int period_size_out_overridden;
+} ALSAConf;
+
struct pollhlp {
snd_pcm_t *handle;
struct pollfd *pfds;
+ ALSAConf *conf;
int count;
int mask;
};
@@ -56,30 +76,6 @@ typedef struct ALSAVoiceIn {
struct pollhlp pollhlp;
} ALSAVoiceIn;
-static struct {
- int size_in_usec_in;
- int size_in_usec_out;
- const char *pcm_name_in;
- const char *pcm_name_out;
- unsigned int buffer_size_in;
- unsigned int period_size_in;
- unsigned int buffer_size_out;
- unsigned int period_size_out;
- unsigned int threshold;
-
- int buffer_size_in_overridden;
- int period_size_in_overridden;
-
- int buffer_size_out_overridden;
- int period_size_out_overridden;
- int verbose;
-} conf = {
- .buffer_size_out = 4096,
- .period_size_out = 1024,
- .pcm_name_out = "default",
- .pcm_name_in = "default",
-};
-
struct alsa_params_req {
int freq;
snd_pcm_format_t fmt;
@@ -205,9 +201,7 @@ static void alsa_poll_handler (void *opaque)
}
if (!(revents & hlp->mask)) {
- if (conf.verbose) {
- dolog ("revents = %d\n", revents);
- }
+ trace_alsa_revents(revents);
return;
}
@@ -266,31 +260,14 @@ static int alsa_poll_helper (snd_pcm_t *handle, struct pollhlp *hlp, int mask)
for (i = 0; i < count; ++i) {
if (pfds[i].events & POLLIN) {
- err = qemu_set_fd_handler (pfds[i].fd, alsa_poll_handler,
- NULL, hlp);
+ qemu_set_fd_handler (pfds[i].fd, alsa_poll_handler, NULL, hlp);
}
if (pfds[i].events & POLLOUT) {
- if (conf.verbose) {
- dolog ("POLLOUT %d %d\n", i, pfds[i].fd);
- }
- err = qemu_set_fd_handler (pfds[i].fd, NULL,
- alsa_poll_handler, hlp);
+ trace_alsa_pollout(i, pfds[i].fd);
+ qemu_set_fd_handler (pfds[i].fd, NULL, alsa_poll_handler, hlp);
}
- if (conf.verbose) {
- dolog ("Set handler events=%#x index=%d fd=%d err=%d\n",
- pfds[i].events, i, pfds[i].fd, err);
- }
-
- if (err) {
- dolog ("Failed to set handler events=%#x index=%d fd=%d err=%d\n",
- pfds[i].events, i, pfds[i].fd, err);
+ trace_alsa_set_handler(pfds[i].events, i, pfds[i].fd, err);
- while (i--) {
- qemu_set_fd_handler (pfds[i].fd, NULL, NULL, NULL);
- }
- g_free (pfds);
- return -1;
- }
}
hlp->pfds = pfds;
hlp->count = count;
@@ -476,14 +453,15 @@ static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
}
static int alsa_open (int in, struct alsa_params_req *req,
- struct alsa_params_obt *obt, snd_pcm_t **handlep)
+ struct alsa_params_obt *obt, snd_pcm_t **handlep,
+ ALSAConf *conf)
{
snd_pcm_t *handle;
snd_pcm_hw_params_t *hw_params;
int err;
int size_in_usec;
unsigned int freq, nchannels;
- const char *pcm_name = in ? conf.pcm_name_in : conf.pcm_name_out;
+ const char *pcm_name = in ? conf->pcm_name_in : conf->pcm_name_out;
snd_pcm_uframes_t obt_buffer_size;
const char *typ = in ? "ADC" : "DAC";
snd_pcm_format_t obtfmt;
@@ -522,7 +500,7 @@ static int alsa_open (int in, struct alsa_params_req *req,
}
err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt);
- if (err < 0 && conf.verbose) {
+ if (err < 0) {
alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt);
}
@@ -654,7 +632,7 @@ static int alsa_open (int in, struct alsa_params_req *req,
goto err;
}
- if (!in && conf.threshold) {
+ if (!in && conf->threshold) {
snd_pcm_uframes_t threshold;
int bytes_per_sec;
@@ -676,7 +654,7 @@ static int alsa_open (int in, struct alsa_params_req *req,
break;
}
- threshold = (conf.threshold * bytes_per_sec) / 1000;
+ threshold = (conf->threshold * bytes_per_sec) / 1000;
alsa_set_threshold (handle, threshold);
}
@@ -686,10 +664,9 @@ static int alsa_open (int in, struct alsa_params_req *req,
*handlep = handle;
- if (conf.verbose &&
- (obtfmt != req->fmt ||
+ if (obtfmt != req->fmt ||
obt->nchannels != req->nchannels ||
- obt->freq != req->freq)) {
+ obt->freq != req->freq) {
dolog ("Audio parameters for %s\n", typ);
alsa_dump_info (req, obt, obtfmt);
}
@@ -743,9 +720,7 @@ static void alsa_write_pending (ALSAVoiceOut *alsa)
if (written <= 0) {
switch (written) {
case 0:
- if (conf.verbose) {
- dolog ("Failed to write %d frames (wrote zero)\n", len);
- }
+ trace_alsa_wrote_zero(len);
return;
case -EPIPE:
@@ -754,9 +729,7 @@ static void alsa_write_pending (ALSAVoiceOut *alsa)
len);
return;
}
- if (conf.verbose) {
- dolog ("Recovering from playback xrun\n");
- }
+ trace_alsa_xrun_out();
continue;
case -ESTRPIPE:
@@ -767,9 +740,7 @@ static void alsa_write_pending (ALSAVoiceOut *alsa)
len);
return;
}
- if (conf.verbose) {
- dolog ("Resuming suspended output stream\n");
- }
+ trace_alsa_resume_out();
continue;
case -EAGAIN:
@@ -819,25 +790,27 @@ static void alsa_fini_out (HWVoiceOut *hw)
alsa->pcm_buf = NULL;
}
-static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as)
+static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
+ void *drv_opaque)
{
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
struct alsa_params_req req;
struct alsa_params_obt obt;
snd_pcm_t *handle;
struct audsettings obt_as;
+ ALSAConf *conf = drv_opaque;
req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
req.freq = as->freq;
req.nchannels = as->nchannels;
- req.period_size = conf.period_size_out;
- req.buffer_size = conf.buffer_size_out;
- req.size_in_usec = conf.size_in_usec_out;
+ req.period_size = conf->period_size_out;
+ req.buffer_size = conf->buffer_size_out;
+ req.size_in_usec = conf->size_in_usec_out;
req.override_mask =
- (conf.period_size_out_overridden ? 1 : 0) |
- (conf.buffer_size_out_overridden ? 2 : 0);
+ (conf->period_size_out_overridden ? 1 : 0) |
+ (conf->buffer_size_out_overridden ? 2 : 0);
- if (alsa_open (0, &req, &obt, &handle)) {
+ if (alsa_open (0, &req, &obt, &handle, conf)) {
return -1;
}
@@ -858,6 +831,7 @@ static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as)
}
alsa->handle = handle;
+ alsa->pollhlp.conf = conf;
return 0;
}
@@ -928,25 +902,26 @@ static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
return -1;
}
-static int alsa_init_in (HWVoiceIn *hw, struct audsettings *as)
+static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
{
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
struct alsa_params_req req;
struct alsa_params_obt obt;
snd_pcm_t *handle;
struct audsettings obt_as;
+ ALSAConf *conf = drv_opaque;
req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
req.freq = as->freq;
req.nchannels = as->nchannels;
- req.period_size = conf.period_size_in;
- req.buffer_size = conf.buffer_size_in;
- req.size_in_usec = conf.size_in_usec_in;
+ req.period_size = conf->period_size_in;
+ req.buffer_size = conf->buffer_size_in;
+ req.size_in_usec = conf->size_in_usec_in;
req.override_mask =
- (conf.period_size_in_overridden ? 1 : 0) |
- (conf.buffer_size_in_overridden ? 2 : 0);
+ (conf->period_size_in_overridden ? 1 : 0) |
+ (conf->buffer_size_in_overridden ? 2 : 0);
- if (alsa_open (1, &req, &obt, &handle)) {
+ if (alsa_open (1, &req, &obt, &handle, conf)) {
return -1;
}
@@ -967,6 +942,7 @@ static int alsa_init_in (HWVoiceIn *hw, struct audsettings *as)
}
alsa->handle = handle;
+ alsa->pollhlp.conf = conf;
return 0;
}
@@ -1022,14 +998,10 @@ static int alsa_run_in (HWVoiceIn *hw)
dolog ("Failed to resume suspended input stream\n");
return 0;
}
- if (conf.verbose) {
- dolog ("Resuming suspended input stream\n");
- }
+ trace_alsa_resume_in();
break;
default:
- if (conf.verbose) {
- dolog ("No frames available and ALSA state is %d\n", state);
- }
+ trace_alsa_no_frames(state);
return 0;
}
}
@@ -1064,9 +1036,7 @@ static int alsa_run_in (HWVoiceIn *hw)
if (nread <= 0) {
switch (nread) {
case 0:
- if (conf.verbose) {
- dolog ("Failed to read %ld frames (read zero)\n", len);
- }
+ trace_alsa_read_zero(len);
goto exit;
case -EPIPE:
@@ -1074,9 +1044,7 @@ static int alsa_run_in (HWVoiceIn *hw)
alsa_logerr (nread, "Failed to read %ld frames\n", len);
goto exit;
}
- if (conf.verbose) {
- dolog ("Recovering from capture xrun\n");
- }
+ trace_alsa_xrun_in();
continue;
case -EAGAIN:
@@ -1148,82 +1116,85 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
return -1;
}
+static ALSAConf glob_conf = {
+ .buffer_size_out = 4096,
+ .period_size_out = 1024,
+ .pcm_name_out = "default",
+ .pcm_name_in = "default",
+};
+
static void *alsa_audio_init (void)
{
- return &conf;
+ ALSAConf *conf = g_malloc(sizeof(ALSAConf));
+ *conf = glob_conf;
+ return conf;
}
static void alsa_audio_fini (void *opaque)
{
- (void) opaque;
+ g_free(opaque);
}
static struct audio_option alsa_options[] = {
{
.name = "DAC_SIZE_IN_USEC",
.tag = AUD_OPT_BOOL,
- .valp = &conf.size_in_usec_out,
+ .valp = &glob_conf.size_in_usec_out,
.descr = "DAC period/buffer size in microseconds (otherwise in frames)"
},
{
.name = "DAC_PERIOD_SIZE",
.tag = AUD_OPT_INT,
- .valp = &conf.period_size_out,
+ .valp = &glob_conf.period_size_out,
.descr = "DAC period size (0 to go with system default)",
- .overriddenp = &conf.period_size_out_overridden
+ .overriddenp = &glob_conf.period_size_out_overridden
},
{
.name = "DAC_BUFFER_SIZE",
.tag = AUD_OPT_INT,
- .valp = &conf.buffer_size_out,
+ .valp = &glob_conf.buffer_size_out,
.descr = "DAC buffer size (0 to go with system default)",
- .overriddenp = &conf.buffer_size_out_overridden
+ .overriddenp = &glob_conf.buffer_size_out_overridden
},
{
.name = "ADC_SIZE_IN_USEC",
.tag = AUD_OPT_BOOL,
- .valp = &conf.size_in_usec_in,
+ .valp = &glob_conf.size_in_usec_in,
.descr =
"ADC period/buffer size in microseconds (otherwise in frames)"
},
{
.name = "ADC_PERIOD_SIZE",
.tag = AUD_OPT_INT,
- .valp = &conf.period_size_in,
+ .valp = &glob_conf.period_size_in,
.descr = "ADC period size (0 to go with system default)",
- .overriddenp = &conf.period_size_in_overridden
+ .overriddenp = &glob_conf.period_size_in_overridden
},
{
.name = "ADC_BUFFER_SIZE",
.tag = AUD_OPT_INT,
- .valp = &conf.buffer_size_in,
+ .valp = &glob_conf.buffer_size_in,
.descr = "ADC buffer size (0 to go with system default)",
- .overriddenp = &conf.buffer_size_in_overridden
+ .overriddenp = &glob_conf.buffer_size_in_overridden
},
{
.name = "THRESHOLD",
.tag = AUD_OPT_INT,
- .valp = &conf.threshold,
+ .valp = &glob_conf.threshold,
.descr = "(undocumented)"
},
{
.name = "DAC_DEV",
.tag = AUD_OPT_STR,
- .valp = &conf.pcm_name_out,
+ .valp = &glob_conf.pcm_name_out,
.descr = "DAC device name (for instance dmix)"
},
{
.name = "ADC_DEV",
.tag = AUD_OPT_STR,
- .valp = &conf.pcm_name_in,
+ .valp = &glob_conf.pcm_name_in,
.descr = "ADC device name"
},
- {
- .name = "VERBOSE",
- .tag = AUD_OPT_BOOL,
- .valp = &conf.verbose,
- .descr = "Behave in a more verbose way"
- },
{ /* End of list */ }
};
diff --git a/audio/audio.c b/audio/audio.c
index 9d018e9ded..5be4b15fcf 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -30,7 +30,6 @@
#define AUDIO_CAP "audio"
#include "audio_int.h"
-/* #define DEBUG_PLIVE */
/* #define DEBUG_LIVE */
/* #define DEBUG_OUT */
/* #define DEBUG_CAPTURE */
@@ -66,8 +65,6 @@ static struct {
int hertz;
int64_t ticks;
} period;
- int plive;
- int log_to_monitor;
int try_poll_in;
int try_poll_out;
} conf = {
@@ -96,8 +93,6 @@ static struct {
},
.period = { .hertz = 100 },
- .plive = 0,
- .log_to_monitor = 0,
.try_poll_in = 1,
.try_poll_out = 1,
};
@@ -331,20 +326,11 @@ static const char *audio_get_conf_str (const char *key,
void AUD_vlog (const char *cap, const char *fmt, va_list ap)
{
- if (conf.log_to_monitor) {
- if (cap) {
- monitor_printf(default_mon, "%s: ", cap);
- }
-
- monitor_vprintf(default_mon, fmt, ap);
+ if (cap) {
+ fprintf(stderr, "%s: ", cap);
}
- else {
- if (cap) {
- fprintf (stderr, "%s: ", cap);
- }
- vfprintf (stderr, fmt, ap);
- }
+ vfprintf(stderr, fmt, ap);
}
void AUD_log (const char *cap, const char *fmt, ...)
@@ -1454,9 +1440,6 @@ static void audio_run_out (AudioState *s)
while (sw) {
sw1 = sw->entries.le_next;
if (!sw->active && !sw->callback.fn) {
-#ifdef DEBUG_PLIVE
- dolog ("Finishing with old voice\n");
-#endif
audio_close_out (sw);
}
sw = sw1;
@@ -1648,18 +1631,6 @@ static struct audio_option audio_options[] = {
.valp = &conf.period.hertz,
.descr = "Timer period in HZ (0 - use lowest possible)"
},
- {
- .name = "PLIVE",
- .tag = AUD_OPT_BOOL,
- .valp = &conf.plive,
- .descr = "(undocumented)"
- },
- {
- .name = "LOG_TO_MONITOR",
- .tag = AUD_OPT_BOOL,
- .valp = &conf.log_to_monitor,
- .descr = "Print logging messages to monitor instead of stderr"
- },
{ /* End of list */ }
};
diff --git a/audio/audio_int.h b/audio/audio_int.h
index fd019a0fc3..566df5edf4 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -156,13 +156,13 @@ struct audio_driver {
};
struct audio_pcm_ops {
- int (*init_out)(HWVoiceOut *hw, struct audsettings *as);
+ int (*init_out)(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque);
void (*fini_out)(HWVoiceOut *hw);
int (*run_out) (HWVoiceOut *hw, int live);
int (*write) (SWVoiceOut *sw, void *buf, int size);
int (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
- int (*init_in) (HWVoiceIn *hw, struct audsettings *as);
+ int (*init_in) (HWVoiceIn *hw, struct audsettings *as, void *drv_opaque);
void (*fini_in) (HWVoiceIn *hw);
int (*run_in) (HWVoiceIn *hw);
int (*read) (SWVoiceIn *sw, void *buf, int size);
@@ -206,14 +206,11 @@ extern struct audio_driver no_audio_driver;
extern struct audio_driver oss_audio_driver;
extern struct audio_driver sdl_audio_driver;
extern struct audio_driver wav_audio_driver;
-extern struct audio_driver fmod_audio_driver;
extern struct audio_driver alsa_audio_driver;
extern struct audio_driver coreaudio_audio_driver;
extern struct audio_driver dsound_audio_driver;
-extern struct audio_driver esd_audio_driver;
extern struct audio_driver pa_audio_driver;
extern struct audio_driver spice_audio_driver;
-extern struct audio_driver winwave_audio_driver;
extern const struct mixeng_volume nominal_volume;
void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
diff --git a/audio/audio_template.h b/audio/audio_template.h
index 584e536fac..99b27b285e 100644
--- a/audio/audio_template.h
+++ b/audio/audio_template.h
@@ -262,7 +262,7 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
#ifdef DAC
QLIST_INIT (&hw->cap_head);
#endif
- if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) {
+ if (glue (hw->pcm_ops->init_, TYPE) (hw, as, s->drv_opaque)) {
goto err0;
}
@@ -398,10 +398,6 @@ SW *glue (AUD_open_, TYPE) (
)
{
AudioState *s = &glob_audio_state;
-#ifdef DAC
- int live = 0;
- SW *old_sw = NULL;
-#endif
if (audio_bug (AUDIO_FUNC, !card || !name || !callback_fn || !as)) {
dolog ("card=%p name=%p callback_fn=%p as=%p\n",
@@ -426,29 +422,6 @@ SW *glue (AUD_open_, TYPE) (
return sw;
}
-#ifdef DAC
- if (conf.plive && sw && (!sw->active && !sw->empty)) {
- live = sw->total_hw_samples_mixed;
-
-#ifdef DEBUG_PLIVE
- dolog ("Replacing voice %s with %d live samples\n", SW_NAME (sw), live);
- dolog ("Old %s freq %d, bits %d, channels %d\n",
- SW_NAME (sw), sw->info.freq, sw->info.bits, sw->info.nchannels);
- dolog ("New %s freq %d, bits %d, channels %d\n",
- name,
- as->freq,
- (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) ? 16 : 8,
- as->nchannels);
-#endif
-
- if (live) {
- old_sw = sw;
- old_sw->callback.fn = NULL;
- sw = NULL;
- }
- }
-#endif
-
if (!glue (conf.fixed_, TYPE).enabled && sw) {
glue (AUD_close_, TYPE) (card, sw);
sw = NULL;
@@ -481,20 +454,6 @@ SW *glue (AUD_open_, TYPE) (
sw->callback.fn = callback_fn;
sw->callback.opaque = callback_opaque;
-#ifdef DAC
- if (live) {
- int mixed =
- (live << old_sw->info.shift)
- * old_sw->info.bytes_per_second
- / sw->info.bytes_per_second;
-
-#ifdef DEBUG_PLIVE
- dolog ("Silence will be mixed %d\n", mixed);
-#endif
- sw->total_hw_samples_mixed += mixed;
- }
-#endif
-
#ifdef DEBUG_AUDIO
dolog ("%s\n", name);
audio_pcm_print_info ("hw", &sw->hw->info);
diff --git a/audio/coreaudio.c b/audio/coreaudio.c
index 5964c62eaf..6dfd63eb42 100644
--- a/audio/coreaudio.c
+++ b/audio/coreaudio.c
@@ -32,20 +32,16 @@
#define AUDIO_CAP "coreaudio"
#include "audio_int.h"
-struct {
+static int isAtexit;
+
+typedef struct {
int buffer_frames;
int nbuffers;
- int isAtexit;
-} conf = {
- .buffer_frames = 512,
- .nbuffers = 4,
- .isAtexit = 0
-};
+} CoreaudioConf;
typedef struct coreaudioVoiceOut {
HWVoiceOut hw;
pthread_mutex_t mutex;
- int isAtexit;
AudioDeviceID outputDeviceID;
UInt32 audioDevicePropertyBufferFrameSize;
AudioStreamBasicDescription outputStreamBasicDescription;
@@ -161,7 +157,7 @@ static inline UInt32 isPlaying (AudioDeviceID outputDeviceID)
static void coreaudio_atexit (void)
{
- conf.isAtexit = 1;
+ isAtexit = 1;
}
static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
@@ -287,7 +283,8 @@ static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
return audio_pcm_sw_write (sw, buf, len);
}
-static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
+static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
+ void *drv_opaque)
{
OSStatus status;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
@@ -295,6 +292,7 @@ static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
int err;
const char *typ = "playback";
AudioValueRange frameRange;
+ CoreaudioConf *conf = drv_opaque;
/* create mutex */
err = pthread_mutex_init(&core->mutex, NULL);
@@ -336,16 +334,16 @@ static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
return -1;
}
- if (frameRange.mMinimum > conf.buffer_frames) {
+ if (frameRange.mMinimum > conf->buffer_frames) {
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
}
- else if (frameRange.mMaximum < conf.buffer_frames) {
+ else if (frameRange.mMaximum < conf->buffer_frames) {
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
}
else {
- core->audioDevicePropertyBufferFrameSize = conf.buffer_frames;
+ core->audioDevicePropertyBufferFrameSize = conf->buffer_frames;
}
/* set Buffer Frame Size */
@@ -379,7 +377,7 @@ static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
"Could not get device buffer frame size\n");
return -1;
}
- hw->samples = conf.nbuffers * core->audioDevicePropertyBufferFrameSize;
+ hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize;
/* get StreamFormat */
propertySize = sizeof(core->outputStreamBasicDescription);
@@ -443,7 +441,7 @@ static void coreaudio_fini_out (HWVoiceOut *hw)
int err;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
- if (!conf.isAtexit) {
+ if (!isAtexit) {
/* stop playback */
if (isPlaying(core->outputDeviceID)) {
status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
@@ -486,7 +484,7 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
case VOICE_DISABLE:
/* stop playback */
- if (!conf.isAtexit) {
+ if (!isAtexit) {
if (isPlaying(core->outputDeviceID)) {
status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
if (status != kAudioHardwareNoError) {
@@ -499,28 +497,36 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
return 0;
}
+static CoreaudioConf glob_conf = {
+ .buffer_frames = 512,
+ .nbuffers = 4,
+};
+
static void *coreaudio_audio_init (void)
{
+ CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf));
+ *conf = glob_conf;
+
atexit(coreaudio_atexit);
- return &coreaudio_audio_init;
+ return conf;
}
static void coreaudio_audio_fini (void *opaque)
{
- (void) opaque;
+ g_free(opaque);
}
static struct audio_option coreaudio_options[] = {
{
.name = "BUFFER_SIZE",
.tag = AUD_OPT_INT,
- .valp = &conf.buffer_frames,
+ .valp = &glob_conf.buffer_frames,
.descr = "Size of the buffer in frames"
},
{
.name = "BUFFER_COUNT",
.tag = AUD_OPT_INT,
- .valp = &conf.nbuffers,
+ .valp = &glob_conf.nbuffers,
.descr = "Number of buffers"
},
{ /* End of list */ }
diff --git a/audio/dsound_template.h b/audio/dsound_template.h
index 8b37d16a8c..b439f33f58 100644
--- a/audio/dsound_template.h
+++ b/audio/dsound_template.h
@@ -67,11 +67,11 @@ static int glue (dsound_lock_, TYPE) (
LPVOID *p2p,
DWORD *blen1p,
DWORD *blen2p,
- int entire
+ int entire,
+ dsound *s
)
{
HRESULT hr;
- int i;
LPVOID p1 = NULL, p2 = NULL;
DWORD blen1 = 0, blen2 = 0;
DWORD flag;
@@ -81,37 +81,18 @@ static int glue (dsound_lock_, TYPE) (
#else
flag = entire ? DSBLOCK_ENTIREBUFFER : 0;
#endif
- for (i = 0; i < conf.lock_retries; ++i) {
- hr = glue (IFACE, _Lock) (
- buf,
- pos,
- len,
- &p1,
- &blen1,
- &p2,
- &blen2,
- flag
- );
+ hr = glue(IFACE, _Lock)(buf, pos, len, &p1, &blen1, &p2, &blen2, flag);
- if (FAILED (hr)) {
+ if (FAILED (hr)) {
#ifndef DSBTYPE_IN
- if (hr == DSERR_BUFFERLOST) {
- if (glue (dsound_restore_, TYPE) (buf)) {
- dsound_logerr (hr, "Could not lock " NAME "\n");
- goto fail;
- }
- continue;
+ if (hr == DSERR_BUFFERLOST) {
+ if (glue (dsound_restore_, TYPE) (buf, s)) {
+ dsound_logerr (hr, "Could not lock " NAME "\n");
}
-#endif
- dsound_logerr (hr, "Could not lock " NAME "\n");
goto fail;
}
-
- break;
- }
-
- if (i == conf.lock_retries) {
- dolog ("%d attempts to lock " NAME " failed\n", i);
+#endif
+ dsound_logerr (hr, "Could not lock " NAME "\n");
goto fail;
}
@@ -174,16 +155,19 @@ static void dsound_fini_out (HWVoiceOut *hw)
}
#ifdef DSBTYPE_IN
-static int dsound_init_in (HWVoiceIn *hw, struct audsettings *as)
+static int dsound_init_in(HWVoiceIn *hw, struct audsettings *as,
+ void *drv_opaque)
#else
-static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as)
+static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
+ void *drv_opaque)
#endif
{
int err;
HRESULT hr;
- dsound *s = &glob_dsound;
+ dsound *s = drv_opaque;
WAVEFORMATEX wfx;
struct audsettings obt_as;
+ DSoundConf *conf = &s->conf;
#ifdef DSBTYPE_IN
const char *typ = "ADC";
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
@@ -210,7 +194,7 @@ static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as)
bd.dwSize = sizeof (bd);
bd.lpwfxFormat = &wfx;
#ifdef DSBTYPE_IN
- bd.dwBufferBytes = conf.bufsize_in;
+ bd.dwBufferBytes = conf->bufsize_in;
hr = IDirectSoundCapture_CreateCaptureBuffer (
s->dsound_capture,
&bd,
@@ -219,7 +203,7 @@ static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as)
);
#else
bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
- bd.dwBufferBytes = conf.bufsize_out;
+ bd.dwBufferBytes = conf->bufsize_out;
hr = IDirectSound_CreateSoundBuffer (
s->dsound,
&bd,
@@ -269,6 +253,7 @@ static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as)
);
}
hw->samples = bc.dwBufferBytes >> hw->info.shift;
+ ds->s = s;
#ifdef DEBUG_DSOUND
dolog ("caps %ld, desc %ld\n",
diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c
index e2d89fd5d5..e9472c105c 100644
--- a/audio/dsoundaudio.c
+++ b/audio/dsoundaudio.c
@@ -41,42 +41,25 @@
/* #define DEBUG_DSOUND */
-static struct {
- int lock_retries;
- int restore_retries;
- int getstatus_retries;
- int set_primary;
+typedef struct {
int bufsize_in;
int bufsize_out;
- struct audsettings settings;
int latency_millis;
-} conf = {
- .lock_retries = 1,
- .restore_retries = 1,
- .getstatus_retries = 1,
- .set_primary = 0,
- .bufsize_in = 16384,
- .bufsize_out = 16384,
- .settings.freq = 44100,
- .settings.nchannels = 2,
- .settings.fmt = AUD_FMT_S16,
- .latency_millis = 10
-};
+} DSoundConf;
typedef struct {
LPDIRECTSOUND dsound;
LPDIRECTSOUNDCAPTURE dsound_capture;
- LPDIRECTSOUNDBUFFER dsound_primary_buffer;
struct audsettings settings;
+ DSoundConf conf;
} dsound;
-static dsound glob_dsound;
-
typedef struct {
HWVoiceOut hw;
LPDIRECTSOUNDBUFFER dsound_buffer;
DWORD old_pos;
int first_time;
+ dsound *s;
#ifdef DEBUG_DSOUND
DWORD old_ppos;
DWORD played;
@@ -88,6 +71,7 @@ typedef struct {
HWVoiceIn hw;
int first_time;
LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
+ dsound *s;
} DSoundVoiceIn;
static void dsound_log_hresult (HRESULT hr)
@@ -281,29 +265,17 @@ static void print_wave_format (WAVEFORMATEX *wfx)
}
#endif
-static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb)
+static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb, dsound *s)
{
HRESULT hr;
- int i;
-
- for (i = 0; i < conf.restore_retries; ++i) {
- hr = IDirectSoundBuffer_Restore (dsb);
-
- switch (hr) {
- case DS_OK:
- return 0;
- case DSERR_BUFFERLOST:
- continue;
+ hr = IDirectSoundBuffer_Restore (dsb);
- default:
- dsound_logerr (hr, "Could not restore playback buffer\n");
- return -1;
- }
+ if (hr != DS_OK) {
+ dsound_logerr (hr, "Could not restore playback buffer\n");
+ return -1;
}
-
- dolog ("%d attempts to restore playback buffer failed\n", i);
- return -1;
+ return 0;
}
#include "dsound_template.h"
@@ -311,25 +283,20 @@ static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb)
#include "dsound_template.h"
#undef DSBTYPE_IN
-static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp)
+static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp,
+ dsound *s)
{
HRESULT hr;
- int i;
- for (i = 0; i < conf.getstatus_retries; ++i) {
- hr = IDirectSoundBuffer_GetStatus (dsb, statusp);
- if (FAILED (hr)) {
- dsound_logerr (hr, "Could not get playback buffer status\n");
- return -1;
- }
+ hr = IDirectSoundBuffer_GetStatus (dsb, statusp);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not get playback buffer status\n");
+ return -1;
+ }
- if (*statusp & DSERR_BUFFERLOST) {
- if (dsound_restore_out (dsb)) {
- return -1;
- }
- continue;
- }
- break;
+ if (*statusp & DSERR_BUFFERLOST) {
+ dsound_restore_out(dsb, s);
+ return -1;
}
return 0;
@@ -376,7 +343,8 @@ static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
hw->rpos = pos % hw->samples;
}
-static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb)
+static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
+ dsound *s)
{
int err;
LPVOID p1, p2;
@@ -389,7 +357,8 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb)
hw->samples << hw->info.shift,
&p1, &p2,
&blen1, &blen2,
- 1
+ 1,
+ s
);
if (err) {
return;
@@ -415,25 +384,9 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb)
dsound_unlock_out (dsb, p1, p2, blen1, blen2);
}
-static void dsound_close (dsound *s)
-{
- HRESULT hr;
-
- if (s->dsound_primary_buffer) {
- hr = IDirectSoundBuffer_Release (s->dsound_primary_buffer);
- if (FAILED (hr)) {
- dsound_logerr (hr, "Could not release primary buffer\n");
- }
- s->dsound_primary_buffer = NULL;
- }
-}
-
static int dsound_open (dsound *s)
{
- int err;
HRESULT hr;
- WAVEFORMATEX wfx;
- DSBUFFERDESC dsbd;
HWND hwnd;
hwnd = GetForegroundWindow ();
@@ -449,63 +402,7 @@ static int dsound_open (dsound *s)
return -1;
}
- if (!conf.set_primary) {
- return 0;
- }
-
- err = waveformat_from_audio_settings (&wfx, &conf.settings);
- if (err) {
- return -1;
- }
-
- memset (&dsbd, 0, sizeof (dsbd));
- dsbd.dwSize = sizeof (dsbd);
- dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
- dsbd.dwBufferBytes = 0;
- dsbd.lpwfxFormat = NULL;
-
- hr = IDirectSound_CreateSoundBuffer (
- s->dsound,
- &dsbd,
- &s->dsound_primary_buffer,
- NULL
- );
- if (FAILED (hr)) {
- dsound_logerr (hr, "Could not create primary playback buffer\n");
- return -1;
- }
-
- hr = IDirectSoundBuffer_SetFormat (s->dsound_primary_buffer, &wfx);
- if (FAILED (hr)) {
- dsound_logerr (hr, "Could not set primary playback buffer format\n");
- }
-
- hr = IDirectSoundBuffer_GetFormat (
- s->dsound_primary_buffer,
- &wfx,
- sizeof (wfx),
- NULL
- );
- if (FAILED (hr)) {
- dsound_logerr (hr, "Could not get primary playback buffer format\n");
- goto fail0;
- }
-
-#ifdef DEBUG_DSOUND
- dolog ("Primary\n");
- print_wave_format (&wfx);
-#endif
-
- err = waveformat_to_audio_settings (&wfx, &s->settings);
- if (err) {
- goto fail0;
- }
-
return 0;
-
- fail0:
- dsound_close (s);
- return -1;
}
static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
@@ -514,6 +411,7 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
DWORD status;
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
+ dsound *s = ds->s;
if (!dsb) {
dolog ("Attempt to control voice without a buffer\n");
@@ -522,7 +420,7 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
switch (cmd) {
case VOICE_ENABLE:
- if (dsound_get_status_out (dsb, &status)) {
+ if (dsound_get_status_out (dsb, &status, s)) {
return -1;
}
@@ -531,7 +429,7 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
return 0;
}
- dsound_clear_sample (hw, dsb);
+ dsound_clear_sample (hw, dsb, s);
hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
if (FAILED (hr)) {
@@ -541,7 +439,7 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
break;
case VOICE_DISABLE:
- if (dsound_get_status_out (dsb, &status)) {
+ if (dsound_get_status_out (dsb, &status, s)) {
return -1;
}
@@ -578,6 +476,8 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
DWORD wpos, ppos, old_pos;
LPVOID p1, p2;
int bufsize;
+ dsound *s = ds->s;
+ DSoundConf *conf = &s->conf;
if (!dsb) {
dolog ("Attempt to run empty with playback buffer\n");
@@ -600,14 +500,14 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
len = live << hwshift;
if (ds->first_time) {
- if (conf.latency_millis) {
+ if (conf->latency_millis) {
DWORD cur_blat;
cur_blat = audio_ring_dist (wpos, ppos, bufsize);
ds->first_time = 0;
old_pos = wpos;
old_pos +=
- millis_to_bytes (&hw->info, conf.latency_millis) - cur_blat;
+ millis_to_bytes (&hw->info, conf->latency_millis) - cur_blat;
old_pos %= bufsize;
old_pos &= ~hw->info.align;
}
@@ -663,7 +563,8 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
len,
&p1, &p2,
&blen1, &blen2,
- 0
+ 0,
+ s
);
if (err) {
return 0;
@@ -766,6 +667,7 @@ static int dsound_run_in (HWVoiceIn *hw)
DWORD cpos, rpos;
LPVOID p1, p2;
int hwshift;
+ dsound *s = ds->s;
if (!dscb) {
dolog ("Attempt to run without capture buffer\n");
@@ -820,7 +722,8 @@ static int dsound_run_in (HWVoiceIn *hw)
&p2,
&blen1,
&blen2,
- 0
+ 0,
+ s
);
if (err) {
return 0;
@@ -843,12 +746,19 @@ static int dsound_run_in (HWVoiceIn *hw)
return decr;
}
+static DSoundConf glob_conf = {
+ .bufsize_in = 16384,
+ .bufsize_out = 16384,
+ .latency_millis = 10
+};
+
static void dsound_audio_fini (void *opaque)
{
HRESULT hr;
dsound *s = opaque;
if (!s->dsound) {
+ g_free(s);
return;
}
@@ -859,6 +769,7 @@ static void dsound_audio_fini (void *opaque)
s->dsound = NULL;
if (!s->dsound_capture) {
+ g_free(s);
return;
}
@@ -867,17 +778,21 @@ static void dsound_audio_fini (void *opaque)
dsound_logerr (hr, "Could not release DirectSoundCapture\n");
}
s->dsound_capture = NULL;
+
+ g_free(s);
}
static void *dsound_audio_init (void)
{
int err;
HRESULT hr;
- dsound *s = &glob_dsound;
+ dsound *s = g_malloc0(sizeof(dsound));
+ s->conf = glob_conf;
hr = CoInitialize (NULL);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not initialize COM\n");
+ g_free(s);
return NULL;
}
@@ -890,6 +805,7 @@ static void *dsound_audio_init (void)
);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not create DirectSound instance\n");
+ g_free(s);
return NULL;
}
@@ -901,7 +817,7 @@ static void *dsound_audio_init (void)
if (FAILED (hr)) {
dsound_logerr (hr, "Could not release DirectSound\n");
}
- s->dsound = NULL;
+ g_free(s);
return NULL;
}
@@ -939,63 +855,21 @@ static void *dsound_audio_init (void)
static struct audio_option dsound_options[] = {
{
- .name = "LOCK_RETRIES",
- .tag = AUD_OPT_INT,
- .valp = &conf.lock_retries,
- .descr = "Number of times to attempt locking the buffer"
- },
- {
- .name = "RESTOURE_RETRIES",
- .tag = AUD_OPT_INT,
- .valp = &conf.restore_retries,
- .descr = "Number of times to attempt restoring the buffer"
- },
- {
- .name = "GETSTATUS_RETRIES",
- .tag = AUD_OPT_INT,
- .valp = &conf.getstatus_retries,
- .descr = "Number of times to attempt getting status of the buffer"
- },
- {
- .name = "SET_PRIMARY",
- .tag = AUD_OPT_BOOL,
- .valp = &conf.set_primary,
- .descr = "Set the parameters of primary buffer"
- },
- {
.name = "LATENCY_MILLIS",
.tag = AUD_OPT_INT,
- .valp = &conf.latency_millis,
+ .valp = &glob_conf.latency_millis,
.descr = "(undocumented)"
},
{
- .name = "PRIMARY_FREQ",
- .tag = AUD_OPT_INT,
- .valp = &conf.settings.freq,
- .descr = "Primary buffer frequency"
- },
- {
- .name = "PRIMARY_CHANNELS",
- .tag = AUD_OPT_INT,
- .valp = &conf.settings.nchannels,
- .descr = "Primary buffer number of channels (1 - mono, 2 - stereo)"
- },
- {
- .name = "PRIMARY_FMT",
- .tag = AUD_OPT_FMT,
- .valp = &conf.settings.fmt,
- .descr = "Primary buffer format"
- },
- {
.name = "BUFSIZE_OUT",
.tag = AUD_OPT_INT,
- .valp = &conf.bufsize_out,
+ .valp = &glob_conf.bufsize_out,
.descr = "(undocumented)"
},
{
.name = "BUFSIZE_IN",
.tag = AUD_OPT_INT,
- .valp = &conf.bufsize_in,
+ .valp = &glob_conf.bufsize_in,
.descr = "(undocumented)"
},
{ /* End of list */ }
diff --git a/audio/esdaudio.c b/audio/esdaudio.c
deleted file mode 100644
index eea9ccec0b..0000000000
--- a/audio/esdaudio.c
+++ /dev/null
@@ -1,557 +0,0 @@
-/*
- * QEMU ESD audio driver
- *
- * Copyright (c) 2006 Frederick Reeve (brushed up by malc)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include <esd.h>
-#include "qemu-common.h"
-#include "audio.h"
-
-#define AUDIO_CAP "esd"
-#include "audio_int.h"
-#include "audio_pt_int.h"
-
-typedef struct {
- HWVoiceOut hw;
- int done;
- int live;
- int decr;
- int rpos;
- void *pcm_buf;
- int fd;
- struct audio_pt pt;
-} ESDVoiceOut;
-
-typedef struct {
- HWVoiceIn hw;
- int done;
- int dead;
- int incr;
- int wpos;
- void *pcm_buf;
- int fd;
- struct audio_pt pt;
-} ESDVoiceIn;
-
-static struct {
- int samples;
- int divisor;
- char *dac_host;
- char *adc_host;
-} conf = {
- .samples = 1024,
- .divisor = 2,
-};
-
-static void GCC_FMT_ATTR (2, 3) qesd_logerr (int err, const char *fmt, ...)
-{
- va_list ap;
-
- va_start (ap, fmt);
- AUD_vlog (AUDIO_CAP, fmt, ap);
- va_end (ap);
-
- AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
-}
-
-/* playback */
-static void *qesd_thread_out (void *arg)
-{
- ESDVoiceOut *esd = arg;
- HWVoiceOut *hw = &esd->hw;
- int threshold;
-
- threshold = conf.divisor ? hw->samples / conf.divisor : 0;
-
- if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
- return NULL;
- }
-
- for (;;) {
- int decr, to_mix, rpos;
-
- for (;;) {
- if (esd->done) {
- goto exit;
- }
-
- if (esd->live > threshold) {
- break;
- }
-
- if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
- goto exit;
- }
- }
-
- decr = to_mix = esd->live;
- rpos = hw->rpos;
-
- if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
- return NULL;
- }
-
- while (to_mix) {
- ssize_t written;
- int chunk = audio_MIN (to_mix, hw->samples - rpos);
- struct st_sample *src = hw->mix_buf + rpos;
-
- hw->clip (esd->pcm_buf, src, chunk);
-
- again:
- written = write (esd->fd, esd->pcm_buf, chunk << hw->info.shift);
- if (written == -1) {
- if (errno == EINTR || errno == EAGAIN) {
- goto again;
- }
- qesd_logerr (errno, "write failed\n");
- return NULL;
- }
-
- if (written != chunk << hw->info.shift) {
- int wsamples = written >> hw->info.shift;
- int wbytes = wsamples << hw->info.shift;
- if (wbytes != written) {
- dolog ("warning: Misaligned write %d (requested %zd), "
- "alignment %d\n",
- wbytes, written, hw->info.align + 1);
- }
- to_mix -= wsamples;
- rpos = (rpos + wsamples) % hw->samples;
- break;
- }
-
- rpos = (rpos + chunk) % hw->samples;
- to_mix -= chunk;
- }
-
- if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
- return NULL;
- }
-
- esd->rpos = rpos;
- esd->live -= decr;
- esd->decr += decr;
- }
-
- exit:
- audio_pt_unlock (&esd->pt, AUDIO_FUNC);
- return NULL;
-}
-
-static int qesd_run_out (HWVoiceOut *hw, int live)
-{
- int decr;
- ESDVoiceOut *esd = (ESDVoiceOut *) hw;
-
- if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
- return 0;
- }
-
- decr = audio_MIN (live, esd->decr);
- esd->decr -= decr;
- esd->live = live - decr;
- hw->rpos = esd->rpos;
- if (esd->live > 0) {
- audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
- }
- else {
- audio_pt_unlock (&esd->pt, AUDIO_FUNC);
- }
- return decr;
-}
-
-static int qesd_write (SWVoiceOut *sw, void *buf, int len)
-{
- return audio_pcm_sw_write (sw, buf, len);
-}
-
-static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as)
-{
- ESDVoiceOut *esd = (ESDVoiceOut *) hw;
- struct audsettings obt_as = *as;
- int esdfmt = ESD_STREAM | ESD_PLAY;
-
- esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
- switch (as->fmt) {
- case AUD_FMT_S8:
- case AUD_FMT_U8:
- esdfmt |= ESD_BITS8;
- obt_as.fmt = AUD_FMT_U8;
- break;
-
- case AUD_FMT_S32:
- case AUD_FMT_U32:
- dolog ("Will use 16 instead of 32 bit samples\n");
- /* fall through */
- case AUD_FMT_S16:
- case AUD_FMT_U16:
- deffmt:
- esdfmt |= ESD_BITS16;
- obt_as.fmt = AUD_FMT_S16;
- break;
-
- default:
- dolog ("Internal logic error: Bad audio format %d\n", as->fmt);
- goto deffmt;
-
- }
- obt_as.endianness = AUDIO_HOST_ENDIANNESS;
-
- audio_pcm_init_info (&hw->info, &obt_as);
-
- hw->samples = conf.samples;
- esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
- if (!esd->pcm_buf) {
- dolog ("Could not allocate buffer (%d bytes)\n",
- hw->samples << hw->info.shift);
- return -1;
- }
-
- esd->fd = esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL);
- if (esd->fd < 0) {
- qesd_logerr (errno, "esd_play_stream failed\n");
- goto fail1;
- }
-
- if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) {
- goto fail2;
- }
-
- return 0;
-
- fail2:
- if (close (esd->fd)) {
- qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
- AUDIO_FUNC, esd->fd);
- }
- esd->fd = -1;
-
- fail1:
- g_free (esd->pcm_buf);
- esd->pcm_buf = NULL;
- return -1;
-}
-
-static void qesd_fini_out (HWVoiceOut *hw)
-{
- void *ret;
- ESDVoiceOut *esd = (ESDVoiceOut *) hw;
-
- audio_pt_lock (&esd->pt, AUDIO_FUNC);
- esd->done = 1;
- audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
- audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
-
- if (esd->fd >= 0) {
- if (close (esd->fd)) {
- qesd_logerr (errno, "failed to close esd socket\n");
- }
- esd->fd = -1;
- }
-
- audio_pt_fini (&esd->pt, AUDIO_FUNC);
-
- g_free (esd->pcm_buf);
- esd->pcm_buf = NULL;
-}
-
-static int qesd_ctl_out (HWVoiceOut *hw, int cmd, ...)
-{
- (void) hw;
- (void) cmd;
- return 0;
-}
-
-/* capture */
-static void *qesd_thread_in (void *arg)
-{
- ESDVoiceIn *esd = arg;
- HWVoiceIn *hw = &esd->hw;
- int threshold;
-
- threshold = conf.divisor ? hw->samples / conf.divisor : 0;
-
- if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
- return NULL;
- }
-
- for (;;) {
- int incr, to_grab, wpos;
-
- for (;;) {
- if (esd->done) {
- goto exit;
- }
-
- if (esd->dead > threshold) {
- break;
- }
-
- if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
- goto exit;
- }
- }
-
- incr = to_grab = esd->dead;
- wpos = hw->wpos;
-
- if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
- return NULL;
- }
-
- while (to_grab) {
- ssize_t nread;
- int chunk = audio_MIN (to_grab, hw->samples - wpos);
- void *buf = advance (esd->pcm_buf, wpos);
-
- again:
- nread = read (esd->fd, buf, chunk << hw->info.shift);
- if (nread == -1) {
- if (errno == EINTR || errno == EAGAIN) {
- goto again;
- }
- qesd_logerr (errno, "read failed\n");
- return NULL;
- }
-
- if (nread != chunk << hw->info.shift) {
- int rsamples = nread >> hw->info.shift;
- int rbytes = rsamples << hw->info.shift;
- if (rbytes != nread) {
- dolog ("warning: Misaligned write %d (requested %zd), "
- "alignment %d\n",
- rbytes, nread, hw->info.align + 1);
- }
- to_grab -= rsamples;
- wpos = (wpos + rsamples) % hw->samples;
- break;
- }
-
- hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift);
- wpos = (wpos + chunk) % hw->samples;
- to_grab -= chunk;
- }
-
- if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
- return NULL;
- }
-
- esd->wpos = wpos;
- esd->dead -= incr;
- esd->incr += incr;
- }
-
- exit:
- audio_pt_unlock (&esd->pt, AUDIO_FUNC);
- return NULL;
-}
-
-static int qesd_run_in (HWVoiceIn *hw)
-{
- int live, incr, dead;
- ESDVoiceIn *esd = (ESDVoiceIn *) hw;
-
- if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
- return 0;
- }
-
- live = audio_pcm_hw_get_live_in (hw);
- dead = hw->samples - live;
- incr = audio_MIN (dead, esd->incr);
- esd->incr -= incr;
- esd->dead = dead - incr;
- hw->wpos = esd->wpos;
- if (esd->dead > 0) {
- audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
- }
- else {
- audio_pt_unlock (&esd->pt, AUDIO_FUNC);
- }
- return incr;
-}
-
-static int qesd_read (SWVoiceIn *sw, void *buf, int len)
-{
- return audio_pcm_sw_read (sw, buf, len);
-}
-
-static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as)
-{
- ESDVoiceIn *esd = (ESDVoiceIn *) hw;
- struct audsettings obt_as = *as;
- int esdfmt = ESD_STREAM | ESD_RECORD;
-
- esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
- switch (as->fmt) {
- case AUD_FMT_S8:
- case AUD_FMT_U8:
- esdfmt |= ESD_BITS8;
- obt_as.fmt = AUD_FMT_U8;
- break;
-
- case AUD_FMT_S16:
- case AUD_FMT_U16:
- esdfmt |= ESD_BITS16;
- obt_as.fmt = AUD_FMT_S16;
- break;
-
- case AUD_FMT_S32:
- case AUD_FMT_U32:
- dolog ("Will use 16 instead of 32 bit samples\n");
- esdfmt |= ESD_BITS16;
- obt_as.fmt = AUD_FMT_S16;
- break;
- }
- obt_as.endianness = AUDIO_HOST_ENDIANNESS;
-
- audio_pcm_init_info (&hw->info, &obt_as);
-
- hw->samples = conf.samples;
- esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
- if (!esd->pcm_buf) {
- dolog ("Could not allocate buffer (%d bytes)\n",
- hw->samples << hw->info.shift);
- return -1;
- }
-
- esd->fd = esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL);
- if (esd->fd < 0) {
- qesd_logerr (errno, "esd_record_stream failed\n");
- goto fail1;
- }
-
- if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) {
- goto fail2;
- }
-
- return 0;
-
- fail2:
- if (close (esd->fd)) {
- qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
- AUDIO_FUNC, esd->fd);
- }
- esd->fd = -1;
-
- fail1:
- g_free (esd->pcm_buf);
- esd->pcm_buf = NULL;
- return -1;
-}
-
-static void qesd_fini_in (HWVoiceIn *hw)
-{
- void *ret;
- ESDVoiceIn *esd = (ESDVoiceIn *) hw;
-
- audio_pt_lock (&esd->pt, AUDIO_FUNC);
- esd->done = 1;
- audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
- audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
-
- if (esd->fd >= 0) {
- if (close (esd->fd)) {
- qesd_logerr (errno, "failed to close esd socket\n");
- }
- esd->fd = -1;
- }
-
- audio_pt_fini (&esd->pt, AUDIO_FUNC);
-
- g_free (esd->pcm_buf);
- esd->pcm_buf = NULL;
-}
-
-static int qesd_ctl_in (HWVoiceIn *hw, int cmd, ...)
-{
- (void) hw;
- (void) cmd;
- return 0;
-}
-
-/* common */
-static void *qesd_audio_init (void)
-{
- return &conf;
-}
-
-static void qesd_audio_fini (void *opaque)
-{
- (void) opaque;
- ldebug ("esd_fini");
-}
-
-struct audio_option qesd_options[] = {
- {
- .name = "SAMPLES",
- .tag = AUD_OPT_INT,
- .valp = &conf.samples,
- .descr = "buffer size in samples"
- },
- {
- .name = "DIVISOR",
- .tag = AUD_OPT_INT,
- .valp = &conf.divisor,
- .descr = "threshold divisor"
- },
- {
- .name = "DAC_HOST",
- .tag = AUD_OPT_STR,
- .valp = &conf.dac_host,
- .descr = "playback host"
- },
- {
- .name = "ADC_HOST",
- .tag = AUD_OPT_STR,
- .valp = &conf.adc_host,
- .descr = "capture host"
- },
- { /* End of list */ }
-};
-
-static struct audio_pcm_ops qesd_pcm_ops = {
- .init_out = qesd_init_out,
- .fini_out = qesd_fini_out,
- .run_out = qesd_run_out,
- .write = qesd_write,
- .ctl_out = qesd_ctl_out,
-
- .init_in = qesd_init_in,
- .fini_in = qesd_fini_in,
- .run_in = qesd_run_in,
- .read = qesd_read,
- .ctl_in = qesd_ctl_in,
-};
-
-struct audio_driver esd_audio_driver = {
- .name = "esd",
- .descr = "http://en.wikipedia.org/wiki/Esound",
- .options = qesd_options,
- .init = qesd_audio_init,
- .fini = qesd_audio_fini,
- .pcm_ops = &qesd_pcm_ops,
- .can_be_default = 0,
- .max_voices_out = INT_MAX,
- .max_voices_in = INT_MAX,
- .voice_size_out = sizeof (ESDVoiceOut),
- .voice_size_in = sizeof (ESDVoiceIn)
-};
diff --git a/audio/fmodaudio.c b/audio/fmodaudio.c
deleted file mode 100644
index fabf84dd3b..0000000000
--- a/audio/fmodaudio.c
+++ /dev/null
@@ -1,685 +0,0 @@
-/*
- * QEMU FMOD audio driver
- *
- * Copyright (c) 2004-2005 Vassili Karpov (malc)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include <fmod.h>
-#include <fmod_errors.h>
-#include "qemu-common.h"
-#include "audio.h"
-
-#define AUDIO_CAP "fmod"
-#include "audio_int.h"
-
-typedef struct FMODVoiceOut {
- HWVoiceOut hw;
- unsigned int old_pos;
- FSOUND_SAMPLE *fmod_sample;
- int channel;
-} FMODVoiceOut;
-
-typedef struct FMODVoiceIn {
- HWVoiceIn hw;
- FSOUND_SAMPLE *fmod_sample;
-} FMODVoiceIn;
-
-static struct {
- const char *drvname;
- int nb_samples;
- int freq;
- int nb_channels;
- int bufsize;
- int broken_adc;
-} conf = {
- .nb_samples = 2048 * 2,
- .freq = 44100,
- .nb_channels = 2,
-};
-
-static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...)
-{
- va_list ap;
-
- va_start (ap, fmt);
- AUD_vlog (AUDIO_CAP, fmt, ap);
- va_end (ap);
-
- AUD_log (AUDIO_CAP, "Reason: %s\n",
- FMOD_ErrorString (FSOUND_GetError ()));
-}
-
-static void GCC_FMT_ATTR (2, 3) fmod_logerr2 (
- const char *typ,
- const char *fmt,
- ...
- )
-{
- va_list ap;
-
- AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
-
- va_start (ap, fmt);
- AUD_vlog (AUDIO_CAP, fmt, ap);
- va_end (ap);
-
- AUD_log (AUDIO_CAP, "Reason: %s\n",
- FMOD_ErrorString (FSOUND_GetError ()));
-}
-
-static int fmod_write (SWVoiceOut *sw, void *buf, int len)
-{
- return audio_pcm_sw_write (sw, buf, len);
-}
-
-static void fmod_clear_sample (FMODVoiceOut *fmd)
-{
- HWVoiceOut *hw = &fmd->hw;
- int status;
- void *p1 = 0, *p2 = 0;
- unsigned int len1 = 0, len2 = 0;
-
- status = FSOUND_Sample_Lock (
- fmd->fmod_sample,
- 0,
- hw->samples << hw->info.shift,
- &p1,
- &p2,
- &len1,
- &len2
- );
-
- if (!status) {
- fmod_logerr ("Failed to lock sample\n");
- return;
- }
-
- if ((len1 & hw->info.align) || (len2 & hw->info.align)) {
- dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
- len1, len2, hw->info.align + 1);
- goto fail;
- }
-
- if ((len1 + len2) - (hw->samples << hw->info.shift)) {
- dolog ("Lock returned incomplete length %d, %d\n",
- len1 + len2, hw->samples << hw->info.shift);
- goto fail;
- }
-
- audio_pcm_info_clear_buf (&hw->info, p1, hw->samples);
-
- fail:
- status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2);
- if (!status) {
- fmod_logerr ("Failed to unlock sample\n");
- }
-}
-
-static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
-{
- int src_len1 = dst_len;
- int src_len2 = 0;
- int pos = hw->rpos + dst_len;
- struct st_sample *src1 = hw->mix_buf + hw->rpos;
- struct st_sample *src2 = NULL;
-
- if (pos > hw->samples) {
- src_len1 = hw->samples - hw->rpos;
- src2 = hw->mix_buf;
- src_len2 = dst_len - src_len1;
- pos = src_len2;
- }
-
- if (src_len1) {
- hw->clip (dst, src1, src_len1);
- }
-
- if (src_len2) {
- dst = advance (dst, src_len1 << hw->info.shift);
- hw->clip (dst, src2, src_len2);
- }
-
- hw->rpos = pos % hw->samples;
-}
-
-static int fmod_unlock_sample (FSOUND_SAMPLE *sample, void *p1, void *p2,
- unsigned int blen1, unsigned int blen2)
-{
- int status = FSOUND_Sample_Unlock (sample, p1, p2, blen1, blen2);
- if (!status) {
- fmod_logerr ("Failed to unlock sample\n");
- return -1;
- }
- return 0;
-}
-
-static int fmod_lock_sample (
- FSOUND_SAMPLE *sample,
- struct audio_pcm_info *info,
- int pos,
- int len,
- void **p1,
- void **p2,
- unsigned int *blen1,
- unsigned int *blen2
- )
-{
- int status;
-
- status = FSOUND_Sample_Lock (
- sample,
- pos << info->shift,
- len << info->shift,
- p1,
- p2,
- blen1,
- blen2
- );
-
- if (!status) {
- fmod_logerr ("Failed to lock sample\n");
- return -1;
- }
-
- if ((*blen1 & info->align) || (*blen2 & info->align)) {
- dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
- *blen1, *blen2, info->align + 1);
-
- fmod_unlock_sample (sample, *p1, *p2, *blen1, *blen2);
-
- *p1 = NULL - 1;
- *p2 = NULL - 1;
- *blen1 = ~0U;
- *blen2 = ~0U;
- return -1;
- }
-
- if (!*p1 && *blen1) {
- dolog ("warning: !p1 && blen1=%d\n", *blen1);
- *blen1 = 0;
- }
-
- if (!p2 && *blen2) {
- dolog ("warning: !p2 && blen2=%d\n", *blen2);
- *blen2 = 0;
- }
-
- return 0;
-}
-
-static int fmod_run_out (HWVoiceOut *hw, int live)
-{
- FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
- int decr;
- void *p1 = 0, *p2 = 0;
- unsigned int blen1 = 0, blen2 = 0;
- unsigned int len1 = 0, len2 = 0;
-
- if (!hw->pending_disable) {
- return 0;
- }
-
- decr = live;
-
- if (fmd->channel >= 0) {
- int len = decr;
- int old_pos = fmd->old_pos;
- int ppos = FSOUND_GetCurrentPosition (fmd->channel);
-
- if (ppos == old_pos || !ppos) {
- return 0;
- }
-
- if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
- len = ppos - old_pos;
- }
- else {
- if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->samples))) {
- len = hw->samples - old_pos + ppos;
- }
- }
- decr = len;
-
- if (audio_bug (AUDIO_FUNC, decr < 0)) {
- dolog ("decr=%d live=%d ppos=%d old_pos=%d len=%d\n",
- decr, live, ppos, old_pos, len);
- return 0;
- }
- }
-
-
- if (!decr) {
- return 0;
- }
-
- if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
- fmd->old_pos, decr,
- &p1, &p2,
- &blen1, &blen2)) {
- return 0;
- }
-
- len1 = blen1 >> hw->info.shift;
- len2 = blen2 >> hw->info.shift;
- ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2);
- decr = len1 + len2;
-
- if (p1 && len1) {
- fmod_write_sample (hw, p1, len1);
- }
-
- if (p2 && len2) {
- fmod_write_sample (hw, p2, len2);
- }
-
- fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
-
- fmd->old_pos = (fmd->old_pos + decr) % hw->samples;
- return decr;
-}
-
-static int aud_to_fmodfmt (audfmt_e fmt, int stereo)
-{
- int mode = FSOUND_LOOP_NORMAL;
-
- switch (fmt) {
- case AUD_FMT_S8:
- mode |= FSOUND_SIGNED | FSOUND_8BITS;
- break;
-
- case AUD_FMT_U8:
- mode |= FSOUND_UNSIGNED | FSOUND_8BITS;
- break;
-
- case AUD_FMT_S16:
- mode |= FSOUND_SIGNED | FSOUND_16BITS;
- break;
-
- case AUD_FMT_U16:
- mode |= FSOUND_UNSIGNED | FSOUND_16BITS;
- break;
-
- default:
- dolog ("Internal logic error: Bad audio format %d\n", fmt);
-#ifdef DEBUG_FMOD
- abort ();
-#endif
- mode |= FSOUND_8BITS;
- }
- mode |= stereo ? FSOUND_STEREO : FSOUND_MONO;
- return mode;
-}
-
-static void fmod_fini_out (HWVoiceOut *hw)
-{
- FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
-
- if (fmd->fmod_sample) {
- FSOUND_Sample_Free (fmd->fmod_sample);
- fmd->fmod_sample = 0;
-
- if (fmd->channel >= 0) {
- FSOUND_StopSound (fmd->channel);
- }
- }
-}
-
-static int fmod_init_out (HWVoiceOut *hw, struct audsettings *as)
-{
- int mode, channel;
- FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
- struct audsettings obt_as = *as;
-
- mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
- fmd->fmod_sample = FSOUND_Sample_Alloc (
- FSOUND_FREE, /* index */
- conf.nb_samples, /* length */
- mode, /* mode */
- as->freq, /* freq */
- 255, /* volume */
- 128, /* pan */
- 255 /* priority */
- );
-
- if (!fmd->fmod_sample) {
- fmod_logerr2 ("DAC", "Failed to allocate FMOD sample\n");
- return -1;
- }
-
- channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1);
- if (channel < 0) {
- fmod_logerr2 ("DAC", "Failed to start playing sound\n");
- FSOUND_Sample_Free (fmd->fmod_sample);
- return -1;
- }
- fmd->channel = channel;
-
- /* FMOD always operates on little endian frames? */
- obt_as.endianness = 0;
- audio_pcm_init_info (&hw->info, &obt_as);
- hw->samples = conf.nb_samples;
- return 0;
-}
-
-static int fmod_ctl_out (HWVoiceOut *hw, int cmd, ...)
-{
- int status;
- FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
-
- switch (cmd) {
- case VOICE_ENABLE:
- fmod_clear_sample (fmd);
- status = FSOUND_SetPaused (fmd->channel, 0);
- if (!status) {
- fmod_logerr ("Failed to resume channel %d\n", fmd->channel);
- }
- break;
-
- case VOICE_DISABLE:
- status = FSOUND_SetPaused (fmd->channel, 1);
- if (!status) {
- fmod_logerr ("Failed to pause channel %d\n", fmd->channel);
- }
- break;
- }
- return 0;
-}
-
-static int fmod_init_in (HWVoiceIn *hw, struct audsettings *as)
-{
- int mode;
- FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
- struct audsettings obt_as = *as;
-
- if (conf.broken_adc) {
- return -1;
- }
-
- mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
- fmd->fmod_sample = FSOUND_Sample_Alloc (
- FSOUND_FREE, /* index */
- conf.nb_samples, /* length */
- mode, /* mode */
- as->freq, /* freq */
- 255, /* volume */
- 128, /* pan */
- 255 /* priority */
- );
-
- if (!fmd->fmod_sample) {
- fmod_logerr2 ("ADC", "Failed to allocate FMOD sample\n");
- return -1;
- }
-
- /* FMOD always operates on little endian frames? */
- obt_as.endianness = 0;
- audio_pcm_init_info (&hw->info, &obt_as);
- hw->samples = conf.nb_samples;
- return 0;
-}
-
-static void fmod_fini_in (HWVoiceIn *hw)
-{
- FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
-
- if (fmd->fmod_sample) {
- FSOUND_Record_Stop ();
- FSOUND_Sample_Free (fmd->fmod_sample);
- fmd->fmod_sample = 0;
- }
-}
-
-static int fmod_run_in (HWVoiceIn *hw)
-{
- FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
- int hwshift = hw->info.shift;
- int live, dead, new_pos, len;
- unsigned int blen1 = 0, blen2 = 0;
- unsigned int len1, len2;
- unsigned int decr;
- void *p1, *p2;
-
- live = audio_pcm_hw_get_live_in (hw);
- dead = hw->samples - live;
- if (!dead) {
- return 0;
- }
-
- new_pos = FSOUND_Record_GetPosition ();
- if (new_pos < 0) {
- fmod_logerr ("Could not get recording position\n");
- return 0;
- }
-
- len = audio_ring_dist (new_pos, hw->wpos, hw->samples);
- if (!len) {
- return 0;
- }
- len = audio_MIN (len, dead);
-
- if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
- hw->wpos, len,
- &p1, &p2,
- &blen1, &blen2)) {
- return 0;
- }
-
- len1 = blen1 >> hwshift;
- len2 = blen2 >> hwshift;
- decr = len1 + len2;
-
- if (p1 && blen1) {
- hw->conv (hw->conv_buf + hw->wpos, p1, len1);
- }
- if (p2 && len2) {
- hw->conv (hw->conv_buf, p2, len2);
- }
-
- fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
- hw->wpos = (hw->wpos + decr) % hw->samples;
- return decr;
-}
-
-static struct {
- const char *name;
- int type;
-} drvtab[] = {
- { .name = "none", .type = FSOUND_OUTPUT_NOSOUND },
-#ifdef _WIN32
- { .name = "winmm", .type = FSOUND_OUTPUT_WINMM },
- { .name = "dsound", .type = FSOUND_OUTPUT_DSOUND },
- { .name = "a3d", .type = FSOUND_OUTPUT_A3D },
- { .name = "asio", .type = FSOUND_OUTPUT_ASIO },
-#endif
-#ifdef __linux__
- { .name = "oss", .type = FSOUND_OUTPUT_OSS },
- { .name = "alsa", .type = FSOUND_OUTPUT_ALSA },
- { .name = "esd", .type = FSOUND_OUTPUT_ESD },
-#endif
-#ifdef __APPLE__
- { .name = "mac", .type = FSOUND_OUTPUT_MAC },
-#endif
-#if 0
- { .name = "xbox", .type = FSOUND_OUTPUT_XBOX },
- { .name = "ps2", .type = FSOUND_OUTPUT_PS2 },
- { .name = "gcube", .type = FSOUND_OUTPUT_GC },
-#endif
- { .name = "none-realtime", .type = FSOUND_OUTPUT_NOSOUND_NONREALTIME }
-};
-
-static void *fmod_audio_init (void)
-{
- size_t i;
- double ver;
- int status;
- int output_type = -1;
- const char *drv = conf.drvname;
-
- ver = FSOUND_GetVersion ();
- if (ver < FMOD_VERSION) {
- dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION);
- return NULL;
- }
-
-#ifdef __linux__
- if (ver < 3.75) {
- dolog ("FMOD before 3.75 has bug preventing ADC from working\n"
- "ADC will be disabled.\n");
- conf.broken_adc = 1;
- }
-#endif
-
- if (drv) {
- int found = 0;
- for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
- if (!strcmp (drv, drvtab[i].name)) {
- output_type = drvtab[i].type;
- found = 1;
- break;
- }
- }
- if (!found) {
- dolog ("Unknown FMOD driver `%s'\n", drv);
- dolog ("Valid drivers:\n");
- for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
- dolog (" %s\n", drvtab[i].name);
- }
- }
- }
-
- if (output_type != -1) {
- status = FSOUND_SetOutput (output_type);
- if (!status) {
- fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type);
- return NULL;
- }
- }
-
- if (conf.bufsize) {
- status = FSOUND_SetBufferSize (conf.bufsize);
- if (!status) {
- fmod_logerr ("FSOUND_SetBufferSize (%d) failed\n", conf.bufsize);
- }
- }
-
- status = FSOUND_Init (conf.freq, conf.nb_channels, 0);
- if (!status) {
- fmod_logerr ("FSOUND_Init failed\n");
- return NULL;
- }
-
- return &conf;
-}
-
-static int fmod_read (SWVoiceIn *sw, void *buf, int size)
-{
- return audio_pcm_sw_read (sw, buf, size);
-}
-
-static int fmod_ctl_in (HWVoiceIn *hw, int cmd, ...)
-{
- int status;
- FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
-
- switch (cmd) {
- case VOICE_ENABLE:
- status = FSOUND_Record_StartSample (fmd->fmod_sample, 1);
- if (!status) {
- fmod_logerr ("Failed to start recording\n");
- }
- break;
-
- case VOICE_DISABLE:
- status = FSOUND_Record_Stop ();
- if (!status) {
- fmod_logerr ("Failed to stop recording\n");
- }
- break;
- }
- return 0;
-}
-
-static void fmod_audio_fini (void *opaque)
-{
- (void) opaque;
- FSOUND_Close ();
-}
-
-static struct audio_option fmod_options[] = {
- {
- .name = "DRV",
- .tag = AUD_OPT_STR,
- .valp = &conf.drvname,
- .descr = "FMOD driver"
- },
- {
- .name = "FREQ",
- .tag = AUD_OPT_INT,
- .valp = &conf.freq,
- .descr = "Default frequency"
- },
- {
- .name = "SAMPLES",
- .tag = AUD_OPT_INT,
- .valp = &conf.nb_samples,
- .descr = "Buffer size in samples"
- },
- {
- .name = "CHANNELS",
- .tag = AUD_OPT_INT,
- .valp = &conf.nb_channels,
- .descr = "Number of default channels (1 - mono, 2 - stereo)"
- },
- {
- .name = "BUFSIZE",
- .tag = AUD_OPT_INT,
- .valp = &conf.bufsize,
- .descr = "(undocumented)"
- },
- { /* End of list */ }
-};
-
-static struct audio_pcm_ops fmod_pcm_ops = {
- .init_out = fmod_init_out,
- .fini_out = fmod_fini_out,
- .run_out = fmod_run_out,
- .write = fmod_write,
- .ctl_out = fmod_ctl_out,
-
- .init_in = fmod_init_in,
- .fini_in = fmod_fini_in,
- .run_in = fmod_run_in,
- .read = fmod_read,
- .ctl_in = fmod_ctl_in
-};
-
-struct audio_driver fmod_audio_driver = {
- .name = "fmod",
- .descr = "FMOD 3.xx http://www.fmod.org",
- .options = fmod_options,
- .init = fmod_audio_init,
- .fini = fmod_audio_fini,
- .pcm_ops = &fmod_pcm_ops,
- .can_be_default = 1,
- .max_voices_out = INT_MAX,
- .max_voices_in = INT_MAX,
- .voice_size_out = sizeof (FMODVoiceOut),
- .voice_size_in = sizeof (FMODVoiceIn)
-};
diff --git a/audio/noaudio.c b/audio/noaudio.c
index cb386620ae..50db1f344b 100644
--- a/audio/noaudio.c
+++ b/audio/noaudio.c
@@ -63,7 +63,7 @@ static int no_write (SWVoiceOut *sw, void *buf, int len)
return audio_pcm_sw_write (sw, buf, len);
}
-static int no_init_out (HWVoiceOut *hw, struct audsettings *as)
+static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
{
audio_pcm_init_info (&hw->info, as);
hw->samples = 1024;
@@ -82,7 +82,7 @@ static int no_ctl_out (HWVoiceOut *hw, int cmd, ...)
return 0;
}
-static int no_init_in (HWVoiceIn *hw, struct audsettings *as)
+static int no_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
{
audio_pcm_init_info (&hw->info, as);
hw->samples = 1024;
diff --git a/audio/ossaudio.c b/audio/ossaudio.c
index 4db2ca65bf..11e76a15a2 100644
--- a/audio/ossaudio.c
+++ b/audio/ossaudio.c
@@ -30,6 +30,7 @@
#include "qemu/main-loop.h"
#include "qemu/host-utils.h"
#include "audio.h"
+#include "trace.h"
#define AUDIO_CAP "oss"
#include "audio_int.h"
@@ -38,6 +39,16 @@
#define USE_DSP_POLICY
#endif
+typedef struct OSSConf {
+ int try_mmap;
+ int nfrags;
+ int fragsize;
+ const char *devpath_out;
+ const char *devpath_in;
+ int exclusive;
+ int policy;
+} OSSConf;
+
typedef struct OSSVoiceOut {
HWVoiceOut hw;
void *pcm_buf;
@@ -47,6 +58,7 @@ typedef struct OSSVoiceOut {
int fragsize;
int mmapped;
int pending;
+ OSSConf *conf;
} OSSVoiceOut;
typedef struct OSSVoiceIn {
@@ -55,28 +67,9 @@ typedef struct OSSVoiceIn {
int fd;
int nfrags;
int fragsize;
+ OSSConf *conf;
} OSSVoiceIn;
-static struct {
- int try_mmap;
- int nfrags;
- int fragsize;
- const char *devpath_out;
- const char *devpath_in;
- int debug;
- int exclusive;
- int policy;
-} conf = {
- .try_mmap = 0,
- .nfrags = 4,
- .fragsize = 4096,
- .devpath_out = "/dev/dsp",
- .devpath_in = "/dev/dsp",
- .debug = 0,
- .exclusive = 0,
- .policy = 5
-};
-
struct oss_params {
int freq;
audfmt_e fmt;
@@ -138,18 +131,18 @@ static void oss_helper_poll_in (void *opaque)
audio_run ("oss_poll_in");
}
-static int oss_poll_out (HWVoiceOut *hw)
+static void oss_poll_out (HWVoiceOut *hw)
{
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
- return qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL);
+ qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL);
}
-static int oss_poll_in (HWVoiceIn *hw)
+static void oss_poll_in (HWVoiceIn *hw)
{
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
- return qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL);
+ qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL);
}
static int oss_write (SWVoiceOut *sw, void *buf, int len)
@@ -272,18 +265,18 @@ static int oss_get_version (int fd, int *version, const char *typ)
#endif
static int oss_open (int in, struct oss_params *req,
- struct oss_params *obt, int *pfd)
+ struct oss_params *obt, int *pfd, OSSConf* conf)
{
int fd;
- int oflags = conf.exclusive ? O_EXCL : 0;
+ int oflags = conf->exclusive ? O_EXCL : 0;
audio_buf_info abinfo;
int fmt, freq, nchannels;
int setfragment = 1;
- const char *dspname = in ? conf.devpath_in : conf.devpath_out;
+ const char *dspname = in ? conf->devpath_in : conf->devpath_out;
const char *typ = in ? "ADC" : "DAC";
/* Kludge needed to have working mmap on Linux */
- oflags |= conf.try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
+ oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
fd = open (dspname, oflags | O_NONBLOCK);
if (-1 == fd) {
@@ -317,20 +310,18 @@ static int oss_open (int in, struct oss_params *req,
}
#ifdef USE_DSP_POLICY
- if (conf.policy >= 0) {
+ if (conf->policy >= 0) {
int version;
if (!oss_get_version (fd, &version, typ)) {
- if (conf.debug) {
- dolog ("OSS version = %#x\n", version);
- }
+ trace_oss_version(version);
if (version >= 0x040000) {
- int policy = conf.policy;
+ int policy = conf->policy;
if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
oss_logerr2 (errno, typ,
"Failed to set timing policy to %d\n",
- conf.policy);
+ conf->policy);
goto err;
}
setfragment = 0;
@@ -458,19 +449,12 @@ static int oss_run_out (HWVoiceOut *hw, int live)
}
if (abinfo.bytes > bufsize) {
- if (conf.debug) {
- dolog ("warning: Invalid available size, size=%d bufsize=%d\n"
- "please report your OS/audio hw to av1474@comtv.ru\n",
- abinfo.bytes, bufsize);
- }
+ trace_oss_invalid_available_size(abinfo.bytes, bufsize);
abinfo.bytes = bufsize;
}
if (abinfo.bytes < 0) {
- if (conf.debug) {
- dolog ("warning: Invalid available size, size=%d bufsize=%d\n",
- abinfo.bytes, bufsize);
- }
+ trace_oss_invalid_available_size(abinfo.bytes, bufsize);
return 0;
}
@@ -510,7 +494,8 @@ static void oss_fini_out (HWVoiceOut *hw)
}
}
-static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
+static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
+ void *drv_opaque)
{
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
struct oss_params req, obt;
@@ -519,16 +504,17 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
int fd;
audfmt_e effective_fmt;
struct audsettings obt_as;
+ OSSConf *conf = drv_opaque;
oss->fd = -1;
req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
req.freq = as->freq;
req.nchannels = as->nchannels;
- req.fragsize = conf.fragsize;
- req.nfrags = conf.nfrags;
+ req.fragsize = conf->fragsize;
+ req.nfrags = conf->nfrags;
- if (oss_open (0, &req, &obt, &fd)) {
+ if (oss_open (0, &req, &obt, &fd, conf)) {
return -1;
}
@@ -555,7 +541,7 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
oss->mmapped = 0;
- if (conf.try_mmap) {
+ if (conf->try_mmap) {
oss->pcm_buf = mmap (
NULL,
hw->samples << hw->info.shift,
@@ -615,6 +601,7 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
}
oss->fd = fd;
+ oss->conf = conf;
return 0;
}
@@ -634,7 +621,8 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
va_end (ap);
ldebug ("enabling voice\n");
- if (poll_mode && oss_poll_out (hw)) {
+ if (poll_mode) {
+ oss_poll_out (hw);
poll_mode = 0;
}
hw->poll_mode = poll_mode;
@@ -676,7 +664,7 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
return 0;
}
-static int oss_init_in (HWVoiceIn *hw, struct audsettings *as)
+static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
{
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
struct oss_params req, obt;
@@ -685,15 +673,16 @@ static int oss_init_in (HWVoiceIn *hw, struct audsettings *as)
int fd;
audfmt_e effective_fmt;
struct audsettings obt_as;
+ OSSConf *conf = drv_opaque;
oss->fd = -1;
req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
req.freq = as->freq;
req.nchannels = as->nchannels;
- req.fragsize = conf.fragsize;
- req.nfrags = conf.nfrags;
- if (oss_open (1, &req, &obt, &fd)) {
+ req.fragsize = conf->fragsize;
+ req.nfrags = conf->nfrags;
+ if (oss_open (1, &req, &obt, &fd, conf)) {
return -1;
}
@@ -727,6 +716,7 @@ static int oss_init_in (HWVoiceIn *hw, struct audsettings *as)
}
oss->fd = fd;
+ oss->conf = conf;
return 0;
}
@@ -828,7 +818,8 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
poll_mode = va_arg (ap, int);
va_end (ap);
- if (poll_mode && oss_poll_in (hw)) {
+ if (poll_mode) {
+ oss_poll_in (hw);
poll_mode = 0;
}
hw->poll_mode = poll_mode;
@@ -845,71 +836,78 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
return 0;
}
+static OSSConf glob_conf = {
+ .try_mmap = 0,
+ .nfrags = 4,
+ .fragsize = 4096,
+ .devpath_out = "/dev/dsp",
+ .devpath_in = "/dev/dsp",
+ .exclusive = 0,
+ .policy = 5
+};
+
static void *oss_audio_init (void)
{
- if (access(conf.devpath_in, R_OK | W_OK) < 0 ||
- access(conf.devpath_out, R_OK | W_OK) < 0) {
+ OSSConf *conf = g_malloc(sizeof(OSSConf));
+ *conf = glob_conf;
+
+ if (access(conf->devpath_in, R_OK | W_OK) < 0 ||
+ access(conf->devpath_out, R_OK | W_OK) < 0) {
return NULL;
}
- return &conf;
+ return conf;
}
static void oss_audio_fini (void *opaque)
{
- (void) opaque;
+ g_free(opaque);
}
static struct audio_option oss_options[] = {
{
.name = "FRAGSIZE",
.tag = AUD_OPT_INT,
- .valp = &conf.fragsize,
+ .valp = &glob_conf.fragsize,
.descr = "Fragment size in bytes"
},
{
.name = "NFRAGS",
.tag = AUD_OPT_INT,
- .valp = &conf.nfrags,
+ .valp = &glob_conf.nfrags,
.descr = "Number of fragments"
},
{
.name = "MMAP",
.tag = AUD_OPT_BOOL,
- .valp = &conf.try_mmap,
+ .valp = &glob_conf.try_mmap,
.descr = "Try using memory mapped access"
},
{
.name = "DAC_DEV",
.tag = AUD_OPT_STR,
- .valp = &conf.devpath_out,
+ .valp = &glob_conf.devpath_out,
.descr = "Path to DAC device"
},
{
.name = "ADC_DEV",
.tag = AUD_OPT_STR,
- .valp = &conf.devpath_in,
+ .valp = &glob_conf.devpath_in,
.descr = "Path to ADC device"
},
{
.name = "EXCLUSIVE",
.tag = AUD_OPT_BOOL,
- .valp = &conf.exclusive,
+ .valp = &glob_conf.exclusive,
.descr = "Open device in exclusive mode (vmix wont work)"
},
#ifdef USE_DSP_POLICY
{
.name = "POLICY",
.tag = AUD_OPT_INT,
- .valp = &conf.policy,
+ .valp = &glob_conf.policy,
.descr = "Set the timing policy of the device, -1 to use fragment mode",
},
#endif
- {
- .name = "DEBUG",
- .tag = AUD_OPT_BOOL,
- .valp = &conf.debug,
- .descr = "Turn on some debugging messages"
- },
{ /* End of list */ }
};
diff --git a/audio/paaudio.c b/audio/paaudio.c
index 90ff24500b..fea607166f 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -9,6 +9,19 @@
#include "audio_pt_int.h"
typedef struct {
+ int samples;
+ char *server;
+ char *sink;
+ char *source;
+} PAConf;
+
+typedef struct {
+ PAConf conf;
+ pa_threaded_mainloop *mainloop;
+ pa_context *context;
+} paaudio;
+
+typedef struct {
HWVoiceOut hw;
int done;
int live;
@@ -17,6 +30,7 @@ typedef struct {
pa_stream *stream;
void *pcm_buf;
struct audio_pt pt;
+ paaudio *g;
} PAVoiceOut;
typedef struct {
@@ -30,20 +44,10 @@ typedef struct {
struct audio_pt pt;
const void *read_data;
size_t read_index, read_length;
+ paaudio *g;
} PAVoiceIn;
-typedef struct {
- int samples;
- char *server;
- char *sink;
- char *source;
- pa_threaded_mainloop *mainloop;
- pa_context *context;
-} paaudio;
-
-static paaudio glob_paaudio = {
- .samples = 4096,
-};
+static void qpa_audio_fini(void *opaque);
static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
{
@@ -106,7 +110,7 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror)
{
- paaudio *g = &glob_paaudio;
+ paaudio *g = p->g;
pa_threaded_mainloop_lock (g->mainloop);
@@ -160,7 +164,7 @@ unlock_and_fail:
static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror)
{
- paaudio *g = &glob_paaudio;
+ paaudio *g = p->g;
pa_threaded_mainloop_lock (g->mainloop);
@@ -222,7 +226,7 @@ static void *qpa_thread_out (void *arg)
}
}
- decr = to_mix = audio_MIN (pa->live, glob_paaudio.samples >> 2);
+ decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2);
rpos = pa->rpos;
if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
@@ -314,7 +318,7 @@ static void *qpa_thread_in (void *arg)
}
}
- incr = to_grab = audio_MIN (pa->dead, glob_paaudio.samples >> 2);
+ incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2);
wpos = pa->wpos;
if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
@@ -430,7 +434,7 @@ static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
static void context_state_cb (pa_context *c, void *userdata)
{
- paaudio *g = &glob_paaudio;
+ paaudio *g = userdata;
switch (pa_context_get_state(c)) {
case PA_CONTEXT_READY:
@@ -449,7 +453,7 @@ static void context_state_cb (pa_context *c, void *userdata)
static void stream_state_cb (pa_stream *s, void * userdata)
{
- paaudio *g = &glob_paaudio;
+ paaudio *g = userdata;
switch (pa_stream_get_state (s)) {
@@ -467,23 +471,21 @@ static void stream_state_cb (pa_stream *s, void * userdata)
static void stream_request_cb (pa_stream *s, size_t length, void *userdata)
{
- paaudio *g = &glob_paaudio;
+ paaudio *g = userdata;
pa_threaded_mainloop_signal (g->mainloop, 0);
}
static pa_stream *qpa_simple_new (
- const char *server,
+ paaudio *g,
const char *name,
pa_stream_direction_t dir,
const char *dev,
- const char *stream_name,
const pa_sample_spec *ss,
const pa_channel_map *map,
const pa_buffer_attr *attr,
int *rerror)
{
- paaudio *g = &glob_paaudio;
int r;
pa_stream *stream;
@@ -534,13 +536,15 @@ fail:
return NULL;
}
-static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
+static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
+ void *drv_opaque)
{
int error;
- static pa_sample_spec ss;
- static pa_buffer_attr ba;
+ pa_sample_spec ss;
+ pa_buffer_attr ba;
struct audsettings obt_as = *as;
PAVoiceOut *pa = (PAVoiceOut *) hw;
+ paaudio *g = pa->g = drv_opaque;
ss.format = audfmt_to_pa (as->fmt, as->endianness);
ss.channels = as->nchannels;
@@ -558,11 +562,10 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
pa->stream = qpa_simple_new (
- glob_paaudio.server,
+ g,
"qemu",
PA_STREAM_PLAYBACK,
- glob_paaudio.sink,
- "pcm.playback",
+ g->conf.sink,
&ss,
NULL, /* channel map */
&ba, /* buffering attributes */
@@ -574,7 +577,7 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
}
audio_pcm_init_info (&hw->info, &obt_as);
- hw->samples = glob_paaudio.samples;
+ hw->samples = g->conf.samples;
pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
pa->rpos = hw->rpos;
if (!pa->pcm_buf) {
@@ -601,12 +604,13 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
return -1;
}
-static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
+static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
{
int error;
- static pa_sample_spec ss;
+ pa_sample_spec ss;
struct audsettings obt_as = *as;
PAVoiceIn *pa = (PAVoiceIn *) hw;
+ paaudio *g = pa->g = drv_opaque;
ss.format = audfmt_to_pa (as->fmt, as->endianness);
ss.channels = as->nchannels;
@@ -615,11 +619,10 @@ static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
pa->stream = qpa_simple_new (
- glob_paaudio.server,
+ g,
"qemu",
PA_STREAM_RECORD,
- glob_paaudio.source,
- "pcm.capture",
+ g->conf.source,
&ss,
NULL, /* channel map */
NULL, /* buffering attributes */
@@ -631,7 +634,7 @@ static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
}
audio_pcm_init_info (&hw->info, &obt_as);
- hw->samples = glob_paaudio.samples;
+ hw->samples = g->conf.samples;
pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
pa->wpos = hw->wpos;
if (!pa->pcm_buf) {
@@ -703,7 +706,7 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
PAVoiceOut *pa = (PAVoiceOut *) hw;
pa_operation *op;
pa_cvolume v;
- paaudio *g = &glob_paaudio;
+ paaudio *g = pa->g;
#ifdef PA_CHECK_VERSION /* macro is present in 0.9.16+ */
pa_cvolume_init (&v); /* function is present in 0.9.13+ */
@@ -755,7 +758,7 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
PAVoiceIn *pa = (PAVoiceIn *) hw;
pa_operation *op;
pa_cvolume v;
- paaudio *g = &glob_paaudio;
+ paaudio *g = pa->g;
#ifdef PA_CHECK_VERSION
pa_cvolume_init (&v);
@@ -805,23 +808,31 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
}
/* common */
+static PAConf glob_conf = {
+ .samples = 4096,
+};
+
static void *qpa_audio_init (void)
{
- paaudio *g = &glob_paaudio;
+ paaudio *g = g_malloc(sizeof(paaudio));
+ g->conf = glob_conf;
+ g->mainloop = NULL;
+ g->context = NULL;
g->mainloop = pa_threaded_mainloop_new ();
if (!g->mainloop) {
goto fail;
}
- g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), glob_paaudio.server);
+ g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop),
+ g->conf.server);
if (!g->context) {
goto fail;
}
pa_context_set_state_callback (g->context, context_state_cb, g);
- if (pa_context_connect (g->context, glob_paaudio.server, 0, NULL) < 0) {
+ if (pa_context_connect (g->context, g->conf.server, 0, NULL) < 0) {
qpa_logerr (pa_context_errno (g->context),
"pa_context_connect() failed\n");
goto fail;
@@ -854,12 +865,13 @@ static void *qpa_audio_init (void)
pa_threaded_mainloop_unlock (g->mainloop);
- return &glob_paaudio;
+ return g;
unlock_and_fail:
pa_threaded_mainloop_unlock (g->mainloop);
fail:
AUD_log (AUDIO_CAP, "Failed to initialize PA context");
+ qpa_audio_fini(g);
return NULL;
}
@@ -874,39 +886,38 @@ static void qpa_audio_fini (void *opaque)
if (g->context) {
pa_context_disconnect (g->context);
pa_context_unref (g->context);
- g->context = NULL;
}
if (g->mainloop) {
pa_threaded_mainloop_free (g->mainloop);
}
- g->mainloop = NULL;
+ g_free(g);
}
struct audio_option qpa_options[] = {
{
.name = "SAMPLES",
.tag = AUD_OPT_INT,
- .valp = &glob_paaudio.samples,
+ .valp = &glob_conf.samples,
.descr = "buffer size in samples"
},
{
.name = "SERVER",
.tag = AUD_OPT_STR,
- .valp = &glob_paaudio.server,
+ .valp = &glob_conf.server,
.descr = "server address"
},
{
.name = "SINK",
.tag = AUD_OPT_STR,
- .valp = &glob_paaudio.sink,
+ .valp = &glob_conf.sink,
.descr = "sink device name"
},
{
.name = "SOURCE",
.tag = AUD_OPT_STR,
- .valp = &glob_paaudio.source,
+ .valp = &glob_conf.source,
.descr = "source device name"
},
{ /* End of list */ }
diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c
index d24daa5ead..1140f2ea0a 100644
--- a/audio/sdlaudio.c
+++ b/audio/sdlaudio.c
@@ -55,6 +55,7 @@ static struct SDLAudioState {
SDL_mutex *mutex;
SDL_sem *sem;
int initialized;
+ bool driver_created;
} glob_sdl;
typedef struct SDLAudioState SDLAudioState;
@@ -332,7 +333,8 @@ static void sdl_fini_out (HWVoiceOut *hw)
sdl_close (&glob_sdl);
}
-static int sdl_init_out (HWVoiceOut *hw, struct audsettings *as)
+static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
+ void *drv_opaque)
{
SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
SDLAudioState *s = &glob_sdl;
@@ -392,6 +394,10 @@ static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
static void *sdl_audio_init (void)
{
SDLAudioState *s = &glob_sdl;
+ if (s->driver_created) {
+ sdl_logerr("Can't create multiple sdl backends\n");
+ return NULL;
+ }
if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
sdl_logerr ("SDL failed to initialize audio subsystem\n");
@@ -413,6 +419,7 @@ static void *sdl_audio_init (void)
return NULL;
}
+ s->driver_created = true;
return s;
}
@@ -423,6 +430,7 @@ static void sdl_audio_fini (void *opaque)
SDL_DestroySemaphore (s->sem);
SDL_DestroyMutex (s->mutex);
SDL_QuitSubSystem (SDL_INIT_AUDIO);
+ s->driver_created = false;
}
static struct audio_option sdl_options[] = {
diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c
index 7b79bedca2..5c6f726757 100644
--- a/audio/spiceaudio.c
+++ b/audio/spiceaudio.c
@@ -115,7 +115,8 @@ static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate)
/* playback */
-static int line_out_init (HWVoiceOut *hw, struct audsettings *as)
+static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
+ void *drv_opaque)
{
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
struct audsettings settings;
@@ -243,7 +244,7 @@ static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
/* record */
-static int line_in_init (HWVoiceIn *hw, struct audsettings *as)
+static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
{
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
struct audsettings settings;
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
index 6846a1a9f7..c586020c59 100644
--- a/audio/wavaudio.c
+++ b/audio/wavaudio.c
@@ -36,15 +36,10 @@ typedef struct WAVVoiceOut {
int total_samples;
} WAVVoiceOut;
-static struct {
+typedef struct {
struct audsettings settings;
const char *wav_path;
-} conf = {
- .settings.freq = 44100,
- .settings.nchannels = 2,
- .settings.fmt = AUD_FMT_S16,
- .wav_path = "qemu.wav"
-};
+} WAVConf;
static int wav_run_out (HWVoiceOut *hw, int live)
{
@@ -105,7 +100,8 @@ static void le_store (uint8_t *buf, uint32_t val, int len)
}
}
-static int wav_init_out (HWVoiceOut *hw, struct audsettings *as)
+static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
+ void *drv_opaque)
{
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
int bits16 = 0, stereo = 0;
@@ -115,9 +111,8 @@ static int wav_init_out (HWVoiceOut *hw, struct audsettings *as)
0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
};
- struct audsettings wav_as = conf.settings;
-
- (void) as;
+ WAVConf *conf = drv_opaque;
+ struct audsettings wav_as = conf->settings;
stereo = wav_as.nchannels == 2;
switch (wav_as.fmt) {
@@ -155,10 +150,10 @@ static int wav_init_out (HWVoiceOut *hw, struct audsettings *as)
le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
le_store (hdr + 32, 1 << (bits16 + stereo), 2);
- wav->f = fopen (conf.wav_path, "wb");
+ wav->f = fopen (conf->wav_path, "wb");
if (!wav->f) {
dolog ("Failed to open wave file `%s'\nReason: %s\n",
- conf.wav_path, strerror (errno));
+ conf->wav_path, strerror (errno));
g_free (wav->pcm_buf);
wav->pcm_buf = NULL;
return -1;
@@ -226,40 +221,49 @@ static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
return 0;
}
+static WAVConf glob_conf = {
+ .settings.freq = 44100,
+ .settings.nchannels = 2,
+ .settings.fmt = AUD_FMT_S16,
+ .wav_path = "qemu.wav"
+};
+
static void *wav_audio_init (void)
{
- return &conf;
+ WAVConf *conf = g_malloc(sizeof(WAVConf));
+ *conf = glob_conf;
+ return conf;
}
static void wav_audio_fini (void *opaque)
{
- (void) opaque;
ldebug ("wav_fini");
+ g_free(opaque);
}
static struct audio_option wav_options[] = {
{
.name = "FREQUENCY",
.tag = AUD_OPT_INT,
- .valp = &conf.settings.freq,
+ .valp = &glob_conf.settings.freq,
.descr = "Frequency"
},
{
.name = "FORMAT",
.tag = AUD_OPT_FMT,
- .valp = &conf.settings.fmt,
+ .valp = &glob_conf.settings.fmt,
.descr = "Format"
},
{
.name = "DAC_FIXED_CHANNELS",
.tag = AUD_OPT_INT,
- .valp = &conf.settings.nchannels,
+ .valp = &glob_conf.settings.nchannels,
.descr = "Number of channels (1 - mono, 2 - stereo)"
},
{
.name = "PATH",
.tag = AUD_OPT_STR,
- .valp = &conf.wav_path,
+ .valp = &glob_conf.wav_path,
.descr = "Path to wave file"
},
{ /* End of list */ }
diff --git a/audio/winwaveaudio.c b/audio/winwaveaudio.c
deleted file mode 100644
index 8dbd145ca1..0000000000
--- a/audio/winwaveaudio.c
+++ /dev/null
@@ -1,717 +0,0 @@
-/* public domain */
-
-#include "qemu-common.h"
-#include "sysemu/sysemu.h"
-#include "audio.h"
-
-#define AUDIO_CAP "winwave"
-#include "audio_int.h"
-
-#include <windows.h>
-#include <mmsystem.h>
-
-#include "audio_win_int.h"
-
-static struct {
- int dac_headers;
- int dac_samples;
- int adc_headers;
- int adc_samples;
-} conf = {
- .dac_headers = 4,
- .dac_samples = 1024,
- .adc_headers = 4,
- .adc_samples = 1024
-};
-
-typedef struct {
- HWVoiceOut hw;
- HWAVEOUT hwo;
- WAVEHDR *hdrs;
- HANDLE event;
- void *pcm_buf;
- int avail;
- int pending;
- int curhdr;
- int paused;
- CRITICAL_SECTION crit_sect;
-} WaveVoiceOut;
-
-typedef struct {
- HWVoiceIn hw;
- HWAVEIN hwi;
- WAVEHDR *hdrs;
- HANDLE event;
- void *pcm_buf;
- int curhdr;
- int paused;
- int rpos;
- int avail;
- CRITICAL_SECTION crit_sect;
-} WaveVoiceIn;
-
-static void winwave_log_mmresult (MMRESULT mr)
-{
- const char *str = "BUG";
-
- switch (mr) {
- case MMSYSERR_NOERROR:
- str = "Success";
- break;
-
- case MMSYSERR_INVALHANDLE:
- str = "Specified device handle is invalid";
- break;
-
- case MMSYSERR_BADDEVICEID:
- str = "Specified device id is out of range";
- break;
-
- case MMSYSERR_NODRIVER:
- str = "No device driver is present";
- break;
-
- case MMSYSERR_NOMEM:
- str = "Unable to allocate or lock memory";
- break;
-
- case WAVERR_SYNC:
- str = "Device is synchronous but waveOutOpen was called "
- "without using the WINWAVE_ALLOWSYNC flag";
- break;
-
- case WAVERR_UNPREPARED:
- str = "The data block pointed to by the pwh parameter "
- "hasn't been prepared";
- break;
-
- case WAVERR_STILLPLAYING:
- str = "There are still buffers in the queue";
- break;
-
- default:
- dolog ("Reason: Unknown (MMRESULT %#x)\n", mr);
- return;
- }
-
- dolog ("Reason: %s\n", str);
-}
-
-static void GCC_FMT_ATTR (2, 3) winwave_logerr (
- MMRESULT mr,
- const char *fmt,
- ...
- )
-{
- va_list ap;
-
- va_start (ap, fmt);
- AUD_vlog (AUDIO_CAP, fmt, ap);
- va_end (ap);
-
- AUD_log (NULL, " failed\n");
- winwave_log_mmresult (mr);
-}
-
-static void winwave_anal_close_out (WaveVoiceOut *wave)
-{
- MMRESULT mr;
-
- mr = waveOutClose (wave->hwo);
- if (mr != MMSYSERR_NOERROR) {
- winwave_logerr (mr, "waveOutClose");
- }
- wave->hwo = NULL;
-}
-
-static void CALLBACK winwave_callback_out (
- HWAVEOUT hwo,
- UINT msg,
- DWORD_PTR dwInstance,
- DWORD_PTR dwParam1,
- DWORD_PTR dwParam2
- )
-{
- WaveVoiceOut *wave = (WaveVoiceOut *) dwInstance;
-
- switch (msg) {
- case WOM_DONE:
- {
- WAVEHDR *h = (WAVEHDR *) dwParam1;
- if (!h->dwUser) {
- h->dwUser = 1;
- EnterCriticalSection (&wave->crit_sect);
- {
- wave->avail += conf.dac_samples;
- }
- LeaveCriticalSection (&wave->crit_sect);
- if (wave->hw.poll_mode) {
- if (!SetEvent (wave->event)) {
- dolog ("DAC SetEvent failed %lx\n", GetLastError ());
- }
- }
- }
- }
- break;
-
- case WOM_CLOSE:
- case WOM_OPEN:
- break;
-
- default:
- dolog ("unknown wave out callback msg %x\n", msg);
- }
-}
-
-static int winwave_init_out (HWVoiceOut *hw, struct audsettings *as)
-{
- int i;
- int err;
- MMRESULT mr;
- WAVEFORMATEX wfx;
- WaveVoiceOut *wave;
-
- wave = (WaveVoiceOut *) hw;
-
- InitializeCriticalSection (&wave->crit_sect);
-
- err = waveformat_from_audio_settings (&wfx, as);
- if (err) {
- goto err0;
- }
-
- mr = waveOutOpen (&wave->hwo, WAVE_MAPPER, &wfx,
- (DWORD_PTR) winwave_callback_out,
- (DWORD_PTR) wave, CALLBACK_FUNCTION);
- if (mr != MMSYSERR_NOERROR) {
- winwave_logerr (mr, "waveOutOpen");
- goto err1;
- }
-
- wave->hdrs = audio_calloc (AUDIO_FUNC, conf.dac_headers,
- sizeof (*wave->hdrs));
- if (!wave->hdrs) {
- goto err2;
- }
-
- audio_pcm_init_info (&hw->info, as);
- hw->samples = conf.dac_samples * conf.dac_headers;
- wave->avail = hw->samples;
-
- wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.dac_samples,
- conf.dac_headers << hw->info.shift);
- if (!wave->pcm_buf) {
- goto err3;
- }
-
- for (i = 0; i < conf.dac_headers; ++i) {
- WAVEHDR *h = &wave->hdrs[i];
-
- h->dwUser = 0;
- h->dwBufferLength = conf.dac_samples << hw->info.shift;
- h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength);
- h->dwFlags = 0;
-
- mr = waveOutPrepareHeader (wave->hwo, h, sizeof (*h));
- if (mr != MMSYSERR_NOERROR) {
- winwave_logerr (mr, "waveOutPrepareHeader(%d)", i);
- goto err4;
- }
- }
-
- return 0;
-
- err4:
- g_free (wave->pcm_buf);
- err3:
- g_free (wave->hdrs);
- err2:
- winwave_anal_close_out (wave);
- err1:
- err0:
- return -1;
-}
-
-static int winwave_write (SWVoiceOut *sw, void *buf, int len)
-{
- return audio_pcm_sw_write (sw, buf, len);
-}
-
-static int winwave_run_out (HWVoiceOut *hw, int live)
-{
- WaveVoiceOut *wave = (WaveVoiceOut *) hw;
- int decr;
- int doreset;
-
- EnterCriticalSection (&wave->crit_sect);
- {
- decr = audio_MIN (live, wave->avail);
- decr = audio_pcm_hw_clip_out (hw, wave->pcm_buf, decr, wave->pending);
- wave->pending += decr;
- wave->avail -= decr;
- }
- LeaveCriticalSection (&wave->crit_sect);
-
- doreset = hw->poll_mode && (wave->pending >= conf.dac_samples);
- if (doreset && !ResetEvent (wave->event)) {
- dolog ("DAC ResetEvent failed %lx\n", GetLastError ());
- }
-
- while (wave->pending >= conf.dac_samples) {
- MMRESULT mr;
- WAVEHDR *h = &wave->hdrs[wave->curhdr];
-
- h->dwUser = 0;
- mr = waveOutWrite (wave->hwo, h, sizeof (*h));
- if (mr != MMSYSERR_NOERROR) {
- winwave_logerr (mr, "waveOutWrite(%d)", wave->curhdr);
- break;
- }
-
- wave->pending -= conf.dac_samples;
- wave->curhdr = (wave->curhdr + 1) % conf.dac_headers;
- }
-
- return decr;
-}
-
-static void winwave_poll (void *opaque)
-{
- (void) opaque;
- audio_run ("winwave_poll");
-}
-
-static void winwave_fini_out (HWVoiceOut *hw)
-{
- int i;
- MMRESULT mr;
- WaveVoiceOut *wave = (WaveVoiceOut *) hw;
-
- mr = waveOutReset (wave->hwo);
- if (mr != MMSYSERR_NOERROR) {
- winwave_logerr (mr, "waveOutReset");
- }
-
- for (i = 0; i < conf.dac_headers; ++i) {
- mr = waveOutUnprepareHeader (wave->hwo, &wave->hdrs[i],
- sizeof (wave->hdrs[i]));
- if (mr != MMSYSERR_NOERROR) {
- winwave_logerr (mr, "waveOutUnprepareHeader(%d)", i);
- }
- }
-
- winwave_anal_close_out (wave);
-
- if (wave->event) {
- qemu_del_wait_object (wave->event, winwave_poll, wave);
- if (!CloseHandle (wave->event)) {
- dolog ("DAC CloseHandle failed %lx\n", GetLastError ());
- }
- wave->event = NULL;
- }
-
- g_free (wave->pcm_buf);
- wave->pcm_buf = NULL;
-
- g_free (wave->hdrs);
- wave->hdrs = NULL;
-}
-
-static int winwave_ctl_out (HWVoiceOut *hw, int cmd, ...)
-{
- MMRESULT mr;
- WaveVoiceOut *wave = (WaveVoiceOut *) hw;
-
- switch (cmd) {
- case VOICE_ENABLE:
- {
- va_list ap;
- int poll_mode;
-
- va_start (ap, cmd);
- poll_mode = va_arg (ap, int);
- va_end (ap);
-
- if (poll_mode && !wave->event) {
- wave->event = CreateEvent (NULL, TRUE, TRUE, NULL);
- if (!wave->event) {
- dolog ("DAC CreateEvent: %lx, poll mode will be disabled\n",
- GetLastError ());
- }
- }
-
- if (wave->event) {
- int ret;
-
- ret = qemu_add_wait_object (wave->event, winwave_poll, wave);
- hw->poll_mode = (ret == 0);
- }
- else {
- hw->poll_mode = 0;
- }
- wave->paused = 0;
- }
- return 0;
-
- case VOICE_DISABLE:
- if (!wave->paused) {
- mr = waveOutReset (wave->hwo);
- if (mr != MMSYSERR_NOERROR) {
- winwave_logerr (mr, "waveOutReset");
- }
- else {
- wave->paused = 1;
- }
- }
- if (wave->event) {
- qemu_del_wait_object (wave->event, winwave_poll, wave);
- }
- return 0;
- }
- return -1;
-}
-
-static void winwave_anal_close_in (WaveVoiceIn *wave)
-{
- MMRESULT mr;
-
- mr = waveInClose (wave->hwi);
- if (mr != MMSYSERR_NOERROR) {
- winwave_logerr (mr, "waveInClose");
- }
- wave->hwi = NULL;
-}
-
-static void CALLBACK winwave_callback_in (
- HWAVEIN *hwi,
- UINT msg,
- DWORD_PTR dwInstance,
- DWORD_PTR dwParam1,
- DWORD_PTR dwParam2
- )
-{
- WaveVoiceIn *wave = (WaveVoiceIn *) dwInstance;
-
- switch (msg) {
- case WIM_DATA:
- {
- WAVEHDR *h = (WAVEHDR *) dwParam1;
- if (!h->dwUser) {
- h->dwUser = 1;
- EnterCriticalSection (&wave->crit_sect);
- {
- wave->avail += conf.adc_samples;
- }
- LeaveCriticalSection (&wave->crit_sect);
- if (wave->hw.poll_mode) {
- if (!SetEvent (wave->event)) {
- dolog ("ADC SetEvent failed %lx\n", GetLastError ());
- }
- }
- }
- }
- break;
-
- case WIM_CLOSE:
- case WIM_OPEN:
- break;
-
- default:
- dolog ("unknown wave in callback msg %x\n", msg);
- }
-}
-
-static void winwave_add_buffers (WaveVoiceIn *wave, int samples)
-{
- int doreset;
-
- doreset = wave->hw.poll_mode && (samples >= conf.adc_samples);
- if (doreset && !ResetEvent (wave->event)) {
- dolog ("ADC ResetEvent failed %lx\n", GetLastError ());
- }
-
- while (samples >= conf.adc_samples) {
- MMRESULT mr;
- WAVEHDR *h = &wave->hdrs[wave->curhdr];
-
- h->dwUser = 0;
- mr = waveInAddBuffer (wave->hwi, h, sizeof (*h));
- if (mr != MMSYSERR_NOERROR) {
- winwave_logerr (mr, "waveInAddBuffer(%d)", wave->curhdr);
- }
- wave->curhdr = (wave->curhdr + 1) % conf.adc_headers;
- samples -= conf.adc_samples;
- }
-}
-
-static int winwave_init_in (HWVoiceIn *hw, struct audsettings *as)
-{
- int i;
- int err;
- MMRESULT mr;
- WAVEFORMATEX wfx;
- WaveVoiceIn *wave;
-
- wave = (WaveVoiceIn *) hw;
-
- InitializeCriticalSection (&wave->crit_sect);
-
- err = waveformat_from_audio_settings (&wfx, as);
- if (err) {
- goto err0;
- }
-
- mr = waveInOpen (&wave->hwi, WAVE_MAPPER, &wfx,
- (DWORD_PTR) winwave_callback_in,
- (DWORD_PTR) wave, CALLBACK_FUNCTION);
- if (mr != MMSYSERR_NOERROR) {
- winwave_logerr (mr, "waveInOpen");
- goto err1;
- }
-
- wave->hdrs = audio_calloc (AUDIO_FUNC, conf.dac_headers,
- sizeof (*wave->hdrs));
- if (!wave->hdrs) {
- goto err2;
- }
-
- audio_pcm_init_info (&hw->info, as);
- hw->samples = conf.adc_samples * conf.adc_headers;
- wave->avail = 0;
-
- wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.adc_samples,
- conf.adc_headers << hw->info.shift);
- if (!wave->pcm_buf) {
- goto err3;
- }
-
- for (i = 0; i < conf.adc_headers; ++i) {
- WAVEHDR *h = &wave->hdrs[i];
-
- h->dwUser = 0;
- h->dwBufferLength = conf.adc_samples << hw->info.shift;
- h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength);
- h->dwFlags = 0;
-
- mr = waveInPrepareHeader (wave->hwi, h, sizeof (*h));
- if (mr != MMSYSERR_NOERROR) {
- winwave_logerr (mr, "waveInPrepareHeader(%d)", i);
- goto err4;
- }
- }
-
- wave->paused = 1;
- winwave_add_buffers (wave, hw->samples);
- return 0;
-
- err4:
- g_free (wave->pcm_buf);
- err3:
- g_free (wave->hdrs);
- err2:
- winwave_anal_close_in (wave);
- err1:
- err0:
- return -1;
-}
-
-static void winwave_fini_in (HWVoiceIn *hw)
-{
- int i;
- MMRESULT mr;
- WaveVoiceIn *wave = (WaveVoiceIn *) hw;
-
- mr = waveInReset (wave->hwi);
- if (mr != MMSYSERR_NOERROR) {
- winwave_logerr (mr, "waveInReset");
- }
-
- for (i = 0; i < conf.adc_headers; ++i) {
- mr = waveInUnprepareHeader (wave->hwi, &wave->hdrs[i],
- sizeof (wave->hdrs[i]));
- if (mr != MMSYSERR_NOERROR) {
- winwave_logerr (mr, "waveInUnprepareHeader(%d)", i);
- }
- }
-
- winwave_anal_close_in (wave);
-
- if (wave->event) {
- qemu_del_wait_object (wave->event, winwave_poll, wave);
- if (!CloseHandle (wave->event)) {
- dolog ("ADC CloseHandle failed %lx\n", GetLastError ());
- }
- wave->event = NULL;
- }
-
- g_free (wave->pcm_buf);
- wave->pcm_buf = NULL;
-
- g_free (wave->hdrs);
- wave->hdrs = NULL;
-}
-
-static int winwave_run_in (HWVoiceIn *hw)
-{
- WaveVoiceIn *wave = (WaveVoiceIn *) hw;
- int live = audio_pcm_hw_get_live_in (hw);
- int dead = hw->samples - live;
- int decr, ret;
-
- if (!dead) {
- return 0;
- }
-
- EnterCriticalSection (&wave->crit_sect);
- {
- decr = audio_MIN (dead, wave->avail);
- wave->avail -= decr;
- }
- LeaveCriticalSection (&wave->crit_sect);
-
- ret = decr;
- while (decr) {
- int left = hw->samples - hw->wpos;
- int conv = audio_MIN (left, decr);
- hw->conv (hw->conv_buf + hw->wpos,
- advance (wave->pcm_buf, wave->rpos << hw->info.shift),
- conv);
-
- wave->rpos = (wave->rpos + conv) % hw->samples;
- hw->wpos = (hw->wpos + conv) % hw->samples;
- decr -= conv;
- }
-
- winwave_add_buffers (wave, ret);
- return ret;
-}
-
-static int winwave_read (SWVoiceIn *sw, void *buf, int size)
-{
- return audio_pcm_sw_read (sw, buf, size);
-}
-
-static int winwave_ctl_in (HWVoiceIn *hw, int cmd, ...)
-{
- MMRESULT mr;
- WaveVoiceIn *wave = (WaveVoiceIn *) hw;
-
- switch (cmd) {
- case VOICE_ENABLE:
- {
- va_list ap;
- int poll_mode;
-
- va_start (ap, cmd);
- poll_mode = va_arg (ap, int);
- va_end (ap);
-
- if (poll_mode && !wave->event) {
- wave->event = CreateEvent (NULL, TRUE, TRUE, NULL);
- if (!wave->event) {
- dolog ("ADC CreateEvent: %lx, poll mode will be disabled\n",
- GetLastError ());
- }
- }
-
- if (wave->event) {
- int ret;
-
- ret = qemu_add_wait_object (wave->event, winwave_poll, wave);
- hw->poll_mode = (ret == 0);
- }
- else {
- hw->poll_mode = 0;
- }
- if (wave->paused) {
- mr = waveInStart (wave->hwi);
- if (mr != MMSYSERR_NOERROR) {
- winwave_logerr (mr, "waveInStart");
- }
- wave->paused = 0;
- }
- }
- return 0;
-
- case VOICE_DISABLE:
- if (!wave->paused) {
- mr = waveInStop (wave->hwi);
- if (mr != MMSYSERR_NOERROR) {
- winwave_logerr (mr, "waveInStop");
- }
- else {
- wave->paused = 1;
- }
- }
- if (wave->event) {
- qemu_del_wait_object (wave->event, winwave_poll, wave);
- }
- return 0;
- }
- return 0;
-}
-
-static void *winwave_audio_init (void)
-{
- return &conf;
-}
-
-static void winwave_audio_fini (void *opaque)
-{
- (void) opaque;
-}
-
-static struct audio_option winwave_options[] = {
- {
- .name = "DAC_HEADERS",
- .tag = AUD_OPT_INT,
- .valp = &conf.dac_headers,
- .descr = "DAC number of headers",
- },
- {
- .name = "DAC_SAMPLES",
- .tag = AUD_OPT_INT,
- .valp = &conf.dac_samples,
- .descr = "DAC number of samples per header",
- },
- {
- .name = "ADC_HEADERS",
- .tag = AUD_OPT_INT,
- .valp = &conf.adc_headers,
- .descr = "ADC number of headers",
- },
- {
- .name = "ADC_SAMPLES",
- .tag = AUD_OPT_INT,
- .valp = &conf.adc_samples,
- .descr = "ADC number of samples per header",
- },
- { /* End of list */ }
-};
-
-static struct audio_pcm_ops winwave_pcm_ops = {
- .init_out = winwave_init_out,
- .fini_out = winwave_fini_out,
- .run_out = winwave_run_out,
- .write = winwave_write,
- .ctl_out = winwave_ctl_out,
- .init_in = winwave_init_in,
- .fini_in = winwave_fini_in,
- .run_in = winwave_run_in,
- .read = winwave_read,
- .ctl_in = winwave_ctl_in
-};
-
-struct audio_driver winwave_audio_driver = {
- .name = "winwave",
- .descr = "Windows Waveform Audio http://msdn.microsoft.com",
- .options = winwave_options,
- .init = winwave_audio_init,
- .fini = winwave_audio_fini,
- .pcm_ops = &winwave_pcm_ops,
- .can_be_default = 1,
- .max_voices_out = INT_MAX,
- .max_voices_in = INT_MAX,
- .voice_size_out = sizeof (WaveVoiceOut),
- .voice_size_in = sizeof (WaveVoiceIn)
-};
diff --git a/block.c b/block.c
index 2b9ceae02f..dd4f58d6cb 100644
--- a/block.c
+++ b/block.c
@@ -36,6 +36,7 @@
#include "qmp-commands.h"
#include "qemu/timer.h"
#include "qapi-event.h"
+#include "block/throttle-groups.h"
#ifdef CONFIG_BSD
#include <sys/types.h>
@@ -79,6 +80,12 @@ static QTAILQ_HEAD(, BlockDriverState) graph_bdrv_states =
static QLIST_HEAD(, BlockDriver) bdrv_drivers =
QLIST_HEAD_INITIALIZER(bdrv_drivers);
+static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
+ const char *reference, QDict *options, int flags,
+ BlockDriverState *parent,
+ const BdrvChildRole *child_role,
+ BlockDriver *drv, Error **errp);
+
static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs);
/* If non-zero, use only whitelisted block drivers */
static int use_bdrv_whitelist;
@@ -682,8 +689,8 @@ static int bdrv_temp_snapshot_flags(int flags)
}
/*
- * Returns the flags that bs->file should get, based on the given flags for
- * the parent BDS
+ * Returns the flags that bs->file should get if a protocol driver is expected,
+ * based on the given flags for the parent BDS
*/
static int bdrv_inherited_flags(int flags)
{
@@ -700,6 +707,25 @@ static int bdrv_inherited_flags(int flags)
return flags;
}
+const BdrvChildRole child_file = {
+ .inherit_flags = bdrv_inherited_flags,
+};
+
+/*
+ * Returns the flags that bs->file should get if the use of formats (and not
+ * only protocols) is permitted for it, based on the given flags for the parent
+ * BDS
+ */
+static int bdrv_inherited_fmt_flags(int parent_flags)
+{
+ int flags = child_file.inherit_flags(parent_flags);
+ return flags & ~BDRV_O_PROTOCOL;
+}
+
+const BdrvChildRole child_format = {
+ .inherit_flags = bdrv_inherited_fmt_flags,
+};
+
/*
* Returns the flags that bs->backing_hd should get, based on the given flags
* for the parent BDS
@@ -715,6 +741,10 @@ static int bdrv_backing_flags(int flags)
return flags;
}
+static const BdrvChildRole child_backing = {
+ .inherit_flags = bdrv_backing_flags,
+};
+
static int bdrv_open_flags(BlockDriverState *bs, int flags)
{
int open_flags = flags | BDRV_O_CACHE_WB;
@@ -767,6 +797,19 @@ static void bdrv_assign_node_name(BlockDriverState *bs,
QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs, node_list);
}
+static QemuOptsList bdrv_runtime_opts = {
+ .name = "bdrv_common",
+ .head = QTAILQ_HEAD_INITIALIZER(bdrv_runtime_opts.head),
+ .desc = {
+ {
+ .name = "node-name",
+ .type = QEMU_OPT_STRING,
+ .help = "Node name of the block device node",
+ },
+ { /* end of list */ }
+ },
+};
+
/*
* Common part for opening disk images and files
*
@@ -778,6 +821,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
int ret, open_flags;
const char *filename;
const char *node_name = NULL;
+ QemuOpts *opts;
Error *local_err = NULL;
assert(drv != NULL);
@@ -798,23 +842,22 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
trace_bdrv_open_common(bs, filename ?: "", flags, drv->format_name);
- node_name = qdict_get_try_str(options, "node-name");
- bdrv_assign_node_name(bs, node_name, &local_err);
+ opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort);
+ qemu_opts_absorb_qdict(opts, options, &local_err);
if (local_err) {
error_propagate(errp, local_err);
- return -EINVAL;
+ ret = -EINVAL;
+ goto fail_opts;
}
- qdict_del(options, "node-name");
- /* bdrv_open() with directly using a protocol as drv. This layer is already
- * opened, so assign it to bs (while file becomes a closed BlockDriverState)
- * and return immediately. */
- if (file != NULL && drv->bdrv_file_open) {
- bdrv_swap(file, bs);
- return 0;
+ node_name = qemu_opt_get(opts, "node-name");
+ bdrv_assign_node_name(bs, node_name, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ ret = -EINVAL;
+ goto fail_opts;
}
- bs->open_flags = flags;
bs->guest_block_size = 512;
bs->request_alignment = 512;
bs->zero_beyond_eof = true;
@@ -827,7 +870,8 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
? "Driver '%s' can only be used for read-only devices"
: "Driver '%s' is not whitelisted",
drv->format_name);
- return -ENOTSUP;
+ ret = -ENOTSUP;
+ goto fail_opts;
}
assert(bs->copy_on_read == 0); /* bdrv_new() and bdrv_close() make it so */
@@ -836,7 +880,8 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
bdrv_enable_copy_on_read(bs);
} else {
error_setg(errp, "Can't use copy-on-read on read-only device");
- return -EINVAL;
+ ret = -EINVAL;
+ goto fail_opts;
}
}
@@ -902,6 +947,8 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
assert(bdrv_opt_mem_align(bs) != 0);
assert(bdrv_min_mem_align(bs) != 0);
assert((bs->request_alignment != 0) || bs->sg);
+
+ qemu_opts_del(opts);
return 0;
free_and_fail:
@@ -909,6 +956,8 @@ free_and_fail:
g_free(bs->opaque);
bs->opaque = NULL;
bs->drv = NULL;
+fail_opts:
+ qemu_opts_del(opts);
return ret;
}
@@ -942,14 +991,17 @@ static QDict *parse_json_filename(const char *filename, Error **errp)
/*
* Fills in default options for opening images and converts the legacy
* filename/flags pair to option QDict entries.
+ * The BDRV_O_PROTOCOL flag in *flags will be set or cleared accordingly if a
+ * block driver has been specified explicitly.
*/
-static int bdrv_fill_options(QDict **options, const char **pfilename, int flags,
- BlockDriver *drv, Error **errp)
+static int bdrv_fill_options(QDict **options, const char **pfilename,
+ int *flags, BlockDriver *drv, Error **errp)
{
const char *filename = *pfilename;
const char *drvname;
- bool protocol = flags & BDRV_O_PROTOCOL;
+ bool protocol = *flags & BDRV_O_PROTOCOL;
bool parse_filename = false;
+ BlockDriver *tmp_drv;
Error *local_err = NULL;
/* Parse json: pseudo-protocol */
@@ -967,6 +1019,24 @@ static int bdrv_fill_options(QDict **options, const char **pfilename, int flags,
*pfilename = filename = NULL;
}
+ drvname = qdict_get_try_str(*options, "driver");
+
+ /* If the user has explicitly specified the driver, this choice should
+ * override the BDRV_O_PROTOCOL flag */
+ tmp_drv = drv;
+ if (!tmp_drv && drvname) {
+ tmp_drv = bdrv_find_format(drvname);
+ }
+ if (tmp_drv) {
+ protocol = tmp_drv->bdrv_file_open;
+ }
+
+ if (protocol) {
+ *flags |= BDRV_O_PROTOCOL;
+ } else {
+ *flags &= ~BDRV_O_PROTOCOL;
+ }
+
/* Fetch the file name from the options QDict if necessary */
if (protocol && filename) {
if (!qdict_haskey(*options, "filename")) {
@@ -981,7 +1051,6 @@ static int bdrv_fill_options(QDict **options, const char **pfilename, int flags,
/* Find the right block driver */
filename = qdict_get_try_str(*options, "filename");
- drvname = qdict_get_try_str(*options, "driver");
if (drv) {
if (drvname) {
@@ -1118,9 +1187,10 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
}
assert(bs->backing_hd == NULL);
- ret = bdrv_open(&backing_hd,
- *backing_filename ? backing_filename : NULL, NULL, options,
- bdrv_backing_flags(bs->open_flags), NULL, &local_err);
+ ret = bdrv_open_inherit(&backing_hd,
+ *backing_filename ? backing_filename : NULL,
+ NULL, options, 0, bs, &child_backing,
+ NULL, &local_err);
if (ret < 0) {
bdrv_unref(backing_hd);
backing_hd = NULL;
@@ -1154,7 +1224,8 @@ free_exit:
* To conform with the behavior of bdrv_open(), *pbs has to be NULL.
*/
int bdrv_open_image(BlockDriverState **pbs, const char *filename,
- QDict *options, const char *bdref_key, int flags,
+ QDict *options, const char *bdref_key,
+ BlockDriverState* parent, const BdrvChildRole *child_role,
bool allow_none, Error **errp)
{
QDict *image_options;
@@ -1182,7 +1253,8 @@ int bdrv_open_image(BlockDriverState **pbs, const char *filename,
goto done;
}
- ret = bdrv_open(pbs, filename, reference, image_options, flags, NULL, errp);
+ ret = bdrv_open_inherit(pbs, filename, reference, image_options, 0,
+ parent, child_role, NULL, errp);
done:
qdict_del(options, bdref_key);
@@ -1254,6 +1326,19 @@ out:
return ret;
}
+static void bdrv_attach_child(BlockDriverState *parent_bs,
+ BlockDriverState *child_bs,
+ const BdrvChildRole *child_role)
+{
+ BdrvChild *child = g_new(BdrvChild, 1);
+ *child = (BdrvChild) {
+ .bs = child_bs,
+ .role = child_role,
+ };
+
+ QLIST_INSERT_HEAD(&parent_bs->children, child, next);
+}
+
/*
* Opens a disk image (raw, qcow2, vmdk, ...)
*
@@ -1269,9 +1354,11 @@ out:
* should be opened. If specified, neither options nor a filename may be given,
* nor can an existing BDS be reused (that is, *pbs has to be NULL).
*/
-int bdrv_open(BlockDriverState **pbs, const char *filename,
- const char *reference, QDict *options, int flags,
- BlockDriver *drv, Error **errp)
+static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
+ const char *reference, QDict *options, int flags,
+ BlockDriverState *parent,
+ const BdrvChildRole *child_role,
+ BlockDriver *drv, Error **errp)
{
int ret;
BlockDriverState *file = NULL, *bs;
@@ -1280,6 +1367,8 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
int snapshot_flags = 0;
assert(pbs);
+ assert(!child_role || !flags);
+ assert(!child_role == !parent);
if (reference) {
bool options_non_empty = options ? qdict_size(options) : false;
@@ -1302,6 +1391,9 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
return -ENODEV;
}
bdrv_ref(bs);
+ if (child_role) {
+ bdrv_attach_child(parent, bs, child_role);
+ }
*pbs = bs;
return 0;
}
@@ -1317,7 +1409,12 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
options = qdict_new();
}
- ret = bdrv_fill_options(&options, &filename, flags, drv, &local_err);
+ if (child_role) {
+ bs->inherits_from = parent;
+ flags = child_role->inherit_flags(parent->open_flags);
+ }
+
+ ret = bdrv_fill_options(&options, &filename, &flags, drv, &local_err);
if (local_err) {
goto fail;
}
@@ -1336,12 +1433,8 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
}
assert(drvname || !(flags & BDRV_O_PROTOCOL));
- if (drv && !drv->bdrv_file_open) {
- /* If the user explicitly wants a format driver here, we'll need to add
- * another layer for the protocol in bs->file */
- flags &= ~BDRV_O_PROTOCOL;
- }
+ bs->open_flags = flags;
bs->options = options;
options = qdict_clone_shallow(options);
@@ -1356,9 +1449,9 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
}
assert(file == NULL);
+ bs->open_flags = flags;
ret = bdrv_open_image(&file, filename, options, "file",
- bdrv_inherited_flags(flags),
- true, &local_err);
+ bs, &child_file, true, &local_err);
if (ret < 0) {
goto fail;
}
@@ -1377,6 +1470,12 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
goto fail;
}
+ /* BDRV_O_PROTOCOL must be set iff a protocol BDS is about to be created */
+ assert(!!(flags & BDRV_O_PROTOCOL) == !!drv->bdrv_file_open);
+ /* file must be NULL if a protocol BDS is about to be created
+ * (the inverse results in an error message from bdrv_open_common()) */
+ assert(!(flags & BDRV_O_PROTOCOL) || !file);
+
/* Open the image */
ret = bdrv_open_common(bs, file, options, flags, drv, &local_err);
if (ret < 0) {
@@ -1439,6 +1538,10 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
goto close_and_fail;
}
+ if (child_role) {
+ bdrv_attach_child(parent, bs, child_role);
+ }
+
QDECREF(options);
*pbs = bs;
return 0;
@@ -1475,6 +1578,14 @@ close_and_fail:
return ret;
}
+int bdrv_open(BlockDriverState **pbs, const char *filename,
+ const char *reference, QDict *options, int flags,
+ BlockDriver *drv, Error **errp)
+{
+ return bdrv_open_inherit(pbs, filename, reference, options, flags, NULL,
+ NULL, drv, errp);
+}
+
typedef struct BlockReopenQueueEntry {
bool prepared;
BDRVReopenState state;
@@ -1505,6 +1616,8 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
assert(bs != NULL);
BlockReopenQueueEntry *bs_entry;
+ BdrvChild *child;
+
if (bs_queue == NULL) {
bs_queue = g_new0(BlockReopenQueue, 1);
QSIMPLEQ_INIT(bs_queue);
@@ -1513,8 +1626,15 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
/* bdrv_open() masks this flag out */
flags &= ~BDRV_O_PROTOCOL;
- if (bs->file) {
- bdrv_reopen_queue(bs_queue, bs->file, bdrv_inherited_flags(flags));
+ QLIST_FOREACH(child, &bs->children, next) {
+ int child_flags;
+
+ if (child->bs->inherits_from != bs) {
+ continue;
+ }
+
+ child_flags = child->role->inherit_flags(flags);
+ bdrv_reopen_queue(bs_queue, child->bs, child_flags);
}
bs_entry = g_new0(BlockReopenQueueEntry, 1);
@@ -1725,6 +1845,16 @@ void bdrv_close(BlockDriverState *bs)
notifier_list_notify(&bs->close_notifiers, bs);
if (bs->drv) {
+ BdrvChild *child, *next;
+
+ QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
+ if (child->bs->inherits_from == bs) {
+ child->bs->inherits_from = NULL;
+ }
+ QLIST_REMOVE(child, next);
+ g_free(child);
+ }
+
if (bs->backing_hd) {
BlockDriverState *backing_hd = bs->backing_hd;
bdrv_set_backing_hd(bs, NULL);
@@ -1822,12 +1952,18 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
bs_dest->enable_write_cache = bs_src->enable_write_cache;
/* i/o throttled req */
- memcpy(&bs_dest->throttle_state,
- &bs_src->throttle_state,
- sizeof(ThrottleState));
+ bs_dest->throttle_state = bs_src->throttle_state,
+ bs_dest->io_limits_enabled = bs_src->io_limits_enabled;
+ bs_dest->pending_reqs[0] = bs_src->pending_reqs[0];
+ bs_dest->pending_reqs[1] = bs_src->pending_reqs[1];
bs_dest->throttled_reqs[0] = bs_src->throttled_reqs[0];
bs_dest->throttled_reqs[1] = bs_src->throttled_reqs[1];
- bs_dest->io_limits_enabled = bs_src->io_limits_enabled;
+ memcpy(&bs_dest->round_robin,
+ &bs_src->round_robin,
+ sizeof(bs_dest->round_robin));
+ memcpy(&bs_dest->throttle_timers,
+ &bs_src->throttle_timers,
+ sizeof(ThrottleTimers));
/* r/w error */
bs_dest->on_read_error = bs_src->on_read_error;
@@ -1869,6 +2005,10 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
{
BlockDriverState tmp;
+ BdrvChild *child;
+
+ bdrv_drain(bs_new);
+ bdrv_drain(bs_old);
/* The code needs to swap the node_name but simply swapping node_list won't
* work so first remove the nodes from the graph list, do the swap then
@@ -1881,12 +2021,21 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
QTAILQ_REMOVE(&graph_bdrv_states, bs_old, node_list);
}
+ /* If the BlockDriverState is part of a throttling group acquire
+ * its lock since we're going to mess with the protected fields.
+ * Otherwise there's no need to worry since no one else can touch
+ * them. */
+ if (bs_old->throttle_state) {
+ throttle_group_lock(bs_old);
+ }
+
/* bs_new must be unattached and shouldn't have anything fancy enabled */
assert(!bs_new->blk);
assert(QLIST_EMPTY(&bs_new->dirty_bitmaps));
assert(bs_new->job == NULL);
assert(bs_new->io_limits_enabled == false);
- assert(!throttle_have_timer(&bs_new->throttle_state));
+ assert(bs_new->throttle_state == NULL);
+ assert(!throttle_timers_are_initialized(&bs_new->throttle_timers));
tmp = *bs_new;
*bs_new = *bs_old;
@@ -1903,7 +2052,13 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
/* Check a few fields that should remain attached to the device */
assert(bs_new->job == NULL);
assert(bs_new->io_limits_enabled == false);
- assert(!throttle_have_timer(&bs_new->throttle_state));
+ assert(bs_new->throttle_state == NULL);
+ assert(!throttle_timers_are_initialized(&bs_new->throttle_timers));
+
+ /* Release the ThrottleGroup lock */
+ if (bs_old->throttle_state) {
+ throttle_group_unlock(bs_old);
+ }
/* insert the nodes back into the graph node list if needed */
if (bs_new->node_name[0] != '\0') {
@@ -1913,6 +2068,30 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs_old, node_list);
}
+ /*
+ * Update lh_first.le_prev for non-empty lists.
+ *
+ * The head of the op blocker list doesn't change because it is moved back
+ * in bdrv_move_feature_fields().
+ */
+ assert(QLIST_EMPTY(&bs_old->tracked_requests));
+ assert(QLIST_EMPTY(&bs_new->tracked_requests));
+
+ QLIST_FIX_HEAD_PTR(&bs_new->children, next);
+ QLIST_FIX_HEAD_PTR(&bs_old->children, next);
+
+ /* Update references in bs->opaque and children */
+ QLIST_FOREACH(child, &bs_old->children, next) {
+ if (child->bs->inherits_from == bs_new) {
+ child->bs->inherits_from = bs_old;
+ }
+ }
+ QLIST_FOREACH(child, &bs_new->children, next) {
+ if (child->bs->inherits_from == bs_old) {
+ child->bs->inherits_from = bs_new;
+ }
+ }
+
bdrv_rebind(bs_new);
bdrv_rebind(bs_old);
}
@@ -1935,6 +2114,7 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top)
/* The contents of 'tmp' will become bs_top, as we are
* swapping bs_new and bs_top contents. */
bdrv_set_backing_hd(bs_top, bs_new);
+ bdrv_attach_child(bs_top, bs_new, &child_backing);
}
static void bdrv_delete(BlockDriverState *bs)
@@ -3220,10 +3400,9 @@ static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs)
uint64_t size = bdrv_nb_sectors(bs);
QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
- if (bdrv_dirty_bitmap_frozen(bitmap)) {
- continue;
- }
+ assert(!bdrv_dirty_bitmap_frozen(bitmap));
hbitmap_truncate(bitmap->bitmap, size);
+ bitmap->size = size;
}
}
@@ -3691,7 +3870,7 @@ void bdrv_detach_aio_context(BlockDriverState *bs)
}
if (bs->io_limits_enabled) {
- throttle_detach_aio_context(&bs->throttle_state);
+ throttle_timers_detach_aio_context(&bs->throttle_timers);
}
if (bs->drv->bdrv_detach_aio_context) {
bs->drv->bdrv_detach_aio_context(bs);
@@ -3727,7 +3906,7 @@ void bdrv_attach_aio_context(BlockDriverState *bs,
bs->drv->bdrv_attach_aio_context(bs, new_context);
}
if (bs->io_limits_enabled) {
- throttle_attach_aio_context(&bs->throttle_state, new_context);
+ throttle_timers_attach_aio_context(&bs->throttle_timers, new_context);
}
QLIST_FOREACH(ban, &bs->aio_notifiers, list) {
diff --git a/block/Makefile.objs b/block/Makefile.objs
index 0d8c2a4ab6..c34fd7cdc2 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -10,6 +10,7 @@ block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
block-obj-$(CONFIG_POSIX) += raw-posix.o
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
block-obj-y += null.o mirror.o io.o
+block-obj-y += throttle-groups.o
block-obj-y += nbd.o nbd-client.o sheepdog.o
block-obj-$(CONFIG_LIBISCSI) += iscsi.o
diff --git a/block/blkdebug.c b/block/blkdebug.c
index 3c30edba73..bc247f46f5 100644
--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -216,10 +216,9 @@ static int get_event_by_name(const char *name, BlkDebugEvent *event)
struct add_rule_data {
BDRVBlkdebugState *s;
int action;
- Error **errp;
};
-static int add_rule(QemuOpts *opts, void *opaque)
+static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
{
struct add_rule_data *d = opaque;
BDRVBlkdebugState *s = d->s;
@@ -230,10 +229,10 @@ static int add_rule(QemuOpts *opts, void *opaque)
/* Find the right event for the rule */
event_name = qemu_opt_get(opts, "event");
if (!event_name) {
- error_setg(d->errp, "Missing event name for rule");
+ error_setg(errp, "Missing event name for rule");
return -1;
} else if (get_event_by_name(event_name, &event) < 0) {
- error_setg(d->errp, "Invalid event name \"%s\"", event_name);
+ error_setg(errp, "Invalid event name \"%s\"", event_name);
return -1;
}
@@ -319,8 +318,7 @@ static int read_config(BDRVBlkdebugState *s, const char *filename,
d.s = s;
d.action = ACTION_INJECT_ERROR;
- d.errp = &local_err;
- qemu_opts_foreach(&inject_error_opts, add_rule, &d, 1);
+ qemu_opts_foreach(&inject_error_opts, add_rule, &d, &local_err);
if (local_err) {
error_propagate(errp, local_err);
ret = -EINVAL;
@@ -328,7 +326,7 @@ static int read_config(BDRVBlkdebugState *s, const char *filename,
}
d.action = ACTION_SET_STATE;
- qemu_opts_foreach(&set_state_opts, add_rule, &d, 1);
+ qemu_opts_foreach(&set_state_opts, add_rule, &d, &local_err);
if (local_err) {
error_propagate(errp, local_err);
ret = -EINVAL;
@@ -431,7 +429,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
/* Open the backing file */
assert(bs->file == NULL);
ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-image"), options, "image",
- flags | BDRV_O_PROTOCOL, false, &local_err);
+ bs, &child_file, false, &local_err);
if (ret < 0) {
error_propagate(errp, local_err);
goto out;
diff --git a/block/blkverify.c b/block/blkverify.c
index 438dff8bcb..d277e63220 100644
--- a/block/blkverify.c
+++ b/block/blkverify.c
@@ -125,7 +125,7 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
/* Open the raw file */
assert(bs->file == NULL);
ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-raw"), options,
- "raw", flags | BDRV_O_PROTOCOL, false, &local_err);
+ "raw", bs, &child_file, false, &local_err);
if (ret < 0) {
error_propagate(errp, local_err);
goto fail;
@@ -134,7 +134,7 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
/* Open the test file */
assert(s->test_file == NULL);
ret = bdrv_open_image(&s->test_file, qemu_opt_get(opts, "x-image"), options,
- "test", flags, false, &local_err);
+ "test", bs, &child_format, false, &local_err);
if (ret < 0) {
error_propagate(errp, local_err);
s->test_file = NULL;
diff --git a/block/io.c b/block/io.c
index e394d92626..bb4f78784e 100644
--- a/block/io.c
+++ b/block/io.c
@@ -23,9 +23,9 @@
*/
#include "trace.h"
-#include "sysemu/qtest.h"
#include "block/blockjob.h"
#include "block/block_int.h"
+#include "block/throttle-groups.h"
#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */
@@ -65,7 +65,7 @@ void bdrv_set_io_limits(BlockDriverState *bs,
{
int i;
- throttle_config(&bs->throttle_state, cfg);
+ throttle_group_config(bs, cfg);
for (i = 0; i < 2; i++) {
qemu_co_enter_next(&bs->throttled_reqs[i]);
@@ -95,72 +95,33 @@ static bool bdrv_start_throttled_reqs(BlockDriverState *bs)
void bdrv_io_limits_disable(BlockDriverState *bs)
{
bs->io_limits_enabled = false;
-
bdrv_start_throttled_reqs(bs);
-
- throttle_destroy(&bs->throttle_state);
-}
-
-static void bdrv_throttle_read_timer_cb(void *opaque)
-{
- BlockDriverState *bs = opaque;
- qemu_co_enter_next(&bs->throttled_reqs[0]);
-}
-
-static void bdrv_throttle_write_timer_cb(void *opaque)
-{
- BlockDriverState *bs = opaque;
- qemu_co_enter_next(&bs->throttled_reqs[1]);
+ throttle_group_unregister_bs(bs);
}
/* should be called before bdrv_set_io_limits if a limit is set */
-void bdrv_io_limits_enable(BlockDriverState *bs)
+void bdrv_io_limits_enable(BlockDriverState *bs, const char *group)
{
- int clock_type = QEMU_CLOCK_REALTIME;
-
- if (qtest_enabled()) {
- /* For testing block IO throttling only */
- clock_type = QEMU_CLOCK_VIRTUAL;
- }
assert(!bs->io_limits_enabled);
- throttle_init(&bs->throttle_state,
- bdrv_get_aio_context(bs),
- clock_type,
- bdrv_throttle_read_timer_cb,
- bdrv_throttle_write_timer_cb,
- bs);
+ throttle_group_register_bs(bs, group);
bs->io_limits_enabled = true;
}
-/* This function makes an IO wait if needed
- *
- * @nb_sectors: the number of sectors of the IO
- * @is_write: is the IO a write
- */
-static void bdrv_io_limits_intercept(BlockDriverState *bs,
- unsigned int bytes,
- bool is_write)
+void bdrv_io_limits_update_group(BlockDriverState *bs, const char *group)
{
- /* does this io must wait */
- bool must_wait = throttle_schedule_timer(&bs->throttle_state, is_write);
-
- /* if must wait or any request of this type throttled queue the IO */
- if (must_wait ||
- !qemu_co_queue_empty(&bs->throttled_reqs[is_write])) {
- qemu_co_queue_wait(&bs->throttled_reqs[is_write]);
+ /* this bs is not part of any group */
+ if (!bs->throttle_state) {
+ return;
}
- /* the IO will be executed, do the accounting */
- throttle_account(&bs->throttle_state, is_write, bytes);
-
-
- /* if the next request must wait -> do nothing */
- if (throttle_schedule_timer(&bs->throttle_state, is_write)) {
+ /* this bs is a part of the same group than the one we want */
+ if (!g_strcmp0(throttle_group_get_name(bs), group)) {
return;
}
- /* else queue next request for execution */
- qemu_co_queue_next(&bs->throttled_reqs[is_write]);
+ /* need to change the group this bs belong to */
+ bdrv_io_limits_disable(bs);
+ bdrv_io_limits_enable(bs, group);
}
void bdrv_setup_io_funcs(BlockDriver *bdrv)
@@ -967,7 +928,7 @@ static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs,
/* throttling disk I/O */
if (bs->io_limits_enabled) {
- bdrv_io_limits_intercept(bs, bytes, false);
+ throttle_group_co_io_limits_intercept(bs, bytes, false);
}
/* Align read if necessary by padding qiov */
@@ -1297,7 +1258,7 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs,
/* throttling disk I/O */
if (bs->io_limits_enabled) {
- bdrv_io_limits_intercept(bs, bytes, true);
+ throttle_group_co_io_limits_intercept(bs, bytes, true);
}
/*
diff --git a/block/iscsi.c b/block/iscsi.c
index 8fca1d32cb..14e97a6b48 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -1323,13 +1323,6 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
const char *filename;
int i, ret = 0;
- if ((BDRV_SECTOR_SIZE % 512) != 0) {
- error_setg(errp, "iSCSI: Invalid BDRV_SECTOR_SIZE. "
- "BDRV_SECTOR_SIZE(%lld) is not a multiple "
- "of 512", BDRV_SECTOR_SIZE);
- return -EINVAL;
- }
-
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (local_err) {
diff --git a/block/qapi.c b/block/qapi.c
index 18d2b95f54..a738148bce 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -24,6 +24,7 @@
#include "block/qapi.h"
#include "block/block_int.h"
+#include "block/throttle-groups.h"
#include "block/write-threshold.h"
#include "qmp-commands.h"
#include "qapi-visit.h"
@@ -65,7 +66,9 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp)
if (bs->io_limits_enabled) {
ThrottleConfig cfg;
- throttle_get_config(&bs->throttle_state, &cfg);
+
+ throttle_group_get_config(bs, &cfg);
+
info->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg;
info->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg;
info->bps_wr = cfg.buckets[THROTTLE_BPS_WRITE].avg;
@@ -90,6 +93,9 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp)
info->has_iops_size = cfg.op_size;
info->iops_size = cfg.op_size;
+
+ info->has_group = true;
+ info->group = g_strdup(throttle_group_get_name(bs));
}
info->write_threshold = bdrv_write_threshold_get(bs);
diff --git a/block/qcow2.c b/block/qcow2.c
index f7b4cc6a32..c4f6938a36 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -483,9 +483,11 @@ static const char *overlap_bool_option_names[QCOW2_OL_MAX_BITNR] = {
[QCOW2_OL_INACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L2,
};
-static void read_cache_sizes(QemuOpts *opts, uint64_t *l2_cache_size,
+static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
+ uint64_t *l2_cache_size,
uint64_t *refcount_cache_size, Error **errp)
{
+ BDRVQcowState *s = bs->opaque;
uint64_t combined_cache_size;
bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set;
@@ -525,7 +527,9 @@ static void read_cache_sizes(QemuOpts *opts, uint64_t *l2_cache_size,
}
} else {
if (!l2_cache_size_set && !refcount_cache_size_set) {
- *l2_cache_size = DEFAULT_L2_CACHE_BYTE_SIZE;
+ *l2_cache_size = MAX(DEFAULT_L2_CACHE_BYTE_SIZE,
+ (uint64_t)DEFAULT_L2_CACHE_CLUSTERS
+ * s->cluster_size);
*refcount_cache_size = *l2_cache_size
/ DEFAULT_L2_REFCOUNT_SIZE_RATIO;
} else if (!l2_cache_size_set) {
@@ -803,7 +807,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
goto fail;
}
- read_cache_sizes(opts, &l2_cache_size, &refcount_cache_size, &local_err);
+ read_cache_sizes(bs, opts, &l2_cache_size, &refcount_cache_size,
+ &local_err);
if (local_err) {
error_propagate(errp, local_err);
ret = -EINVAL;
diff --git a/block/qcow2.h b/block/qcow2.h
index 0076512af4..5936d299a3 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -62,11 +62,14 @@
#define MIN_CLUSTER_BITS 9
#define MAX_CLUSTER_BITS 21
-#define MIN_L2_CACHE_SIZE 1 /* cluster */
+/* Must be at least 2 to cover COW */
+#define MIN_L2_CACHE_SIZE 2 /* clusters */
/* Must be at least 4 to cover all cases of refcount table growth */
#define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */
+/* Whichever is more */
+#define DEFAULT_L2_CACHE_CLUSTERS 8 /* clusters */
#define DEFAULT_L2_CACHE_BYTE_SIZE 1048576 /* bytes */
/* The refblock cache needs only a fourth of the L2 cache size to cover as many
diff --git a/block/quorum.c b/block/quorum.c
index f91ef75a84..77e55b2775 100644
--- a/block/quorum.c
+++ b/block/quorum.c
@@ -866,25 +866,18 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
Error *local_err = NULL;
QemuOpts *opts = NULL;
bool *opened;
- QDict *sub = NULL;
- QList *list = NULL;
- const QListEntry *lentry;
int i;
int ret = 0;
qdict_flatten(options);
- qdict_extract_subqdict(options, &sub, "children.");
- qdict_array_split(sub, &list);
- if (qdict_size(sub)) {
- error_setg(&local_err, "Invalid option children.%s",
- qdict_first(sub)->key);
+ /* count how many different children are present */
+ s->num_children = qdict_array_entries(options, "children.");
+ if (s->num_children < 0) {
+ error_setg(&local_err, "Option children is not a valid array");
ret = -EINVAL;
goto exit;
}
-
- /* count how many different children are present */
- s->num_children = qlist_size(list);
if (s->num_children < 2) {
error_setg(&local_err,
"Number of provided children must be greater than 1");
@@ -937,37 +930,17 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
s->bs = g_new0(BlockDriverState *, s->num_children);
opened = g_new0(bool, s->num_children);
- for (i = 0, lentry = qlist_first(list); lentry;
- lentry = qlist_next(lentry), i++) {
- QDict *d;
- QString *string;
-
- switch (qobject_type(lentry->value))
- {
- /* List of options */
- case QTYPE_QDICT:
- d = qobject_to_qdict(lentry->value);
- QINCREF(d);
- ret = bdrv_open(&s->bs[i], NULL, NULL, d, flags, NULL,
- &local_err);
- break;
-
- /* QMP reference */
- case QTYPE_QSTRING:
- string = qobject_to_qstring(lentry->value);
- ret = bdrv_open(&s->bs[i], NULL, qstring_get_str(string), NULL,
- flags, NULL, &local_err);
- break;
-
- default:
- error_setg(&local_err, "Specification of child block device %i "
- "is invalid", i);
- ret = -EINVAL;
- }
+ for (i = 0; i < s->num_children; i++) {
+ char indexstr[32];
+ ret = snprintf(indexstr, 32, "children.%d", i);
+ assert(ret < 32);
+ ret = bdrv_open_image(&s->bs[i], NULL, options, indexstr, bs,
+ &child_format, false, &local_err);
if (ret < 0) {
goto close_exit;
}
+
opened[i] = true;
}
@@ -990,8 +963,6 @@ exit:
if (local_err) {
error_propagate(errp, local_err);
}
- QDECREF(list);
- QDECREF(sub);
return ret;
}
diff --git a/block/raw-posix.c b/block/raw-posix.c
index 2990e954ae..44ade8cf4e 100644
--- a/block/raw-posix.c
+++ b/block/raw-posix.c
@@ -1848,8 +1848,9 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
*pnum = nb_sectors;
ret = BDRV_BLOCK_DATA;
} else if (data == start) {
- /* On a data extent, compute sectors to the end of the extent. */
- *pnum = MIN(nb_sectors, (hole - start) / BDRV_SECTOR_SIZE);
+ /* On a data extent, compute sectors to the end of the extent,
+ * possibly including a partial sector at EOF. */
+ *pnum = MIN(nb_sectors, DIV_ROUND_UP(hole - start, BDRV_SECTOR_SIZE));
ret = BDRV_BLOCK_DATA;
} else {
/* On a hole, compute sectors to the beginning of the next extent. */
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
new file mode 100644
index 0000000000..efc462fbc5
--- /dev/null
+++ b/block/throttle-groups.c
@@ -0,0 +1,496 @@
+/*
+ * QEMU block throttling group infrastructure
+ *
+ * Copyright (C) Nodalink, EURL. 2014
+ * Copyright (C) Igalia, S.L. 2015
+ *
+ * Authors:
+ * Benoît Canet <benoit.canet@nodalink.com>
+ * Alberto Garcia <berto@igalia.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 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 "block/throttle-groups.h"
+#include "qemu/queue.h"
+#include "qemu/thread.h"
+#include "sysemu/qtest.h"
+
+/* The ThrottleGroup structure (with its ThrottleState) is shared
+ * among different BlockDriverState and it's independent from
+ * AioContext, so in order to use it from different threads it needs
+ * its own locking.
+ *
+ * This locking is however handled internally in this file, so it's
+ * mostly transparent to outside users (but see the documentation in
+ * throttle_groups_lock()).
+ *
+ * The whole ThrottleGroup structure is private and invisible to
+ * outside users, that only use it through its ThrottleState.
+ *
+ * In addition to the ThrottleGroup structure, BlockDriverState has
+ * fields that need to be accessed by other members of the group and
+ * therefore also need to be protected by this lock. Once a BDS is
+ * registered in a group those fields can be accessed by other threads
+ * any time.
+ *
+ * Again, all this is handled internally and is mostly transparent to
+ * the outside. The 'throttle_timers' field however has an additional
+ * constraint because it may be temporarily invalid (see for example
+ * bdrv_set_aio_context()). Therefore in this file a thread will
+ * access some other BDS's timers only after verifying that that BDS
+ * has throttled requests in the queue.
+ */
+typedef struct ThrottleGroup {
+ char *name; /* This is constant during the lifetime of the group */
+
+ QemuMutex lock; /* This lock protects the following four fields */
+ ThrottleState ts;
+ QLIST_HEAD(, BlockDriverState) head;
+ BlockDriverState *tokens[2];
+ bool any_timer_armed[2];
+
+ /* These two are protected by the global throttle_groups_lock */
+ unsigned refcount;
+ QTAILQ_ENTRY(ThrottleGroup) list;
+} ThrottleGroup;
+
+static QemuMutex throttle_groups_lock;
+static QTAILQ_HEAD(, ThrottleGroup) throttle_groups =
+ QTAILQ_HEAD_INITIALIZER(throttle_groups);
+
+/* Increments the reference count of a ThrottleGroup given its name.
+ *
+ * If no ThrottleGroup is found with the given name a new one is
+ * created.
+ *
+ * @name: the name of the ThrottleGroup
+ * @ret: the ThrottleGroup
+ */
+static ThrottleGroup *throttle_group_incref(const char *name)
+{
+ ThrottleGroup *tg = NULL;
+ ThrottleGroup *iter;
+
+ qemu_mutex_lock(&throttle_groups_lock);
+
+ /* Look for an existing group with that name */
+ QTAILQ_FOREACH(iter, &throttle_groups, list) {
+ if (!strcmp(name, iter->name)) {
+ tg = iter;
+ break;
+ }
+ }
+
+ /* Create a new one if not found */
+ if (!tg) {
+ tg = g_new0(ThrottleGroup, 1);
+ tg->name = g_strdup(name);
+ qemu_mutex_init(&tg->lock);
+ throttle_init(&tg->ts);
+ QLIST_INIT(&tg->head);
+
+ QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
+ }
+
+ tg->refcount++;
+
+ qemu_mutex_unlock(&throttle_groups_lock);
+
+ return tg;
+}
+
+/* Decrease the reference count of a ThrottleGroup.
+ *
+ * When the reference count reaches zero the ThrottleGroup is
+ * destroyed.
+ *
+ * @tg: The ThrottleGroup to unref
+ */
+static void throttle_group_unref(ThrottleGroup *tg)
+{
+ qemu_mutex_lock(&throttle_groups_lock);
+ if (--tg->refcount == 0) {
+ QTAILQ_REMOVE(&throttle_groups, tg, list);
+ qemu_mutex_destroy(&tg->lock);
+ g_free(tg->name);
+ g_free(tg);
+ }
+ qemu_mutex_unlock(&throttle_groups_lock);
+}
+
+/* Get the name from a BlockDriverState's ThrottleGroup. The name (and
+ * the pointer) is guaranteed to remain constant during the lifetime
+ * of the group.
+ *
+ * @bs: a BlockDriverState that is member of a throttling group
+ * @ret: the name of the group.
+ */
+const char *throttle_group_get_name(BlockDriverState *bs)
+{
+ ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
+ return tg->name;
+}
+
+/* Return the next BlockDriverState in the round-robin sequence,
+ * simulating a circular list.
+ *
+ * This assumes that tg->lock is held.
+ *
+ * @bs: the current BlockDriverState
+ * @ret: the next BlockDriverState in the sequence
+ */
+static BlockDriverState *throttle_group_next_bs(BlockDriverState *bs)
+{
+ ThrottleState *ts = bs->throttle_state;
+ ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+ BlockDriverState *next = QLIST_NEXT(bs, round_robin);
+
+ if (!next) {
+ return QLIST_FIRST(&tg->head);
+ }
+
+ return next;
+}
+
+/* Return the next BlockDriverState in the round-robin sequence with
+ * pending I/O requests.
+ *
+ * This assumes that tg->lock is held.
+ *
+ * @bs: the current BlockDriverState
+ * @is_write: the type of operation (read/write)
+ * @ret: the next BlockDriverState with pending requests, or bs
+ * if there is none.
+ */
+static BlockDriverState *next_throttle_token(BlockDriverState *bs,
+ bool is_write)
+{
+ ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
+ BlockDriverState *token, *start;
+
+ start = token = tg->tokens[is_write];
+
+ /* get next bs round in round robin style */
+ token = throttle_group_next_bs(token);
+ while (token != start && !token->pending_reqs[is_write]) {
+ token = throttle_group_next_bs(token);
+ }
+
+ /* If no IO are queued for scheduling on the next round robin token
+ * then decide the token is the current bs because chances are
+ * the current bs get the current request queued.
+ */
+ if (token == start && !token->pending_reqs[is_write]) {
+ token = bs;
+ }
+
+ return token;
+}
+
+/* Check if the next I/O request for a BlockDriverState needs to be
+ * throttled or not. If there's no timer set in this group, set one
+ * and update the token accordingly.
+ *
+ * This assumes that tg->lock is held.
+ *
+ * @bs: the current BlockDriverState
+ * @is_write: the type of operation (read/write)
+ * @ret: whether the I/O request needs to be throttled or not
+ */
+static bool throttle_group_schedule_timer(BlockDriverState *bs,
+ bool is_write)
+{
+ ThrottleState *ts = bs->throttle_state;
+ ThrottleTimers *tt = &bs->throttle_timers;
+ ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+ bool must_wait;
+
+ /* Check if any of the timers in this group is already armed */
+ if (tg->any_timer_armed[is_write]) {
+ return true;
+ }
+
+ must_wait = throttle_schedule_timer(ts, tt, is_write);
+
+ /* If a timer just got armed, set bs as the current token */
+ if (must_wait) {
+ tg->tokens[is_write] = bs;
+ tg->any_timer_armed[is_write] = true;
+ }
+
+ return must_wait;
+}
+
+/* Look for the next pending I/O request and schedule it.
+ *
+ * This assumes that tg->lock is held.
+ *
+ * @bs: the current BlockDriverState
+ * @is_write: the type of operation (read/write)
+ */
+static void schedule_next_request(BlockDriverState *bs, bool is_write)
+{
+ ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
+ bool must_wait;
+ BlockDriverState *token;
+
+ /* Check if there's any pending request to schedule next */
+ token = next_throttle_token(bs, is_write);
+ if (!token->pending_reqs[is_write]) {
+ return;
+ }
+
+ /* Set a timer for the request if it needs to be throttled */
+ must_wait = throttle_group_schedule_timer(token, is_write);
+
+ /* If it doesn't have to wait, queue it for immediate execution */
+ if (!must_wait) {
+ /* Give preference to requests from the current bs */
+ if (qemu_in_coroutine() &&
+ qemu_co_queue_next(&bs->throttled_reqs[is_write])) {
+ token = bs;
+ } else {
+ ThrottleTimers *tt = &token->throttle_timers;
+ int64_t now = qemu_clock_get_ns(tt->clock_type);
+ timer_mod(tt->timers[is_write], now + 1);
+ tg->any_timer_armed[is_write] = true;
+ }
+ tg->tokens[is_write] = token;
+ }
+}
+
+/* Check if an I/O request needs to be throttled, wait and set a timer
+ * if necessary, and schedule the next request using a round robin
+ * algorithm.
+ *
+ * @bs: the current BlockDriverState
+ * @bytes: the number of bytes for this I/O
+ * @is_write: the type of operation (read/write)
+ */
+void coroutine_fn throttle_group_co_io_limits_intercept(BlockDriverState *bs,
+ unsigned int bytes,
+ bool is_write)
+{
+ bool must_wait;
+ BlockDriverState *token;
+
+ ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
+ qemu_mutex_lock(&tg->lock);
+
+ /* First we check if this I/O has to be throttled. */
+ token = next_throttle_token(bs, is_write);
+ must_wait = throttle_group_schedule_timer(token, is_write);
+
+ /* Wait if there's a timer set or queued requests of this type */
+ if (must_wait || bs->pending_reqs[is_write]) {
+ bs->pending_reqs[is_write]++;
+ qemu_mutex_unlock(&tg->lock);
+ qemu_co_queue_wait(&bs->throttled_reqs[is_write]);
+ qemu_mutex_lock(&tg->lock);
+ bs->pending_reqs[is_write]--;
+ }
+
+ /* The I/O will be executed, so do the accounting */
+ throttle_account(bs->throttle_state, is_write, bytes);
+
+ /* Schedule the next request */
+ schedule_next_request(bs, is_write);
+
+ qemu_mutex_unlock(&tg->lock);
+}
+
+/* Update the throttle configuration for a particular group. Similar
+ * to throttle_config(), but guarantees atomicity within the
+ * throttling group.
+ *
+ * @bs: a BlockDriverState that is member of the group
+ * @cfg: the configuration to set
+ */
+void throttle_group_config(BlockDriverState *bs, ThrottleConfig *cfg)
+{
+ ThrottleTimers *tt = &bs->throttle_timers;
+ ThrottleState *ts = bs->throttle_state;
+ ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+ qemu_mutex_lock(&tg->lock);
+ throttle_config(ts, tt, cfg);
+ /* throttle_config() cancels the timers */
+ tg->any_timer_armed[0] = tg->any_timer_armed[1] = false;
+ qemu_mutex_unlock(&tg->lock);
+}
+
+/* Get the throttle configuration from a particular group. Similar to
+ * throttle_get_config(), but guarantees atomicity within the
+ * throttling group.
+ *
+ * @bs: a BlockDriverState that is member of the group
+ * @cfg: the configuration will be written here
+ */
+void throttle_group_get_config(BlockDriverState *bs, ThrottleConfig *cfg)
+{
+ ThrottleState *ts = bs->throttle_state;
+ ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+ qemu_mutex_lock(&tg->lock);
+ throttle_get_config(ts, cfg);
+ qemu_mutex_unlock(&tg->lock);
+}
+
+/* ThrottleTimers callback. This wakes up a request that was waiting
+ * because it had been throttled.
+ *
+ * @bs: the BlockDriverState whose request had been throttled
+ * @is_write: the type of operation (read/write)
+ */
+static void timer_cb(BlockDriverState *bs, bool is_write)
+{
+ ThrottleState *ts = bs->throttle_state;
+ ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
+ bool empty_queue;
+
+ /* The timer has just been fired, so we can update the flag */
+ qemu_mutex_lock(&tg->lock);
+ tg->any_timer_armed[is_write] = false;
+ qemu_mutex_unlock(&tg->lock);
+
+ /* Run the request that was waiting for this timer */
+ empty_queue = !qemu_co_enter_next(&bs->throttled_reqs[is_write]);
+
+ /* If the request queue was empty then we have to take care of
+ * scheduling the next one */
+ if (empty_queue) {
+ qemu_mutex_lock(&tg->lock);
+ schedule_next_request(bs, is_write);
+ qemu_mutex_unlock(&tg->lock);
+ }
+}
+
+static void read_timer_cb(void *opaque)
+{
+ timer_cb(opaque, false);
+}
+
+static void write_timer_cb(void *opaque)
+{
+ timer_cb(opaque, true);
+}
+
+/* Register a BlockDriverState in the throttling group, also
+ * initializing its timers and updating its throttle_state pointer to
+ * point to it. If a throttling group with that name does not exist
+ * yet, it will be created.
+ *
+ * @bs: the BlockDriverState to insert
+ * @groupname: the name of the group
+ */
+void throttle_group_register_bs(BlockDriverState *bs, const char *groupname)
+{
+ int i;
+ ThrottleGroup *tg = throttle_group_incref(groupname);
+ int clock_type = QEMU_CLOCK_REALTIME;
+
+ if (qtest_enabled()) {
+ /* For testing block IO throttling only */
+ clock_type = QEMU_CLOCK_VIRTUAL;
+ }
+
+ bs->throttle_state = &tg->ts;
+
+ qemu_mutex_lock(&tg->lock);
+ /* If the ThrottleGroup is new set this BlockDriverState as the token */
+ for (i = 0; i < 2; i++) {
+ if (!tg->tokens[i]) {
+ tg->tokens[i] = bs;
+ }
+ }
+
+ QLIST_INSERT_HEAD(&tg->head, bs, round_robin);
+
+ throttle_timers_init(&bs->throttle_timers,
+ bdrv_get_aio_context(bs),
+ clock_type,
+ read_timer_cb,
+ write_timer_cb,
+ bs);
+
+ qemu_mutex_unlock(&tg->lock);
+}
+
+/* Unregister a BlockDriverState from its group, removing it from the
+ * list, destroying the timers and setting the throttle_state pointer
+ * to NULL.
+ *
+ * The group will be destroyed if it's empty after this operation.
+ *
+ * @bs: the BlockDriverState to remove
+ */
+void throttle_group_unregister_bs(BlockDriverState *bs)
+{
+ ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
+ int i;
+
+ qemu_mutex_lock(&tg->lock);
+ for (i = 0; i < 2; i++) {
+ if (tg->tokens[i] == bs) {
+ BlockDriverState *token = throttle_group_next_bs(bs);
+ /* Take care of the case where this is the last bs in the group */
+ if (token == bs) {
+ token = NULL;
+ }
+ tg->tokens[i] = token;
+ }
+ }
+
+ /* remove the current bs from the list */
+ QLIST_REMOVE(bs, round_robin);
+ throttle_timers_destroy(&bs->throttle_timers);
+ qemu_mutex_unlock(&tg->lock);
+
+ throttle_group_unref(tg);
+ bs->throttle_state = NULL;
+}
+
+/* Acquire the lock of this throttling group.
+ *
+ * You won't normally need to use this. None of the functions from the
+ * ThrottleGroup API require you to acquire the lock since all of them
+ * deal with it internally.
+ *
+ * This should only be used in exceptional cases when you want to
+ * access the protected fields of a BlockDriverState directly
+ * (e.g. bdrv_swap()).
+ *
+ * @bs: a BlockDriverState that is member of the group
+ */
+void throttle_group_lock(BlockDriverState *bs)
+{
+ ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
+ qemu_mutex_lock(&tg->lock);
+}
+
+/* Release the lock of this throttling group.
+ *
+ * See the comments in throttle_group_lock().
+ */
+void throttle_group_unlock(BlockDriverState *bs)
+{
+ ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts);
+ qemu_mutex_unlock(&tg->lock);
+}
+
+static void throttle_groups_init(void)
+{
+ qemu_mutex_init(&throttle_groups_lock);
+}
+
+block_init(throttle_groups_init);
diff --git a/block/vmdk.c b/block/vmdk.c
index b66745dfdd..be9263a34e 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -321,37 +321,13 @@ static int vmdk_is_cid_valid(BlockDriverState *bs)
return 1;
}
-/* Queue extents, if any, for reopen() */
+/* We have nothing to do for VMDK reopen, stubs just return success */
static int vmdk_reopen_prepare(BDRVReopenState *state,
BlockReopenQueue *queue, Error **errp)
{
- BDRVVmdkState *s;
- int ret = -1;
- int i;
- VmdkExtent *e;
-
assert(state != NULL);
assert(state->bs != NULL);
-
- if (queue == NULL) {
- error_setg(errp, "No reopen queue for VMDK extents");
- goto exit;
- }
-
- s = state->bs->opaque;
-
- assert(s != NULL);
-
- for (i = 0; i < s->num_extents; i++) {
- e = &s->extents[i];
- if (e->file != state->bs->file) {
- bdrv_reopen_queue(queue, e->file, state->flags);
- }
- }
- ret = 0;
-
-exit:
- return ret;
+ return 0;
}
static int vmdk_parent_open(BlockDriverState *bs)
@@ -543,7 +519,7 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
}
static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
- Error **errp);
+ QDict *options, Error **errp);
static char *vmdk_read_desc(BlockDriverState *file, uint64_t desc_offset,
Error **errp)
@@ -582,7 +558,7 @@ static char *vmdk_read_desc(BlockDriverState *file, uint64_t desc_offset,
static int vmdk_open_vmdk4(BlockDriverState *bs,
BlockDriverState *file,
- int flags, Error **errp)
+ int flags, QDict *options, Error **errp)
{
int ret;
uint32_t magic;
@@ -606,7 +582,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
if (!buf) {
return -EINVAL;
}
- ret = vmdk_open_desc_file(bs, flags, buf, errp);
+ ret = vmdk_open_desc_file(bs, flags, buf, options, errp);
g_free(buf);
return ret;
}
@@ -763,7 +739,7 @@ static int vmdk_parse_description(const char *desc, const char *opt_name,
/* Open an extent file and append to bs array */
static int vmdk_open_sparse(BlockDriverState *bs,
BlockDriverState *file, int flags,
- char *buf, Error **errp)
+ char *buf, QDict *options, Error **errp)
{
uint32_t magic;
@@ -773,7 +749,7 @@ static int vmdk_open_sparse(BlockDriverState *bs,
return vmdk_open_vmfs_sparse(bs, file, flags, errp);
break;
case VMDK4_MAGIC:
- return vmdk_open_vmdk4(bs, file, flags, errp);
+ return vmdk_open_vmdk4(bs, file, flags, options, errp);
break;
default:
error_setg(errp, "Image not in VMDK format");
@@ -783,7 +759,8 @@ static int vmdk_open_sparse(BlockDriverState *bs,
}
static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
- const char *desc_file_path, Error **errp)
+ const char *desc_file_path, QDict *options,
+ Error **errp)
{
int ret;
int matches;
@@ -797,6 +774,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
BlockDriverState *extent_file;
BDRVVmdkState *s = bs->opaque;
VmdkExtent *extent;
+ char extent_opt_prefix[32];
while (*p) {
/* parse extent line in one of below formats:
@@ -846,8 +824,12 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
extent_path = g_malloc0(PATH_MAX);
path_combine(extent_path, PATH_MAX, desc_file_path, fname);
extent_file = NULL;
- ret = bdrv_open(&extent_file, extent_path, NULL, NULL,
- bs->open_flags | BDRV_O_PROTOCOL, NULL, errp);
+
+ ret = snprintf(extent_opt_prefix, 32, "extents.%d", s->num_extents);
+ assert(ret < 32);
+
+ ret = bdrv_open_image(&extent_file, extent_path, options,
+ extent_opt_prefix, bs, &child_file, false, errp);
g_free(extent_path);
if (ret) {
return ret;
@@ -870,7 +852,8 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
if (!buf) {
ret = -EINVAL;
} else {
- ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, buf, errp);
+ ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, buf,
+ options, errp);
}
g_free(buf);
if (ret) {
@@ -898,7 +881,7 @@ next_line:
}
static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
- Error **errp)
+ QDict *options, Error **errp)
{
int ret;
char ct[128];
@@ -920,7 +903,7 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
}
s->create_type = g_strdup(ct);
s->desc_offset = 0;
- ret = vmdk_parse_extents(buf, bs, bs->file->exact_filename, errp);
+ ret = vmdk_parse_extents(buf, bs, bs->file->exact_filename, options, errp);
exit:
return ret;
}
@@ -942,11 +925,11 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
switch (magic) {
case VMDK3_MAGIC:
case VMDK4_MAGIC:
- ret = vmdk_open_sparse(bs, bs->file, flags, buf, errp);
+ ret = vmdk_open_sparse(bs, bs->file, flags, buf, options, errp);
s->desc_offset = 0x200;
break;
default:
- ret = vmdk_open_desc_file(bs, flags, buf, errp);
+ ret = vmdk_open_desc_file(bs, flags, buf, options, errp);
break;
}
if (ret) {
@@ -1248,6 +1231,17 @@ static VmdkExtent *find_extent(BDRVVmdkState *s,
return NULL;
}
+static inline uint64_t vmdk_find_index_in_cluster(VmdkExtent *extent,
+ int64_t sector_num)
+{
+ uint64_t index_in_cluster, extent_begin_sector, extent_relative_sector_num;
+
+ extent_begin_sector = extent->end_sector - extent->sectors;
+ extent_relative_sector_num = sector_num - extent_begin_sector;
+ index_in_cluster = extent_relative_sector_num % extent->cluster_sectors;
+ return index_in_cluster;
+}
+
static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, int *pnum)
{
@@ -1285,7 +1279,7 @@ static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs,
break;
}
- index_in_cluster = sector_num % extent->cluster_sectors;
+ index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num);
n = extent->cluster_sectors - index_in_cluster;
if (n > nb_sectors) {
n = nb_sectors;
@@ -1413,7 +1407,6 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
BDRVVmdkState *s = bs->opaque;
int ret;
uint64_t n, index_in_cluster;
- uint64_t extent_begin_sector, extent_relative_sector_num;
VmdkExtent *extent = NULL;
uint64_t cluster_offset;
@@ -1425,9 +1418,7 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
ret = get_cluster_offset(bs, extent, NULL,
sector_num << 9, false, &cluster_offset,
0, 0);
- extent_begin_sector = extent->end_sector - extent->sectors;
- extent_relative_sector_num = sector_num - extent_begin_sector;
- index_in_cluster = extent_relative_sector_num % extent->cluster_sectors;
+ index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num);
n = extent->cluster_sectors - index_in_cluster;
if (n > nb_sectors) {
n = nb_sectors;
@@ -1489,7 +1480,6 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
VmdkExtent *extent = NULL;
int ret;
int64_t index_in_cluster, n;
- uint64_t extent_begin_sector, extent_relative_sector_num;
uint64_t cluster_offset;
VmdkMetaData m_data;
@@ -1505,9 +1495,7 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
if (!extent) {
return -EIO;
}
- extent_begin_sector = extent->end_sector - extent->sectors;
- extent_relative_sector_num = sector_num - extent_begin_sector;
- index_in_cluster = extent_relative_sector_num % extent->cluster_sectors;
+ index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num);
n = extent->cluster_sectors - index_in_cluster;
if (n > nb_sectors) {
n = nb_sectors;
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index 85cda4cfa7..0d9df47ce4 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -43,7 +43,7 @@ void qmp_nbd_server_start(SocketAddress *addr, Error **errp)
server_fd = socket_listen(addr, errp);
if (server_fd != -1) {
- qemu_set_fd_handler2(server_fd, NULL, nbd_accept, NULL, NULL);
+ qemu_set_fd_handler(server_fd, nbd_accept, NULL, NULL);
}
}
@@ -129,7 +129,7 @@ void qmp_nbd_server_stop(Error **errp)
}
if (server_fd != -1) {
- qemu_set_fd_handler2(server_fd, NULL, NULL, NULL, NULL);
+ qemu_set_fd_handler(server_fd, NULL, NULL, NULL);
close(server_fd);
server_fd = -1;
}
diff --git a/blockdev.c b/blockdev.c
index 5eaf77e599..41d7e0fe54 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -34,6 +34,7 @@
#include "sysemu/blockdev.h"
#include "hw/block/block.h"
#include "block/blockjob.h"
+#include "block/throttle-groups.h"
#include "monitor/monitor.h"
#include "qemu/option.h"
#include "qemu/config-file.h"
@@ -357,6 +358,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
const char *id;
bool has_driver_specific_opts;
BlockdevDetectZeroesOptions detect_zeroes;
+ const char *throttling_group;
/* Check common options by copying from bs_opts to opts, all other options
* stay in bs_opts for processing by bdrv_open(). */
@@ -391,13 +393,13 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
}
}
- if (qemu_opt_get_bool(opts, "cache.writeback", true)) {
+ if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, true)) {
bdrv_flags |= BDRV_O_CACHE_WB;
}
- if (qemu_opt_get_bool(opts, "cache.direct", false)) {
+ if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_DIRECT, false)) {
bdrv_flags |= BDRV_O_NOCACHE;
}
- if (qemu_opt_get_bool(opts, "cache.no-flush", false)) {
+ if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) {
bdrv_flags |= BDRV_O_NO_FLUSH;
}
@@ -459,6 +461,8 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
cfg.op_size = qemu_opt_get_number(opts, "throttling.iops-size", 0);
+ throttling_group = qemu_opt_get(opts, "throttling.group");
+
if (!check_throttle_config(&cfg, &error)) {
error_propagate(errp, error);
goto early_err;
@@ -547,7 +551,10 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
/* disk I/O throttling */
if (throttle_enabled(&cfg)) {
- bdrv_io_limits_enable(bs);
+ if (!throttling_group) {
+ throttling_group = blk_name(blk);
+ }
+ bdrv_io_limits_enable(bs, throttling_group);
bdrv_set_io_limits(bs, &cfg);
}
@@ -711,6 +718,8 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
{ "iops_size", "throttling.iops-size" },
+ { "group", "throttling.group" },
+
{ "readonly", "read-only" },
};
@@ -733,16 +742,16 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
}
/* Specific options take precedence */
- if (!qemu_opt_get(all_opts, "cache.writeback")) {
- qemu_opt_set_bool(all_opts, "cache.writeback",
+ if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_WB)) {
+ qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_WB,
!!(flags & BDRV_O_CACHE_WB), &error_abort);
}
- if (!qemu_opt_get(all_opts, "cache.direct")) {
- qemu_opt_set_bool(all_opts, "cache.direct",
+ if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_DIRECT)) {
+ qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_DIRECT,
!!(flags & BDRV_O_NOCACHE), &error_abort);
}
- if (!qemu_opt_get(all_opts, "cache.no-flush")) {
- qemu_opt_set_bool(all_opts, "cache.no-flush",
+ if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_NO_FLUSH)) {
+ qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_NO_FLUSH,
!!(flags & BDRV_O_NO_FLUSH), &error_abort);
}
qemu_opt_unset(all_opts, "cache");
@@ -933,7 +942,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
&error_abort);
if (arch_type == QEMU_ARCH_S390X) {
- qemu_opt_set(devopts, "driver", "virtio-blk-s390", &error_abort);
+ qemu_opt_set(devopts, "driver", "virtio-blk-ccw", &error_abort);
} else {
qemu_opt_set(devopts, "driver", "virtio-blk-pci", &error_abort);
}
@@ -1951,7 +1960,9 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
bool has_iops_wr_max,
int64_t iops_wr_max,
bool has_iops_size,
- int64_t iops_size, Error **errp)
+ int64_t iops_size,
+ bool has_group,
+ const char *group, Error **errp)
{
ThrottleConfig cfg;
BlockDriverState *bs;
@@ -2004,14 +2015,19 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
- if (!bs->io_limits_enabled && throttle_enabled(&cfg)) {
- bdrv_io_limits_enable(bs);
- } else if (bs->io_limits_enabled && !throttle_enabled(&cfg)) {
- bdrv_io_limits_disable(bs);
- }
-
- if (bs->io_limits_enabled) {
+ if (throttle_enabled(&cfg)) {
+ /* Enable I/O limits if they're not enabled yet, otherwise
+ * just update the throttling group. */
+ if (!bs->io_limits_enabled) {
+ bdrv_io_limits_enable(bs, has_group ? group : device);
+ } else if (has_group) {
+ bdrv_io_limits_update_group(bs, group);
+ }
+ /* Set the new throttling configuration */
bdrv_set_io_limits(bs, &cfg);
+ } else if (bs->io_limits_enabled) {
+ /* If all throttling settings are set to 0, disable I/O limits */
+ bdrv_io_limits_disable(bs);
}
aio_context_release(aio_context);
@@ -2113,7 +2129,7 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
aio_context_release(aio_context);
}
-int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
+void hmp_drive_del(Monitor *mon, const QDict *qdict)
{
const char *id = qdict_get_str(qdict, "id");
BlockBackend *blk;
@@ -2124,14 +2140,14 @@ int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
blk = blk_by_name(id);
if (!blk) {
error_report("Device '%s' not found", id);
- return -1;
+ return;
}
bs = blk_bs(blk);
if (!blk_legacy_dinfo(blk)) {
error_report("Deleting device added with blockdev-add"
" is not supported");
- return -1;
+ return;
}
aio_context = bdrv_get_aio_context(bs);
@@ -2140,7 +2156,7 @@ int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) {
error_report_err(local_err);
aio_context_release(aio_context);
- return -1;
+ return;
}
/* quiesce block driver; prevent further io */
@@ -2163,7 +2179,6 @@ int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
}
aio_context_release(aio_context);
- return 0;
}
void qmp_block_resize(bool has_device, const char *device,
@@ -3106,15 +3121,15 @@ QemuOptsList qemu_common_drive_opts = {
.type = QEMU_OPT_STRING,
.help = "discard operation (ignore/off, unmap/on)",
},{
- .name = "cache.writeback",
+ .name = BDRV_OPT_CACHE_WB,
.type = QEMU_OPT_BOOL,
.help = "enables writeback mode for any caches",
},{
- .name = "cache.direct",
+ .name = BDRV_OPT_CACHE_DIRECT,
.type = QEMU_OPT_BOOL,
.help = "enables use of O_DIRECT (bypass the host page cache)",
},{
- .name = "cache.no-flush",
+ .name = BDRV_OPT_CACHE_NO_FLUSH,
.type = QEMU_OPT_BOOL,
.help = "ignore any flush requests for the device",
},{
@@ -3190,6 +3205,10 @@ QemuOptsList qemu_common_drive_opts = {
.type = QEMU_OPT_NUMBER,
.help = "when limiting by iops max size of an I/O in bytes",
},{
+ .name = "throttling.group",
+ .type = QEMU_OPT_STRING,
+ .help = "name of the block throttling group",
+ },{
.name = "copy-on-read",
.type = QEMU_OPT_BOOL,
.help = "copy read data from backing file into image file",
diff --git a/bsd-user/main.c b/bsd-user/main.c
index 5bfaf5c421..ba0b9981f5 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -108,10 +108,6 @@ void cpu_list_unlock(void)
/***********************************************************/
/* CPUX86 core interface */
-void cpu_smm_update(CPUX86State *env)
-{
-}
-
uint64_t cpu_get_tsc(CPUX86State *env)
{
return cpu_get_real_ticks();
diff --git a/configure b/configure
index 4e2f78a173..222694f34d 100755
--- a/configure
+++ b/configure
@@ -285,8 +285,6 @@ sysconfdir="\${prefix}/etc"
local_statedir="\${prefix}/var"
confsuffix="/qemu"
slirp="yes"
-fmod_lib=""
-fmod_inc=""
oss_lib=""
bsd="no"
linux="no"
@@ -353,7 +351,7 @@ for opt do
;;
--cpu=*) cpu="$optarg"
;;
- --extra-cflags=*) QEMU_CFLAGS="$optarg $QEMU_CFLAGS"
+ --extra-cflags=*) QEMU_CFLAGS="$QEMU_CFLAGS $optarg"
EXTRA_CFLAGS="$optarg"
;;
--extra-ldflags=*) LDFLAGS="$optarg $LDFLAGS"
@@ -437,6 +435,14 @@ EOF
compile_object
}
+check_include() {
+cat > $TMPC <<EOF
+#include <$1>
+int main(void) { return 0; }
+EOF
+ compile_object
+}
+
write_c_skeleton() {
cat > $TMPC <<EOF
int main(void) { return 0; }
@@ -564,24 +570,28 @@ case $targetos in
CYGWIN*)
mingw32="yes"
QEMU_CFLAGS="-mno-cygwin $QEMU_CFLAGS"
- audio_possible_drivers="winwave sdl"
- audio_drv_list="winwave"
+ audio_possible_drivers="sdl"
+ audio_drv_list="sdl"
;;
MINGW32*)
mingw32="yes"
- audio_possible_drivers="winwave dsound sdl fmod"
- audio_drv_list="winwave"
+ audio_possible_drivers="dsound sdl"
+ if check_include dsound.h; then
+ audio_drv_list="dsound"
+ else
+ audio_drv_list=""
+ fi
;;
GNU/kFreeBSD)
bsd="yes"
audio_drv_list="oss"
- audio_possible_drivers="oss sdl esd pa"
+ audio_possible_drivers="oss sdl pa"
;;
FreeBSD)
bsd="yes"
make="${MAKE-gmake}"
audio_drv_list="oss"
- audio_possible_drivers="oss sdl esd pa"
+ audio_possible_drivers="oss sdl pa"
# needed for kinfo_getvmmap(3) in libutil.h
LIBS="-lutil $LIBS"
netmap="" # enable netmap autodetect
@@ -591,14 +601,14 @@ DragonFly)
bsd="yes"
make="${MAKE-gmake}"
audio_drv_list="oss"
- audio_possible_drivers="oss sdl esd pa"
+ audio_possible_drivers="oss sdl pa"
HOST_VARIANT_DIR="dragonfly"
;;
NetBSD)
bsd="yes"
make="${MAKE-gmake}"
audio_drv_list="oss"
- audio_possible_drivers="oss sdl esd"
+ audio_possible_drivers="oss sdl"
oss_lib="-lossaudio"
HOST_VARIANT_DIR="netbsd"
;;
@@ -606,7 +616,7 @@ OpenBSD)
bsd="yes"
make="${MAKE-gmake}"
audio_drv_list="sdl"
- audio_possible_drivers="sdl esd"
+ audio_possible_drivers="sdl"
HOST_VARIANT_DIR="openbsd"
;;
Darwin)
@@ -619,7 +629,7 @@ Darwin)
fi
cocoa="yes"
audio_drv_list="coreaudio"
- audio_possible_drivers="coreaudio sdl fmod"
+ audio_possible_drivers="coreaudio sdl"
LDFLAGS="-framework CoreFoundation -framework IOKit $LDFLAGS"
libs_softmmu="-F/System/Library/Frameworks -framework Cocoa -framework IOKit $libs_softmmu"
# Disable attempts to use ObjectiveC features in os/object.h since they
@@ -674,15 +684,12 @@ Haiku)
;;
*)
audio_drv_list="oss"
- audio_possible_drivers="oss alsa sdl esd pa"
+ audio_possible_drivers="oss alsa sdl pa"
linux="yes"
linux_user="yes"
kvm="yes"
vhost_net="yes"
vhost_scsi="yes"
- if [ "$cpu" = "i386" -o "$cpu" = "x86_64" -o "$cpu" = "x32" ] ; then
- audio_possible_drivers="$audio_possible_drivers fmod"
- fi
QEMU_INCLUDES="-I\$(SRC_PATH)/linux-headers -I$(pwd)/linux-headers $QEMU_INCLUDES"
;;
esac
@@ -847,10 +854,6 @@ for opt do
;;
--enable-vnc) vnc="yes"
;;
- --fmod-lib=*) fmod_lib="$optarg"
- ;;
- --fmod-inc=*) fmod_inc="$optarg"
- ;;
--oss-lib=*) oss_lib="$optarg"
;;
--audio-drv-list=*) audio_drv_list="$optarg"
@@ -1349,8 +1352,6 @@ Advanced options (experts only):
--disable-guest-base disable GUEST_BASE support
--enable-pie build Position Independent Executables
--disable-pie do not build Position Independent Executables
- --fmod-lib path to FMOD library
- --fmod-inc path to FMOD includes
--oss-lib path to OSS library
--cpu=CPU Build for host CPU [$cpu]
--disable-uuid disable uuid support
@@ -2621,21 +2622,6 @@ for drv in $audio_drv_list; do
libs_softmmu="-lasound $libs_softmmu"
;;
- fmod)
- if test -z $fmod_lib || test -z $fmod_inc; then
- error_exit "You must specify path to FMOD library and headers" \
- "Example: --fmod-inc=/path/include/fmod --fmod-lib=/path/lib/libfmod-3.74.so"
- fi
- audio_drv_probe $drv fmod.h $fmod_lib "return FSOUND_GetVersion();" "-I $fmod_inc"
- libs_softmmu="$fmod_lib $libs_softmmu"
- ;;
-
- esd)
- audio_drv_probe $drv esd.h -lesd 'return esd_play_stream(0, 0, "", 0);'
- libs_softmmu="-lesd $libs_softmmu"
- audio_pt_int="yes"
- ;;
-
pa)
audio_drv_probe $drv pulse/mainloop.h "-lpulse" \
"pa_mainloop *m = 0; pa_mainloop_free (m); return 0;"
@@ -2660,11 +2646,6 @@ for drv in $audio_drv_list; do
# XXX: Probes for CoreAudio, DirectSound, SDL(?)
;;
- winwave)
- libs_softmmu="-lwinmm $libs_softmmu"
- audio_win_int="yes"
- ;;
-
*)
echo "$audio_possible_drivers" | grep -q "\<$drv\>" || {
error_exit "Unknown driver '$drv' selected" \
@@ -3115,9 +3096,11 @@ fi
if test "$fdt" != "no" ; then
fdt_libs="-lfdt"
# explicitly check for libfdt_env.h as it is missing in some stable installs
+ # and test for required functions to make sure we are on a version >= 1.4.0
cat > $TMPC << EOF
+#include <libfdt.h>
#include <libfdt_env.h>
-int main(void) { return 0; }
+int main(void) { fdt_get_property_by_offset(0, 0, 0); return 0; }
EOF
if compile_prog "" "$fdt_libs" ; then
# system DTC is good - use it
@@ -3135,7 +3118,7 @@ EOF
fdt_libs="-L\$(BUILD_DIR)/dtc/libfdt $fdt_libs"
elif test "$fdt" = "yes" ; then
# have neither and want - prompt for system/submodule install
- error_exit "DTC (libfdt) not present. Your options:" \
+ error_exit "DTC (libfdt) version >= 1.4.0 not present. Your options:" \
" (1) Preferred: Install the DTC (libfdt) devel package" \
" (2) Fetch the DTC submodule, using:" \
" git submodule update --init dtc"
@@ -4627,9 +4610,6 @@ echo "CONFIG_AUDIO_DRIVERS=$audio_drv_list" >> $config_host_mak
for drv in $audio_drv_list; do
def=CONFIG_`echo $drv | LC_ALL=C tr '[a-z]' '[A-Z]'`
echo "$def=y" >> $config_host_mak
- if test "$drv" = "fmod"; then
- echo "FMOD_CFLAGS=-I$fmod_inc" >> $config_host_mak
- fi
done
if test "$audio_pt_int" = "yes" ; then
echo "CONFIG_AUDIO_PT_INT=y" >> $config_host_mak
diff --git a/cpus.c b/cpus.c
index de6469fa8d..b85fb5f03f 100644
--- a/cpus.c
+++ b/cpus.c
@@ -105,6 +105,7 @@ static bool all_cpu_threads_idle(void)
/* Protected by TimersState seqlock */
+static bool icount_sleep = true;
static int64_t vm_clock_warp_start = -1;
/* Conversion factor from emulated instructions to virtual clock ticks. */
static int icount_time_shift;
@@ -393,15 +394,18 @@ void qemu_clock_warp(QEMUClockType type)
return;
}
- /*
- * If the CPUs have been sleeping, advance QEMU_CLOCK_VIRTUAL timer now.
- * This ensures that the deadline for the timer is computed correctly below.
- * This also makes sure that the insn counter is synchronized before the
- * CPU starts running, in case the CPU is woken by an event other than
- * the earliest QEMU_CLOCK_VIRTUAL timer.
- */
- icount_warp_rt(NULL);
- timer_del(icount_warp_timer);
+ if (icount_sleep) {
+ /*
+ * If the CPUs have been sleeping, advance QEMU_CLOCK_VIRTUAL timer now.
+ * This ensures that the deadline for the timer is computed correctly
+ * below.
+ * This also makes sure that the insn counter is synchronized before
+ * the CPU starts running, in case the CPU is woken by an event other
+ * than the earliest QEMU_CLOCK_VIRTUAL timer.
+ */
+ icount_warp_rt(NULL);
+ timer_del(icount_warp_timer);
+ }
if (!all_cpu_threads_idle()) {
return;
}
@@ -415,6 +419,11 @@ void qemu_clock_warp(QEMUClockType type)
clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT);
deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL);
if (deadline < 0) {
+ static bool notified;
+ if (!icount_sleep && !notified) {
+ error_report("WARNING: icount sleep disabled and no active timers");
+ notified = true;
+ }
return;
}
@@ -425,23 +434,35 @@ void qemu_clock_warp(QEMUClockType type)
* interrupt to wake it up, but the interrupt never comes because
* the vCPU isn't running any insns and thus doesn't advance the
* QEMU_CLOCK_VIRTUAL.
- *
- * An extreme solution for this problem would be to never let VCPUs
- * sleep in icount mode if there is a pending QEMU_CLOCK_VIRTUAL
- * timer; rather time could just advance to the next QEMU_CLOCK_VIRTUAL
- * event. Instead, we do stop VCPUs and only advance QEMU_CLOCK_VIRTUAL
- * after some "real" time, (related to the time left until the next
- * event) has passed. The QEMU_CLOCK_VIRTUAL_RT clock will do this.
- * This avoids that the warps are visible externally; for example,
- * you will not be sending network packets continuously instead of
- * every 100ms.
*/
- seqlock_write_lock(&timers_state.vm_clock_seqlock);
- if (vm_clock_warp_start == -1 || vm_clock_warp_start > clock) {
- vm_clock_warp_start = clock;
+ if (!icount_sleep) {
+ /*
+ * We never let VCPUs sleep in no sleep icount mode.
+ * If there is a pending QEMU_CLOCK_VIRTUAL timer we just advance
+ * to the next QEMU_CLOCK_VIRTUAL event and notify it.
+ * It is useful when we want a deterministic execution time,
+ * isolated from host latencies.
+ */
+ seqlock_write_lock(&timers_state.vm_clock_seqlock);
+ timers_state.qemu_icount_bias += deadline;
+ seqlock_write_unlock(&timers_state.vm_clock_seqlock);
+ qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
+ } else {
+ /*
+ * We do stop VCPUs and only advance QEMU_CLOCK_VIRTUAL after some
+ * "real" time, (related to the time left until the next event) has
+ * passed. The QEMU_CLOCK_VIRTUAL_RT clock will do this.
+ * This avoids that the warps are visible externally; for example,
+ * you will not be sending network packets continuously instead of
+ * every 100ms.
+ */
+ seqlock_write_lock(&timers_state.vm_clock_seqlock);
+ if (vm_clock_warp_start == -1 || vm_clock_warp_start > clock) {
+ vm_clock_warp_start = clock;
+ }
+ seqlock_write_unlock(&timers_state.vm_clock_seqlock);
+ timer_mod_anticipate(icount_warp_timer, clock + deadline);
}
- seqlock_write_unlock(&timers_state.vm_clock_seqlock);
- timer_mod_anticipate(icount_warp_timer, clock + deadline);
} else if (deadline == 0) {
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
}
@@ -459,6 +480,7 @@ static const VMStateDescription icount_vmstate_timers = {
.name = "timer/icount",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = icount_state_needed,
.fields = (VMStateField[]) {
VMSTATE_INT64(qemu_icount_bias, TimersState),
VMSTATE_INT64(qemu_icount, TimersState),
@@ -476,13 +498,9 @@ static const VMStateDescription vmstate_timers = {
VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &icount_vmstate_timers,
- .needed = icount_state_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &icount_vmstate_timers,
+ NULL
}
};
@@ -504,9 +522,18 @@ void configure_icount(QemuOpts *opts, Error **errp)
}
return;
}
+
+ icount_sleep = qemu_opt_get_bool(opts, "sleep", true);
+ if (icount_sleep) {
+ icount_warp_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT,
+ icount_warp_rt, NULL);
+ }
+
icount_align_option = qemu_opt_get_bool(opts, "align", false);
- icount_warp_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT,
- icount_warp_rt, NULL);
+
+ if (icount_align_option && !icount_sleep) {
+ error_setg(errp, "align=on and sleep=no are incompatible");
+ }
if (strcmp(option, "auto") != 0) {
errno = 0;
icount_time_shift = strtol(option, &rem_str, 0);
@@ -517,6 +544,8 @@ void configure_icount(QemuOpts *opts, Error **errp)
return;
} else if (icount_align_option) {
error_setg(errp, "shift=auto and align=on are incompatible");
+ } else if (!icount_sleep) {
+ error_setg(errp, "shift=auto and sleep=no are incompatible");
}
use_icount = 2;
diff --git a/cputlb.c b/cputlb.c
index 7606548200..a50608676c 100644
--- a/cputlb.c
+++ b/cputlb.c
@@ -125,14 +125,13 @@ void tlb_flush_page(CPUState *cpu, target_ulong addr)
can be detected */
void tlb_protect_code(ram_addr_t ram_addr)
{
- cpu_physical_memory_reset_dirty(ram_addr, TARGET_PAGE_SIZE,
- DIRTY_MEMORY_CODE);
+ cpu_physical_memory_test_and_clear_dirty(ram_addr, TARGET_PAGE_SIZE,
+ DIRTY_MEMORY_CODE);
}
/* update the TLB so that writes in physical page 'phys_addr' are no longer
tested for self modifying code */
-void tlb_unprotect_code_phys(CPUState *cpu, ram_addr_t ram_addr,
- target_ulong vaddr)
+void tlb_unprotect_code(ram_addr_t ram_addr)
{
cpu_physical_memory_set_dirty_flag(ram_addr, DIRTY_MEMORY_CODE);
}
diff --git a/default-configs/mips-softmmu.mak b/default-configs/mips-softmmu.mak
index fd0607db6b..44467c37c1 100644
--- a/default-configs/mips-softmmu.mak
+++ b/default-configs/mips-softmmu.mak
@@ -24,14 +24,9 @@ CONFIG_PIIX4=y
CONFIG_IDE_ISA=y
CONFIG_IDE_PIIX=y
CONFIG_NE2000_ISA=y
-CONFIG_RC4030=y
-CONFIG_DP8393X=y
-CONFIG_DS1225Y=y
CONFIG_MIPSNET=y
CONFIG_PFLASH_CFI01=y
-CONFIG_G364FB=y
CONFIG_I8259=y
-CONFIG_JAZZ_LED=y
CONFIG_MC146818RTC=y
CONFIG_ISA_TESTDEV=y
CONFIG_EMPTY_SLOT=y
diff --git a/default-configs/mips64-softmmu.mak b/default-configs/mips64-softmmu.mak
index b8c791021a..66ed5f94c5 100644
--- a/default-configs/mips64-softmmu.mak
+++ b/default-configs/mips64-softmmu.mak
@@ -29,6 +29,7 @@ CONFIG_DP8393X=y
CONFIG_DS1225Y=y
CONFIG_MIPSNET=y
CONFIG_PFLASH_CFI01=y
+CONFIG_JAZZ=y
CONFIG_G364FB=y
CONFIG_I8259=y
CONFIG_JAZZ_LED=y
diff --git a/default-configs/mips64el-softmmu.mak b/default-configs/mips64el-softmmu.mak
index ae4274b3a7..bfca2b2b7c 100644
--- a/default-configs/mips64el-softmmu.mak
+++ b/default-configs/mips64el-softmmu.mak
@@ -31,6 +31,7 @@ CONFIG_DS1225Y=y
CONFIG_MIPSNET=y
CONFIG_PFLASH_CFI01=y
CONFIG_FULONG=y
+CONFIG_JAZZ=y
CONFIG_G364FB=y
CONFIG_I8259=y
CONFIG_JAZZ_LED=y
diff --git a/default-configs/mipsel-softmmu.mak b/default-configs/mipsel-softmmu.mak
index 1e2374be13..0162ef0249 100644
--- a/default-configs/mipsel-softmmu.mak
+++ b/default-configs/mipsel-softmmu.mak
@@ -24,14 +24,9 @@ CONFIG_PIIX4=y
CONFIG_IDE_ISA=y
CONFIG_IDE_PIIX=y
CONFIG_NE2000_ISA=y
-CONFIG_RC4030=y
-CONFIG_DP8393X=y
-CONFIG_DS1225Y=y
CONFIG_MIPSNET=y
CONFIG_PFLASH_CFI01=y
-CONFIG_G364FB=y
CONFIG_I8259=y
-CONFIG_JAZZ_LED=y
CONFIG_MC146818RTC=y
CONFIG_ISA_TESTDEV=y
CONFIG_EMPTY_SLOT=y
diff --git a/default-configs/s390x-softmmu.mak b/default-configs/s390x-softmmu.mak
index f9e13f177e..36e15de336 100644
--- a/default-configs/s390x-softmmu.mak
+++ b/default-configs/s390x-softmmu.mak
@@ -4,3 +4,4 @@ CONFIG_VIRTIO=y
CONFIG_SCLPCONSOLE=y
CONFIG_S390_FLIC=y
CONFIG_S390_FLIC_KVM=$(CONFIG_KVM)
+CONFIG_WDT_DIAG288=y
diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index 2f2955bf5d..62575ebcd8 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -7,6 +7,7 @@ CONFIG_QXL=$(CONFIG_SPICE)
CONFIG_VGA_ISA=y
CONFIG_VGA_CIRRUS=y
CONFIG_VMWARE_VGA=y
+CONFIG_VIRTIO_VGA=y
CONFIG_VMMOUSE=y
CONFIG_SERIAL=y
CONFIG_PARALLEL=y
diff --git a/device_tree.c b/device_tree.c
index 3d119ef0bd..d2de580947 100644
--- a/device_tree.c
+++ b/device_tree.c
@@ -18,7 +18,6 @@
#include <unistd.h>
#include <stdlib.h>
-#include "config.h"
#include "qemu-common.h"
#include "qemu/error-report.h"
#include "sysemu/device_tree.h"
diff --git a/disas/mips.c b/disas/mips.c
index 1afe0c5511..32940feb95 100644
--- a/disas/mips.c
+++ b/disas/mips.c
@@ -2238,6 +2238,8 @@ const struct mips_opcode mips_builtin_opcodes[] =
{"ceil.l.s", "D,S", 0x4600000a, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I3|I33 },
{"ceil.w.d", "D,S", 0x4620000e, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I2 },
{"ceil.w.s", "D,S", 0x4600000e, 0xffff003f, WR_D|RD_S|FP_S, 0, I2 },
+{"mfhc0", "t,G,H", 0x40400000, 0xffe007f8, LCD|WR_t|RD_C0, 0, I33},
+{"mthc0", "t,G,H", 0x40c00000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, 0, I33},
{"cfc0", "t,G", 0x40400000, 0xffe007ff, LCD|WR_t|RD_C0, 0, I1 },
{"cfc1", "t,G", 0x44400000, 0xffe007ff, LCD|WR_t|RD_C1|FP_S, 0, I1 },
{"cfc1", "t,S", 0x44400000, 0xffe007ff, LCD|WR_t|RD_C1|FP_S, 0, I1 },
@@ -2407,6 +2409,7 @@ const struct mips_opcode mips_builtin_opcodes[] =
{"emt", "", 0x41600be1, 0xffffffff, TRAP, 0, MT32 },
{"emt", "t", 0x41600be1, 0xffe0ffff, TRAP|WR_t, 0, MT32 },
{"eret", "", 0x42000018, 0xffffffff, 0, 0, I3|I32 },
+{"eretnc", "", 0x42000058, 0xffffffff, 0, 0, I33},
{"evpe", "", 0x41600021, 0xffffffff, TRAP, 0, MT32 },
{"evpe", "t", 0x41600021, 0xffe0ffff, TRAP|WR_t, 0, MT32 },
{"ext", "t,r,+A,+C", 0x7c000000, 0xfc00003f, WR_t|RD_s, 0, I33 },
diff --git a/docs/migration.txt b/docs/migration.txt
index 0492a4547a..f6df4beb2a 100644
--- a/docs/migration.txt
+++ b/docs/migration.txt
@@ -257,6 +257,7 @@ const VMStateDescription vmstate_ide_drive_pio_state = {
.minimum_version_id = 1,
.pre_save = ide_drive_pio_pre_save,
.post_load = ide_drive_pio_post_load,
+ .needed = ide_drive_pio_state_needed,
.fields = (VMStateField[]) {
VMSTATE_INT32(req_nb_sectors, IDEState),
VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1,
@@ -279,13 +280,9 @@ const VMStateDescription vmstate_ide_drive = {
.... several fields ....
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection []) {
- {
- .vmsd = &vmstate_ide_drive_pio_state,
- .needed = ide_drive_pio_state_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_ide_drive_pio_state,
+ NULL
}
};
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 3f0522ea0f..61b5be47fb 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -680,8 +680,6 @@ Example:
out:
error_propagate(errp, err);
}
- $ python scripts/qapi-commands.py --output-dir="qapi-generated" \
- --prefix="example-" example-schema.json
$ cat qapi-generated/example-qapi-visit.h
[Uninteresting stuff omitted...]
diff --git a/docs/specs/fw_cfg.txt b/docs/specs/fw_cfg.txt
index 6accd924bd..74351dd18f 100644
--- a/docs/specs/fw_cfg.txt
+++ b/docs/specs/fw_cfg.txt
@@ -203,3 +203,24 @@ completes fully overwriting the item's data.
NOTE: This function is deprecated, and will be completely removed
starting with QEMU v2.4.
+
+== Externally Provided Items ==
+
+As of v2.4, "file" fw_cfg items (i.e., items with selector keys above
+FW_CFG_FILE_FIRST, and with a corresponding entry in the fw_cfg file
+directory structure) may be inserted via the QEMU command line, using
+the following syntax:
+
+ -fw_cfg [name=]<item_name>,file=<path>
+
+where <item_name> is the fw_cfg item name, and <path> is the location
+on the host file system of a file containing the data to be inserted.
+
+NOTE: Users *SHOULD* choose item names beginning with the prefix "opt/"
+when using the "-fw_cfg" command line option, to avoid conflicting with
+item names used internally by QEMU. For instance:
+
+ -fw_cfg name=opt/my_item_name,file=./my_blob.bin
+
+Similarly, QEMU developers *SHOULD NOT* use item names prefixed with
+"opt/" when inserting items programmatically, e.g. via fw_cfg_add_file().
diff --git a/docs/specs/ppc-spapr-hotplug.txt b/docs/specs/ppc-spapr-hotplug.txt
new file mode 100644
index 0000000000..d35771cc2b
--- /dev/null
+++ b/docs/specs/ppc-spapr-hotplug.txt
@@ -0,0 +1,287 @@
+= sPAPR Dynamic Reconfiguration =
+
+sPAPR/"pseries" guests make use of a facility called dynamic-reconfiguration
+to handle hotplugging of dynamic "physical" resources like PCI cards, or
+"logical"/paravirtual resources like memory, CPUs, and "physical"
+host-bridges, which are generally managed by the host/hypervisor and provided
+to guests as virtualized resources. The specifics of dynamic-reconfiguration
+are documented extensively in PAPR+ v2.7, Section 13.1. This document
+provides a summary of that information as it applies to the implementation
+within QEMU.
+
+== Dynamic-reconfiguration Connectors ==
+
+To manage hotplug/unplug of these resources, a firmware abstraction known as
+a Dynamic Resource Connector (DRC) is used to assign a particular dynamic
+resource to the guest, and provide an interface for the guest to manage
+configuration/removal of the resource associated with it.
+
+== Device-tree description of DRCs ==
+
+A set of 4 Open Firmware device tree array properties are used to describe
+the name/index/power-domain/type of each DRC allocated to a guest at
+boot-time. There may be multiple sets of these arrays, rooted at different
+paths in the device tree depending on the type of resource the DRCs manage.
+
+In some cases, the DRCs themselves may be provided by a dynamic resource,
+such as the DRCs managing PCI slots on a hotplugged PHB. In this case the
+arrays would be fetched as part of the device tree retrieval interfaces
+for hotplugged resources described under "Guest->Host interface".
+
+The array properties are described below. Each entry/element in an array
+describes the DRC identified by the element in the corresponding position
+of ibm,drc-indexes:
+
+ibm,drc-names:
+ first 4-bytes: BE-encoded integer denoting the number of entries
+ each entry: a NULL-terminated <name> string encoded as a byte array
+
+ <name> values for logical/virtual resources are defined in PAPR+ v2.7,
+ Section 13.5.2.4, and basically consist of the type of the resource
+ followed by a space and a numerical value that's unique across resources
+ of that type.
+
+ <name> values for "physical" resources such as PCI or VIO devices are
+ defined as being "location codes", which are the "location labels" of
+ each encapsulating device, starting from the chassis down to the
+ individual slot for the device, concatenated by a hyphen. This provides
+ a mapping of resources to a physical location in a chassis for debugging
+ purposes. For QEMU, this mapping is less important, so we assign a
+ location code that conforms to naming specifications, but is simply a
+ location label for the slot by itself to simplify the implementation.
+ The naming convention for location labels is documented in detail in
+ PAPR+ v2.7, Section 12.3.1.5, and in our case amounts to using "C<n>"
+ for PCI/VIO device slots, where <n> is unique across all PCI/VIO
+ device slots.
+
+ibm,drc-indexes:
+ first 4-bytes: BE-encoded integer denoting the number of entries
+ each 4-byte entry: BE-encoded <index> integer that is unique across all DRCs
+ in the machine
+
+ <index> is arbitrary, but in the case of QEMU we try to maintain the
+ convention used to assign them to pSeries guests on pHyp:
+
+ bit[31:28]: integer encoding of <type>, where <type> is:
+ 1 for CPU resource
+ 2 for PHB resource
+ 3 for VIO resource
+ 4 for PCI resource
+ 8 for Memory resource
+ bit[27:0]: integer encoding of <id>, where <id> is unique across
+ all resources of specified type
+
+ibm,drc-power-domains:
+ first 4-bytes: BE-encoded integer denoting the number of entries
+ each 4-byte entry: 32-bit, BE-encoded <index> integer that specifies the
+ power domain the resource will be assigned to. In the case of QEMU
+ we associated all resources with a "live insertion" domain, where the
+ power is assumed to be managed automatically. The integer value for
+ this domain is a special value of -1.
+
+
+ibm,drc-types:
+ first 4-bytes: BE-encoded integer denoting the number of entries
+ each entry: a NULL-terminated <type> string encoded as a byte array
+
+ <type> is assigned as follows:
+ "CPU" for a CPU
+ "PHB" for a physical host-bridge
+ "SLOT" for a VIO slot
+ "28" for a PCI slot
+ "MEM" for memory resource
+
+== Guest->Host interface to manage dynamic resources ==
+
+Each DRC is given a globally unique DRC Index, and resources associated with
+a particular DRC are configured/managed by the guest via a number of RTAS
+calls which reference individual DRCs based on the DRC index. This can be
+considered the guest->host interface.
+
+rtas-set-power-level:
+ arg[0]: integer identifying power domain
+ arg[1]: new power level for the domain, 0-100
+ output[0]: status, 0 on success
+ output[1]: power level after command
+
+ Set the power level for a specified power domain
+
+rtas-get-power-level:
+ arg[0]: integer identifying power domain
+ output[0]: status, 0 on success
+ output[1]: current power level
+
+ Get the power level for a specified power domain
+
+rtas-set-indicator:
+ arg[0]: integer identifying sensor/indicator type
+ arg[1]: index of sensor, for DR-related sensors this is generally the
+ DRC index
+ arg[2]: desired sensor value
+ output[0]: status, 0 on success
+
+ Set the state of an indicator or sensor. For the purpose of this document we
+ focus on the indicator/sensor types associated with a DRC. The types are:
+
+ 9001: isolation-state, controls/indicates whether a device has been made
+ accessible to a guest
+
+ supported sensor values:
+ 0: isolate, device is made unaccessible by guest OS
+ 1: unisolate, device is made available to guest OS
+
+ 9002: dr-indicator, controls "visual" indicator associated with device
+
+ supported sensor values:
+ 0: inactive, resource may be safely removed
+ 1: active, resource is in use and cannot be safely removed
+ 2: identify, used to visually identify slot for interactive hotplug
+ 3: action, in most cases, used in the same manner as identify
+
+ 9003: allocation-state, generally only used for "logical" DR resources to
+ request the allocation/deallocation of a resource prior to acquiring
+ it via isolation-state->unisolate, or after releasing it via
+ isolation-state->isolate, respectively. for "physical" DR (like PCI
+ hotplug/unplug) the pre-allocation of the resource is implied and
+ this sensor is unused.
+
+ supported sensor values:
+ 0: unusable, tell firmware/system the resource can be
+ unallocated/reclaimed and added back to the system resource pool
+ 1: usable, request the resource be allocated/reserved for use by
+ guest OS
+ 2: exchange, used to allocate a spare resource to use for fail-over
+ in certain situations. unused in QEMU
+ 3: recover, used to reclaim a previously allocated resource that's
+ not currently allocated to the guest OS. unused in QEMU
+
+rtas-get-sensor-state:
+ arg[0]: integer identifying sensor/indicator type
+ arg[1]: index of sensor, for DR-related sensors this is generally the
+ DRC index
+ output[0]: status, 0 on success
+
+ Used to read an indicator or sensor value.
+
+ For DR-related operations, the only noteworthy sensor is dr-entity-sense,
+ which has a type value of 9003, as allocation-state does in the case of
+ rtas-set-indicator. The semantics/encodings of the sensor values are distinct
+ however:
+
+ supported sensor values for dr-entity-sense (9003) sensor:
+ 0: empty,
+ for physical resources: DRC/slot is empty
+ for logical resources: unused
+ 1: present,
+ for physical resources: DRC/slot is populated with a device/resource
+ for logical resources: resource has been allocated to the DRC
+ 2: unusable,
+ for physical resources: unused
+ for logical resources: DRC has no resource allocated to it
+ 3: exchange,
+ for physical resources: unused
+ for logical resources: resource available for exchange (see
+ allocation-state sensor semantics above)
+ 4: recovery,
+ for physical resources: unused
+ for logical resources: resource available for recovery (see
+ allocation-state sensor semantics above)
+
+rtas-ibm-configure-connector:
+ arg[0]: guest physical address of 4096-byte work area buffer
+ arg[1]: 0, or address of additional 4096-byte work area buffer. only non-zero
+ if a prior RTAS response indicated a need for additional memory
+ output[0]: status:
+ 0: completed transmittal of device-tree node
+ 1: instruct guest to prepare for next DT sibling node
+ 2: instruct guest to prepare for next DT child node
+ 3: instruct guest to prepare for next DT property
+ 4: instruct guest to ascend to parent DT node
+ 5: instruct guest to provide additional work-area buffer
+ via arg[1]
+ 990x: instruct guest that operation took too long and to try
+ again later
+
+ Used to fetch an OF device-tree description of the resource associated with
+ a particular DRC. The DRC index is encoded in the first 4-bytes of the first
+ work area buffer.
+
+ Work area layout, using 4-byte offsets:
+ wa[0]: DRC index of the DRC to fetch device-tree nodes from
+ wa[1]: 0 (hard-coded)
+ wa[2]: for next-sibling/next-child response:
+ wa offset of null-terminated string denoting the new node's name
+ for next-property response:
+ wa offset of null-terminated string denoting new property's name
+ wa[3]: for next-property response (unused otherwise):
+ byte-length of new property's value
+ wa[4]: for next-property response (unused otherwise):
+ new property's value, encoded as an OFDT-compatible byte array
+
+== hotplug/unplug events ==
+
+For most DR operations, the hypervisor will issue host->guest add/remove events
+using the EPOW/check-exception notification framework, where the host issues a
+check-exception interrupt, then provides an RTAS event log via an
+rtas-check-exception call issued by the guest in response. This framework is
+documented by PAPR+ v2.7, and already use in by QEMU for generating powerdown
+requests via EPOW events.
+
+For DR, this framework has been extended to include hotplug events, which were
+previously unneeded due to direct manipulation of DR-related guest userspace
+tools by host-level management such as an HMC. This level of management is not
+applicable to PowerKVM, hence the reason for extending the notification
+framework to support hotplug events.
+
+Note that these events are not yet formally part of the PAPR+ specification,
+but support for this format has already been implemented in DR-related
+guest tools such as powerpc-utils/librtas, as well as kernel patches that have
+been submitted to handle in-kernel processing of memory/cpu-related hotplug
+events[1], and is planned for formal inclusion is PAPR+ specification. The
+hotplug-specific payload is QEMU implemented as follows (with all values
+encoded in big-endian format):
+
+struct rtas_event_log_v6_hp {
+#define SECTION_ID_HOTPLUG 0x4850 /* HP */
+ struct section_header {
+ uint16_t section_id; /* set to SECTION_ID_HOTPLUG */
+ uint16_t section_length; /* sizeof(rtas_event_log_v6_hp),
+ * plus the length of the DRC name
+ * if a DRC name identifier is
+ * specified for hotplug_identifier
+ */
+ uint8_t section_version; /* version 1 */
+ uint8_t section_subtype; /* unused */
+ uint16_t creator_component_id; /* unused */
+ } hdr;
+#define RTAS_LOG_V6_HP_TYPE_CPU 1
+#define RTAS_LOG_V6_HP_TYPE_MEMORY 2
+#define RTAS_LOG_V6_HP_TYPE_SLOT 3
+#define RTAS_LOG_V6_HP_TYPE_PHB 4
+#define RTAS_LOG_V6_HP_TYPE_PCI 5
+ uint8_t hotplug_type; /* type of resource/device */
+#define RTAS_LOG_V6_HP_ACTION_ADD 1
+#define RTAS_LOG_V6_HP_ACTION_REMOVE 2
+ uint8_t hotplug_action; /* action (add/remove) */
+#define RTAS_LOG_V6_HP_ID_DRC_NAME 1
+#define RTAS_LOG_V6_HP_ID_DRC_INDEX 2
+#define RTAS_LOG_V6_HP_ID_DRC_COUNT 3
+ uint8_t hotplug_identifier; /* type of the resource identifier,
+ * which serves as the discriminator
+ * for the 'drc' union field below
+ */
+ uint8_t reserved;
+ union {
+ uint32_t index; /* DRC index of resource to take action
+ * on
+ */
+ uint32_t count; /* number of DR resources to take
+ * action on (guest chooses which)
+ */
+ char name[1]; /* string representing the name of the
+ * DRC to take action on
+ */
+ } drc;
+} QEMU_PACKED;
+
+[1] http://thread.gmane.org/gmane.linux.ports.ppc.embedded/75350/focus=106867
diff --git a/docs/specs/rocker.txt b/docs/specs/rocker.txt
index 1e7e1e1859..0af5c61585 100644
--- a/docs/specs/rocker.txt
+++ b/docs/specs/rocker.txt
@@ -420,6 +420,7 @@ Other properties for front-panel ports are available via DMA CMD descriptors:
LEARNING 1 MAC address learning on port
1 = enabled
0 = disabled
+ PHYS_NAME <var> Physical port name (string)
Set PORT_SETTINGS descriptor:
diff --git a/docs/writing-qmp-commands.txt b/docs/writing-qmp-commands.txt
index f3df2066a4..ab1fdd36b4 100644
--- a/docs/writing-qmp-commands.txt
+++ b/docs/writing-qmp-commands.txt
@@ -598,7 +598,7 @@ stored in its "value" member. In our example, the "value" member is a pointer
to an TimerAlarmMethod instance.
Notice that the "current" variable is used as "true" only in the first
-interation of the loop. That's because the alarm timer method in use is the
+iteration of the loop. That's because the alarm timer method in use is the
first element of the alarm_timers array. Also notice that QAPI lists are handled
by hand and we return the head of the list.
diff --git a/dtc b/dtc
-Subproject bc895d6d09695d05ceb8b52486ffe861d6cfbdd
+Subproject 65cc4d2748a2c2e6f27f1cf39e07a5dbabd80eb
diff --git a/exec.c b/exec.c
index e19ab22cd6..76bfc4ac4a 100644
--- a/exec.c
+++ b/exec.c
@@ -59,8 +59,6 @@
//#define DEBUG_SUBPAGE
#if !defined(CONFIG_USER_ONLY)
-static bool in_migration;
-
/* ram_list is read under rcu_read_lock()/rcu_read_unlock(). Writes
* are protected by the ramlist lock.
*/
@@ -173,17 +171,22 @@ static void phys_map_node_reserve(PhysPageMap *map, unsigned nodes)
}
}
-static uint32_t phys_map_node_alloc(PhysPageMap *map)
+static uint32_t phys_map_node_alloc(PhysPageMap *map, bool leaf)
{
unsigned i;
uint32_t ret;
+ PhysPageEntry e;
+ PhysPageEntry *p;
ret = map->nodes_nb++;
+ p = map->nodes[ret];
assert(ret != PHYS_MAP_NODE_NIL);
assert(ret != map->nodes_nb_alloc);
+
+ e.skip = leaf ? 0 : 1;
+ e.ptr = leaf ? PHYS_SECTION_UNASSIGNED : PHYS_MAP_NODE_NIL;
for (i = 0; i < P_L2_SIZE; ++i) {
- map->nodes[ret][i].skip = 1;
- map->nodes[ret][i].ptr = PHYS_MAP_NODE_NIL;
+ memcpy(&p[i], &e, sizeof(e));
}
return ret;
}
@@ -193,21 +196,12 @@ static void phys_page_set_level(PhysPageMap *map, PhysPageEntry *lp,
int level)
{
PhysPageEntry *p;
- int i;
hwaddr step = (hwaddr)1 << (level * P_L2_BITS);
if (lp->skip && lp->ptr == PHYS_MAP_NODE_NIL) {
- lp->ptr = phys_map_node_alloc(map);
- p = map->nodes[lp->ptr];
- if (level == 0) {
- for (i = 0; i < P_L2_SIZE; i++) {
- p[i].skip = 0;
- p[i].ptr = PHYS_SECTION_UNASSIGNED;
- }
- }
- } else {
- p = map->nodes[lp->ptr];
+ lp->ptr = phys_map_node_alloc(map, level == 0);
}
+ p = map->nodes[lp->ptr];
lp = &p[(*index >> (level * P_L2_BITS)) & (P_L2_SIZE - 1)];
while (*nb && lp < &p[P_L2_SIZE]) {
@@ -460,6 +454,7 @@ static const VMStateDescription vmstate_cpu_common_exception_index = {
.name = "cpu_common/exception_index",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = cpu_common_exception_index_needed,
.fields = (VMStateField[]) {
VMSTATE_INT32(exception_index, CPUState),
VMSTATE_END_OF_LIST()
@@ -477,13 +472,9 @@ const VMStateDescription vmstate_cpu_common = {
VMSTATE_UINT32(interrupt_request, CPUState),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_cpu_common_exception_index,
- .needed = cpu_common_exception_index_needed,
- } , {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_cpu_common_exception_index,
+ NULL
}
};
@@ -858,21 +849,27 @@ static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length)
}
/* Note: start and end must be within the same ram block. */
-void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t length,
- unsigned client)
+bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start,
+ ram_addr_t length,
+ unsigned client)
{
- if (length == 0)
- return;
- cpu_physical_memory_clear_dirty_range_type(start, length, client);
+ unsigned long end, page;
+ bool dirty;
- if (tcg_enabled()) {
+ if (length == 0) {
+ return false;
+ }
+
+ end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS;
+ page = start >> TARGET_PAGE_BITS;
+ dirty = bitmap_test_and_clear_atomic(ram_list.dirty_memory[client],
+ page, end - page);
+
+ if (dirty && tcg_enabled()) {
tlb_reset_dirty_range_all(start, length);
}
-}
-static void cpu_physical_memory_set_dirty_tracking(bool enable)
-{
- in_migration = enable;
+ return dirty;
}
/* Called from RCU critical section */
@@ -1362,7 +1359,8 @@ int qemu_ram_resize(ram_addr_t base, ram_addr_t newsize, Error **errp)
cpu_physical_memory_clear_dirty_range(block->offset, block->used_length);
block->used_length = newsize;
- cpu_physical_memory_set_dirty_range(block->offset, block->used_length);
+ cpu_physical_memory_set_dirty_range(block->offset, block->used_length,
+ DIRTY_CLIENTS_ALL);
memory_region_set_size(block->mr, newsize);
if (block->resized) {
block->resized(block->idstr, newsize, block->host);
@@ -1436,7 +1434,8 @@ static ram_addr_t ram_block_add(RAMBlock *new_block, Error **errp)
}
}
cpu_physical_memory_set_dirty_range(new_block->offset,
- new_block->used_length);
+ new_block->used_length,
+ DIRTY_CLIENTS_ALL);
if (new_block->host) {
qemu_ram_setup_dump(new_block->host, new_block->max_length);
@@ -1824,7 +1823,11 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr,
default:
abort();
}
- cpu_physical_memory_set_dirty_range_nocode(ram_addr, size);
+ /* Set both VGA and migration bits for simplicity and to remove
+ * the notdirty callback faster.
+ */
+ cpu_physical_memory_set_dirty_range(ram_addr, size,
+ DIRTY_CLIENTS_NOCODE);
/* we remove the notdirty callback only if the code has been
flushed */
if (!cpu_physical_memory_is_clean(ram_addr)) {
@@ -2165,22 +2168,6 @@ static void tcg_commit(MemoryListener *listener)
}
}
-static void core_log_global_start(MemoryListener *listener)
-{
- cpu_physical_memory_set_dirty_tracking(true);
-}
-
-static void core_log_global_stop(MemoryListener *listener)
-{
- cpu_physical_memory_set_dirty_tracking(false);
-}
-
-static MemoryListener core_memory_listener = {
- .log_global_start = core_log_global_start,
- .log_global_stop = core_log_global_stop,
- .priority = 1,
-};
-
void address_space_init_dispatch(AddressSpace *as)
{
as->dispatch = NULL;
@@ -2220,8 +2207,6 @@ static void memory_map_init(void)
memory_region_init_io(system_io, NULL, &unassigned_io_ops, NULL, "io",
65536);
address_space_init(&address_space_io, system_io, "I/O");
-
- memory_listener_register(&core_memory_listener, &address_space_memory);
}
MemoryRegion *get_system_memory(void)
@@ -2279,14 +2264,23 @@ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr,
#else
-static void invalidate_and_set_dirty(hwaddr addr,
+static void invalidate_and_set_dirty(MemoryRegion *mr, hwaddr addr,
hwaddr length)
{
- if (cpu_physical_memory_range_includes_clean(addr, length)) {
- tb_invalidate_phys_range(addr, addr + length, 0);
- cpu_physical_memory_set_dirty_range_nocode(addr, length);
+ uint8_t dirty_log_mask = memory_region_get_dirty_log_mask(mr);
+ /* No early return if dirty_log_mask is or becomes 0, because
+ * cpu_physical_memory_set_dirty_range will still call
+ * xen_modified_memory.
+ */
+ if (dirty_log_mask) {
+ dirty_log_mask =
+ cpu_physical_memory_range_includes_clean(addr, length, dirty_log_mask);
+ }
+ if (dirty_log_mask & (1 << DIRTY_MEMORY_CODE)) {
+ tb_invalidate_phys_range(addr, addr + length);
+ dirty_log_mask &= ~(1 << DIRTY_MEMORY_CODE);
}
- xen_modified_memory(addr, length);
+ cpu_physical_memory_set_dirty_range(addr, length, dirty_log_mask);
}
static int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr)
@@ -2371,7 +2365,7 @@ MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
/* RAM case */
ptr = qemu_get_ram_ptr(addr1);
memcpy(ptr, buf, l);
- invalidate_and_set_dirty(addr1, l);
+ invalidate_and_set_dirty(mr, addr1, l);
}
} else {
if (!memory_access_is_direct(mr, is_write)) {
@@ -2468,7 +2462,7 @@ static inline void cpu_physical_memory_write_rom_internal(AddressSpace *as,
switch (type) {
case WRITE_DATA:
memcpy(ptr, buf, l);
- invalidate_and_set_dirty(addr1, l);
+ invalidate_and_set_dirty(mr, addr1, l);
break;
case FLUSH_CACHE:
flush_icache_range((uintptr_t)ptr, (uintptr_t)ptr + l);
@@ -2693,7 +2687,7 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len,
mr = qemu_ram_addr_from_host(buffer, &addr1);
assert(mr != NULL);
if (is_write) {
- invalidate_and_set_dirty(addr1, access_len);
+ invalidate_and_set_dirty(mr, addr1, access_len);
}
if (xen_enabled()) {
xen_invalidate_map_cache_entry(buffer);
@@ -3022,6 +3016,7 @@ void address_space_stl_notdirty(AddressSpace *as, hwaddr addr, uint32_t val,
hwaddr l = 4;
hwaddr addr1;
MemTxResult r;
+ uint8_t dirty_log_mask;
rcu_read_lock();
mr = address_space_translate(as, addr, &addr1, &l,
@@ -3033,14 +3028,9 @@ void address_space_stl_notdirty(AddressSpace *as, hwaddr addr, uint32_t val,
ptr = qemu_get_ram_ptr(addr1);
stl_p(ptr, val);
- if (unlikely(in_migration)) {
- if (cpu_physical_memory_is_clean(addr1)) {
- /* invalidate code */
- tb_invalidate_phys_page_range(addr1, addr1 + 4, 0);
- /* set dirty bit */
- cpu_physical_memory_set_dirty_range_nocode(addr1, 4);
- }
- }
+ dirty_log_mask = memory_region_get_dirty_log_mask(mr);
+ dirty_log_mask &= ~(1 << DIRTY_MEMORY_CODE);
+ cpu_physical_memory_set_dirty_range(addr1, 4, dirty_log_mask);
r = MEMTX_OK;
}
if (result) {
@@ -3096,7 +3086,7 @@ static inline void address_space_stl_internal(AddressSpace *as,
stl_p(ptr, val);
break;
}
- invalidate_and_set_dirty(addr1, 4);
+ invalidate_and_set_dirty(mr, addr1, 4);
r = MEMTX_OK;
}
if (result) {
@@ -3200,7 +3190,7 @@ static inline void address_space_stw_internal(AddressSpace *as,
stw_p(ptr, val);
break;
}
- invalidate_and_set_dirty(addr1, 2);
+ invalidate_and_set_dirty(mr, addr1, 2);
r = MEMTX_OK;
}
if (result) {
@@ -3355,14 +3345,20 @@ bool cpu_physical_memory_is_io(hwaddr phys_addr)
return res;
}
-void qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque)
+int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque)
{
RAMBlock *block;
+ int ret = 0;
rcu_read_lock();
QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
- func(block->host, block->offset, block->used_length, opaque);
+ ret = func(block->idstr, block->host, block->offset,
+ block->used_length, opaque);
+ if (ret) {
+ break;
+ }
}
rcu_read_unlock();
+ return ret;
}
#endif
diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h
index fa1214a4b8..6dd41d8978 100644
--- a/fpu/softfloat-specialize.h
+++ b/fpu/softfloat-specialize.h
@@ -113,7 +113,7 @@ const float16 float16_default_nan = const_float16(0xFE00);
#if defined(TARGET_SPARC)
const float32 float32_default_nan = const_float32(0x7FFFFFFF);
#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \
- defined(TARGET_XTENSA)
+ defined(TARGET_XTENSA) || defined(TARGET_S390X)
const float32 float32_default_nan = const_float32(0x7FC00000);
#elif SNAN_BIT_IS_ONE
const float32 float32_default_nan = const_float32(0x7FBFFFFF);
@@ -126,7 +126,8 @@ const float32 float32_default_nan = const_float32(0xFFC00000);
*----------------------------------------------------------------------------*/
#if defined(TARGET_SPARC)
const float64 float64_default_nan = const_float64(LIT64( 0x7FFFFFFFFFFFFFFF ));
-#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA)
+#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \
+ defined(TARGET_S390X)
const float64 float64_default_nan = const_float64(LIT64( 0x7FF8000000000000 ));
#elif SNAN_BIT_IS_ONE
const float64 float64_default_nan = const_float64(LIT64(0x7FF7FFFFFFFFFFFF));
@@ -155,6 +156,9 @@ const floatx80 floatx80_default_nan
#if SNAN_BIT_IS_ONE
#define float128_default_nan_high LIT64(0x7FFF7FFFFFFFFFFF)
#define float128_default_nan_low LIT64(0xFFFFFFFFFFFFFFFF)
+#elif defined(TARGET_S390X)
+#define float128_default_nan_high LIT64( 0x7FFF800000000000 )
+#define float128_default_nan_low LIT64( 0x0000000000000000 )
#else
#define float128_default_nan_high LIT64( 0xFFFF800000000000 )
#define float128_default_nan_low LIT64( 0x0000000000000000 )
diff --git a/fsdev/virtfs-proxy-helper.c b/fsdev/virtfs-proxy-helper.c
index a698e2dbb3..9097d15c98 100644
--- a/fsdev/virtfs-proxy-helper.c
+++ b/fsdev/virtfs-proxy-helper.c
@@ -49,6 +49,7 @@ static struct option helper_opts[] = {
{"socket", required_argument, NULL, 's'},
{"uid", required_argument, NULL, 'u'},
{"gid", required_argument, NULL, 'g'},
+ {},
};
static bool is_daemon;
@@ -738,7 +739,12 @@ static int proxy_socket(const char *path, uid_t uid, gid_t gid)
return -1;
}
- g_assert(strlen(path) < sizeof(proxy.sun_path));
+ if (strlen(path) >= sizeof(proxy.sun_path)) {
+ do_log(LOG_CRIT, "UNIX domain socket path exceeds %zu characters\n",
+ sizeof(proxy.sun_path));
+ return -1;
+ }
+
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
do_perror("socket");
diff --git a/hmp-commands.hx b/hmp-commands.hx
index e864a6ca81..d3b7932ff6 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -178,8 +178,7 @@ ETEXI
.args_type = "id:B",
.params = "device",
.help = "remove host block device",
- .user_print = monitor_user_noop,
- .mhandler.cmd_new = hmp_drive_del,
+ .mhandler.cmd = hmp_drive_del,
},
STEXI
@@ -654,8 +653,7 @@ ETEXI
.args_type = "device:O",
.params = "driver[,prop=value][,...]",
.help = "add device, like -device on the command line",
- .user_print = monitor_user_noop,
- .mhandler.cmd_new = do_device_add,
+ .mhandler.cmd = hmp_device_add,
.command_completion = device_add_completion,
},
@@ -1011,17 +1009,16 @@ ETEXI
.name = "client_migrate_info",
.args_type = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?",
.params = "protocol hostname port tls-port cert-subject",
- .help = "send migration info to spice/vnc client",
- .user_print = monitor_user_noop,
- .mhandler.cmd_new = client_migrate_info,
+ .help = "set migration information for remote display",
+ .mhandler.cmd = hmp_client_migrate_info,
},
STEXI
@item client_migrate_info @var{protocol} @var{hostname} @var{port} @var{tls-port} @var{cert-subject}
@findex client_migrate_info
-Set the spice/vnc connection info for the migration target. The spice/vnc
-server will ask the spice/vnc client to automatically reconnect using the
-new parameters (if specified) once the vm migration finished successfully.
+Set migration information for remote display. This makes the server
+ask the client to automatically reconnect using the new parameters
+once migration finished successfully. Only implemented for SPICE.
ETEXI
{
@@ -1186,8 +1183,7 @@ ETEXI
"<error_status> = error string or 32bit\n\t\t\t"
"<tlb header> = 32bit x 4\n\t\t\t"
"<tlb header prefix> = 32bit x 4",
- .user_print = pcie_aer_inject_error_print,
- .mhandler.cmd_new = hmp_pcie_aer_inject_error,
+ .mhandler.cmd = hmp_pcie_aer_inject_error,
},
STEXI
@@ -1803,5 +1799,29 @@ show available trace events and their state
ETEXI
STEXI
+@item rocker @var{name}
+@findex rocker
+Show Rocker(s)
+ETEXI
+
+STEXI
+@item rocker_ports @var{name}
+@findex rocker_ports
+Show Rocker ports
+ETEXI
+
+STEXI
+@item rocker_of_dpa_flows @var{name} [@var{tbl_id}]
+@findex rocker_of_dpa_flows
+Show Rocker OF-DPA flow tables
+ETEXI
+
+STEXI
+@item rocker_of_dpa_groups @var{name} [@var{type}]
+@findex rocker_of_dpa_groups
+Show Rocker OF-DPA groups
+ETEXI
+
+STEXI
@end table
ETEXI
diff --git a/hmp.c b/hmp.c
index e17852d1f9..23abc7d9f1 100644
--- a/hmp.c
+++ b/hmp.c
@@ -15,6 +15,7 @@
#include "hmp.h"
#include "net/net.h"
+#include "net/eth.h"
#include "sysemu/char.h"
#include "sysemu/block-backend.h"
#include "qemu/option.h"
@@ -22,6 +23,7 @@
#include "qmp-commands.h"
#include "qemu/sockets.h"
#include "monitor/monitor.h"
+#include "monitor/qdev.h"
#include "qapi/opts-visitor.h"
#include "qapi/string-output-visitor.h"
#include "qapi-visit.h"
@@ -398,7 +400,8 @@ static void print_block_info(Monitor *mon, BlockInfo *info,
" iops_max=%" PRId64
" iops_rd_max=%" PRId64
" iops_wr_max=%" PRId64
- " iops_size=%" PRId64 "\n",
+ " iops_size=%" PRId64
+ " group=%s\n",
inserted->bps,
inserted->bps_rd,
inserted->bps_wr,
@@ -411,7 +414,8 @@ static void print_block_info(Monitor *mon, BlockInfo *info,
inserted->iops_max,
inserted->iops_rd_max,
inserted->iops_wr_max,
- inserted->iops_size);
+ inserted->iops_size,
+ inserted->group);
}
if (verbose) {
@@ -1250,6 +1254,23 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
}
}
+void hmp_client_migrate_info(Monitor *mon, const QDict *qdict)
+{
+ Error *err = NULL;
+ const char *protocol = qdict_get_str(qdict, "protocol");
+ const char *hostname = qdict_get_str(qdict, "hostname");
+ bool has_port = qdict_haskey(qdict, "port");
+ int port = qdict_get_try_int(qdict, "port", -1);
+ bool has_tls_port = qdict_haskey(qdict, "tls-port");
+ int tls_port = qdict_get_try_int(qdict, "tls-port", -1);
+ const char *cert_subject = qdict_get_try_str(qdict, "cert-subject");
+
+ qmp_client_migrate_info(protocol, hostname,
+ has_port, port, has_tls_port, tls_port,
+ !!cert_subject, cert_subject, &err);
+ hmp_handle_error(mon, &err);
+}
+
void hmp_set_password(Monitor *mon, const QDict *qdict)
{
const char *protocol = qdict_get_str(qdict, "protocol");
@@ -1338,7 +1359,9 @@ void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict)
false,
0,
false, /* No default I/O size */
- 0, &err);
+ 0,
+ false,
+ NULL, &err);
hmp_handle_error(mon, &err);
}
@@ -1482,6 +1505,11 @@ void hmp_migrate(Monitor *mon, const QDict *qdict)
}
}
+void hmp_device_add(Monitor *mon, const QDict *qdict)
+{
+ do_device_add(mon, qdict, NULL);
+}
+
void hmp_device_del(Monitor *mon, const QDict *qdict)
{
const char *id = qdict_get_str(qdict, "id");
@@ -1976,3 +2004,305 @@ void hmp_qom_set(Monitor *mon, const QDict *qdict)
}
hmp_handle_error(mon, &err);
}
+
+void hmp_rocker(Monitor *mon, const QDict *qdict)
+{
+ const char *name = qdict_get_str(qdict, "name");
+ RockerSwitch *rocker;
+ Error *errp = NULL;
+
+ rocker = qmp_query_rocker(name, &errp);
+ if (errp != NULL) {
+ hmp_handle_error(mon, &errp);
+ return;
+ }
+
+ monitor_printf(mon, "name: %s\n", rocker->name);
+ monitor_printf(mon, "id: 0x%" PRIx64 "\n", rocker->id);
+ monitor_printf(mon, "ports: %d\n", rocker->ports);
+
+ qapi_free_RockerSwitch(rocker);
+}
+
+void hmp_rocker_ports(Monitor *mon, const QDict *qdict)
+{
+ RockerPortList *list, *port;
+ const char *name = qdict_get_str(qdict, "name");
+ Error *errp = NULL;
+
+ list = qmp_query_rocker_ports(name, &errp);
+ if (errp != NULL) {
+ hmp_handle_error(mon, &errp);
+ return;
+ }
+
+ monitor_printf(mon, " ena/ speed/ auto\n");
+ monitor_printf(mon, " port link duplex neg?\n");
+
+ for (port = list; port; port = port->next) {
+ monitor_printf(mon, "%10s %-4s %-3s %2s %-3s\n",
+ port->value->name,
+ port->value->enabled ? port->value->link_up ?
+ "up" : "down" : "!ena",
+ port->value->speed == 10000 ? "10G" : "??",
+ port->value->duplex ? "FD" : "HD",
+ port->value->autoneg ? "Yes" : "No");
+ }
+
+ qapi_free_RockerPortList(list);
+}
+
+void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict)
+{
+ RockerOfDpaFlowList *list, *info;
+ const char *name = qdict_get_str(qdict, "name");
+ uint32_t tbl_id = qdict_get_try_int(qdict, "tbl_id", -1);
+ Error *errp = NULL;
+
+ list = qmp_query_rocker_of_dpa_flows(name, tbl_id != -1, tbl_id, &errp);
+ if (errp != NULL) {
+ hmp_handle_error(mon, &errp);
+ return;
+ }
+
+ monitor_printf(mon, "prio tbl hits key(mask) --> actions\n");
+
+ for (info = list; info; info = info->next) {
+ RockerOfDpaFlow *flow = info->value;
+ RockerOfDpaFlowKey *key = flow->key;
+ RockerOfDpaFlowMask *mask = flow->mask;
+ RockerOfDpaFlowAction *action = flow->action;
+
+ if (flow->hits) {
+ monitor_printf(mon, "%-4d %-3d %-4" PRIu64,
+ key->priority, key->tbl_id, flow->hits);
+ } else {
+ monitor_printf(mon, "%-4d %-3d ",
+ key->priority, key->tbl_id);
+ }
+
+ if (key->has_in_pport) {
+ monitor_printf(mon, " pport %d", key->in_pport);
+ if (mask->has_in_pport) {
+ monitor_printf(mon, "(0x%x)", mask->in_pport);
+ }
+ }
+
+ if (key->has_vlan_id) {
+ monitor_printf(mon, " vlan %d",
+ key->vlan_id & VLAN_VID_MASK);
+ if (mask->has_vlan_id) {
+ monitor_printf(mon, "(0x%x)", mask->vlan_id);
+ }
+ }
+
+ if (key->has_tunnel_id) {
+ monitor_printf(mon, " tunnel %d", key->tunnel_id);
+ if (mask->has_tunnel_id) {
+ monitor_printf(mon, "(0x%x)", mask->tunnel_id);
+ }
+ }
+
+ if (key->has_eth_type) {
+ switch (key->eth_type) {
+ case 0x0806:
+ monitor_printf(mon, " ARP");
+ break;
+ case 0x0800:
+ monitor_printf(mon, " IP");
+ break;
+ case 0x86dd:
+ monitor_printf(mon, " IPv6");
+ break;
+ case 0x8809:
+ monitor_printf(mon, " LACP");
+ break;
+ case 0x88cc:
+ monitor_printf(mon, " LLDP");
+ break;
+ default:
+ monitor_printf(mon, " eth type 0x%04x", key->eth_type);
+ break;
+ }
+ }
+
+ if (key->has_eth_src) {
+ if ((strcmp(key->eth_src, "01:00:00:00:00:00") == 0) &&
+ (mask->has_eth_src) &&
+ (strcmp(mask->eth_src, "01:00:00:00:00:00") == 0)) {
+ monitor_printf(mon, " src <any mcast/bcast>");
+ } else if ((strcmp(key->eth_src, "00:00:00:00:00:00") == 0) &&
+ (mask->has_eth_src) &&
+ (strcmp(mask->eth_src, "01:00:00:00:00:00") == 0)) {
+ monitor_printf(mon, " src <any ucast>");
+ } else {
+ monitor_printf(mon, " src %s", key->eth_src);
+ if (mask->has_eth_src) {
+ monitor_printf(mon, "(%s)", mask->eth_src);
+ }
+ }
+ }
+
+ if (key->has_eth_dst) {
+ if ((strcmp(key->eth_dst, "01:00:00:00:00:00") == 0) &&
+ (mask->has_eth_dst) &&
+ (strcmp(mask->eth_dst, "01:00:00:00:00:00") == 0)) {
+ monitor_printf(mon, " dst <any mcast/bcast>");
+ } else if ((strcmp(key->eth_dst, "00:00:00:00:00:00") == 0) &&
+ (mask->has_eth_dst) &&
+ (strcmp(mask->eth_dst, "01:00:00:00:00:00") == 0)) {
+ monitor_printf(mon, " dst <any ucast>");
+ } else {
+ monitor_printf(mon, " dst %s", key->eth_dst);
+ if (mask->has_eth_dst) {
+ monitor_printf(mon, "(%s)", mask->eth_dst);
+ }
+ }
+ }
+
+ if (key->has_ip_proto) {
+ monitor_printf(mon, " proto %d", key->ip_proto);
+ if (mask->has_ip_proto) {
+ monitor_printf(mon, "(0x%x)", mask->ip_proto);
+ }
+ }
+
+ if (key->has_ip_tos) {
+ monitor_printf(mon, " TOS %d", key->ip_tos);
+ if (mask->has_ip_tos) {
+ monitor_printf(mon, "(0x%x)", mask->ip_tos);
+ }
+ }
+
+ if (key->has_ip_dst) {
+ monitor_printf(mon, " dst %s", key->ip_dst);
+ }
+
+ if (action->has_goto_tbl || action->has_group_id ||
+ action->has_new_vlan_id) {
+ monitor_printf(mon, " -->");
+ }
+
+ if (action->has_new_vlan_id) {
+ monitor_printf(mon, " apply new vlan %d",
+ ntohs(action->new_vlan_id));
+ }
+
+ if (action->has_group_id) {
+ monitor_printf(mon, " write group 0x%08x", action->group_id);
+ }
+
+ if (action->has_goto_tbl) {
+ monitor_printf(mon, " goto tbl %d", action->goto_tbl);
+ }
+
+ monitor_printf(mon, "\n");
+ }
+
+ qapi_free_RockerOfDpaFlowList(list);
+}
+
+void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict)
+{
+ RockerOfDpaGroupList *list, *g;
+ const char *name = qdict_get_str(qdict, "name");
+ uint8_t type = qdict_get_try_int(qdict, "type", 9);
+ Error *errp = NULL;
+ bool set = false;
+
+ list = qmp_query_rocker_of_dpa_groups(name, type != 9, type, &errp);
+ if (errp != NULL) {
+ hmp_handle_error(mon, &errp);
+ return;
+ }
+
+ monitor_printf(mon, "id (decode) --> buckets\n");
+
+ for (g = list; g; g = g->next) {
+ RockerOfDpaGroup *group = g->value;
+
+ monitor_printf(mon, "0x%08x", group->id);
+
+ monitor_printf(mon, " (type %s", group->type == 0 ? "L2 interface" :
+ group->type == 1 ? "L2 rewrite" :
+ group->type == 2 ? "L3 unicast" :
+ group->type == 3 ? "L2 multicast" :
+ group->type == 4 ? "L2 flood" :
+ group->type == 5 ? "L3 interface" :
+ group->type == 6 ? "L3 multicast" :
+ group->type == 7 ? "L3 ECMP" :
+ group->type == 8 ? "L2 overlay" :
+ "unknown");
+
+ if (group->has_vlan_id) {
+ monitor_printf(mon, " vlan %d", group->vlan_id);
+ }
+
+ if (group->has_pport) {
+ monitor_printf(mon, " pport %d", group->pport);
+ }
+
+ if (group->has_index) {
+ monitor_printf(mon, " index %d", group->index);
+ }
+
+ monitor_printf(mon, ") -->");
+
+ if (group->has_set_vlan_id && group->set_vlan_id) {
+ set = true;
+ monitor_printf(mon, " set vlan %d",
+ group->set_vlan_id & VLAN_VID_MASK);
+ }
+
+ if (group->has_set_eth_src) {
+ if (!set) {
+ set = true;
+ monitor_printf(mon, " set");
+ }
+ monitor_printf(mon, " src %s", group->set_eth_src);
+ }
+
+ if (group->has_set_eth_dst) {
+ if (!set) {
+ set = true;
+ monitor_printf(mon, " set");
+ }
+ monitor_printf(mon, " dst %s", group->set_eth_dst);
+ }
+
+ set = false;
+
+ if (group->has_ttl_check && group->ttl_check) {
+ monitor_printf(mon, " check TTL");
+ }
+
+ if (group->has_group_id && group->group_id) {
+ monitor_printf(mon, " group id 0x%08x", group->group_id);
+ }
+
+ if (group->has_pop_vlan && group->pop_vlan) {
+ monitor_printf(mon, " pop vlan");
+ }
+
+ if (group->has_out_pport) {
+ monitor_printf(mon, " out pport %d", group->out_pport);
+ }
+
+ if (group->has_group_ids) {
+ struct uint32List *id;
+
+ monitor_printf(mon, " groups [");
+ for (id = group->group_ids; id; id = id->next) {
+ monitor_printf(mon, "0x%08x", id->value);
+ if (id->next) {
+ monitor_printf(mon, ",");
+ }
+ }
+ monitor_printf(mon, "]");
+ }
+
+ monitor_printf(mon, "\n");
+ }
+
+ qapi_free_RockerOfDpaGroupList(list);
+}
diff --git a/hmp.h b/hmp.h
index a158e3fda1..0cf4f2a3d1 100644
--- a/hmp.h
+++ b/hmp.h
@@ -67,6 +67,7 @@ void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict);
void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict);
void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict);
void hmp_migrate_set_cache_size(Monitor *mon, const QDict *qdict);
+void hmp_client_migrate_info(Monitor *mon, const QDict *qdict);
void hmp_set_password(Monitor *mon, const QDict *qdict);
void hmp_expire_password(Monitor *mon, const QDict *qdict);
void hmp_eject(Monitor *mon, const QDict *qdict);
@@ -79,6 +80,7 @@ void hmp_block_job_pause(Monitor *mon, const QDict *qdict);
void hmp_block_job_resume(Monitor *mon, const QDict *qdict);
void hmp_block_job_complete(Monitor *mon, const QDict *qdict);
void hmp_migrate(Monitor *mon, const QDict *qdict);
+void hmp_device_add(Monitor *mon, const QDict *qdict);
void hmp_device_del(Monitor *mon, const QDict *qdict);
void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict);
void hmp_netdev_add(Monitor *mon, const QDict *qdict);
@@ -122,5 +124,9 @@ void host_net_remove_completion(ReadLineState *rs, int nb_args,
const char *str);
void delvm_completion(ReadLineState *rs, int nb_args, const char *str);
void loadvm_completion(ReadLineState *rs, int nb_args, const char *str);
+void hmp_rocker(Monitor *mon, const QDict *qdict);
+void hmp_rocker_ports(Monitor *mon, const QDict *qdict);
+void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict);
+void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict);
#endif
diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
index 799351ea44..8a64ffb38f 100644
--- a/hw/acpi/ich9.c
+++ b/hw/acpi/ich9.c
@@ -94,7 +94,8 @@ static void ich9_smi_writel(void *opaque, hwaddr addr, uint64_t val,
ICH9LPCPMRegs *pm = opaque;
switch (addr) {
case 0:
- pm->smi_en = val;
+ pm->smi_en &= ~pm->smi_en_wmask;
+ pm->smi_en |= (val & pm->smi_en_wmask);
break;
}
}
@@ -151,6 +152,7 @@ static const VMStateDescription vmstate_memhp_state = {
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
+ .needed = vmstate_test_use_memhp,
.fields = (VMStateField[]) {
VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug, ICH9LPCPMRegs),
VMSTATE_END_OF_LIST()
@@ -174,12 +176,9 @@ const VMStateDescription vmstate_ich9_pm = {
VMSTATE_UINT32(smi_sts, ICH9LPCPMRegs),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_memhp_state,
- .needed = vmstate_test_use_memhp,
- },
- VMSTATE_END_OF_LIST()
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_memhp_state,
+ NULL
}
};
@@ -198,6 +197,7 @@ static void pm_reset(void *opaque)
* support SMM mode. */
pm->smi_en |= ICH9_PMIO_SMI_EN_APMC_EN;
}
+ pm->smi_en_wmask = ~0;
acpi_update_sci(&pm->acpi_regs, pm->irq);
}
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
index b730ca6ced..3bd1d5a865 100644
--- a/hw/acpi/piix4.c
+++ b/hw/acpi/piix4.c
@@ -260,6 +260,7 @@ static const VMStateDescription vmstate_memhp_state = {
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
+ .needed = vmstate_test_use_memhp,
.fields = (VMStateField[]) {
VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug, PIIX4PMState),
VMSTATE_END_OF_LIST()
@@ -298,12 +299,9 @@ static const VMStateDescription vmstate_acpi = {
vmstate_test_use_acpi_pci_hotplug),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_memhp_state,
- .needed = vmstate_test_use_memhp,
- },
- VMSTATE_END_OF_LIST()
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_memhp_state,
+ NULL
}
};
diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c
index 9fe7e8b5cb..f86e7bb830 100644
--- a/hw/alpha/dp264.c
+++ b/hw/alpha/dp264.c
@@ -55,7 +55,7 @@ static void clipper_init(MachineState *machine)
ISABus *isa_bus;
qemu_irq rtc_irq;
long size, i;
- const char *palcode_filename;
+ char *palcode_filename;
uint64_t palcode_entry, palcode_low, palcode_high;
uint64_t kernel_entry, kernel_low, kernel_high;
@@ -101,8 +101,8 @@ static void clipper_init(MachineState *machine)
/* Load PALcode. Given that this is not "real" cpu palcode,
but one explicitly written for the emulation, we might as
well load it directly from and ELF image. */
- palcode_filename = (bios_name ? bios_name : "palcode-clipper");
- palcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, palcode_filename);
+ palcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS,
+ bios_name ? bios_name : "palcode-clipper");
if (palcode_filename == NULL) {
hw_error("no palcode provided\n");
exit(1);
@@ -114,6 +114,7 @@ static void clipper_init(MachineState *machine)
hw_error("could not load palcode '%s'\n", palcode_filename);
exit(1);
}
+ g_free(palcode_filename);
/* Start all cpus at the PALcode RESET entry point. */
for (i = 0; i < smp_cpus; ++i) {
diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c
index 7df842dff7..421162e1d4 100644
--- a/hw/alpha/typhoon.c
+++ b/hw/alpha/typhoon.c
@@ -841,7 +841,7 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus,
}
}
- *p_rtc_irq = *qemu_allocate_irqs(typhoon_set_timer_irq, s, 1);
+ *p_rtc_irq = qemu_allocate_irq(typhoon_set_timer_irq, s, 0);
/* Main memory region, 0x00.0000.0000. Real hardware supports 32GB,
but the address space hole reserved at this point is 8TB. */
@@ -918,11 +918,11 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus,
/* Init the ISA bus. */
/* ??? Technically there should be a cy82c693ub pci-isa bridge. */
{
- qemu_irq isa_pci_irq, *isa_irqs;
+ qemu_irq *isa_irqs;
*isa_bus = isa_bus_new(NULL, get_system_memory(), &s->pchip.reg_io);
- isa_pci_irq = *qemu_allocate_irqs(typhoon_set_isa_irq, s, 1);
- isa_irqs = i8259_init(*isa_bus, isa_pci_irq);
+ isa_irqs = i8259_init(*isa_bus,
+ qemu_allocate_irq(typhoon_set_isa_irq, s, 0));
isa_bus_irqs(*isa_bus, isa_irqs);
}
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 4b09caf594..cf346c1d0a 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -5,6 +5,7 @@ obj-y += omap_sx1.o palm.o realview.o spitz.o stellaris.o
obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o
obj-$(CONFIG_ACPI) += virt-acpi-build.o
obj-y += netduino2.o
+obj-y += sysbus-fdt.o
obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
obj-$(CONFIG_DIGIC) += digic.o
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index fa6950352c..1e7fd28daa 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -557,7 +557,7 @@ static void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key,
fw_cfg_add_bytes(fw_cfg, data_key, data, size);
}
-void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
+static void arm_load_kernel_notify(Notifier *notifier, void *data)
{
CPUState *cs;
int kernel_size;
@@ -568,15 +568,11 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
hwaddr entry, kernel_load_offset;
int big_endian;
static const ARMInsnFixup *primary_loader;
-
- /* CPU objects (unlike devices) are not automatically reset on system
- * reset, so we must always register a handler to do so. If we're
- * actually loading a kernel, the handler is also responsible for
- * arranging that we start it correctly.
- */
- for (cs = CPU(cpu); cs; cs = CPU_NEXT(cs)) {
- qemu_register_reset(do_cpu_reset, ARM_CPU(cs));
- }
+ ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier,
+ notifier, notifier);
+ ARMCPU *cpu = n->cpu;
+ struct arm_boot_info *info =
+ container_of(n, struct arm_boot_info, load_kernel_notifier);
/* Load the kernel. */
if (!info->kernel_filename || info->firmware_loaded) {
@@ -775,3 +771,21 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
ARM_CPU(cs)->env.boot_info = info;
}
}
+
+void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
+{
+ CPUState *cs;
+
+ info->load_kernel_notifier.cpu = cpu;
+ info->load_kernel_notifier.notifier.notify = arm_load_kernel_notify;
+ qemu_add_machine_init_done_notifier(&info->load_kernel_notifier.notifier);
+
+ /* CPU objects (unlike devices) are not automatically reset on system
+ * reset, so we must always register a handler to do so. If we're
+ * actually loading a kernel, the handler is also responsible for
+ * arranging that we start it correctly.
+ */
+ for (cs = CPU(cpu); cs; cs = CPU_NEXT(cs)) {
+ qemu_register_reset(do_cpu_reset, ARM_CPU(cs));
+ }
+}
diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c
index d243159664..a659e8525d 100644
--- a/hw/arm/nseries.c
+++ b/hw/arm/nseries.c
@@ -133,9 +133,8 @@ static void n800_mmc_cs_cb(void *opaque, int line, int level)
static void n8x0_gpio_setup(struct n800_s *s)
{
- qemu_irq *mmc_cs = qemu_allocate_irqs(n800_mmc_cs_cb, s->mpu->mmc, 1);
- qdev_connect_gpio_out(s->mpu->gpio, N8X0_MMC_CS_GPIO, mmc_cs[0]);
-
+ qdev_connect_gpio_out(s->mpu->gpio, N8X0_MMC_CS_GPIO,
+ qemu_allocate_irq(n800_mmc_cs_cb, s->mpu->mmc, 0));
qemu_irq_lower(qdev_get_gpio_in(s->mpu->gpio, N800_BAT_COVER_GPIO));
}
diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c
index 671e02c4ed..4b0f7f9c42 100644
--- a/hw/arm/omap_sx1.c
+++ b/hw/arm/omap_sx1.c
@@ -103,7 +103,6 @@ static void sx1_init(MachineState *machine, const int version)
struct omap_mpu_state_s *mpu;
MemoryRegion *address_space = get_system_memory();
MemoryRegion *flash = g_new(MemoryRegion, 1);
- MemoryRegion *flash_1 = g_new(MemoryRegion, 1);
MemoryRegion *cs = g_new(MemoryRegion, 4);
static uint32_t cs0val = 0x00213090;
static uint32_t cs1val = 0x00215070;
@@ -165,6 +164,7 @@ static void sx1_init(MachineState *machine, const int version)
if ((version == 1) &&
(dinfo = drive_get(IF_PFLASH, 0, fl_idx)) != NULL) {
+ MemoryRegion *flash_1 = g_new(MemoryRegion, 1);
memory_region_init_ram(flash_1, NULL, "omap_sx1.flash1-0", flash1_size,
&error_abort);
vmstate_register_ram_global(flash_1);
diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
index f921a5680c..ec353f79c4 100644
--- a/hw/arm/pxa2xx.c
+++ b/hw/arm/pxa2xx.c
@@ -334,10 +334,10 @@ static uint64_t pxa2xx_cpccnt_read(CPUARMState *env, const ARMCPRegInfo *ri)
static const ARMCPRegInfo pxa_cp_reginfo[] = {
/* cp14 crm==1: perf registers */
{ .name = "CPPMNC", .cp = 14, .crn = 0, .crm = 1, .opc1 = 0, .opc2 = 0,
- .access = PL1_RW,
+ .access = PL1_RW, .type = ARM_CP_IO,
.readfn = pxa2xx_cppmnc_read, .writefn = pxa2xx_cppmnc_write },
{ .name = "CPCCNT", .cp = 14, .crn = 1, .crm = 1, .opc1 = 0, .opc2 = 0,
- .access = PL1_RW,
+ .access = PL1_RW, .type = ARM_CP_IO,
.readfn = pxa2xx_cpccnt_read, .writefn = arm_cp_write_ignore },
{ .name = "CPINTEN", .cp = 14, .crn = 4, .crm = 1, .opc1 = 0, .opc2 = 0,
.access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
@@ -356,11 +356,11 @@ static const ARMCPRegInfo pxa_cp_reginfo[] = {
.access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
/* cp14 crn==6: CLKCFG */
{ .name = "CLKCFG", .cp = 14, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0,
- .access = PL1_RW,
+ .access = PL1_RW, .type = ARM_CP_IO,
.readfn = pxa2xx_clkcfg_read, .writefn = pxa2xx_clkcfg_write },
/* cp14 crn==7: PWRMODE */
{ .name = "PWRMODE", .cp = 14, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 0,
- .access = PL1_RW,
+ .access = PL1_RW, .type = ARM_CP_IO,
.readfn = arm_cp_read_zero, .writefn = pxa2xx_pwrmode_write },
REGINFO_SENTINEL
};
@@ -457,7 +457,7 @@ typedef struct {
MemoryRegion iomem;
qemu_irq irq;
- int enable;
+ uint32_t enable;
SSIBus *bus;
uint32_t sscr[2];
@@ -470,10 +470,39 @@ typedef struct {
uint8_t ssacd;
uint32_t rx_fifo[16];
- int rx_level;
- int rx_start;
+ uint32_t rx_level;
+ uint32_t rx_start;
} PXA2xxSSPState;
+static bool pxa2xx_ssp_vmstate_validate(void *opaque, int version_id)
+{
+ PXA2xxSSPState *s = opaque;
+
+ return s->rx_start < sizeof(s->rx_fifo);
+}
+
+static const VMStateDescription vmstate_pxa2xx_ssp = {
+ .name = "pxa2xx-ssp",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(enable, PXA2xxSSPState),
+ VMSTATE_UINT32_ARRAY(sscr, PXA2xxSSPState, 2),
+ VMSTATE_UINT32(sspsp, PXA2xxSSPState),
+ VMSTATE_UINT32(ssto, PXA2xxSSPState),
+ VMSTATE_UINT32(ssitr, PXA2xxSSPState),
+ VMSTATE_UINT32(sssr, PXA2xxSSPState),
+ VMSTATE_UINT8(sstsa, PXA2xxSSPState),
+ VMSTATE_UINT8(ssrsa, PXA2xxSSPState),
+ VMSTATE_UINT8(ssacd, PXA2xxSSPState),
+ VMSTATE_UINT32(rx_level, PXA2xxSSPState),
+ VMSTATE_UINT32(rx_start, PXA2xxSSPState),
+ VMSTATE_VALIDATE("fifo is 16 bytes", pxa2xx_ssp_vmstate_validate),
+ VMSTATE_UINT32_ARRAY(rx_fifo, PXA2xxSSPState, 16),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
#define SSCR0 0x00 /* SSP Control register 0 */
#define SSCR1 0x04 /* SSP Control register 1 */
#define SSSR 0x08 /* SSP Status register */
@@ -705,55 +734,20 @@ static const MemoryRegionOps pxa2xx_ssp_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static void pxa2xx_ssp_save(QEMUFile *f, void *opaque)
+static void pxa2xx_ssp_reset(DeviceState *d)
{
- PXA2xxSSPState *s = (PXA2xxSSPState *) opaque;
- int i;
-
- qemu_put_be32(f, s->enable);
+ PXA2xxSSPState *s = PXA2XX_SSP(d);
- qemu_put_be32s(f, &s->sscr[0]);
- qemu_put_be32s(f, &s->sscr[1]);
- qemu_put_be32s(f, &s->sspsp);
- qemu_put_be32s(f, &s->ssto);
- qemu_put_be32s(f, &s->ssitr);
- qemu_put_be32s(f, &s->sssr);
- qemu_put_8s(f, &s->sstsa);
- qemu_put_8s(f, &s->ssrsa);
- qemu_put_8s(f, &s->ssacd);
-
- qemu_put_byte(f, s->rx_level);
- for (i = 0; i < s->rx_level; i ++)
- qemu_put_byte(f, s->rx_fifo[(s->rx_start + i) & 0xf]);
-}
-
-static int pxa2xx_ssp_load(QEMUFile *f, void *opaque, int version_id)
-{
- PXA2xxSSPState *s = (PXA2xxSSPState *) opaque;
- int i, v;
-
- s->enable = qemu_get_be32(f);
-
- qemu_get_be32s(f, &s->sscr[0]);
- qemu_get_be32s(f, &s->sscr[1]);
- qemu_get_be32s(f, &s->sspsp);
- qemu_get_be32s(f, &s->ssto);
- qemu_get_be32s(f, &s->ssitr);
- qemu_get_be32s(f, &s->sssr);
- qemu_get_8s(f, &s->sstsa);
- qemu_get_8s(f, &s->ssrsa);
- qemu_get_8s(f, &s->ssacd);
-
- v = qemu_get_byte(f);
- if (v < 0 || v > ARRAY_SIZE(s->rx_fifo)) {
- return -EINVAL;
- }
- s->rx_level = v;
- s->rx_start = 0;
- for (i = 0; i < s->rx_level; i ++)
- s->rx_fifo[i] = qemu_get_byte(f);
-
- return 0;
+ s->enable = 0;
+ s->sscr[0] = s->sscr[1] = 0;
+ s->sspsp = 0;
+ s->ssto = 0;
+ s->ssitr = 0;
+ s->sssr = 0;
+ s->sstsa = 0;
+ s->ssrsa = 0;
+ s->ssacd = 0;
+ s->rx_start = s->rx_level = 0;
}
static int pxa2xx_ssp_init(SysBusDevice *sbd)
@@ -766,8 +760,6 @@ static int pxa2xx_ssp_init(SysBusDevice *sbd)
memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_ssp_ops, s,
"pxa2xx-ssp", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
- register_savevm(dev, "pxa2xx_ssp", -1, 0,
- pxa2xx_ssp_save, pxa2xx_ssp_load, s);
s->bus = ssi_create_bus(dev, "ssi");
return 0;
@@ -1759,24 +1751,33 @@ static PXA2xxI2SState *pxa2xx_i2s_init(MemoryRegion *sysmem,
}
/* PXA Fast Infra-red Communications Port */
+#define TYPE_PXA2XX_FIR "pxa2xx-fir"
+#define PXA2XX_FIR(obj) OBJECT_CHECK(PXA2xxFIrState, (obj), TYPE_PXA2XX_FIR)
+
struct PXA2xxFIrState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+
MemoryRegion iomem;
qemu_irq irq;
qemu_irq rx_dma;
qemu_irq tx_dma;
- int enable;
+ uint32_t enable;
CharDriverState *chr;
uint8_t control[3];
uint8_t status[2];
- int rx_len;
- int rx_start;
+ uint32_t rx_len;
+ uint32_t rx_start;
uint8_t rx_fifo[64];
};
-static void pxa2xx_fir_reset(PXA2xxFIrState *s)
+static void pxa2xx_fir_reset(DeviceState *d)
{
+ PXA2xxFIrState *s = PXA2XX_FIR(d);
+
s->control[0] = 0x00;
s->control[1] = 0x00;
s->control[2] = 0x00;
@@ -1953,73 +1954,94 @@ static void pxa2xx_fir_event(void *opaque, int event)
{
}
-static void pxa2xx_fir_save(QEMUFile *f, void *opaque)
+static void pxa2xx_fir_instance_init(Object *obj)
{
- PXA2xxFIrState *s = (PXA2xxFIrState *) opaque;
- int i;
+ PXA2xxFIrState *s = PXA2XX_FIR(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
- qemu_put_be32(f, s->enable);
-
- qemu_put_8s(f, &s->control[0]);
- qemu_put_8s(f, &s->control[1]);
- qemu_put_8s(f, &s->control[2]);
- qemu_put_8s(f, &s->status[0]);
- qemu_put_8s(f, &s->status[1]);
-
- qemu_put_byte(f, s->rx_len);
- for (i = 0; i < s->rx_len; i ++)
- qemu_put_byte(f, s->rx_fifo[(s->rx_start + i) & 63]);
+ memory_region_init_io(&s->iomem, NULL, &pxa2xx_fir_ops, s,
+ "pxa2xx-fir", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->irq);
+ sysbus_init_irq(sbd, &s->rx_dma);
+ sysbus_init_irq(sbd, &s->tx_dma);
}
-static int pxa2xx_fir_load(QEMUFile *f, void *opaque, int version_id)
+static void pxa2xx_fir_realize(DeviceState *dev, Error **errp)
{
- PXA2xxFIrState *s = (PXA2xxFIrState *) opaque;
- int i;
-
- s->enable = qemu_get_be32(f);
+ PXA2xxFIrState *s = PXA2XX_FIR(dev);
- qemu_get_8s(f, &s->control[0]);
- qemu_get_8s(f, &s->control[1]);
- qemu_get_8s(f, &s->control[2]);
- qemu_get_8s(f, &s->status[0]);
- qemu_get_8s(f, &s->status[1]);
+ if (s->chr) {
+ qemu_chr_fe_claim_no_fail(s->chr);
+ qemu_chr_add_handlers(s->chr, pxa2xx_fir_is_empty,
+ pxa2xx_fir_rx, pxa2xx_fir_event, s);
+ }
+}
- s->rx_len = qemu_get_byte(f);
- s->rx_start = 0;
- for (i = 0; i < s->rx_len; i ++)
- s->rx_fifo[i] = qemu_get_byte(f);
+static bool pxa2xx_fir_vmstate_validate(void *opaque, int version_id)
+{
+ PXA2xxFIrState *s = opaque;
- return 0;
+ return s->rx_start < ARRAY_SIZE(s->rx_fifo);
}
-static PXA2xxFIrState *pxa2xx_fir_init(MemoryRegion *sysmem,
- hwaddr base,
- qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma,
- CharDriverState *chr)
-{
- PXA2xxFIrState *s = (PXA2xxFIrState *)
- g_malloc0(sizeof(PXA2xxFIrState));
+static const VMStateDescription pxa2xx_fir_vmsd = {
+ .name = "pxa2xx-fir",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(enable, PXA2xxFIrState),
+ VMSTATE_UINT8_ARRAY(control, PXA2xxFIrState, 3),
+ VMSTATE_UINT8_ARRAY(status, PXA2xxFIrState, 2),
+ VMSTATE_UINT32(rx_len, PXA2xxFIrState),
+ VMSTATE_UINT32(rx_start, PXA2xxFIrState),
+ VMSTATE_VALIDATE("fifo is 64 bytes", pxa2xx_fir_vmstate_validate),
+ VMSTATE_UINT8_ARRAY(rx_fifo, PXA2xxFIrState, 64),
+ VMSTATE_END_OF_LIST()
+ }
+};
- s->irq = irq;
- s->rx_dma = rx_dma;
- s->tx_dma = tx_dma;
- s->chr = chr;
+static Property pxa2xx_fir_properties[] = {
+ DEFINE_PROP_CHR("chardev", PXA2xxFIrState, chr),
+ DEFINE_PROP_END_OF_LIST(),
+};
- pxa2xx_fir_reset(s);
+static void pxa2xx_fir_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
- memory_region_init_io(&s->iomem, NULL, &pxa2xx_fir_ops, s, "pxa2xx-fir", 0x1000);
- memory_region_add_subregion(sysmem, base, &s->iomem);
+ dc->realize = pxa2xx_fir_realize;
+ dc->vmsd = &pxa2xx_fir_vmsd;
+ dc->props = pxa2xx_fir_properties;
+ dc->reset = pxa2xx_fir_reset;
+}
- if (chr) {
- qemu_chr_fe_claim_no_fail(chr);
- qemu_chr_add_handlers(chr, pxa2xx_fir_is_empty,
- pxa2xx_fir_rx, pxa2xx_fir_event, s);
- }
+static const TypeInfo pxa2xx_fir_info = {
+ .name = TYPE_PXA2XX_FIR,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(PXA2xxFIrState),
+ .class_init = pxa2xx_fir_class_init,
+ .instance_init = pxa2xx_fir_instance_init,
+};
- register_savevm(NULL, "pxa2xx_fir", 0, 0, pxa2xx_fir_save,
- pxa2xx_fir_load, s);
+static PXA2xxFIrState *pxa2xx_fir_init(MemoryRegion *sysmem,
+ hwaddr base,
+ qemu_irq irq, qemu_irq rx_dma,
+ qemu_irq tx_dma,
+ CharDriverState *chr)
+{
+ DeviceState *dev;
+ SysBusDevice *sbd;
- return s;
+ dev = qdev_create(NULL, TYPE_PXA2XX_FIR);
+ qdev_prop_set_chr(dev, "chardev", chr);
+ qdev_init_nofail(dev);
+ sbd = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(sbd, 0, base);
+ sysbus_connect_irq(sbd, 0, irq);
+ sysbus_connect_irq(sbd, 1, rx_dma);
+ sysbus_connect_irq(sbd, 2, tx_dma);
+ return PXA2XX_FIR(dev);
}
static void pxa2xx_reset(void *opaque, int line, int level)
@@ -2306,8 +2328,11 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size)
static void pxa2xx_ssp_class_init(ObjectClass *klass, void *data)
{
SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
sdc->init = pxa2xx_ssp_init;
+ dc->reset = pxa2xx_ssp_reset;
+ dc->vmsd = &vmstate_pxa2xx_ssp;
}
static const TypeInfo pxa2xx_ssp_info = {
@@ -2323,6 +2348,7 @@ static void pxa2xx_register_types(void)
type_register_static(&pxa2xx_ssp_info);
type_register_static(&pxa2xx_i2c_info);
type_register_static(&pxa2xx_rtc_sysbus_info);
+ type_register_static(&pxa2xx_fir_info);
}
type_init(pxa2xx_register_types)
diff --git a/hw/arm/pxa2xx_pic.c b/hw/arm/pxa2xx_pic.c
index 9cfc714874..d41ac93416 100644
--- a/hw/arm/pxa2xx_pic.c
+++ b/hw/arm/pxa2xx_pic.c
@@ -232,7 +232,7 @@ static void pxa2xx_pic_cp_write(CPUARMState *env, const ARMCPRegInfo *ri,
#define REGINFO_FOR_PIC_CP(NAME, CRN) \
{ .name = NAME, .cp = 6, .crn = CRN, .crm = 0, .opc1 = 0, .opc2 = 0, \
- .access = PL1_RW, \
+ .access = PL1_RW, .type = ARM_CP_IO, \
.readfn = pxa2xx_pic_cp_read, .writefn = pxa2xx_pic_cp_write }
static const ARMCPRegInfo pxa_pic_cp_reginfo[] = {
diff --git a/hw/arm/sysbus-fdt.c b/hw/arm/sysbus-fdt.c
new file mode 100644
index 0000000000..3038b94b4a
--- /dev/null
+++ b/hw/arm/sysbus-fdt.c
@@ -0,0 +1,174 @@
+/*
+ * ARM Platform Bus device tree generation helpers
+ *
+ * Copyright (c) 2014 Linaro Limited
+ *
+ * Authors:
+ * Alex Graf <agraf@suse.de>
+ * Eric Auger <eric.auger@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "hw/arm/sysbus-fdt.h"
+#include "qemu/error-report.h"
+#include "sysemu/device_tree.h"
+#include "hw/platform-bus.h"
+#include "sysemu/sysemu.h"
+
+/*
+ * internal struct that contains the information to create dynamic
+ * sysbus device node
+ */
+typedef struct PlatformBusFDTData {
+ void *fdt; /* device tree handle */
+ int irq_start; /* index of the first IRQ usable by platform bus devices */
+ const char *pbus_node_name; /* name of the platform bus node */
+ PlatformBusDevice *pbus;
+} PlatformBusFDTData;
+
+/*
+ * struct used when calling the machine init done notifier
+ * that constructs the fdt nodes of platform bus devices
+ */
+typedef struct PlatformBusFDTNotifierParams {
+ Notifier notifier;
+ ARMPlatformBusFDTParams *fdt_params;
+} PlatformBusFDTNotifierParams;
+
+/* struct that associates a device type name and a node creation function */
+typedef struct NodeCreationPair {
+ const char *typename;
+ int (*add_fdt_node_fn)(SysBusDevice *sbdev, void *opaque);
+} NodeCreationPair;
+
+/* list of supported dynamic sysbus devices */
+static const NodeCreationPair add_fdt_node_functions[] = {
+ {"", NULL}, /* last element */
+};
+
+/**
+ * add_fdt_node - add the device tree node of a dynamic sysbus device
+ *
+ * @sbdev: handle to the sysbus device
+ * @opaque: handle to the PlatformBusFDTData
+ *
+ * Checks the sysbus type belongs to the list of device types that
+ * are dynamically instantiable and if so call the node creation
+ * function.
+ */
+static int add_fdt_node(SysBusDevice *sbdev, void *opaque)
+{
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(add_fdt_node_functions); i++) {
+ if (!strcmp(object_get_typename(OBJECT(sbdev)),
+ add_fdt_node_functions[i].typename)) {
+ ret = add_fdt_node_functions[i].add_fdt_node_fn(sbdev, opaque);
+ assert(!ret);
+ return 0;
+ }
+ }
+ error_report("Device %s can not be dynamically instantiated",
+ qdev_fw_name(DEVICE(sbdev)));
+ exit(1);
+}
+
+/**
+ * add_all_platform_bus_fdt_nodes - create all the platform bus nodes
+ *
+ * builds the parent platform bus node and all the nodes of dynamic
+ * sysbus devices attached to it.
+ */
+static void add_all_platform_bus_fdt_nodes(ARMPlatformBusFDTParams *fdt_params)
+{
+ const char platcomp[] = "qemu,platform\0simple-bus";
+ PlatformBusDevice *pbus;
+ DeviceState *dev;
+ gchar *node;
+ uint64_t addr, size;
+ int irq_start, dtb_size;
+ struct arm_boot_info *info = fdt_params->binfo;
+ const ARMPlatformBusSystemParams *params = fdt_params->system_params;
+ const char *intc = fdt_params->intc;
+ void *fdt = info->get_dtb(info, &dtb_size);
+
+ /*
+ * If the user provided a dtb, we assume the dynamic sysbus nodes
+ * already are integrated there. This corresponds to a use case where
+ * the dynamic sysbus nodes are complex and their generation is not yet
+ * supported. In that case the user can take charge of the guest dt
+ * while qemu takes charge of the qom stuff.
+ */
+ if (info->dtb_filename) {
+ return;
+ }
+
+ assert(fdt);
+
+ node = g_strdup_printf("/platform@%"PRIx64, params->platform_bus_base);
+ addr = params->platform_bus_base;
+ size = params->platform_bus_size;
+ irq_start = params->platform_bus_first_irq;
+
+ /* Create a /platform node that we can put all devices into */
+ qemu_fdt_add_subnode(fdt, node);
+ qemu_fdt_setprop(fdt, node, "compatible", platcomp, sizeof(platcomp));
+
+ /* Our platform bus region is less than 32bits, so 1 cell is enough for
+ * address and size
+ */
+ qemu_fdt_setprop_cells(fdt, node, "#size-cells", 1);
+ qemu_fdt_setprop_cells(fdt, node, "#address-cells", 1);
+ qemu_fdt_setprop_cells(fdt, node, "ranges", 0, addr >> 32, addr, size);
+
+ qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", intc);
+
+ dev = qdev_find_recursive(sysbus_get_default(), TYPE_PLATFORM_BUS_DEVICE);
+ pbus = PLATFORM_BUS_DEVICE(dev);
+
+ /* We can only create dt nodes for dynamic devices when they're ready */
+ assert(pbus->done_gathering);
+
+ PlatformBusFDTData data = {
+ .fdt = fdt,
+ .irq_start = irq_start,
+ .pbus_node_name = node,
+ .pbus = pbus,
+ };
+
+ /* Loop through all dynamic sysbus devices and create their node */
+ foreach_dynamic_sysbus_device(add_fdt_node, &data);
+
+ g_free(node);
+}
+
+static void platform_bus_fdt_notify(Notifier *notifier, void *data)
+{
+ PlatformBusFDTNotifierParams *p = DO_UPCAST(PlatformBusFDTNotifierParams,
+ notifier, notifier);
+
+ add_all_platform_bus_fdt_nodes(p->fdt_params);
+ g_free(p->fdt_params);
+ g_free(p);
+}
+
+void arm_register_platform_bus_fdt_creator(ARMPlatformBusFDTParams *fdt_params)
+{
+ PlatformBusFDTNotifierParams *p = g_new(PlatformBusFDTNotifierParams, 1);
+
+ p->fdt_params = fdt_params;
+ p->notifier.notify = platform_bus_fdt_notify;
+ qemu_add_machine_init_done_notifier(&p->notifier);
+}
diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c
index 8f1a5ea992..da217884e6 100644
--- a/hw/arm/vexpress.c
+++ b/hw/arm/vexpress.c
@@ -525,7 +525,7 @@ static pflash_t *ve_pflash_cfi01_register(hwaddr base, const char *name,
qdev_prop_set_uint64(dev, "sector-length", VEXPRESS_FLASH_SECT_SIZE);
qdev_prop_set_uint8(dev, "width", 4);
qdev_prop_set_uint8(dev, "device-width", 2);
- qdev_prop_set_uint8(dev, "big-endian", 0);
+ qdev_prop_set_bit(dev, "big-endian", false);
qdev_prop_set_uint16(dev, "id0", 0x89);
qdev_prop_set_uint16(dev, "id1", 0x18);
qdev_prop_set_uint16(dev, "id2", 0x00);
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index a9373ccaca..d5a8b9c017 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -84,6 +84,12 @@ static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
AML_EXCLUSIVE, uart_irq));
aml_append(dev, aml_name_decl("_CRS", crs));
+
+ /* The _ADR entry is used to link this device to the UART described
+ * in the SPCR table, i.e. SPCR.base_address.address == _ADR.
+ */
+ aml_append(dev, aml_name_decl("_ADR", aml_int(uart_memmap->base)));
+
aml_append(scope, dev);
}
@@ -334,6 +340,38 @@ build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt)
}
static void
+build_spcr(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
+{
+ AcpiSerialPortConsoleRedirection *spcr;
+ const MemMapEntry *uart_memmap = &guest_info->memmap[VIRT_UART];
+ int irq = guest_info->irqmap[VIRT_UART] + ARM_SPI_BASE;
+
+ spcr = acpi_data_push(table_data, sizeof(*spcr));
+
+ spcr->interface_type = 0x3; /* ARM PL011 UART */
+
+ spcr->base_address.space_id = AML_SYSTEM_MEMORY;
+ spcr->base_address.bit_width = 8;
+ spcr->base_address.bit_offset = 0;
+ spcr->base_address.access_width = 1;
+ spcr->base_address.address = cpu_to_le64(uart_memmap->base);
+
+ spcr->interrupt_types = (1 << 3); /* Bit[3] ARMH GIC interrupt */
+ spcr->gsi = cpu_to_le32(irq); /* Global System Interrupt */
+
+ spcr->baud = 3; /* Baud Rate: 3 = 9600 */
+ spcr->parity = 0; /* No Parity */
+ spcr->stopbits = 1; /* 1 Stop bit */
+ spcr->flowctrl = (1 << 1); /* Bit[1] = RTS/CTS hardware flow control */
+ spcr->term_type = 0; /* Terminal Type: 0 = VT100 */
+
+ spcr->pci_device_id = 0xffff; /* PCI Device ID: not a PCI device */
+ spcr->pci_vendor_id = 0xffff; /* PCI Vendor ID: not a PCI device */
+
+ build_header(linker, table_data, (void *)spcr, "SPCR", sizeof(*spcr), 2);
+}
+
+static void
build_mcfg(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
{
AcpiTableMcfg *mcfg;
@@ -514,7 +552,7 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
dsdt = tables_blob->len;
build_dsdt(tables_blob, tables->linker, guest_info);
- /* FADT MADT GTDT pointed to by RSDT */
+ /* FADT MADT GTDT SPCR pointed to by RSDT */
acpi_add_table(table_offsets, tables_blob);
build_fadt(tables_blob, tables->linker, dsdt);
@@ -527,6 +565,9 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables)
acpi_add_table(table_offsets, tables_blob);
build_mcfg(tables_blob, tables->linker, guest_info);
+ acpi_add_table(table_offsets, tables_blob);
+ build_spcr(tables_blob, tables->linker, guest_info);
+
/* RSDT is pointed to by RSDP */
rsdt = tables_blob->len;
build_rsdt(tables_blob, tables->linker, table_offsets);
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 05db8cb2f7..f1e85c8e92 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -45,9 +45,11 @@
#include "qemu/error-report.h"
#include "hw/pci-host/gpex.h"
#include "hw/arm/virt-acpi-build.h"
+#include "hw/arm/sysbus-fdt.h"
+#include "hw/platform-bus.h"
/* Number of external interrupt lines to configure the GIC with */
-#define NUM_IRQS 128
+#define NUM_IRQS 256
#define GIC_FDT_IRQ_TYPE_SPI 0
#define GIC_FDT_IRQ_TYPE_PPI 1
@@ -60,6 +62,10 @@
#define GIC_FDT_IRQ_PPI_CPU_START 8
#define GIC_FDT_IRQ_PPI_CPU_WIDTH 8
+#define PLATFORM_BUS_NUM_IRQS 64
+
+static ARMPlatformBusSystemParams platform_bus_params;
+
typedef struct VirtBoardInfo {
struct arm_boot_info bootinfo;
const char *cpu_model;
@@ -69,6 +75,8 @@ typedef struct VirtBoardInfo {
void *fdt;
int fdt_size;
uint32_t clock_phandle;
+ uint32_t gic_phandle;
+ uint32_t v2m_phandle;
} VirtBoardInfo;
typedef struct {
@@ -103,20 +111,22 @@ typedef struct {
*/
static const MemMapEntry a15memmap[] = {
/* Space up to 0x8000000 is reserved for a boot ROM */
- [VIRT_FLASH] = { 0, 0x08000000 },
- [VIRT_CPUPERIPHS] = { 0x08000000, 0x00020000 },
+ [VIRT_FLASH] = { 0, 0x08000000 },
+ [VIRT_CPUPERIPHS] = { 0x08000000, 0x00020000 },
/* GIC distributor and CPU interfaces sit inside the CPU peripheral space */
- [VIRT_GIC_DIST] = { 0x08000000, 0x00010000 },
- [VIRT_GIC_CPU] = { 0x08010000, 0x00010000 },
- [VIRT_UART] = { 0x09000000, 0x00001000 },
- [VIRT_RTC] = { 0x09010000, 0x00001000 },
- [VIRT_FW_CFG] = { 0x09020000, 0x0000000a },
- [VIRT_MMIO] = { 0x0a000000, 0x00000200 },
+ [VIRT_GIC_DIST] = { 0x08000000, 0x00010000 },
+ [VIRT_GIC_CPU] = { 0x08010000, 0x00010000 },
+ [VIRT_GIC_V2M] = { 0x08020000, 0x00001000 },
+ [VIRT_UART] = { 0x09000000, 0x00001000 },
+ [VIRT_RTC] = { 0x09010000, 0x00001000 },
+ [VIRT_FW_CFG] = { 0x09020000, 0x0000000a },
+ [VIRT_MMIO] = { 0x0a000000, 0x00000200 },
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
- [VIRT_PCIE_MMIO] = { 0x10000000, 0x2eff0000 },
- [VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 },
- [VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 },
- [VIRT_MEM] = { 0x40000000, 30ULL * 1024 * 1024 * 1024 },
+ [VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
+ [VIRT_PCIE_MMIO] = { 0x10000000, 0x2eff0000 },
+ [VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 },
+ [VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 },
+ [VIRT_MEM] = { 0x40000000, 30ULL * 1024 * 1024 * 1024 },
};
static const int a15irqmap[] = {
@@ -124,6 +134,8 @@ static const int a15irqmap[] = {
[VIRT_RTC] = 2,
[VIRT_PCIE] = 3, /* ... to 6 */
[VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
+ [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
+ [VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1 */
};
static VirtBoardInfo machines[] = {
@@ -133,6 +145,11 @@ static VirtBoardInfo machines[] = {
.irqmap = a15irqmap,
},
{
+ .cpu_model = "cortex-a53",
+ .memmap = a15memmap,
+ .irqmap = a15irqmap,
+ },
+ {
.cpu_model = "cortex-a57",
.memmap = a15memmap,
.irqmap = a15irqmap,
@@ -294,17 +311,28 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi)
"enable-method", "psci");
}
- qemu_fdt_setprop_cell(vbi->fdt, nodename, "reg", cpu);
+ qemu_fdt_setprop_cell(vbi->fdt, nodename, "reg", armcpu->mp_affinity);
g_free(nodename);
}
}
-static uint32_t fdt_add_gic_node(const VirtBoardInfo *vbi)
+static void fdt_add_v2m_gic_node(VirtBoardInfo *vbi)
{
- uint32_t gic_phandle;
+ vbi->v2m_phandle = qemu_fdt_alloc_phandle(vbi->fdt);
+ qemu_fdt_add_subnode(vbi->fdt, "/intc/v2m");
+ qemu_fdt_setprop_string(vbi->fdt, "/intc/v2m", "compatible",
+ "arm,gic-v2m-frame");
+ qemu_fdt_setprop(vbi->fdt, "/intc/v2m", "msi-controller", NULL, 0);
+ qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc/v2m", "reg",
+ 2, vbi->memmap[VIRT_GIC_V2M].base,
+ 2, vbi->memmap[VIRT_GIC_V2M].size);
+ qemu_fdt_setprop_cell(vbi->fdt, "/intc/v2m", "phandle", vbi->v2m_phandle);
+}
- gic_phandle = qemu_fdt_alloc_phandle(vbi->fdt);
- qemu_fdt_setprop_cell(vbi->fdt, "/", "interrupt-parent", gic_phandle);
+static void fdt_add_gic_node(VirtBoardInfo *vbi)
+{
+ vbi->gic_phandle = qemu_fdt_alloc_phandle(vbi->fdt);
+ qemu_fdt_setprop_cell(vbi->fdt, "/", "interrupt-parent", vbi->gic_phandle);
qemu_fdt_add_subnode(vbi->fdt, "/intc");
/* 'cortex-a15-gic' means 'GIC v2' */
@@ -317,12 +345,32 @@ static uint32_t fdt_add_gic_node(const VirtBoardInfo *vbi)
2, vbi->memmap[VIRT_GIC_DIST].size,
2, vbi->memmap[VIRT_GIC_CPU].base,
2, vbi->memmap[VIRT_GIC_CPU].size);
- qemu_fdt_setprop_cell(vbi->fdt, "/intc", "phandle", gic_phandle);
+ qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#address-cells", 0x2);
+ qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#size-cells", 0x2);
+ qemu_fdt_setprop(vbi->fdt, "/intc", "ranges", NULL, 0);
+ qemu_fdt_setprop_cell(vbi->fdt, "/intc", "phandle", vbi->gic_phandle);
+}
+
+static void create_v2m(VirtBoardInfo *vbi, qemu_irq *pic)
+{
+ int i;
+ int irq = vbi->irqmap[VIRT_GIC_V2M];
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "arm-gicv2m");
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vbi->memmap[VIRT_GIC_V2M].base);
+ qdev_prop_set_uint32(dev, "base-spi", irq);
+ qdev_prop_set_uint32(dev, "num-spi", NUM_GICV2M_SPIS);
+ qdev_init_nofail(dev);
+
+ for (i = 0; i < NUM_GICV2M_SPIS; i++) {
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]);
+ }
- return gic_phandle;
+ fdt_add_v2m_gic_node(vbi);
}
-static uint32_t create_gic(const VirtBoardInfo *vbi, qemu_irq *pic)
+static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic)
{
/* We create a standalone GIC v2 */
DeviceState *gicdev;
@@ -371,7 +419,9 @@ static uint32_t create_gic(const VirtBoardInfo *vbi, qemu_irq *pic)
pic[i] = qdev_get_gpio_in(gicdev, i);
}
- return fdt_add_gic_node(vbi);
+ fdt_add_gic_node(vbi);
+
+ create_v2m(vbi, pic);
}
static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic)
@@ -510,7 +560,7 @@ static void create_one_flash(const char *name, hwaddr flashbase,
qdev_prop_set_uint64(dev, "sector-length", sectorlength);
qdev_prop_set_uint8(dev, "width", 4);
qdev_prop_set_uint8(dev, "device-width", 2);
- qdev_prop_set_uint8(dev, "big-endian", 0);
+ qdev_prop_set_bit(dev, "big-endian", false);
qdev_prop_set_uint16(dev, "id0", 0x89);
qdev_prop_set_uint16(dev, "id1", 0x18);
qdev_prop_set_uint16(dev, "id2", 0x00);
@@ -587,7 +637,7 @@ static void create_pcie_irq_map(const VirtBoardInfo *vbi, uint32_t gic_phandle,
int first_irq, const char *nodename)
{
int devfn, pin;
- uint32_t full_irq_map[4 * 4 * 8] = { 0 };
+ uint32_t full_irq_map[4 * 4 * 10] = { 0 };
uint32_t *irq_map = full_irq_map;
for (devfn = 0; devfn <= 0x18; devfn += 0x8) {
@@ -600,13 +650,13 @@ static void create_pcie_irq_map(const VirtBoardInfo *vbi, uint32_t gic_phandle,
uint32_t map[] = {
devfn << 8, 0, 0, /* devfn */
pin + 1, /* PCI pin */
- gic_phandle, irq_type, irq_nr, irq_level }; /* GIC irq */
+ gic_phandle, 0, 0, irq_type, irq_nr, irq_level }; /* GIC irq */
/* Convert map to big endian */
- for (i = 0; i < 8; i++) {
+ for (i = 0; i < 10; i++) {
irq_map[i] = cpu_to_be32(map[i]);
}
- irq_map += 8;
+ irq_map += 10;
}
}
@@ -618,8 +668,7 @@ static void create_pcie_irq_map(const VirtBoardInfo *vbi, uint32_t gic_phandle,
0x7 /* PCI irq */);
}
-static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
- uint32_t gic_phandle)
+static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic)
{
hwaddr base_mmio = vbi->memmap[VIRT_PCIE_MMIO].base;
hwaddr size_mmio = vbi->memmap[VIRT_PCIE_MMIO].size;
@@ -676,6 +725,8 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
qemu_fdt_setprop_cells(vbi->fdt, nodename, "bus-range", 0,
nr_pcie_buses - 1);
+ qemu_fdt_setprop_cells(vbi->fdt, nodename, "msi-parent", vbi->v2m_phandle);
+
qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
2, base_ecam, 2, size_ecam);
qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "ranges",
@@ -685,11 +736,52 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
2, base_mmio, 2, size_mmio);
qemu_fdt_setprop_cell(vbi->fdt, nodename, "#interrupt-cells", 1);
- create_pcie_irq_map(vbi, gic_phandle, irq, nodename);
+ create_pcie_irq_map(vbi, vbi->gic_phandle, irq, nodename);
g_free(nodename);
}
+static void create_platform_bus(VirtBoardInfo *vbi, qemu_irq *pic)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ int i;
+ ARMPlatformBusFDTParams *fdt_params = g_new(ARMPlatformBusFDTParams, 1);
+ MemoryRegion *sysmem = get_system_memory();
+
+ platform_bus_params.platform_bus_base = vbi->memmap[VIRT_PLATFORM_BUS].base;
+ platform_bus_params.platform_bus_size = vbi->memmap[VIRT_PLATFORM_BUS].size;
+ platform_bus_params.platform_bus_first_irq = vbi->irqmap[VIRT_PLATFORM_BUS];
+ platform_bus_params.platform_bus_num_irqs = PLATFORM_BUS_NUM_IRQS;
+
+ fdt_params->system_params = &platform_bus_params;
+ fdt_params->binfo = &vbi->bootinfo;
+ fdt_params->intc = "/intc";
+ /*
+ * register a machine init done notifier that creates the device tree
+ * nodes of the platform bus and its children dynamic sysbus devices
+ */
+ arm_register_platform_bus_fdt_creator(fdt_params);
+
+ dev = qdev_create(NULL, TYPE_PLATFORM_BUS_DEVICE);
+ dev->id = TYPE_PLATFORM_BUS_DEVICE;
+ qdev_prop_set_uint32(dev, "num_irqs",
+ platform_bus_params.platform_bus_num_irqs);
+ qdev_prop_set_uint32(dev, "mmio_size",
+ platform_bus_params.platform_bus_size);
+ qdev_init_nofail(dev);
+ s = SYS_BUS_DEVICE(dev);
+
+ for (i = 0; i < platform_bus_params.platform_bus_num_irqs; i++) {
+ int irqn = platform_bus_params.platform_bus_first_irq + i;
+ sysbus_connect_irq(s, i, pic[irqn]);
+ }
+
+ memory_region_add_subregion(sysmem,
+ platform_bus_params.platform_bus_base,
+ sysbus_mmio_get_region(s, 0));
+}
+
static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size)
{
const VirtBoardInfo *board = (const VirtBoardInfo *)binfo;
@@ -717,7 +809,6 @@ static void machvirt_init(MachineState *machine)
VirtBoardInfo *vbi;
VirtGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state);
VirtGuestInfo *guest_info = &guest_info_state->info;
- uint32_t gic_phandle;
char **cpustr;
if (!cpu_model) {
@@ -794,13 +885,13 @@ static void machvirt_init(MachineState *machine)
create_flash(vbi);
- gic_phandle = create_gic(vbi, pic);
+ create_gic(vbi, pic);
create_uart(vbi, pic);
create_rtc(vbi, pic);
- create_pcie(vbi, pic, gic_phandle);
+ create_pcie(vbi, pic);
/* Create mmio transports, so the user can create virtio backends
* (which will be automatically plugged in to the transports). If
@@ -828,6 +919,14 @@ static void machvirt_init(MachineState *machine)
vbi->bootinfo.get_dtb = machvirt_dtb;
vbi->bootinfo.firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0);
arm_load_kernel(ARM_CPU(first_cpu), &vbi->bootinfo);
+
+ /*
+ * arm_load_kernel machine init done notifier registration must
+ * happen before the platform_bus_create call. In this latter,
+ * another notifier is registered which adds platform bus nodes.
+ * Notifiers are executed in registration reverse order.
+ */
+ create_platform_bus(vbi, pic);
}
static bool virt_get_secure(Object *obj, Error **errp)
@@ -866,6 +965,7 @@ static void virt_class_init(ObjectClass *oc, void *data)
mc->desc = "ARM Virtual Machine",
mc->init = machvirt_init;
mc->max_cpus = 8;
+ mc->has_dynamic_sysbus = true;
}
static const TypeInfo machvirt_info = {
diff --git a/hw/block/fdc.c b/hw/block/fdc.c
index d8a8edd936..5e1b67ee43 100644
--- a/hw/block/fdc.c
+++ b/hw/block/fdc.c
@@ -324,7 +324,7 @@ static void fd_revalidate(FDrive *drv)
/* Intel 82078 floppy disk controller emulation */
static void fdctrl_reset(FDCtrl *fdctrl, int do_irq);
-static void fdctrl_reset_fifo(FDCtrl *fdctrl);
+static void fdctrl_to_command_phase(FDCtrl *fdctrl);
static int fdctrl_transfer_handler (void *opaque, int nchan,
int dma_pos, int dma_len);
static void fdctrl_raise_irq(FDCtrl *fdctrl);
@@ -495,6 +495,33 @@ enum {
FD_DIR_DSKCHG = 0x80,
};
+/*
+ * See chapter 5.0 "Controller phases" of the spec:
+ *
+ * Command phase:
+ * The host writes a command and its parameters into the FIFO. The command
+ * phase is completed when all parameters for the command have been supplied,
+ * and execution phase is entered.
+ *
+ * Execution phase:
+ * Data transfers, either DMA or non-DMA. For non-DMA transfers, the FIFO
+ * contains the payload now, otherwise it's unused. When all bytes of the
+ * required data have been transferred, the state is switched to either result
+ * phase (if the command produces status bytes) or directly back into the
+ * command phase for the next command.
+ *
+ * Result phase:
+ * The host reads out the FIFO, which contains one or more result bytes now.
+ */
+enum {
+ /* Only for migration: reconstruct phase from registers like qemu 2.3 */
+ FD_PHASE_RECONSTRUCT = 0,
+
+ FD_PHASE_COMMAND = 1,
+ FD_PHASE_EXECUTION = 2,
+ FD_PHASE_RESULT = 3,
+};
+
#define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI)
#define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT)
@@ -504,6 +531,7 @@ struct FDCtrl {
/* Controller state */
QEMUTimer *result_timer;
int dma_chann;
+ uint8_t phase;
/* Controller's identification */
uint8_t version;
/* HW */
@@ -671,6 +699,7 @@ static const VMStateDescription vmstate_fdrive_media_changed = {
.name = "fdrive/media_changed",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = fdrive_media_changed_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8(media_changed, FDrive),
VMSTATE_END_OF_LIST()
@@ -688,6 +717,7 @@ static const VMStateDescription vmstate_fdrive_media_rate = {
.name = "fdrive/media_rate",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = fdrive_media_rate_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8(media_rate, FDrive),
VMSTATE_END_OF_LIST()
@@ -705,6 +735,7 @@ static const VMStateDescription vmstate_fdrive_perpendicular = {
.name = "fdrive/perpendicular",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = fdrive_perpendicular_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8(perpendicular, FDrive),
VMSTATE_END_OF_LIST()
@@ -728,22 +759,36 @@ static const VMStateDescription vmstate_fdrive = {
VMSTATE_UINT8(sect, FDrive),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_fdrive_media_changed,
- .needed = &fdrive_media_changed_needed,
- } , {
- .vmsd = &vmstate_fdrive_media_rate,
- .needed = &fdrive_media_rate_needed,
- } , {
- .vmsd = &vmstate_fdrive_perpendicular,
- .needed = &fdrive_perpendicular_needed,
- } , {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_fdrive_media_changed,
+ &vmstate_fdrive_media_rate,
+ &vmstate_fdrive_perpendicular,
+ NULL
}
};
+/*
+ * Reconstructs the phase from register values according to the logic that was
+ * implemented in qemu 2.3. This is the default value that is used if the phase
+ * subsection is not present on migration.
+ *
+ * Don't change this function to reflect newer qemu versions, it is part of
+ * the migration ABI.
+ */
+static int reconstruct_phase(FDCtrl *fdctrl)
+{
+ if (fdctrl->msr & FD_MSR_NONDMA) {
+ return FD_PHASE_EXECUTION;
+ } else if ((fdctrl->msr & FD_MSR_RQM) == 0) {
+ /* qemu 2.3 disabled RQM only during DMA transfers */
+ return FD_PHASE_EXECUTION;
+ } else if (fdctrl->msr & FD_MSR_DIO) {
+ return FD_PHASE_RESULT;
+ } else {
+ return FD_PHASE_COMMAND;
+ }
+}
+
static void fdc_pre_save(void *opaque)
{
FDCtrl *s = opaque;
@@ -751,12 +796,24 @@ static void fdc_pre_save(void *opaque)
s->dor_vmstate = s->dor | GET_CUR_DRV(s);
}
+static int fdc_pre_load(void *opaque)
+{
+ FDCtrl *s = opaque;
+ s->phase = FD_PHASE_RECONSTRUCT;
+ return 0;
+}
+
static int fdc_post_load(void *opaque, int version_id)
{
FDCtrl *s = opaque;
SET_CUR_DRV(s, s->dor_vmstate & FD_DOR_SELMASK);
s->dor = s->dor_vmstate & ~FD_DOR_SELMASK;
+
+ if (s->phase == FD_PHASE_RECONSTRUCT) {
+ s->phase = reconstruct_phase(s);
+ }
+
return 0;
}
@@ -771,6 +828,7 @@ static const VMStateDescription vmstate_fdc_reset_sensei = {
.name = "fdc/reset_sensei",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = fdc_reset_sensei_needed,
.fields = (VMStateField[]) {
VMSTATE_INT32(reset_sensei, FDCtrl),
VMSTATE_END_OF_LIST()
@@ -788,17 +846,37 @@ static const VMStateDescription vmstate_fdc_result_timer = {
.name = "fdc/result_timer",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = fdc_result_timer_needed,
.fields = (VMStateField[]) {
VMSTATE_TIMER_PTR(result_timer, FDCtrl),
VMSTATE_END_OF_LIST()
}
};
+static bool fdc_phase_needed(void *opaque)
+{
+ FDCtrl *fdctrl = opaque;
+
+ return reconstruct_phase(fdctrl) != fdctrl->phase;
+}
+
+static const VMStateDescription vmstate_fdc_phase = {
+ .name = "fdc/phase",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = fdc_phase_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(phase, FDCtrl),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_fdc = {
.name = "fdc",
.version_id = 2,
.minimum_version_id = 2,
.pre_save = fdc_pre_save,
+ .pre_load = fdc_pre_load,
.post_load = fdc_post_load,
.fields = (VMStateField[]) {
/* Controller State */
@@ -831,16 +909,11 @@ static const VMStateDescription vmstate_fdc = {
vmstate_fdrive, FDrive),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_fdc_reset_sensei,
- .needed = fdc_reset_sensei_needed,
- } , {
- .vmsd = &vmstate_fdc_result_timer,
- .needed = fdc_result_timer_needed,
- } , {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_fdc_reset_sensei,
+ &vmstate_fdc_result_timer,
+ &vmstate_fdc_phase,
+ NULL
}
};
@@ -918,7 +991,7 @@ static void fdctrl_reset(FDCtrl *fdctrl, int do_irq)
fdctrl->data_dir = FD_DIR_WRITE;
for (i = 0; i < MAX_FD; i++)
fd_recalibrate(&fdctrl->drives[i]);
- fdctrl_reset_fifo(fdctrl);
+ fdctrl_to_command_phase(fdctrl);
if (do_irq) {
fdctrl->status0 |= FD_SR0_RDYCHG;
fdctrl_raise_irq(fdctrl);
@@ -1134,17 +1207,22 @@ static uint32_t fdctrl_read_dir(FDCtrl *fdctrl)
return retval;
}
-/* FIFO state control */
-static void fdctrl_reset_fifo(FDCtrl *fdctrl)
+/* Clear the FIFO and update the state for receiving the next command */
+static void fdctrl_to_command_phase(FDCtrl *fdctrl)
{
+ fdctrl->phase = FD_PHASE_COMMAND;
fdctrl->data_dir = FD_DIR_WRITE;
fdctrl->data_pos = 0;
+ fdctrl->data_len = 1; /* Accept command byte, adjust for params later */
fdctrl->msr &= ~(FD_MSR_CMDBUSY | FD_MSR_DIO);
+ fdctrl->msr |= FD_MSR_RQM;
}
-/* Set FIFO status for the host to read */
-static void fdctrl_set_fifo(FDCtrl *fdctrl, int fifo_len)
+/* Update the state to allow the guest to read out the command status.
+ * @fifo_len is the number of result bytes to be read out. */
+static void fdctrl_to_result_phase(FDCtrl *fdctrl, int fifo_len)
{
+ fdctrl->phase = FD_PHASE_RESULT;
fdctrl->data_dir = FD_DIR_READ;
fdctrl->data_len = fifo_len;
fdctrl->data_pos = 0;
@@ -1157,7 +1235,7 @@ static void fdctrl_unimplemented(FDCtrl *fdctrl, int direction)
qemu_log_mask(LOG_UNIMP, "fdc: unimplemented command 0x%02x\n",
fdctrl->fifo[0]);
fdctrl->fifo[0] = FD_SR0_INVCMD;
- fdctrl_set_fifo(fdctrl, 1);
+ fdctrl_to_result_phase(fdctrl, 1);
}
/* Seek to next sector
@@ -1238,7 +1316,7 @@ static void fdctrl_stop_transfer(FDCtrl *fdctrl, uint8_t status0,
fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO;
fdctrl->msr &= ~FD_MSR_NONDMA;
- fdctrl_set_fifo(fdctrl, 7);
+ fdctrl_to_result_phase(fdctrl, 7);
fdctrl_raise_irq(fdctrl);
}
@@ -1352,7 +1430,7 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction)
}
}
FLOPPY_DPRINTF("start non-DMA transfer\n");
- fdctrl->msr |= FD_MSR_NONDMA;
+ fdctrl->msr |= FD_MSR_NONDMA | FD_MSR_RQM;
if (direction != FD_DIR_WRITE)
fdctrl->msr |= FD_MSR_DIO;
/* IO based transfer: calculate len */
@@ -1505,9 +1583,16 @@ static uint32_t fdctrl_read_data(FDCtrl *fdctrl)
FLOPPY_DPRINTF("error: controller not ready for reading\n");
return 0;
}
+
+ /* If data_len spans multiple sectors, the current position in the FIFO
+ * wraps around while fdctrl->data_pos is the real position in the whole
+ * request. */
pos = fdctrl->data_pos;
pos %= FD_SECTOR_LEN;
- if (fdctrl->msr & FD_MSR_NONDMA) {
+
+ switch (fdctrl->phase) {
+ case FD_PHASE_EXECUTION:
+ assert(fdctrl->msr & FD_MSR_NONDMA);
if (pos == 0) {
if (fdctrl->data_pos != 0)
if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) {
@@ -1523,20 +1608,28 @@ static uint32_t fdctrl_read_data(FDCtrl *fdctrl)
memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
}
}
- }
- retval = fdctrl->fifo[pos];
- if (++fdctrl->data_pos == fdctrl->data_len) {
- fdctrl->data_pos = 0;
- /* Switch from transfer mode to status mode
- * then from status mode to command mode
- */
- if (fdctrl->msr & FD_MSR_NONDMA) {
+
+ if (++fdctrl->data_pos == fdctrl->data_len) {
+ fdctrl->msr &= ~FD_MSR_RQM;
fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
- } else {
- fdctrl_reset_fifo(fdctrl);
+ }
+ break;
+
+ case FD_PHASE_RESULT:
+ assert(!(fdctrl->msr & FD_MSR_NONDMA));
+ if (++fdctrl->data_pos == fdctrl->data_len) {
+ fdctrl->msr &= ~FD_MSR_RQM;
+ fdctrl_to_command_phase(fdctrl);
fdctrl_reset_irq(fdctrl);
}
+ break;
+
+ case FD_PHASE_COMMAND:
+ default:
+ abort();
}
+
+ retval = fdctrl->fifo[pos];
FLOPPY_DPRINTF("data register: 0x%02x\n", retval);
return retval;
@@ -1606,7 +1699,7 @@ static void fdctrl_handle_lock(FDCtrl *fdctrl, int direction)
{
fdctrl->lock = (fdctrl->fifo[0] & 0x80) ? 1 : 0;
fdctrl->fifo[0] = fdctrl->lock << 4;
- fdctrl_set_fifo(fdctrl, 1);
+ fdctrl_to_result_phase(fdctrl, 1);
}
static void fdctrl_handle_dumpreg(FDCtrl *fdctrl, int direction)
@@ -1631,20 +1724,20 @@ static void fdctrl_handle_dumpreg(FDCtrl *fdctrl, int direction)
(cur_drv->perpendicular << 2);
fdctrl->fifo[8] = fdctrl->config;
fdctrl->fifo[9] = fdctrl->precomp_trk;
- fdctrl_set_fifo(fdctrl, 10);
+ fdctrl_to_result_phase(fdctrl, 10);
}
static void fdctrl_handle_version(FDCtrl *fdctrl, int direction)
{
/* Controller's version */
fdctrl->fifo[0] = fdctrl->version;
- fdctrl_set_fifo(fdctrl, 1);
+ fdctrl_to_result_phase(fdctrl, 1);
}
static void fdctrl_handle_partid(FDCtrl *fdctrl, int direction)
{
fdctrl->fifo[0] = 0x41; /* Stepping 1 */
- fdctrl_set_fifo(fdctrl, 1);
+ fdctrl_to_result_phase(fdctrl, 1);
}
static void fdctrl_handle_restore(FDCtrl *fdctrl, int direction)
@@ -1667,7 +1760,7 @@ static void fdctrl_handle_restore(FDCtrl *fdctrl, int direction)
fdctrl->config = fdctrl->fifo[11];
fdctrl->precomp_trk = fdctrl->fifo[12];
fdctrl->pwrd = fdctrl->fifo[13];
- fdctrl_reset_fifo(fdctrl);
+ fdctrl_to_command_phase(fdctrl);
}
static void fdctrl_handle_save(FDCtrl *fdctrl, int direction)
@@ -1697,7 +1790,7 @@ static void fdctrl_handle_save(FDCtrl *fdctrl, int direction)
fdctrl->fifo[12] = fdctrl->pwrd;
fdctrl->fifo[13] = 0;
fdctrl->fifo[14] = 0;
- fdctrl_set_fifo(fdctrl, 15);
+ fdctrl_to_result_phase(fdctrl, 15);
}
static void fdctrl_handle_readid(FDCtrl *fdctrl, int direction)
@@ -1746,7 +1839,7 @@ static void fdctrl_handle_specify(FDCtrl *fdctrl, int direction)
else
fdctrl->dor |= FD_DOR_DMAEN;
/* No result back */
- fdctrl_reset_fifo(fdctrl);
+ fdctrl_to_command_phase(fdctrl);
}
static void fdctrl_handle_sense_drive_status(FDCtrl *fdctrl, int direction)
@@ -1762,7 +1855,7 @@ static void fdctrl_handle_sense_drive_status(FDCtrl *fdctrl, int direction)
(cur_drv->head << 2) |
GET_CUR_DRV(fdctrl) |
0x28;
- fdctrl_set_fifo(fdctrl, 1);
+ fdctrl_to_result_phase(fdctrl, 1);
}
static void fdctrl_handle_recalibrate(FDCtrl *fdctrl, int direction)
@@ -1772,7 +1865,7 @@ static void fdctrl_handle_recalibrate(FDCtrl *fdctrl, int direction)
SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
cur_drv = get_cur_drv(fdctrl);
fd_recalibrate(cur_drv);
- fdctrl_reset_fifo(fdctrl);
+ fdctrl_to_command_phase(fdctrl);
/* Raise Interrupt */
fdctrl->status0 |= FD_SR0_SEEK;
fdctrl_raise_irq(fdctrl);
@@ -1788,7 +1881,7 @@ static void fdctrl_handle_sense_interrupt_status(FDCtrl *fdctrl, int direction)
fdctrl->reset_sensei--;
} else if (!(fdctrl->sra & FD_SRA_INTPEND)) {
fdctrl->fifo[0] = FD_SR0_INVCMD;
- fdctrl_set_fifo(fdctrl, 1);
+ fdctrl_to_result_phase(fdctrl, 1);
return;
} else {
fdctrl->fifo[0] =
@@ -1797,7 +1890,7 @@ static void fdctrl_handle_sense_interrupt_status(FDCtrl *fdctrl, int direction)
}
fdctrl->fifo[1] = cur_drv->track;
- fdctrl_set_fifo(fdctrl, 2);
+ fdctrl_to_result_phase(fdctrl, 2);
fdctrl_reset_irq(fdctrl);
fdctrl->status0 = FD_SR0_RDYCHG;
}
@@ -1808,7 +1901,7 @@ static void fdctrl_handle_seek(FDCtrl *fdctrl, int direction)
SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
cur_drv = get_cur_drv(fdctrl);
- fdctrl_reset_fifo(fdctrl);
+ fdctrl_to_command_phase(fdctrl);
/* The seek command just sends step pulses to the drive and doesn't care if
* there is a medium inserted of if it's banging the head against the drive.
*/
@@ -1825,7 +1918,7 @@ static void fdctrl_handle_perpendicular_mode(FDCtrl *fdctrl, int direction)
if (fdctrl->fifo[1] & 0x80)
cur_drv->perpendicular = fdctrl->fifo[1] & 0x7;
/* No result back */
- fdctrl_reset_fifo(fdctrl);
+ fdctrl_to_command_phase(fdctrl);
}
static void fdctrl_handle_configure(FDCtrl *fdctrl, int direction)
@@ -1833,20 +1926,20 @@ static void fdctrl_handle_configure(FDCtrl *fdctrl, int direction)
fdctrl->config = fdctrl->fifo[2];
fdctrl->precomp_trk = fdctrl->fifo[3];
/* No result back */
- fdctrl_reset_fifo(fdctrl);
+ fdctrl_to_command_phase(fdctrl);
}
static void fdctrl_handle_powerdown_mode(FDCtrl *fdctrl, int direction)
{
fdctrl->pwrd = fdctrl->fifo[1];
fdctrl->fifo[0] = fdctrl->fifo[1];
- fdctrl_set_fifo(fdctrl, 1);
+ fdctrl_to_result_phase(fdctrl, 1);
}
static void fdctrl_handle_option(FDCtrl *fdctrl, int direction)
{
/* No result back */
- fdctrl_reset_fifo(fdctrl);
+ fdctrl_to_command_phase(fdctrl);
}
static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direction)
@@ -1862,15 +1955,15 @@ static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direct
fdctrl->fifo[0] = fdctrl->fifo[1];
fdctrl->fifo[2] = 0;
fdctrl->fifo[3] = 0;
- fdctrl_set_fifo(fdctrl, 4);
+ fdctrl_to_result_phase(fdctrl, 4);
} else {
- fdctrl_reset_fifo(fdctrl);
+ fdctrl_to_command_phase(fdctrl);
}
} else if (fdctrl->data_len > 7) {
/* ERROR */
fdctrl->fifo[0] = 0x80 |
(cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
- fdctrl_set_fifo(fdctrl, 1);
+ fdctrl_to_result_phase(fdctrl, 1);
}
}
@@ -1887,7 +1980,7 @@ static void fdctrl_handle_relative_seek_in(FDCtrl *fdctrl, int direction)
fd_seek(cur_drv, cur_drv->head,
cur_drv->track + fdctrl->fifo[2], cur_drv->sect, 1);
}
- fdctrl_reset_fifo(fdctrl);
+ fdctrl_to_command_phase(fdctrl);
/* Raise Interrupt */
fdctrl->status0 |= FD_SR0_SEEK;
fdctrl_raise_irq(fdctrl);
@@ -1905,20 +1998,25 @@ static void fdctrl_handle_relative_seek_out(FDCtrl *fdctrl, int direction)
fd_seek(cur_drv, cur_drv->head,
cur_drv->track - fdctrl->fifo[2], cur_drv->sect, 1);
}
- fdctrl_reset_fifo(fdctrl);
+ fdctrl_to_command_phase(fdctrl);
/* Raise Interrupt */
fdctrl->status0 |= FD_SR0_SEEK;
fdctrl_raise_irq(fdctrl);
}
-static const struct {
+/*
+ * Handlers for the execution phase of each command
+ */
+typedef struct FDCtrlCommand {
uint8_t value;
uint8_t mask;
const char* name;
int parameters;
void (*handler)(FDCtrl *fdctrl, int direction);
int direction;
-} handlers[] = {
+} FDCtrlCommand;
+
+static const FDCtrlCommand handlers[] = {
{ FD_CMD_READ, 0x1f, "READ", 8, fdctrl_start_transfer, FD_DIR_READ },
{ FD_CMD_WRITE, 0x3f, "WRITE", 8, fdctrl_start_transfer, FD_DIR_WRITE },
{ FD_CMD_SEEK, 0xff, "SEEK", 2, fdctrl_handle_seek },
@@ -1955,9 +2053,19 @@ static const struct {
/* Associate command to an index in the 'handlers' array */
static uint8_t command_to_handler[256];
+static const FDCtrlCommand *get_command(uint8_t cmd)
+{
+ int idx;
+
+ idx = command_to_handler[cmd];
+ FLOPPY_DPRINTF("%s command\n", handlers[idx].name);
+ return &handlers[idx];
+}
+
static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value)
{
FDrive *cur_drv;
+ const FDCtrlCommand *cmd;
uint32_t pos;
/* Reset mode */
@@ -1970,12 +2078,27 @@ static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value)
return;
}
fdctrl->dsr &= ~FD_DSR_PWRDOWN;
- /* Is it write command time ? */
- if (fdctrl->msr & FD_MSR_NONDMA) {
+
+ FLOPPY_DPRINTF("%s: %02x\n", __func__, value);
+
+ /* If data_len spans multiple sectors, the current position in the FIFO
+ * wraps around while fdctrl->data_pos is the real position in the whole
+ * request. */
+ pos = fdctrl->data_pos++;
+ pos %= FD_SECTOR_LEN;
+ fdctrl->fifo[pos] = value;
+
+ if (fdctrl->data_pos == fdctrl->data_len) {
+ fdctrl->msr &= ~FD_MSR_RQM;
+ }
+
+ switch (fdctrl->phase) {
+ case FD_PHASE_EXECUTION:
+ /* For DMA requests, RQM should be cleared during execution phase, so
+ * we would have errored out above. */
+ assert(fdctrl->msr & FD_MSR_NONDMA);
+
/* FIFO data write */
- pos = fdctrl->data_pos++;
- pos %= FD_SECTOR_LEN;
- fdctrl->fifo[pos] = value;
if (pos == FD_SECTOR_LEN - 1 ||
fdctrl->data_pos == fdctrl->data_len) {
cur_drv = get_cur_drv(fdctrl);
@@ -1983,45 +2106,54 @@ static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value)
< 0) {
FLOPPY_DPRINTF("error writing sector %d\n",
fd_sector(cur_drv));
- return;
+ break;
}
if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) {
FLOPPY_DPRINTF("error seeking to next sector %d\n",
fd_sector(cur_drv));
- return;
+ break;
}
}
- /* Switch from transfer mode to status mode
- * then from status mode to command mode
- */
- if (fdctrl->data_pos == fdctrl->data_len)
+
+ /* Switch to result phase when done with the transfer */
+ if (fdctrl->data_pos == fdctrl->data_len) {
fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
- return;
- }
- if (fdctrl->data_pos == 0) {
- /* Command */
- pos = command_to_handler[value & 0xff];
- FLOPPY_DPRINTF("%s command\n", handlers[pos].name);
- fdctrl->data_len = handlers[pos].parameters + 1;
- fdctrl->msr |= FD_MSR_CMDBUSY;
- }
+ }
+ break;
- FLOPPY_DPRINTF("%s: %02x\n", __func__, value);
- pos = fdctrl->data_pos++;
- pos %= FD_SECTOR_LEN;
- fdctrl->fifo[pos] = value;
- if (fdctrl->data_pos == fdctrl->data_len) {
- /* We now have all parameters
- * and will be able to treat the command
- */
- if (fdctrl->data_state & FD_STATE_FORMAT) {
- fdctrl_format_sector(fdctrl);
- return;
+ case FD_PHASE_COMMAND:
+ assert(!(fdctrl->msr & FD_MSR_NONDMA));
+ assert(fdctrl->data_pos < FD_SECTOR_LEN);
+
+ if (pos == 0) {
+ /* The first byte specifies the command. Now we start reading
+ * as many parameters as this command requires. */
+ cmd = get_command(value);
+ fdctrl->data_len = cmd->parameters + 1;
+ if (cmd->parameters) {
+ fdctrl->msr |= FD_MSR_RQM;
+ }
+ fdctrl->msr |= FD_MSR_CMDBUSY;
+ }
+
+ if (fdctrl->data_pos == fdctrl->data_len) {
+ /* We have all parameters now, execute the command */
+ fdctrl->phase = FD_PHASE_EXECUTION;
+
+ if (fdctrl->data_state & FD_STATE_FORMAT) {
+ fdctrl_format_sector(fdctrl);
+ break;
+ }
+
+ cmd = get_command(fdctrl->fifo[0]);
+ FLOPPY_DPRINTF("Calling handler for '%s'\n", cmd->name);
+ cmd->handler(fdctrl, cmd->direction);
}
+ break;
- pos = command_to_handler[fdctrl->fifo[0] & 0xff];
- FLOPPY_DPRINTF("treat %s command\n", handlers[pos].name);
- (*handlers[pos].handler)(fdctrl, handlers[pos].direction);
+ case FD_PHASE_RESULT:
+ default:
+ abort();
}
}
diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c
index d282695086..2ba6c77293 100644
--- a/hw/block/pflash_cfi01.c
+++ b/hw/block/pflash_cfi01.c
@@ -64,6 +64,9 @@ do { \
#define TYPE_CFI_PFLASH01 "cfi.pflash01"
#define CFI_PFLASH01(obj) OBJECT_CHECK(pflash_t, (obj), TYPE_CFI_PFLASH01)
+#define PFLASH_BE 0
+#define PFLASH_SECURE 1
+
struct pflash_t {
/*< private >*/
SysBusDevice parent_obj;
@@ -75,7 +78,7 @@ struct pflash_t {
uint8_t bank_width;
uint8_t device_width; /* If 0, device width not specified. */
uint8_t max_device_width; /* max device width in bytes */
- uint8_t be;
+ uint32_t features;
uint8_t wcycle; /* if 0, the flash is read normally */
int ro;
uint8_t cmd;
@@ -235,12 +238,57 @@ static uint32_t pflash_devid_query(pflash_t *pfl, hwaddr offset)
return resp;
}
+static uint32_t pflash_data_read(pflash_t *pfl, hwaddr offset,
+ int width, int be)
+{
+ uint8_t *p;
+ uint32_t ret;
+
+ p = pfl->storage;
+ switch (width) {
+ case 1:
+ ret = p[offset];
+ DPRINTF("%s: data offset " TARGET_FMT_plx " %02x\n",
+ __func__, offset, ret);
+ break;
+ case 2:
+ if (be) {
+ ret = p[offset] << 8;
+ ret |= p[offset + 1];
+ } else {
+ ret = p[offset];
+ ret |= p[offset + 1] << 8;
+ }
+ DPRINTF("%s: data offset " TARGET_FMT_plx " %04x\n",
+ __func__, offset, ret);
+ break;
+ case 4:
+ if (be) {
+ ret = p[offset] << 24;
+ ret |= p[offset + 1] << 16;
+ ret |= p[offset + 2] << 8;
+ ret |= p[offset + 3];
+ } else {
+ ret = p[offset];
+ ret |= p[offset + 1] << 8;
+ ret |= p[offset + 2] << 16;
+ ret |= p[offset + 3] << 24;
+ }
+ DPRINTF("%s: data offset " TARGET_FMT_plx " %08x\n",
+ __func__, offset, ret);
+ break;
+ default:
+ DPRINTF("BUG in %s\n", __func__);
+ abort();
+ }
+ return ret;
+}
+
static uint32_t pflash_read (pflash_t *pfl, hwaddr offset,
int width, int be)
{
hwaddr boff;
uint32_t ret;
- uint8_t *p;
ret = -1;
@@ -257,43 +305,7 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset,
/* fall through to read code */
case 0x00:
/* Flash area read */
- p = pfl->storage;
- switch (width) {
- case 1:
- ret = p[offset];
- DPRINTF("%s: data offset " TARGET_FMT_plx " %02x\n",
- __func__, offset, ret);
- break;
- case 2:
- if (be) {
- ret = p[offset] << 8;
- ret |= p[offset + 1];
- } else {
- ret = p[offset];
- ret |= p[offset + 1] << 8;
- }
- DPRINTF("%s: data offset " TARGET_FMT_plx " %04x\n",
- __func__, offset, ret);
- break;
- case 4:
- if (be) {
- ret = p[offset] << 24;
- ret |= p[offset + 1] << 16;
- ret |= p[offset + 2] << 8;
- ret |= p[offset + 3];
- } else {
- ret = p[offset];
- ret |= p[offset + 1] << 8;
- ret |= p[offset + 2] << 16;
- ret |= p[offset + 3] << 24;
- }
- DPRINTF("%s: data offset " TARGET_FMT_plx " %08x\n",
- __func__, offset, ret);
- break;
- default:
- DPRINTF("BUG in %s\n", __func__);
- }
-
+ ret = pflash_data_read(pfl, offset, width, be);
break;
case 0x10: /* Single byte program */
case 0x20: /* Block erase */
@@ -648,101 +660,37 @@ static void pflash_write(pflash_t *pfl, hwaddr offset,
}
-static uint32_t pflash_readb_be(void *opaque, hwaddr addr)
-{
- return pflash_read(opaque, addr, 1, 1);
-}
-
-static uint32_t pflash_readb_le(void *opaque, hwaddr addr)
-{
- return pflash_read(opaque, addr, 1, 0);
-}
-
-static uint32_t pflash_readw_be(void *opaque, hwaddr addr)
-{
- pflash_t *pfl = opaque;
-
- return pflash_read(pfl, addr, 2, 1);
-}
-
-static uint32_t pflash_readw_le(void *opaque, hwaddr addr)
-{
- pflash_t *pfl = opaque;
-
- return pflash_read(pfl, addr, 2, 0);
-}
-
-static uint32_t pflash_readl_be(void *opaque, hwaddr addr)
-{
- pflash_t *pfl = opaque;
-
- return pflash_read(pfl, addr, 4, 1);
-}
-
-static uint32_t pflash_readl_le(void *opaque, hwaddr addr)
+static MemTxResult pflash_mem_read_with_attrs(void *opaque, hwaddr addr, uint64_t *value,
+ unsigned len, MemTxAttrs attrs)
{
pflash_t *pfl = opaque;
+ bool be = !!(pfl->features & (1 << PFLASH_BE));
- return pflash_read(pfl, addr, 4, 0);
-}
-
-static void pflash_writeb_be(void *opaque, hwaddr addr,
- uint32_t value)
-{
- pflash_write(opaque, addr, value, 1, 1);
-}
-
-static void pflash_writeb_le(void *opaque, hwaddr addr,
- uint32_t value)
-{
- pflash_write(opaque, addr, value, 1, 0);
-}
-
-static void pflash_writew_be(void *opaque, hwaddr addr,
- uint32_t value)
-{
- pflash_t *pfl = opaque;
-
- pflash_write(pfl, addr, value, 2, 1);
-}
-
-static void pflash_writew_le(void *opaque, hwaddr addr,
- uint32_t value)
-{
- pflash_t *pfl = opaque;
-
- pflash_write(pfl, addr, value, 2, 0);
-}
-
-static void pflash_writel_be(void *opaque, hwaddr addr,
- uint32_t value)
-{
- pflash_t *pfl = opaque;
-
- pflash_write(pfl, addr, value, 4, 1);
+ if ((pfl->features & (1 << PFLASH_SECURE)) && !attrs.secure) {
+ *value = pflash_data_read(opaque, addr, len, be);
+ } else {
+ *value = pflash_read(opaque, addr, len, be);
+ }
+ return MEMTX_OK;
}
-static void pflash_writel_le(void *opaque, hwaddr addr,
- uint32_t value)
+static MemTxResult pflash_mem_write_with_attrs(void *opaque, hwaddr addr, uint64_t value,
+ unsigned len, MemTxAttrs attrs)
{
pflash_t *pfl = opaque;
+ bool be = !!(pfl->features & (1 << PFLASH_BE));
- pflash_write(pfl, addr, value, 4, 0);
+ if ((pfl->features & (1 << PFLASH_SECURE)) && !attrs.secure) {
+ return MEMTX_ERROR;
+ } else {
+ pflash_write(opaque, addr, value, len, be);
+ return MEMTX_OK;
+ }
}
-static const MemoryRegionOps pflash_cfi01_ops_be = {
- .old_mmio = {
- .read = { pflash_readb_be, pflash_readw_be, pflash_readl_be, },
- .write = { pflash_writeb_be, pflash_writew_be, pflash_writel_be, },
- },
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static const MemoryRegionOps pflash_cfi01_ops_le = {
- .old_mmio = {
- .read = { pflash_readb_le, pflash_readw_le, pflash_readl_le, },
- .write = { pflash_writeb_le, pflash_writew_le, pflash_writel_le, },
- },
+static const MemoryRegionOps pflash_cfi01_ops = {
+ .read_with_attrs = pflash_mem_read_with_attrs,
+ .write_with_attrs = pflash_mem_write_with_attrs,
.endianness = DEVICE_NATIVE_ENDIAN,
};
@@ -773,7 +721,8 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
memory_region_init_rom_device(
&pfl->mem, OBJECT(dev),
- pfl->be ? &pflash_cfi01_ops_be : &pflash_cfi01_ops_le, pfl,
+ &pflash_cfi01_ops,
+ pfl,
pfl->name, total_len, &local_err);
if (local_err) {
error_propagate(errp, local_err);
@@ -925,7 +874,8 @@ static Property pflash_cfi01_properties[] = {
DEFINE_PROP_UINT8("width", struct pflash_t, bank_width, 0),
DEFINE_PROP_UINT8("device-width", struct pflash_t, device_width, 0),
DEFINE_PROP_UINT8("max-device-width", struct pflash_t, max_device_width, 0),
- DEFINE_PROP_UINT8("big-endian", struct pflash_t, be, 0),
+ DEFINE_PROP_BIT("big-endian", struct pflash_t, features, PFLASH_BE, 0),
+ DEFINE_PROP_BIT("secure", struct pflash_t, features, PFLASH_SECURE, 0),
DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0),
DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0),
DEFINE_PROP_UINT16("id2", struct pflash_t, ident2, 0),
@@ -975,7 +925,7 @@ pflash_t *pflash_cfi01_register(hwaddr base,
qdev_prop_set_uint32(dev, "num-blocks", nb_blocs);
qdev_prop_set_uint64(dev, "sector-length", sector_len);
qdev_prop_set_uint8(dev, "width", bank_width);
- qdev_prop_set_uint8(dev, "big-endian", !!be);
+ qdev_prop_set_bit(dev, "big-endian", !!be);
qdev_prop_set_uint16(dev, "id0", id0);
qdev_prop_set_uint16(dev, "id1", id1);
qdev_prop_set_uint16(dev, "id2", id2);
diff --git a/hw/char/parallel.c b/hw/char/parallel.c
index 4079554bb9..c2b553f0d1 100644
--- a/hw/char/parallel.c
+++ b/hw/char/parallel.c
@@ -641,28 +641,3 @@ static void parallel_register_types(void)
}
type_init(parallel_register_types)
-
-static void parallel_init(ISABus *bus, int index, CharDriverState *chr)
-{
- DeviceState *dev;
- ISADevice *isadev;
-
- isadev = isa_create(bus, "isa-parallel");
- dev = DEVICE(isadev);
- qdev_prop_set_uint32(dev, "index", index);
- qdev_prop_set_chr(dev, "chardev", chr);
- qdev_init_nofail(dev);
-}
-
-void parallel_hds_isa_init(ISABus *bus, int n)
-{
- int i;
-
- assert(n <= MAX_PARALLEL_PORTS);
-
- for (i = 0; i < n; i++) {
- if (parallel_hds[i]) {
- parallel_init(bus, i, parallel_hds[i]);
- }
- }
-}
diff --git a/hw/char/serial.c b/hw/char/serial.c
index 55011cfd26..513d73c27f 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -662,6 +662,7 @@ static const VMStateDescription vmstate_serial_thr_ipending = {
.name = "serial/thr_ipending",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = serial_thr_ipending_needed,
.fields = (VMStateField[]) {
VMSTATE_INT32(thr_ipending, SerialState),
VMSTATE_END_OF_LIST()
@@ -678,6 +679,7 @@ static const VMStateDescription vmstate_serial_tsr = {
.name = "serial/tsr",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = serial_tsr_needed,
.fields = (VMStateField[]) {
VMSTATE_INT32(tsr_retry, SerialState),
VMSTATE_UINT8(thr, SerialState),
@@ -697,6 +699,7 @@ static const VMStateDescription vmstate_serial_recv_fifo = {
.name = "serial/recv_fifo",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = serial_recv_fifo_needed,
.fields = (VMStateField[]) {
VMSTATE_STRUCT(recv_fifo, SerialState, 1, vmstate_fifo8, Fifo8),
VMSTATE_END_OF_LIST()
@@ -713,6 +716,7 @@ static const VMStateDescription vmstate_serial_xmit_fifo = {
.name = "serial/xmit_fifo",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = serial_xmit_fifo_needed,
.fields = (VMStateField[]) {
VMSTATE_STRUCT(xmit_fifo, SerialState, 1, vmstate_fifo8, Fifo8),
VMSTATE_END_OF_LIST()
@@ -729,6 +733,7 @@ static const VMStateDescription vmstate_serial_fifo_timeout_timer = {
.name = "serial/fifo_timeout_timer",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = serial_fifo_timeout_timer_needed,
.fields = (VMStateField[]) {
VMSTATE_TIMER_PTR(fifo_timeout_timer, SerialState),
VMSTATE_END_OF_LIST()
@@ -745,6 +750,7 @@ static const VMStateDescription vmstate_serial_timeout_ipending = {
.name = "serial/timeout_ipending",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = serial_timeout_ipending_needed,
.fields = (VMStateField[]) {
VMSTATE_INT32(timeout_ipending, SerialState),
VMSTATE_END_OF_LIST()
@@ -760,6 +766,7 @@ static bool serial_poll_needed(void *opaque)
static const VMStateDescription vmstate_serial_poll = {
.name = "serial/poll",
.version_id = 1,
+ .needed = serial_poll_needed,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_INT32(poll_msl, SerialState),
@@ -788,31 +795,15 @@ const VMStateDescription vmstate_serial = {
VMSTATE_UINT8_V(fcr_vmstate, SerialState, 3),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_serial_thr_ipending,
- .needed = &serial_thr_ipending_needed,
- } , {
- .vmsd = &vmstate_serial_tsr,
- .needed = &serial_tsr_needed,
- } , {
- .vmsd = &vmstate_serial_recv_fifo,
- .needed = &serial_recv_fifo_needed,
- } , {
- .vmsd = &vmstate_serial_xmit_fifo,
- .needed = &serial_xmit_fifo_needed,
- } , {
- .vmsd = &vmstate_serial_fifo_timeout_timer,
- .needed = &serial_fifo_timeout_timer_needed,
- } , {
- .vmsd = &vmstate_serial_timeout_ipending,
- .needed = &serial_timeout_ipending_needed,
- } , {
- .vmsd = &vmstate_serial_poll,
- .needed = &serial_poll_needed,
- } , {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_serial_thr_ipending,
+ &vmstate_serial_tsr,
+ &vmstate_serial_recv_fifo,
+ &vmstate_serial_xmit_fifo,
+ &vmstate_serial_fifo_timeout_timer,
+ &vmstate_serial_timeout_ipending,
+ &vmstate_serial_poll,
+ NULL
}
};
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 25c45e6f9d..ac4654e9dd 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -294,6 +294,14 @@ static void machine_init_notify(Notifier *notifier, void *data)
foreach_dynamic_sysbus_device(error_on_sysbus_device, NULL);
}
+static void machine_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ /* Default 128 MB as guest ram size */
+ mc->default_ram_size = 128 * M_BYTE;
+}
+
static void machine_initfn(Object *obj)
{
MachineState *ms = MACHINE(obj);
@@ -463,6 +471,7 @@ static const TypeInfo machine_info = {
.parent = TYPE_OBJECT,
.abstract = true,
.class_size = sizeof(MachineClass),
+ .class_init = machine_class_init,
.instance_size = sizeof(MachineState),
.instance_init = machine_initfn,
.instance_finalize = machine_finalize,
diff --git a/hw/core/nmi.c b/hw/core/nmi.c
index 3dff020659..5260d6c1ec 100644
--- a/hw/core/nmi.c
+++ b/hw/core/nmi.c
@@ -21,6 +21,7 @@
#include "hw/nmi.h"
#include "qapi/qmp/qerror.h"
+#include "monitor/monitor.h"
struct do_nmi_s {
int cpu_index;
@@ -70,6 +71,25 @@ void nmi_monitor_handle(int cpu_index, Error **errp)
}
}
+void inject_nmi(void)
+{
+#if defined(TARGET_I386)
+ CPUState *cs;
+
+ CPU_FOREACH(cs) {
+ X86CPU *cpu = X86_CPU(cs);
+
+ if (!cpu->apic_state) {
+ cpu_interrupt(cs, CPU_INTERRUPT_NMI);
+ } else {
+ apic_deliver_nmi(cpu->apic_state);
+ }
+ }
+#else
+ nmi_monitor_handle(0, NULL);
+#endif
+}
+
static const TypeInfo nmi_info = {
.name = TYPE_NMI,
.parent = TYPE_INTERFACE,
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index c413226a97..0309fe5767 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -389,7 +389,7 @@ void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd)
nd->instantiated = 1;
}
-static int qdev_add_one_global(QemuOpts *opts, void *opaque)
+static int qdev_add_one_global(void *opaque, QemuOpts *opts, Error **errp)
{
GlobalProperty *g;
@@ -404,5 +404,6 @@ static int qdev_add_one_global(QemuOpts *opts, void *opaque)
void qemu_add_globals(void)
{
- qemu_opts_foreach(qemu_find_opts("global"), qdev_add_one_global, NULL, 0);
+ qemu_opts_foreach(qemu_find_opts("global"),
+ qdev_add_one_global, NULL, NULL);
}
diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs
index 3ea106d9f3..dd8ea76d17 100644
--- a/hw/display/Makefile.objs
+++ b/hw/display/Makefile.objs
@@ -34,3 +34,7 @@ obj-$(CONFIG_CG3) += cg3.o
obj-$(CONFIG_VGA) += vga.o
common-obj-$(CONFIG_QXL) += qxl.o qxl-logger.o qxl-render.o
+
+obj-$(CONFIG_VIRTIO) += virtio-gpu.o
+obj-$(CONFIG_VIRTIO_PCI) += virtio-gpu-pci.o
+obj-$(CONFIG_VIRTIO_VGA) += virtio-vga.o
diff --git a/hw/display/cg3.c b/hw/display/cg3.c
index 1e6ff2b546..b94e5e0d78 100644
--- a/hw/display/cg3.c
+++ b/hw/display/cg3.c
@@ -106,6 +106,7 @@ static void cg3_update_display(void *opaque)
pix = memory_region_get_ram_ptr(&s->vram_mem);
data = (uint32_t *)surface_data(surface);
+ memory_region_sync_dirty_bitmap(&s->vram_mem);
for (y = 0; y < height; y++) {
int update = s->full_update;
@@ -309,6 +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);
+ 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 45c62afac1..603ef50568 100644
--- a/hw/display/exynos4210_fimd.c
+++ b/hw/display/exynos4210_fimd.c
@@ -337,7 +337,7 @@ static inline void fimd_swap_data(unsigned int swap_ctl, uint64_t *data)
if (swap_ctl & FIMD_WINCON_SWAP_BITS) {
res = 0;
for (i = 0; i < 64; i++) {
- if (x & (1ULL << (64 - i))) {
+ if (x & (1ULL << (63 - i))) {
res |= (1ULL << i);
}
}
@@ -1109,6 +1109,12 @@ static inline int fimd_get_buffer_id(Exynos4210fimdWindow *w)
}
}
+static void exynos4210_fimd_invalidate(void *opaque)
+{
+ Exynos4210fimdState *s = (Exynos4210fimdState *)opaque;
+ s->invalidate = true;
+}
+
/* Updates specified window's MemorySection based on values of WINCON,
* VIDOSDA, VIDOSDB, VIDWADDx and SHADOWCON registers */
static void fimd_update_memory_section(Exynos4210fimdState *s, unsigned win)
@@ -1136,7 +1142,11 @@ static void fimd_update_memory_section(Exynos4210fimdState *s, unsigned win)
/* TODO: add .exit and unref the region there. Not needed yet since sysbus
* does not support hot-unplug.
*/
- memory_region_unref(w->mem_section.mr);
+ if (w->mem_section.mr) {
+ memory_region_set_log(w->mem_section.mr, false, DIRTY_MEMORY_VGA);
+ memory_region_unref(w->mem_section.mr);
+ }
+
w->mem_section = memory_region_find(sysbus_address_space(sbd),
fb_start_addr, w->fb_len);
assert(w->mem_section.mr);
@@ -1162,6 +1172,8 @@ static void fimd_update_memory_section(Exynos4210fimdState *s, unsigned win)
cpu_physical_memory_unmap(w->host_fb_addr, fb_mapped_len, 0, 0);
goto error_return;
}
+ memory_region_set_log(w->mem_section.mr, true, DIRTY_MEMORY_VGA);
+ exynos4210_fimd_invalidate(s);
return;
error_return:
@@ -1224,12 +1236,6 @@ static void exynos4210_fimd_update_irq(Exynos4210fimdState *s)
}
}
-static void exynos4210_fimd_invalidate(void *opaque)
-{
- Exynos4210fimdState *s = (Exynos4210fimdState *)opaque;
- s->invalidate = true;
-}
-
static void exynos4210_update_resolution(Exynos4210fimdState *s)
{
DisplaySurface *surface = qemu_console_surface(s->console);
diff --git a/hw/display/framebuffer.c b/hw/display/framebuffer.c
index 4546e42654..2cabced208 100644
--- a/hw/display/framebuffer.c
+++ b/hw/display/framebuffer.c
@@ -63,6 +63,10 @@ void framebuffer_update_display(
assert(mem_section.offset_within_address_space == base);
memory_region_sync_dirty_bitmap(mem);
+ if (!memory_region_is_logging(mem, DIRTY_MEMORY_VGA)) {
+ invalidate = true;
+ }
+
src_base = cpu_physical_memory_map(base, &src_len, 0);
/* If we can't map the framebuffer then bail. We could try harder,
but it's not really worth it as dirty flag tracking will probably
diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c
index 46f7b41211..52a9733bfd 100644
--- a/hw/display/g364fb.c
+++ b/hw/display/g364fb.c
@@ -260,6 +260,7 @@ static void g364fb_update_display(void *opaque)
qemu_console_resize(s->con, s->width, s->height);
}
+ memory_region_sync_dirty_bitmap(&s->mem_vram);
if (s->ctla & CTLA_FORCE_BLANK) {
g364fb_draw_blank(s);
} else if (s->depth == 8) {
@@ -489,7 +490,7 @@ static void g364fb_init(DeviceState *dev, G364State *s)
memory_region_init_ram_ptr(&s->mem_vram, NULL, "vram",
s->vram_size, s->vram);
vmstate_register_ram(&s->mem_vram, dev);
- memory_region_set_coalescing(&s->mem_vram);
+ memory_region_set_log(&s->mem_vram, true, DIRTY_MEMORY_VGA);
}
#define TYPE_G364 "sysbus-g364"
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index b220e2d5d2..722146ec3a 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -2220,6 +2220,7 @@ static VMStateDescription qxl_vmstate_monitors_config = {
.name = "qxl/monitors-config",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = qxl_monitors_config_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64(guest_monitors_config, PCIQXLDevice),
VMSTATE_END_OF_LIST()
@@ -2253,13 +2254,9 @@ static VMStateDescription qxl_vmstate = {
VMSTATE_UINT64(guest_cursor, PCIQXLDevice),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &qxl_vmstate_monitors_config,
- .needed = qxl_monitors_config_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &qxl_vmstate_monitors_config,
+ NULL
}
};
diff --git a/hw/display/sm501.c b/hw/display/sm501.c
index c72154b6f1..15a5ba8000 100644
--- a/hw/display/sm501.c
+++ b/hw/display/sm501.c
@@ -1322,6 +1322,7 @@ static void sm501_draw_crt(SM501State * s)
}
/* draw each line according to conditions */
+ memory_region_sync_dirty_bitmap(&s->local_mem_region);
for (y = 0; y < height; y++) {
int update_hwc = draw_hwc_line ? within_hwc_y_range(s, y, 1) : 0;
int update = full_update || update_hwc;
@@ -1412,6 +1413,7 @@ void sm501_init(MemoryRegion *address_space_mem, uint32_t base,
memory_region_init_ram(&s->local_mem_region, NULL, "sm501.local",
local_mem_bytes, &error_abort);
vmstate_register_ram_global(&s->local_mem_region);
+ memory_region_set_log(&s->local_mem_region, true, DIRTY_MEMORY_VGA);
s->local_mem = memory_region_get_ram_ptr(&s->local_mem_region);
memory_region_add_subregion(address_space_mem, base, &s->local_mem_region);
diff --git a/hw/display/tc6393xb.c b/hw/display/tc6393xb.c
index 66b7ade8da..f5f3f3e69d 100644
--- a/hw/display/tc6393xb.c
+++ b/hw/display/tc6393xb.c
@@ -571,7 +571,7 @@ TC6393xbState *tc6393xb_init(MemoryRegion *sysmem, uint32_t base, qemu_irq irq)
s->irq = irq;
s->gpio_in = qemu_allocate_irqs(tc6393xb_gpio_set, s, TC6393XB_GPIOS);
- s->l3v = *qemu_allocate_irqs(tc6393xb_l3v, s, 1);
+ s->l3v = qemu_allocate_irq(tc6393xb_l3v, s, 0);
s->blanked = 1;
s->sub_irqs = qemu_allocate_irqs(tc6393xb_sub_irq, s, TC6393XB_NR_IRQS);
diff --git a/hw/display/tcx.c b/hw/display/tcx.c
index a9f9f66d15..f3faf78bf8 100644
--- a/hw/display/tcx.c
+++ b/hw/display/tcx.c
@@ -353,6 +353,7 @@ static void tcx_update_display(void *opaque)
return;
}
+ memory_region_sync_dirty_bitmap(&ts->vram_mem);
for (y = 0; y < ts->height; page += TARGET_PAGE_SIZE) {
if (memory_region_get_dirty(&ts->vram_mem, page, TARGET_PAGE_SIZE,
DIRTY_MEMORY_VGA)) {
@@ -446,6 +447,7 @@ static void tcx24_update_display(void *opaque)
dd = surface_stride(surface);
ds = 1024;
+ memory_region_sync_dirty_bitmap(&ts->vram_mem);
for (y = 0; y < ts->height; page += TARGET_PAGE_SIZE,
page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) {
if (tcx24_check_dirty(ts, page, page24, cpage)) {
@@ -1006,6 +1008,7 @@ static void tcx_realizefn(DeviceState *dev, Error **errp)
memory_region_init_ram(&s->vram_mem, OBJECT(s), "tcx.vram",
s->vram_size * (1 + 4 + 4), &error_abort);
vmstate_register_ram_global(&s->vram_mem);
+ memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA);
vram_base = memory_region_get_ram_ptr(&s->vram_mem);
/* 10/ROM : FCode ROM */
diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c
index ff5dfb2c23..1dfa331e60 100644
--- a/hw/display/vga-pci.c
+++ b/hw/display/vga-pci.c
@@ -54,9 +54,7 @@ typedef struct PCIVGAState {
VGACommonState vga;
uint32_t flags;
MemoryRegion mmio;
- MemoryRegion ioport;
- MemoryRegion bochs;
- MemoryRegion qext;
+ MemoryRegion mrs[3];
} PCIVGAState;
#define TYPE_PCI_VGA "pci-vga"
@@ -76,16 +74,16 @@ static const VMStateDescription vmstate_vga_pci = {
static uint64_t pci_vga_ioport_read(void *ptr, hwaddr addr,
unsigned size)
{
- PCIVGAState *d = ptr;
+ VGACommonState *s = ptr;
uint64_t ret = 0;
switch (size) {
case 1:
- ret = vga_ioport_read(&d->vga, addr);
+ ret = vga_ioport_read(s, addr + 0x3c0);
break;
case 2:
- ret = vga_ioport_read(&d->vga, addr);
- ret |= vga_ioport_read(&d->vga, addr+1) << 8;
+ ret = vga_ioport_read(s, addr + 0x3c0);
+ ret |= vga_ioport_read(s, addr + 0x3c1) << 8;
break;
}
return ret;
@@ -94,11 +92,11 @@ static uint64_t pci_vga_ioport_read(void *ptr, hwaddr addr,
static void pci_vga_ioport_write(void *ptr, hwaddr addr,
uint64_t val, unsigned size)
{
- PCIVGAState *d = ptr;
+ VGACommonState *s = ptr;
switch (size) {
case 1:
- vga_ioport_write(&d->vga, addr + 0x3c0, val);
+ vga_ioport_write(s, addr + 0x3c0, val);
break;
case 2:
/*
@@ -106,8 +104,8 @@ static void pci_vga_ioport_write(void *ptr, hwaddr addr,
* indexed registers with a single word write because the
* index byte is updated first.
*/
- vga_ioport_write(&d->vga, addr + 0x3c0, val & 0xff);
- vga_ioport_write(&d->vga, addr + 0x3c1, (val >> 8) & 0xff);
+ vga_ioport_write(s, addr + 0x3c0, val & 0xff);
+ vga_ioport_write(s, addr + 0x3c1, (val >> 8) & 0xff);
break;
}
}
@@ -125,21 +123,21 @@ static const MemoryRegionOps pci_vga_ioport_ops = {
static uint64_t pci_vga_bochs_read(void *ptr, hwaddr addr,
unsigned size)
{
- PCIVGAState *d = ptr;
+ VGACommonState *s = ptr;
int index = addr >> 1;
- vbe_ioport_write_index(&d->vga, 0, index);
- return vbe_ioport_read_data(&d->vga, 0);
+ vbe_ioport_write_index(s, 0, index);
+ return vbe_ioport_read_data(s, 0);
}
static void pci_vga_bochs_write(void *ptr, hwaddr addr,
uint64_t val, unsigned size)
{
- PCIVGAState *d = ptr;
+ VGACommonState *s = ptr;
int index = addr >> 1;
- vbe_ioport_write_index(&d->vga, 0, index);
- vbe_ioport_write_data(&d->vga, 0, val);
+ vbe_ioport_write_index(s, 0, index);
+ vbe_ioport_write_data(s, 0, val);
}
static const MemoryRegionOps pci_vga_bochs_ops = {
@@ -154,13 +152,13 @@ static const MemoryRegionOps pci_vga_bochs_ops = {
static uint64_t pci_vga_qext_read(void *ptr, hwaddr addr, unsigned size)
{
- PCIVGAState *d = ptr;
+ VGACommonState *s = ptr;
switch (addr) {
case PCI_VGA_QEXT_REG_SIZE:
return PCI_VGA_QEXT_SIZE;
case PCI_VGA_QEXT_REG_BYTEORDER:
- return d->vga.big_endian_fb ?
+ return s->big_endian_fb ?
PCI_VGA_QEXT_BIG_ENDIAN : PCI_VGA_QEXT_LITTLE_ENDIAN;
default:
return 0;
@@ -170,15 +168,15 @@ static uint64_t pci_vga_qext_read(void *ptr, hwaddr addr, unsigned size)
static void pci_vga_qext_write(void *ptr, hwaddr addr,
uint64_t val, unsigned size)
{
- PCIVGAState *d = ptr;
+ VGACommonState *s = ptr;
switch (addr) {
case PCI_VGA_QEXT_REG_BYTEORDER:
if (val == PCI_VGA_QEXT_BIG_ENDIAN) {
- d->vga.big_endian_fb = true;
+ s->big_endian_fb = true;
}
if (val == PCI_VGA_QEXT_LITTLE_ENDIAN) {
- d->vga.big_endian_fb = false;
+ s->big_endian_fb = false;
}
break;
}
@@ -206,10 +204,34 @@ static const MemoryRegionOps pci_vga_qext_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
+void pci_std_vga_mmio_region_init(VGACommonState *s,
+ MemoryRegion *parent,
+ MemoryRegion *subs,
+ bool qext)
+{
+ memory_region_init_io(&subs[0], NULL, &pci_vga_ioport_ops, s,
+ "vga ioports remapped", PCI_VGA_IOPORT_SIZE);
+ memory_region_add_subregion(parent, PCI_VGA_IOPORT_OFFSET,
+ &subs[0]);
+
+ memory_region_init_io(&subs[1], NULL, &pci_vga_bochs_ops, s,
+ "bochs dispi interface", PCI_VGA_BOCHS_SIZE);
+ memory_region_add_subregion(parent, PCI_VGA_BOCHS_OFFSET,
+ &subs[1]);
+
+ if (qext) {
+ memory_region_init_io(&subs[2], NULL, &pci_vga_qext_ops, s,
+ "qemu extended regs", PCI_VGA_QEXT_SIZE);
+ memory_region_add_subregion(parent, PCI_VGA_QEXT_OFFSET,
+ &subs[2]);
+ }
+}
+
static void pci_std_vga_realize(PCIDevice *dev, Error **errp)
{
PCIVGAState *d = PCI_VGA(dev);
VGACommonState *s = &d->vga;
+ bool qext = false;
/* vga + console init */
vga_common_init(s, OBJECT(dev), true);
@@ -224,23 +246,12 @@ static void pci_std_vga_realize(PCIDevice *dev, Error **errp)
/* mmio bar for vga register access */
if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_MMIO)) {
memory_region_init(&d->mmio, NULL, "vga.mmio", 4096);
- memory_region_init_io(&d->ioport, NULL, &pci_vga_ioport_ops, d,
- "vga ioports remapped", PCI_VGA_IOPORT_SIZE);
- memory_region_init_io(&d->bochs, NULL, &pci_vga_bochs_ops, d,
- "bochs dispi interface", PCI_VGA_BOCHS_SIZE);
-
- memory_region_add_subregion(&d->mmio, PCI_VGA_IOPORT_OFFSET,
- &d->ioport);
- memory_region_add_subregion(&d->mmio, PCI_VGA_BOCHS_OFFSET,
- &d->bochs);
if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_QEXT)) {
- memory_region_init_io(&d->qext, NULL, &pci_vga_qext_ops, d,
- "qemu extended regs", PCI_VGA_QEXT_SIZE);
- memory_region_add_subregion(&d->mmio, PCI_VGA_QEXT_OFFSET,
- &d->qext);
+ qext = true;
pci_set_byte(&d->dev.config[PCI_REVISION_ID], 2);
}
+ pci_std_vga_mmio_region_init(s, &d->mmio, d->mrs, qext);
pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
}
@@ -262,6 +273,7 @@ static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp)
{
PCIVGAState *d = PCI_VGA(dev);
VGACommonState *s = &d->vga;
+ bool qext = false;
/* vga + console init */
vga_common_init(s, OBJECT(dev), false);
@@ -269,23 +281,12 @@ static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp)
/* mmio bar */
memory_region_init(&d->mmio, OBJECT(dev), "vga.mmio", 4096);
- memory_region_init_io(&d->ioport, OBJECT(dev), &pci_vga_ioport_ops, d,
- "vga ioports remapped", PCI_VGA_IOPORT_SIZE);
- memory_region_init_io(&d->bochs, OBJECT(dev), &pci_vga_bochs_ops, d,
- "bochs dispi interface", PCI_VGA_BOCHS_SIZE);
-
- memory_region_add_subregion(&d->mmio, PCI_VGA_IOPORT_OFFSET,
- &d->ioport);
- memory_region_add_subregion(&d->mmio, PCI_VGA_BOCHS_OFFSET,
- &d->bochs);
if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_QEXT)) {
- memory_region_init_io(&d->qext, NULL, &pci_vga_qext_ops, d,
- "qemu extended regs", PCI_VGA_QEXT_SIZE);
- memory_region_add_subregion(&d->mmio, PCI_VGA_QEXT_OFFSET,
- &d->qext);
+ qext = true;
pci_set_byte(&d->dev.config[PCI_REVISION_ID], 2);
}
+ pci_std_vga_mmio_region_init(s, &d->mmio, d->mrs, qext);
pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram);
pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
diff --git a/hw/display/vga.c b/hw/display/vga.c
index d1d296c74e..b35d523e65 100644
--- a/hw/display/vga.c
+++ b/hw/display/vga.c
@@ -2035,6 +2035,7 @@ static const VMStateDescription vmstate_vga_endian = {
.name = "vga.endian",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = vga_endian_state_needed,
.fields = (VMStateField[]) {
VMSTATE_BOOL(big_endian_fb, VGACommonState),
VMSTATE_END_OF_LIST()
@@ -2078,13 +2079,9 @@ const VMStateDescription vmstate_vga_common = {
VMSTATE_UINT32(vbe_bank_mask, VGACommonState),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection []) {
- {
- .vmsd = &vmstate_vga_endian,
- .needed = vga_endian_state_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_vga_endian,
+ NULL
}
};
diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h
index fcfcc5f431..40ba6a4207 100644
--- a/hw/display/vga_int.h
+++ b/hw/display/vga_int.h
@@ -219,4 +219,10 @@ extern const uint8_t gr_mask[16];
extern const MemoryRegionOps vga_mem_ops;
+/* vga-pci.c */
+void pci_std_vga_mmio_region_init(VGACommonState *s,
+ MemoryRegion *parent,
+ MemoryRegion *subs,
+ bool qext);
+
#endif
diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c
new file mode 100644
index 0000000000..f0f25c7bc9
--- /dev/null
+++ b/hw/display/virtio-gpu-pci.c
@@ -0,0 +1,68 @@
+/*
+ * Virtio video device
+ *
+ * Copyright Red Hat
+ *
+ * Authors:
+ * Dave Airlie
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#include "hw/pci/pci.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-bus.h"
+#include "hw/virtio/virtio-pci.h"
+#include "hw/virtio/virtio-gpu.h"
+
+static Property virtio_gpu_pci_properties[] = {
+ DEFINE_VIRTIO_GPU_PROPERTIES(VirtIOGPUPCI, vdev.conf),
+ DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_gpu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ VirtIOGPUPCI *vgpu = VIRTIO_GPU_PCI(vpci_dev);
+ DeviceState *vdev = DEVICE(&vgpu->vdev);
+
+ qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+ /* force virtio-1.0 */
+ vpci_dev->flags &= ~VIRTIO_PCI_FLAG_DISABLE_MODERN;
+ vpci_dev->flags |= VIRTIO_PCI_FLAG_DISABLE_LEGACY;
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
+}
+
+static void virtio_gpu_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
+ dc->props = virtio_gpu_pci_properties;
+ k->realize = virtio_gpu_pci_realize;
+ pcidev_k->class_id = PCI_CLASS_DISPLAY_OTHER;
+}
+
+static void virtio_gpu_initfn(Object *obj)
+{
+ VirtIOGPUPCI *dev = VIRTIO_GPU_PCI(obj);
+ object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_GPU);
+ object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
+}
+
+static const TypeInfo virtio_gpu_pci_info = {
+ .name = TYPE_VIRTIO_GPU_PCI,
+ .parent = TYPE_VIRTIO_PCI,
+ .instance_size = sizeof(VirtIOGPUPCI),
+ .instance_init = virtio_gpu_initfn,
+ .class_init = virtio_gpu_pci_class_init,
+};
+
+static void virtio_gpu_pci_register_types(void)
+{
+ type_register_static(&virtio_gpu_pci_info);
+}
+type_init(virtio_gpu_pci_register_types)
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
new file mode 100644
index 0000000000..8c109b79f4
--- /dev/null
+++ b/hw/display/virtio-gpu.c
@@ -0,0 +1,918 @@
+/*
+ * Virtio GPU Device
+ *
+ * Copyright Red Hat, Inc. 2013-2014
+ *
+ * Authors:
+ * Dave Airlie <airlied@redhat.com>
+ * Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu-common.h"
+#include "qemu/iov.h"
+#include "ui/console.h"
+#include "trace.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-gpu.h"
+#include "hw/virtio/virtio-bus.h"
+
+static struct virtio_gpu_simple_resource*
+virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id);
+
+static void update_cursor_data_simple(VirtIOGPU *g,
+ struct virtio_gpu_scanout *s,
+ uint32_t resource_id)
+{
+ struct virtio_gpu_simple_resource *res;
+ uint32_t pixels;
+
+ res = virtio_gpu_find_resource(g, resource_id);
+ if (!res) {
+ return;
+ }
+
+ if (pixman_image_get_width(res->image) != s->current_cursor->width ||
+ pixman_image_get_height(res->image) != s->current_cursor->height) {
+ return;
+ }
+
+ pixels = s->current_cursor->width * s->current_cursor->height;
+ memcpy(s->current_cursor->data,
+ pixman_image_get_data(res->image),
+ pixels * sizeof(uint32_t));
+}
+
+static void update_cursor(VirtIOGPU *g, struct virtio_gpu_update_cursor *cursor)
+{
+ struct virtio_gpu_scanout *s;
+
+ if (cursor->pos.scanout_id >= g->conf.max_outputs) {
+ return;
+ }
+ s = &g->scanout[cursor->pos.scanout_id];
+
+ if (cursor->hdr.type != VIRTIO_GPU_CMD_MOVE_CURSOR) {
+ if (!s->current_cursor) {
+ s->current_cursor = cursor_alloc(64, 64);
+ }
+
+ s->current_cursor->hot_x = cursor->hot_x;
+ s->current_cursor->hot_y = cursor->hot_y;
+
+ if (cursor->resource_id > 0) {
+ update_cursor_data_simple(g, s, cursor->resource_id);
+ }
+ dpy_cursor_define(s->con, s->current_cursor);
+ }
+ dpy_mouse_set(s->con, cursor->pos.x, cursor->pos.y,
+ cursor->resource_id ? 1 : 0);
+}
+
+static void virtio_gpu_get_config(VirtIODevice *vdev, uint8_t *config)
+{
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+ memcpy(config, &g->virtio_config, sizeof(g->virtio_config));
+}
+
+static void virtio_gpu_set_config(VirtIODevice *vdev, const uint8_t *config)
+{
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+ struct virtio_gpu_config vgconfig;
+
+ memcpy(&vgconfig, config, sizeof(g->virtio_config));
+
+ if (vgconfig.events_clear) {
+ g->virtio_config.events_read &= ~vgconfig.events_clear;
+ }
+}
+
+static uint64_t virtio_gpu_get_features(VirtIODevice *vdev, uint64_t features)
+{
+ return features;
+}
+
+static void virtio_gpu_notify_event(VirtIOGPU *g, uint32_t event_type)
+{
+ g->virtio_config.events_read |= event_type;
+ virtio_notify_config(&g->parent_obj);
+}
+
+static struct virtio_gpu_simple_resource *
+virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id)
+{
+ struct virtio_gpu_simple_resource *res;
+
+ QTAILQ_FOREACH(res, &g->reslist, next) {
+ if (res->resource_id == resource_id) {
+ return res;
+ }
+ }
+ return NULL;
+}
+
+void virtio_gpu_ctrl_response(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd,
+ struct virtio_gpu_ctrl_hdr *resp,
+ size_t resp_len)
+{
+ size_t s;
+
+ if (cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_FENCE) {
+ resp->flags |= VIRTIO_GPU_FLAG_FENCE;
+ resp->fence_id = cmd->cmd_hdr.fence_id;
+ resp->ctx_id = cmd->cmd_hdr.ctx_id;
+ }
+ s = iov_from_buf(cmd->elem.in_sg, cmd->elem.in_num, 0, resp, resp_len);
+ if (s != resp_len) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: response size incorrect %zu vs %zu\n",
+ __func__, s, resp_len);
+ }
+ virtqueue_push(cmd->vq, &cmd->elem, s);
+ virtio_notify(VIRTIO_DEVICE(g), cmd->vq);
+ cmd->finished = true;
+}
+
+void virtio_gpu_ctrl_response_nodata(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd,
+ enum virtio_gpu_ctrl_type type)
+{
+ struct virtio_gpu_ctrl_hdr resp;
+
+ memset(&resp, 0, sizeof(resp));
+ resp.type = type;
+ virtio_gpu_ctrl_response(g, cmd, &resp, sizeof(resp));
+}
+
+static void
+virtio_gpu_fill_display_info(VirtIOGPU *g,
+ struct virtio_gpu_resp_display_info *dpy_info)
+{
+ int i;
+
+ for (i = 0; i < g->conf.max_outputs; i++) {
+ if (g->enabled_output_bitmask & (1 << i)) {
+ dpy_info->pmodes[i].enabled = 1;
+ dpy_info->pmodes[i].r.width = g->req_state[i].width;
+ dpy_info->pmodes[i].r.height = g->req_state[i].height;
+ }
+ }
+}
+
+void virtio_gpu_get_display_info(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_resp_display_info display_info;
+
+ trace_virtio_gpu_cmd_get_display_info();
+ memset(&display_info, 0, sizeof(display_info));
+ display_info.hdr.type = VIRTIO_GPU_RESP_OK_DISPLAY_INFO;
+ virtio_gpu_fill_display_info(g, &display_info);
+ virtio_gpu_ctrl_response(g, cmd, &display_info.hdr,
+ sizeof(display_info));
+}
+
+static pixman_format_code_t get_pixman_format(uint32_t virtio_gpu_format)
+{
+ switch (virtio_gpu_format) {
+#ifdef HOST_WORDS_BIGENDIAN
+ case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM:
+ return PIXMAN_b8g8r8x8;
+ case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM:
+ return PIXMAN_b8g8r8a8;
+ case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM:
+ return PIXMAN_x8r8g8b8;
+ case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM:
+ return PIXMAN_a8r8g8b8;
+ case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM:
+ return PIXMAN_r8g8b8x8;
+ case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM:
+ return PIXMAN_r8g8b8a8;
+ case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM:
+ return PIXMAN_x8b8g8r8;
+ case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM:
+ return PIXMAN_a8b8g8r8;
+#else
+ case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM:
+ return PIXMAN_x8r8g8b8;
+ case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM:
+ return PIXMAN_a8r8g8b8;
+ case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM:
+ return PIXMAN_b8g8r8x8;
+ case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM:
+ return PIXMAN_b8g8r8a8;
+ case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM:
+ return PIXMAN_x8b8g8r8;
+ case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM:
+ return PIXMAN_a8b8g8r8;
+ case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM:
+ return PIXMAN_r8g8b8x8;
+ case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM:
+ return PIXMAN_r8g8b8a8;
+#endif
+ default:
+ return 0;
+ }
+}
+
+static void virtio_gpu_resource_create_2d(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ pixman_format_code_t pformat;
+ struct virtio_gpu_simple_resource *res;
+ struct virtio_gpu_resource_create_2d c2d;
+
+ VIRTIO_GPU_FILL_CMD(c2d);
+ trace_virtio_gpu_cmd_res_create_2d(c2d.resource_id, c2d.format,
+ c2d.width, c2d.height);
+
+ if (c2d.resource_id == 0) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: resource id 0 is not allowed\n",
+ __func__);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+
+ res = virtio_gpu_find_resource(g, c2d.resource_id);
+ if (res) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: resource already exists %d\n",
+ __func__, c2d.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+
+ res = g_new0(struct virtio_gpu_simple_resource, 1);
+
+ res->width = c2d.width;
+ res->height = c2d.height;
+ res->format = c2d.format;
+ res->resource_id = c2d.resource_id;
+
+ pformat = get_pixman_format(c2d.format);
+ if (!pformat) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: host couldn't handle guest format %d\n",
+ __func__, c2d.format);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+ return;
+ }
+ res->image = pixman_image_create_bits(pformat,
+ c2d.width,
+ c2d.height,
+ NULL, 0);
+
+ if (!res->image) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: resource creation failed %d %d %d\n",
+ __func__, c2d.resource_id, c2d.width, c2d.height);
+ g_free(res);
+ cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY;
+ return;
+ }
+
+ QTAILQ_INSERT_HEAD(&g->reslist, res, next);
+}
+
+static void virtio_gpu_resource_destroy(VirtIOGPU *g,
+ struct virtio_gpu_simple_resource *res)
+{
+ pixman_image_unref(res->image);
+ QTAILQ_REMOVE(&g->reslist, res, next);
+ g_free(res);
+}
+
+static void virtio_gpu_resource_unref(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_simple_resource *res;
+ struct virtio_gpu_resource_unref unref;
+
+ VIRTIO_GPU_FILL_CMD(unref);
+ trace_virtio_gpu_cmd_res_unref(unref.resource_id);
+
+ res = virtio_gpu_find_resource(g, unref.resource_id);
+ if (!res) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
+ __func__, unref.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+ virtio_gpu_resource_destroy(g, res);
+}
+
+static void virtio_gpu_transfer_to_host_2d(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_simple_resource *res;
+ int h;
+ uint32_t src_offset, dst_offset, stride;
+ int bpp;
+ pixman_format_code_t format;
+ struct virtio_gpu_transfer_to_host_2d t2d;
+
+ VIRTIO_GPU_FILL_CMD(t2d);
+ trace_virtio_gpu_cmd_res_xfer_toh_2d(t2d.resource_id);
+
+ res = virtio_gpu_find_resource(g, t2d.resource_id);
+ if (!res || !res->iov) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
+ __func__, t2d.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+
+ if (t2d.r.x > res->width ||
+ t2d.r.y > res->height ||
+ t2d.r.width > res->width ||
+ t2d.r.height > res->height ||
+ t2d.r.x + t2d.r.width > res->width ||
+ t2d.r.y + t2d.r.height > res->height) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: transfer bounds outside resource"
+ " bounds for resource %d: %d %d %d %d vs %d %d\n",
+ __func__, t2d.resource_id, t2d.r.x, t2d.r.y,
+ t2d.r.width, t2d.r.height, res->width, res->height);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+ return;
+ }
+
+ format = pixman_image_get_format(res->image);
+ bpp = (PIXMAN_FORMAT_BPP(format) + 7) / 8;
+ stride = pixman_image_get_stride(res->image);
+
+ if (t2d.offset || t2d.r.x || t2d.r.y ||
+ t2d.r.width != pixman_image_get_width(res->image)) {
+ void *img_data = pixman_image_get_data(res->image);
+ for (h = 0; h < t2d.r.height; h++) {
+ src_offset = t2d.offset + stride * h;
+ dst_offset = (t2d.r.y + h) * stride + (t2d.r.x * bpp);
+
+ iov_to_buf(res->iov, res->iov_cnt, src_offset,
+ (uint8_t *)img_data
+ + dst_offset, t2d.r.width * bpp);
+ }
+ } else {
+ iov_to_buf(res->iov, res->iov_cnt, 0,
+ pixman_image_get_data(res->image),
+ pixman_image_get_stride(res->image)
+ * pixman_image_get_height(res->image));
+ }
+}
+
+static void virtio_gpu_resource_flush(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_simple_resource *res;
+ struct virtio_gpu_resource_flush rf;
+ pixman_region16_t flush_region;
+ int i;
+
+ VIRTIO_GPU_FILL_CMD(rf);
+ trace_virtio_gpu_cmd_res_flush(rf.resource_id,
+ rf.r.width, rf.r.height, rf.r.x, rf.r.y);
+
+ res = virtio_gpu_find_resource(g, rf.resource_id);
+ if (!res) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
+ __func__, rf.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+
+ if (rf.r.x > res->width ||
+ rf.r.y > res->height ||
+ rf.r.width > res->width ||
+ rf.r.height > res->height ||
+ rf.r.x + rf.r.width > res->width ||
+ rf.r.y + rf.r.height > res->height) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: flush bounds outside resource"
+ " bounds for resource %d: %d %d %d %d vs %d %d\n",
+ __func__, rf.resource_id, rf.r.x, rf.r.y,
+ rf.r.width, rf.r.height, res->width, res->height);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+ return;
+ }
+
+ pixman_region_init_rect(&flush_region,
+ rf.r.x, rf.r.y, rf.r.width, rf.r.height);
+ for (i = 0; i < VIRTIO_GPU_MAX_SCANOUT; i++) {
+ struct virtio_gpu_scanout *scanout;
+ pixman_region16_t region, finalregion;
+ pixman_box16_t *extents;
+
+ if (!(res->scanout_bitmask & (1 << i))) {
+ continue;
+ }
+ scanout = &g->scanout[i];
+
+ pixman_region_init(&finalregion);
+ pixman_region_init_rect(&region, scanout->x, scanout->y,
+ scanout->width, scanout->height);
+
+ pixman_region_intersect(&finalregion, &flush_region, &region);
+ pixman_region_translate(&finalregion, -scanout->x, -scanout->y);
+ extents = pixman_region_extents(&finalregion);
+ /* work out the area we need to update for each console */
+ dpy_gfx_update(g->scanout[i].con,
+ extents->x1, extents->y1,
+ extents->x2 - extents->x1,
+ extents->y2 - extents->y1);
+
+ pixman_region_fini(&region);
+ pixman_region_fini(&finalregion);
+ }
+ pixman_region_fini(&flush_region);
+}
+
+static void virtio_gpu_set_scanout(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_simple_resource *res;
+ struct virtio_gpu_scanout *scanout;
+ pixman_format_code_t format;
+ uint32_t offset;
+ int bpp;
+ struct virtio_gpu_set_scanout ss;
+
+ VIRTIO_GPU_FILL_CMD(ss);
+ trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id,
+ ss.r.width, ss.r.height, ss.r.x, ss.r.y);
+
+ g->enable = 1;
+ if (ss.resource_id == 0) {
+ scanout = &g->scanout[ss.scanout_id];
+ if (scanout->resource_id) {
+ res = virtio_gpu_find_resource(g, scanout->resource_id);
+ if (res) {
+ res->scanout_bitmask &= ~(1 << ss.scanout_id);
+ }
+ }
+ if (ss.scanout_id == 0 ||
+ ss.scanout_id >= g->conf.max_outputs) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: illegal scanout id specified %d",
+ __func__, ss.scanout_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
+ return;
+ }
+ dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL);
+ scanout->ds = NULL;
+ scanout->width = 0;
+ scanout->height = 0;
+ return;
+ }
+
+ /* create a surface for this scanout */
+ if (ss.scanout_id >= VIRTIO_GPU_MAX_SCANOUT ||
+ ss.scanout_id >= g->conf.max_outputs) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %d",
+ __func__, ss.scanout_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
+ return;
+ }
+
+ res = virtio_gpu_find_resource(g, ss.resource_id);
+ if (!res) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
+ __func__, ss.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+
+ if (ss.r.x > res->width ||
+ ss.r.y > res->height ||
+ ss.r.width > res->width ||
+ ss.r.height > res->height ||
+ ss.r.x + ss.r.width > res->width ||
+ ss.r.y + ss.r.height > res->height) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for"
+ " resource %d, (%d,%d)+%d,%d vs %d %d\n",
+ __func__, ss.scanout_id, ss.resource_id, ss.r.x, ss.r.y,
+ ss.r.width, ss.r.height, res->width, res->height);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+ return;
+ }
+
+ scanout = &g->scanout[ss.scanout_id];
+
+ format = pixman_image_get_format(res->image);
+ bpp = (PIXMAN_FORMAT_BPP(format) + 7) / 8;
+ offset = (ss.r.x * bpp) + ss.r.y * pixman_image_get_stride(res->image);
+ if (!scanout->ds || surface_data(scanout->ds)
+ != ((uint8_t *)pixman_image_get_data(res->image) + offset) ||
+ scanout->width != ss.r.width ||
+ scanout->height != ss.r.height) {
+ /* realloc the surface ptr */
+ scanout->ds = qemu_create_displaysurface_from
+ (ss.r.width, ss.r.height, format,
+ pixman_image_get_stride(res->image),
+ (uint8_t *)pixman_image_get_data(res->image) + offset);
+ if (!scanout->ds) {
+ cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+ return;
+ }
+ dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, scanout->ds);
+ }
+
+ res->scanout_bitmask |= (1 << ss.scanout_id);
+ scanout->resource_id = ss.resource_id;
+ scanout->x = ss.r.x;
+ scanout->y = ss.r.y;
+ scanout->width = ss.r.width;
+ scanout->height = ss.r.height;
+}
+
+int virtio_gpu_create_mapping_iov(struct virtio_gpu_resource_attach_backing *ab,
+ struct virtio_gpu_ctrl_command *cmd,
+ struct iovec **iov)
+{
+ struct virtio_gpu_mem_entry *ents;
+ size_t esize, s;
+ int i;
+
+ if (ab->nr_entries > 16384) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: nr_entries is too big (%d > 16384)\n",
+ __func__, ab->nr_entries);
+ return -1;
+ }
+
+ esize = sizeof(*ents) * ab->nr_entries;
+ ents = g_malloc(esize);
+ s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num,
+ sizeof(*ab), ents, esize);
+ if (s != esize) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: command data size incorrect %zu vs %zu\n",
+ __func__, s, esize);
+ g_free(ents);
+ return -1;
+ }
+
+ *iov = g_malloc0(sizeof(struct iovec) * ab->nr_entries);
+ for (i = 0; i < ab->nr_entries; i++) {
+ hwaddr len = ents[i].length;
+ (*iov)[i].iov_len = ents[i].length;
+ (*iov)[i].iov_base = cpu_physical_memory_map(ents[i].addr, &len, 1);
+ if (!(*iov)[i].iov_base || len != ents[i].length) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to map MMIO memory for"
+ " resource %d element %d\n",
+ __func__, ab->resource_id, i);
+ virtio_gpu_cleanup_mapping_iov(*iov, i);
+ g_free(ents);
+ g_free(*iov);
+ *iov = NULL;
+ return -1;
+ }
+ }
+ g_free(ents);
+ return 0;
+}
+
+void virtio_gpu_cleanup_mapping_iov(struct iovec *iov, uint32_t count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ cpu_physical_memory_unmap(iov[i].iov_base, iov[i].iov_len, 1,
+ iov[i].iov_len);
+ }
+}
+
+static void virtio_gpu_cleanup_mapping(struct virtio_gpu_simple_resource *res)
+{
+ virtio_gpu_cleanup_mapping_iov(res->iov, res->iov_cnt);
+ g_free(res->iov);
+ res->iov = NULL;
+ res->iov_cnt = 0;
+}
+
+static void
+virtio_gpu_resource_attach_backing(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_simple_resource *res;
+ struct virtio_gpu_resource_attach_backing ab;
+ int ret;
+
+ VIRTIO_GPU_FILL_CMD(ab);
+ trace_virtio_gpu_cmd_res_back_attach(ab.resource_id);
+
+ res = virtio_gpu_find_resource(g, ab.resource_id);
+ if (!res) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
+ __func__, ab.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+
+ ret = virtio_gpu_create_mapping_iov(&ab, cmd, &res->iov);
+ if (ret != 0) {
+ cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+ return;
+ }
+
+ res->iov_cnt = ab.nr_entries;
+}
+
+static void
+virtio_gpu_resource_detach_backing(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_simple_resource *res;
+ struct virtio_gpu_resource_detach_backing detach;
+
+ VIRTIO_GPU_FILL_CMD(detach);
+ trace_virtio_gpu_cmd_res_back_detach(detach.resource_id);
+
+ res = virtio_gpu_find_resource(g, detach.resource_id);
+ if (!res || !res->iov) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
+ __func__, detach.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+ virtio_gpu_cleanup_mapping(res);
+}
+
+static void virtio_gpu_simple_process_cmd(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ VIRTIO_GPU_FILL_CMD(cmd->cmd_hdr);
+
+ switch (cmd->cmd_hdr.type) {
+ case VIRTIO_GPU_CMD_GET_DISPLAY_INFO:
+ virtio_gpu_get_display_info(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D:
+ virtio_gpu_resource_create_2d(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_UNREF:
+ virtio_gpu_resource_unref(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_FLUSH:
+ virtio_gpu_resource_flush(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D:
+ virtio_gpu_transfer_to_host_2d(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_SET_SCANOUT:
+ virtio_gpu_set_scanout(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING:
+ virtio_gpu_resource_attach_backing(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING:
+ virtio_gpu_resource_detach_backing(g, cmd);
+ break;
+ default:
+ cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+ break;
+ }
+ if (!cmd->finished) {
+ virtio_gpu_ctrl_response_nodata(g, cmd, cmd->error ? cmd->error :
+ VIRTIO_GPU_RESP_OK_NODATA);
+ }
+}
+
+static void virtio_gpu_handle_ctrl_cb(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+ qemu_bh_schedule(g->ctrl_bh);
+}
+
+static void virtio_gpu_handle_cursor_cb(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+ qemu_bh_schedule(g->cursor_bh);
+}
+
+static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+ struct virtio_gpu_ctrl_command *cmd;
+
+ if (!virtio_queue_ready(vq)) {
+ return;
+ }
+
+ cmd = g_new(struct virtio_gpu_ctrl_command, 1);
+ while (virtqueue_pop(vq, &cmd->elem)) {
+ cmd->vq = vq;
+ cmd->error = 0;
+ cmd->finished = false;
+ g->stats.requests++;
+
+ virtio_gpu_simple_process_cmd(g, cmd);
+ if (!cmd->finished) {
+ QTAILQ_INSERT_TAIL(&g->fenceq, cmd, next);
+ g->stats.inflight++;
+ if (g->stats.max_inflight < g->stats.inflight) {
+ g->stats.max_inflight = g->stats.inflight;
+ }
+ fprintf(stderr, "inflight: %3d (+)\r", g->stats.inflight);
+ cmd = g_new(struct virtio_gpu_ctrl_command, 1);
+ }
+ }
+ g_free(cmd);
+}
+
+static void virtio_gpu_ctrl_bh(void *opaque)
+{
+ VirtIOGPU *g = opaque;
+ virtio_gpu_handle_ctrl(&g->parent_obj, g->ctrl_vq);
+}
+
+static void virtio_gpu_handle_cursor(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+ VirtQueueElement elem;
+ size_t s;
+ struct virtio_gpu_update_cursor cursor_info;
+
+ if (!virtio_queue_ready(vq)) {
+ return;
+ }
+ while (virtqueue_pop(vq, &elem)) {
+ s = iov_to_buf(elem.out_sg, elem.out_num, 0,
+ &cursor_info, sizeof(cursor_info));
+ if (s != sizeof(cursor_info)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: cursor size incorrect %zu vs %zu\n",
+ __func__, s, sizeof(cursor_info));
+ } else {
+ update_cursor(g, &cursor_info);
+ }
+ virtqueue_push(vq, &elem, 0);
+ virtio_notify(vdev, vq);
+ }
+}
+
+static void virtio_gpu_cursor_bh(void *opaque)
+{
+ VirtIOGPU *g = opaque;
+ virtio_gpu_handle_cursor(&g->parent_obj, g->cursor_vq);
+}
+
+static void virtio_gpu_invalidate_display(void *opaque)
+{
+}
+
+static void virtio_gpu_update_display(void *opaque)
+{
+}
+
+static void virtio_gpu_text_update(void *opaque, console_ch_t *chardata)
+{
+}
+
+static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
+{
+ VirtIOGPU *g = opaque;
+
+ if (idx > g->conf.max_outputs) {
+ return -1;
+ }
+
+ g->req_state[idx].x = info->xoff;
+ g->req_state[idx].y = info->yoff;
+ g->req_state[idx].width = info->width;
+ g->req_state[idx].height = info->height;
+
+ if (info->width && info->height) {
+ g->enabled_output_bitmask |= (1 << idx);
+ } else {
+ g->enabled_output_bitmask &= ~(1 << idx);
+ }
+
+ /* send event to guest */
+ virtio_gpu_notify_event(g, VIRTIO_GPU_EVENT_DISPLAY);
+ return 0;
+}
+
+const GraphicHwOps virtio_gpu_ops = {
+ .invalidate = virtio_gpu_invalidate_display,
+ .gfx_update = virtio_gpu_update_display,
+ .text_update = virtio_gpu_text_update,
+ .ui_info = virtio_gpu_ui_info,
+};
+
+static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
+ VirtIOGPU *g = VIRTIO_GPU(qdev);
+ int i;
+
+ g->config_size = sizeof(struct virtio_gpu_config);
+ g->virtio_config.num_scanouts = g->conf.max_outputs;
+ virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU,
+ g->config_size);
+
+ g->req_state[0].width = 1024;
+ g->req_state[0].height = 768;
+
+ g->ctrl_vq = virtio_add_queue(vdev, 64, virtio_gpu_handle_ctrl_cb);
+ g->cursor_vq = virtio_add_queue(vdev, 16, virtio_gpu_handle_cursor_cb);
+
+ g->ctrl_bh = qemu_bh_new(virtio_gpu_ctrl_bh, g);
+ g->cursor_bh = qemu_bh_new(virtio_gpu_cursor_bh, g);
+ QTAILQ_INIT(&g->reslist);
+ QTAILQ_INIT(&g->fenceq);
+
+ g->enabled_output_bitmask = 1;
+ g->qdev = qdev;
+
+ for (i = 0; i < g->conf.max_outputs; i++) {
+ g->scanout[i].con =
+ graphic_console_init(DEVICE(g), i, &virtio_gpu_ops, g);
+ if (i > 0) {
+ dpy_gfx_replace_surface(g->scanout[i].con, NULL);
+ }
+ }
+}
+
+static void virtio_gpu_instance_init(Object *obj)
+{
+}
+
+static void virtio_gpu_reset(VirtIODevice *vdev)
+{
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+ struct virtio_gpu_simple_resource *res, *tmp;
+ int i;
+
+ g->enable = 0;
+
+ QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) {
+ virtio_gpu_resource_destroy(g, res);
+ }
+ for (i = 0; i < g->conf.max_outputs; i++) {
+#if 0
+ g->req_state[i].x = 0;
+ g->req_state[i].y = 0;
+ if (i == 0) {
+ g->req_state[0].width = 1024;
+ g->req_state[0].height = 768;
+ } else {
+ g->req_state[i].width = 0;
+ g->req_state[i].height = 0;
+ }
+#endif
+ g->scanout[i].resource_id = 0;
+ g->scanout[i].width = 0;
+ g->scanout[i].height = 0;
+ g->scanout[i].x = 0;
+ g->scanout[i].y = 0;
+ g->scanout[i].ds = NULL;
+ }
+ g->enabled_output_bitmask = 1;
+}
+
+static Property virtio_gpu_properties[] = {
+ DEFINE_VIRTIO_GPU_PROPERTIES(VirtIOGPU, conf),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_gpu_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+
+ vdc->realize = virtio_gpu_device_realize;
+ vdc->get_config = virtio_gpu_get_config;
+ vdc->set_config = virtio_gpu_set_config;
+ vdc->get_features = virtio_gpu_get_features;
+
+ vdc->reset = virtio_gpu_reset;
+
+ dc->props = virtio_gpu_properties;
+}
+
+static const TypeInfo virtio_gpu_info = {
+ .name = TYPE_VIRTIO_GPU,
+ .parent = TYPE_VIRTIO_DEVICE,
+ .instance_size = sizeof(VirtIOGPU),
+ .instance_init = virtio_gpu_instance_init,
+ .class_init = virtio_gpu_class_init,
+};
+
+static void virtio_register_types(void)
+{
+ type_register_static(&virtio_gpu_info);
+}
+
+type_init(virtio_register_types)
+
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctrl_hdr) != 24);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_update_cursor) != 56);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_unref) != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_create_2d) != 40);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_set_scanout) != 48);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_flush) != 48);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_transfer_to_host_2d) != 56);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_mem_entry) != 16);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_attach_backing) != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_detach_backing) != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_display_info) != 408);
diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c
new file mode 100644
index 0000000000..94f9d0eb5a
--- /dev/null
+++ b/hw/display/virtio-vga.c
@@ -0,0 +1,175 @@
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "ui/console.h"
+#include "vga_int.h"
+#include "hw/virtio/virtio-pci.h"
+
+/*
+ * virtio-vga: This extends VirtioPCIProxy.
+ */
+#define TYPE_VIRTIO_VGA "virtio-vga"
+#define VIRTIO_VGA(obj) \
+ OBJECT_CHECK(VirtIOVGA, (obj), TYPE_VIRTIO_VGA)
+
+typedef struct VirtIOVGA {
+ VirtIOPCIProxy parent_obj;
+ VirtIOGPU vdev;
+ VGACommonState vga;
+ MemoryRegion vga_mrs[3];
+} VirtIOVGA;
+
+static void virtio_vga_invalidate_display(void *opaque)
+{
+ VirtIOVGA *vvga = opaque;
+
+ if (vvga->vdev.enable) {
+ virtio_gpu_ops.invalidate(&vvga->vdev);
+ } else {
+ vvga->vga.hw_ops->invalidate(&vvga->vga);
+ }
+}
+
+static void virtio_vga_update_display(void *opaque)
+{
+ VirtIOVGA *vvga = opaque;
+
+ if (vvga->vdev.enable) {
+ virtio_gpu_ops.gfx_update(&vvga->vdev);
+ } else {
+ vvga->vga.hw_ops->gfx_update(&vvga->vga);
+ }
+}
+
+static void virtio_vga_text_update(void *opaque, console_ch_t *chardata)
+{
+ VirtIOVGA *vvga = opaque;
+
+ if (vvga->vdev.enable) {
+ if (virtio_gpu_ops.text_update) {
+ virtio_gpu_ops.text_update(&vvga->vdev, chardata);
+ }
+ } else {
+ if (vvga->vga.hw_ops->text_update) {
+ vvga->vga.hw_ops->text_update(&vvga->vga, chardata);
+ }
+ }
+}
+
+static int virtio_vga_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
+{
+ VirtIOVGA *vvga = opaque;
+
+ if (virtio_gpu_ops.ui_info) {
+ return virtio_gpu_ops.ui_info(&vvga->vdev, idx, info);
+ }
+ return -1;
+}
+
+static const GraphicHwOps virtio_vga_ops = {
+ .invalidate = virtio_vga_invalidate_display,
+ .gfx_update = virtio_vga_update_display,
+ .text_update = virtio_vga_text_update,
+ .ui_info = virtio_vga_ui_info,
+};
+
+/* VGA device wrapper around PCI device around virtio GPU */
+static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ VirtIOVGA *vvga = VIRTIO_VGA(vpci_dev);
+ VirtIOGPU *g = &vvga->vdev;
+ VGACommonState *vga = &vvga->vga;
+ uint32_t offset;
+
+ /* init vga compat bits */
+ vga->vram_size_mb = 8;
+ vga_common_init(vga, OBJECT(vpci_dev), false);
+ vga_init(vga, OBJECT(vpci_dev), pci_address_space(&vpci_dev->pci_dev),
+ pci_address_space_io(&vpci_dev->pci_dev), true);
+ pci_register_bar(&vpci_dev->pci_dev, 0,
+ PCI_BASE_ADDRESS_MEM_PREFETCH, &vga->vram);
+
+ /*
+ * Configure virtio bar and regions
+ *
+ * We use bar #2 for the mmio regions, to be compatible with stdvga.
+ * virtio regions are moved to the end of bar #2, to make room for
+ * the stdvga mmio registers at the start of bar #2.
+ */
+ vpci_dev->modern_mem_bar = 2;
+ vpci_dev->msix_bar = 4;
+ offset = memory_region_size(&vpci_dev->modern_bar);
+ offset -= vpci_dev->notify.size;
+ vpci_dev->notify.offset = offset;
+ offset -= vpci_dev->device.size;
+ vpci_dev->device.offset = offset;
+ offset -= vpci_dev->isr.size;
+ vpci_dev->isr.offset = offset;
+ offset -= vpci_dev->common.size;
+ vpci_dev->common.offset = offset;
+
+ /* init virtio bits */
+ qdev_set_parent_bus(DEVICE(g), BUS(&vpci_dev->bus));
+ /* force virtio-1.0 */
+ vpci_dev->flags &= ~VIRTIO_PCI_FLAG_DISABLE_MODERN;
+ vpci_dev->flags |= VIRTIO_PCI_FLAG_DISABLE_LEGACY;
+ object_property_set_bool(OBJECT(g), true, "realized", errp);
+
+ /* add stdvga mmio regions */
+ pci_std_vga_mmio_region_init(vga, &vpci_dev->modern_bar,
+ vvga->vga_mrs, true);
+
+ vga->con = g->scanout[0].con;
+ graphic_console_set_hwops(vga->con, &virtio_vga_ops, vvga);
+}
+
+static void virtio_vga_reset(DeviceState *dev)
+{
+ VirtIOVGA *vvga = VIRTIO_VGA(dev);
+ vvga->vdev.enable = 0;
+
+ vga_dirty_log_start(&vvga->vga);
+}
+
+static Property virtio_vga_properties[] = {
+ DEFINE_VIRTIO_GPU_PROPERTIES(VirtIOVGA, vdev.conf),
+ DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_vga_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
+ dc->props = virtio_vga_properties;
+ dc->reset = virtio_vga_reset;
+ dc->hotpluggable = false;
+
+ k->realize = virtio_vga_realize;
+ pcidev_k->romfile = "vgabios-virtio.bin";
+ pcidev_k->class_id = PCI_CLASS_DISPLAY_VGA;
+}
+
+static void virtio_vga_inst_initfn(Object *obj)
+{
+ VirtIOVGA *dev = VIRTIO_VGA(obj);
+ object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_GPU);
+ object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
+}
+
+static TypeInfo virtio_vga_info = {
+ .name = TYPE_VIRTIO_VGA,
+ .parent = TYPE_VIRTIO_PCI,
+ .instance_size = sizeof(struct VirtIOVGA),
+ .instance_init = virtio_vga_inst_initfn,
+ .class_init = virtio_vga_class_init,
+};
+
+static void virtio_vga_register_types(void)
+{
+ type_register_static(&virtio_vga_info);
+}
+
+type_init(virtio_vga_register_types)
diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c
index c17ddd1fcd..7f397d3c2e 100644
--- a/hw/display/vmware_vga.c
+++ b/hw/display/vmware_vga.c
@@ -1124,7 +1124,7 @@ static void vmsvga_update_display(void *opaque)
* Is it more efficient to look at vram VGA-dirty bits or wait
* for the driver to issue SVGA_CMD_UPDATE?
*/
- if (memory_region_is_logging(&s->vga.vram)) {
+ if (memory_region_is_logging(&s->vga.vram, DIRTY_MEMORY_VGA)) {
vga_sync_dirty_bitmap(&s->vga);
dirty = memory_region_get_dirty(&s->vga.vram, 0,
surface_stride(surface) * surface_height(surface),
diff --git a/hw/dma/rc4030.c b/hw/dma/rc4030.c
index af2663256e..3efa6de352 100644
--- a/hw/dma/rc4030.c
+++ b/hw/dma/rc4030.c
@@ -1,7 +1,7 @@
/*
* QEMU JAZZ RC4030 chipset
*
- * Copyright (c) 2007-2009 Herve Poussineau
+ * Copyright (c) 2007-2013 Hervé Poussineau
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,29 +24,16 @@
#include "hw/hw.h"
#include "hw/mips/mips.h"
+#include "hw/sysbus.h"
#include "qemu/timer.h"
-
-/********************************************************/
-/* debug rc4030 */
-
-//#define DEBUG_RC4030
-//#define DEBUG_RC4030_DMA
-
-#ifdef DEBUG_RC4030
-#define DPRINTF(fmt, ...) \
-do { printf("rc4030: " fmt , ## __VA_ARGS__); } while (0)
-static const char* irq_names[] = { "parallel", "floppy", "sound", "video",
- "network", "scsi", "keyboard", "mouse", "serial0", "serial1" };
-#else
-#define DPRINTF(fmt, ...)
-#endif
-
-#define RC4030_ERROR(fmt, ...) \
-do { fprintf(stderr, "rc4030 ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
+#include "exec/address-spaces.h"
+#include "trace.h"
/********************************************************/
/* rc4030 emulation */
+#define MAX_TL_ENTRIES 512
+
typedef struct dma_pagetable_entry {
int32_t frame;
int32_t owner;
@@ -63,8 +50,14 @@ typedef struct dma_pagetable_entry {
#define DMA_FLAG_MEM_INTR 0x0200
#define DMA_FLAG_ADDR_INTR 0x0400
+#define TYPE_RC4030 "rc4030"
+#define RC4030(obj) \
+ OBJECT_CHECK(rc4030State, (obj), TYPE_RC4030)
+
typedef struct rc4030State
{
+ SysBusDevice parent;
+
uint32_t config; /* 0x0000: RC4030 config register */
uint32_t revision; /* 0x0008: RC4030 Revision register */
uint32_t invalid_address_register; /* 0x0010: Invalid Address register */
@@ -83,7 +76,7 @@ typedef struct rc4030State
uint32_t cache_bmask; /* 0x0058: I/O Cache Byte Mask */
uint32_t nmi_interrupt; /* 0x0200: interrupt source */
- uint32_t offset210;
+ uint32_t memory_refresh_rate; /* 0x0210: memory refresh rate */
uint32_t nvram_protect; /* 0x0220: NV ram protect register */
uint32_t rem_speed[16];
uint32_t imr_jazz; /* Local bus int enable mask */
@@ -96,6 +89,16 @@ typedef struct rc4030State
qemu_irq timer_irq;
qemu_irq jazz_bus_irq;
+ /* biggest translation table */
+ MemoryRegion dma_tt;
+ /* translation table memory region alias, added to system RAM */
+ MemoryRegion dma_tt_alias;
+ /* whole DMA memory region, root of DMA address space */
+ MemoryRegion dma_mr;
+ /* translation table entry aliases, added to DMA memory region */
+ MemoryRegion dma_mrs[MAX_TL_ENTRIES];
+ AddressSpace dma_as;
+
MemoryRegion iomem_chipset;
MemoryRegion iomem_jazzio;
} rc4030State;
@@ -112,7 +115,7 @@ static void set_next_tick(rc4030State *s)
}
/* called for accesses to rc4030 */
-static uint32_t rc4030_readl(void *opaque, hwaddr addr)
+static uint64_t rc4030_read(void *opaque, hwaddr addr, unsigned int size)
{
rc4030State *s = opaque;
uint32_t val;
@@ -220,9 +223,9 @@ static uint32_t rc4030_readl(void *opaque, hwaddr addr)
case 0x0208:
val = 0;
break;
- /* Offset 0x0210 */
+ /* Memory refresh rate */
case 0x0210:
- val = s->offset210;
+ val = s->memory_refresh_rate;
break;
/* NV ram protect register */
case 0x0220:
@@ -238,39 +241,117 @@ static uint32_t rc4030_readl(void *opaque, hwaddr addr)
val = 7; /* FIXME: should be read from EISA controller */
break;
default:
- RC4030_ERROR("invalid read [" TARGET_FMT_plx "]\n", addr);
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "rc4030: invalid read at 0x%x", (int)addr);
val = 0;
break;
}
if ((addr & ~3) != 0x230) {
- DPRINTF("read 0x%02x at " TARGET_FMT_plx "\n", val, addr);
+ trace_rc4030_read(addr, val);
}
return val;
}
-static uint32_t rc4030_readw(void *opaque, hwaddr addr)
+static void rc4030_dma_as_update_one(rc4030State *s, int index, uint32_t frame)
{
- uint32_t v = rc4030_readl(opaque, addr & ~0x3);
- if (addr & 0x2)
- return v >> 16;
- else
- return v & 0xffff;
+ if (index < MAX_TL_ENTRIES) {
+ memory_region_set_enabled(&s->dma_mrs[index], false);
+ }
+
+ if (!frame) {
+ return;
+ }
+
+ if (index >= MAX_TL_ENTRIES) {
+ qemu_log_mask(LOG_UNIMP,
+ "rc4030: trying to use too high "
+ "translation table entry %d (max allowed=%d)",
+ index, MAX_TL_ENTRIES);
+ return;
+ }
+ memory_region_set_alias_offset(&s->dma_mrs[index], frame);
+ memory_region_set_enabled(&s->dma_mrs[index], true);
}
-static uint32_t rc4030_readb(void *opaque, hwaddr addr)
+static void rc4030_dma_tt_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned int size)
{
- uint32_t v = rc4030_readl(opaque, addr & ~0x3);
- return (v >> (8 * (addr & 0x3))) & 0xff;
+ rc4030State *s = opaque;
+
+ /* write memory */
+ memcpy(memory_region_get_ram_ptr(&s->dma_tt) + addr, &data, size);
+
+ /* update dma address space (only if frame field has been written) */
+ if (addr % sizeof(dma_pagetable_entry) == 0) {
+ int index = addr / sizeof(dma_pagetable_entry);
+ memory_region_transaction_begin();
+ rc4030_dma_as_update_one(s, index, (uint32_t)data);
+ memory_region_transaction_commit();
+ }
}
-static void rc4030_writel(void *opaque, hwaddr addr, uint32_t val)
+static const MemoryRegionOps rc4030_dma_tt_ops = {
+ .write = rc4030_dma_tt_write,
+ .impl.min_access_size = 4,
+ .impl.max_access_size = 4,
+};
+
+static void rc4030_dma_tt_update(rc4030State *s, uint32_t new_tl_base,
+ uint32_t new_tl_limit)
+{
+ int entries, i;
+ dma_pagetable_entry *dma_tl_contents;
+
+ if (s->dma_tl_limit) {
+ /* write old dma tl table to physical memory */
+ memory_region_del_subregion(get_system_memory(), &s->dma_tt_alias);
+ cpu_physical_memory_write(s->dma_tl_limit & 0x7fffffff,
+ memory_region_get_ram_ptr(&s->dma_tt),
+ memory_region_size(&s->dma_tt_alias));
+ }
+ object_unparent(OBJECT(&s->dma_tt_alias));
+
+ s->dma_tl_base = new_tl_base;
+ s->dma_tl_limit = new_tl_limit;
+ new_tl_base &= 0x7fffffff;
+
+ if (s->dma_tl_limit) {
+ uint64_t dma_tt_size;
+ if (s->dma_tl_limit <= memory_region_size(&s->dma_tt)) {
+ dma_tt_size = s->dma_tl_limit;
+ } else {
+ dma_tt_size = memory_region_size(&s->dma_tt);
+ }
+ memory_region_init_alias(&s->dma_tt_alias, OBJECT(s),
+ "dma-table-alias",
+ &s->dma_tt, 0, dma_tt_size);
+ dma_tl_contents = memory_region_get_ram_ptr(&s->dma_tt);
+ cpu_physical_memory_read(new_tl_base, dma_tl_contents, dma_tt_size);
+
+ memory_region_transaction_begin();
+ entries = dma_tt_size / sizeof(dma_pagetable_entry);
+ for (i = 0; i < entries; i++) {
+ rc4030_dma_as_update_one(s, i, dma_tl_contents[i].frame);
+ }
+ memory_region_add_subregion(get_system_memory(), new_tl_base,
+ &s->dma_tt_alias);
+ memory_region_transaction_commit();
+ } else {
+ memory_region_init(&s->dma_tt_alias, OBJECT(s),
+ "dma-table-alias", 0);
+ }
+}
+
+static void rc4030_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned int size)
{
rc4030State *s = opaque;
+ uint32_t val = data;
addr &= 0x3fff;
- DPRINTF("write 0x%02x at " TARGET_FMT_plx "\n", val, addr);
+ trace_rc4030_write(addr, val);
switch (addr & ~0x3) {
/* Global config register */
@@ -279,11 +360,11 @@ static void rc4030_writel(void *opaque, hwaddr addr, uint32_t val)
break;
/* DMA transl. table base */
case 0x0018:
- s->dma_tl_base = val;
+ rc4030_dma_tt_update(s, val, s->dma_tl_limit);
break;
/* DMA transl. table limit */
case 0x0020:
- s->dma_tl_limit = val;
+ rc4030_dma_tt_update(s, s->dma_tl_base, val);
break;
/* DMA transl. table invalidated */
case 0x0028:
@@ -371,9 +452,9 @@ static void rc4030_writel(void *opaque, hwaddr addr, uint32_t val)
s->dma_regs[entry][idx] = val;
}
break;
- /* Offset 0x0210 */
+ /* Memory refresh rate */
case 0x0210:
- s->offset210 = val;
+ s->memory_refresh_rate = val;
break;
/* Interval timer reload */
case 0x0228:
@@ -385,48 +466,18 @@ static void rc4030_writel(void *opaque, hwaddr addr, uint32_t val)
case 0x0238:
break;
default:
- RC4030_ERROR("invalid write of 0x%02x at [" TARGET_FMT_plx "]\n", val, addr);
- break;
- }
-}
-
-static void rc4030_writew(void *opaque, hwaddr addr, uint32_t val)
-{
- uint32_t old_val = rc4030_readl(opaque, addr & ~0x3);
-
- if (addr & 0x2)
- val = (val << 16) | (old_val & 0x0000ffff);
- else
- val = val | (old_val & 0xffff0000);
- rc4030_writel(opaque, addr & ~0x3, val);
-}
-
-static void rc4030_writeb(void *opaque, hwaddr addr, uint32_t val)
-{
- uint32_t old_val = rc4030_readl(opaque, addr & ~0x3);
-
- switch (addr & 3) {
- case 0:
- val = val | (old_val & 0xffffff00);
- break;
- case 1:
- val = (val << 8) | (old_val & 0xffff00ff);
- break;
- case 2:
- val = (val << 16) | (old_val & 0xff00ffff);
- break;
- case 3:
- val = (val << 24) | (old_val & 0x00ffffff);
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "rc4030: invalid write of 0x%02x at 0x%x",
+ val, (int)addr);
break;
}
- rc4030_writel(opaque, addr & ~0x3, val);
}
static const MemoryRegionOps rc4030_ops = {
- .old_mmio = {
- .read = { rc4030_readb, rc4030_readw, rc4030_readl, },
- .write = { rc4030_writeb, rc4030_writew, rc4030_writel, },
- },
+ .read = rc4030_read,
+ .write = rc4030_write,
+ .impl.min_access_size = 4,
+ .impl.max_access_size = 4,
.endianness = DEVICE_NATIVE_ENDIAN,
};
@@ -436,22 +487,6 @@ static void update_jazz_irq(rc4030State *s)
pending = s->isr_jazz & s->imr_jazz;
-#ifdef DEBUG_RC4030
- if (s->isr_jazz != 0) {
- uint32_t irq = 0;
- DPRINTF("pending irqs:");
- for (irq = 0; irq < ARRAY_SIZE(irq_names); irq++) {
- if (s->isr_jazz & (1 << irq)) {
- printf(" %s", irq_names[irq]);
- if (!(s->imr_jazz & (1 << irq))) {
- printf("(ignored)");
- }
- }
- }
- printf("\n");
- }
-#endif
-
if (pending != 0)
qemu_irq_raise(s->jazz_bus_irq);
else
@@ -479,7 +514,7 @@ static void rc4030_periodic_timer(void *opaque)
qemu_irq_raise(s->timer_irq);
}
-static uint32_t jazzio_readw(void *opaque, hwaddr addr)
+static uint64_t jazzio_read(void *opaque, hwaddr addr, unsigned int size)
{
rc4030State *s = opaque;
uint32_t val;
@@ -494,7 +529,6 @@ static uint32_t jazzio_readw(void *opaque, hwaddr addr)
irq = 0;
while (pending) {
if (pending & 1) {
- DPRINTF("returning irq %s\n", irq_names[irq]);
val = (irq + 1) << 2;
break;
}
@@ -508,36 +542,25 @@ static uint32_t jazzio_readw(void *opaque, hwaddr addr)
val = s->imr_jazz;
break;
default:
- RC4030_ERROR("(jazz io controller) invalid read [" TARGET_FMT_plx "]\n", addr);
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "rc4030/jazzio: invalid read at 0x%x", (int)addr);
val = 0;
+ break;
}
- DPRINTF("(jazz io controller) read 0x%04x at " TARGET_FMT_plx "\n", val, addr);
+ trace_jazzio_read(addr, val);
return val;
}
-static uint32_t jazzio_readb(void *opaque, hwaddr addr)
-{
- uint32_t v;
- v = jazzio_readw(opaque, addr & ~0x1);
- return (v >> (8 * (addr & 0x1))) & 0xff;
-}
-
-static uint32_t jazzio_readl(void *opaque, hwaddr addr)
-{
- uint32_t v;
- v = jazzio_readw(opaque, addr);
- v |= jazzio_readw(opaque, addr + 2) << 16;
- return v;
-}
-
-static void jazzio_writew(void *opaque, hwaddr addr, uint32_t val)
+static void jazzio_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned int size)
{
rc4030State *s = opaque;
+ uint32_t val = data;
addr &= 0xfff;
- DPRINTF("(jazz io controller) write 0x%04x at " TARGET_FMT_plx "\n", val, addr);
+ trace_jazzio_write(addr, val);
switch (addr) {
/* Local bus int enable mask */
@@ -546,43 +569,24 @@ static void jazzio_writew(void *opaque, hwaddr addr, uint32_t val)
update_jazz_irq(s);
break;
default:
- RC4030_ERROR("(jazz io controller) invalid write of 0x%04x at [" TARGET_FMT_plx "]\n", val, addr);
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "rc4030/jazzio: invalid write of 0x%02x at 0x%x",
+ val, (int)addr);
break;
}
}
-static void jazzio_writeb(void *opaque, hwaddr addr, uint32_t val)
-{
- uint32_t old_val = jazzio_readw(opaque, addr & ~0x1);
-
- switch (addr & 1) {
- case 0:
- val = val | (old_val & 0xff00);
- break;
- case 1:
- val = (val << 8) | (old_val & 0x00ff);
- break;
- }
- jazzio_writew(opaque, addr & ~0x1, val);
-}
-
-static void jazzio_writel(void *opaque, hwaddr addr, uint32_t val)
-{
- jazzio_writew(opaque, addr, val & 0xffff);
- jazzio_writew(opaque, addr + 2, (val >> 16) & 0xffff);
-}
-
static const MemoryRegionOps jazzio_ops = {
- .old_mmio = {
- .read = { jazzio_readb, jazzio_readw, jazzio_readl, },
- .write = { jazzio_writeb, jazzio_writew, jazzio_writel, },
- },
+ .read = jazzio_read,
+ .write = jazzio_write,
+ .impl.min_access_size = 2,
+ .impl.max_access_size = 2,
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static void rc4030_reset(void *opaque)
+static void rc4030_reset(DeviceState *dev)
{
- rc4030State *s = opaque;
+ rc4030State *s = RC4030(dev);
int i;
s->config = 0x410; /* some boards seem to accept 0x104 too */
@@ -590,14 +594,14 @@ static void rc4030_reset(void *opaque)
s->invalid_address_register = 0;
memset(s->dma_regs, 0, sizeof(s->dma_regs));
- s->dma_tl_base = s->dma_tl_limit = 0;
+ rc4030_dma_tt_update(s, 0, 0);
s->remote_failed_address = s->memory_failed_address = 0;
s->cache_maint = 0;
s->cache_ptag = s->cache_ltag = 0;
s->cache_bmask = 0;
- s->offset210 = 0x18186;
+ s->memory_refresh_rate = 0x18186;
s->nvram_protect = 7;
for (i = 0; i < 15; i++)
s->rem_speed[i] = 7;
@@ -631,7 +635,7 @@ static int rc4030_load(QEMUFile *f, void *opaque, int version_id)
s->cache_ptag = qemu_get_be32(f);
s->cache_ltag = qemu_get_be32(f);
s->cache_bmask = qemu_get_be32(f);
- s->offset210 = qemu_get_be32(f);
+ s->memory_refresh_rate = qemu_get_be32(f);
s->nvram_protect = qemu_get_be32(f);
for (i = 0; i < 15; i++)
s->rem_speed[i] = qemu_get_be32(f);
@@ -663,7 +667,7 @@ static void rc4030_save(QEMUFile *f, void *opaque)
qemu_put_be32(f, s->cache_ptag);
qemu_put_be32(f, s->cache_ltag);
qemu_put_be32(f, s->cache_bmask);
- qemu_put_be32(f, s->offset210);
+ qemu_put_be32(f, s->memory_refresh_rate);
qemu_put_be32(f, s->nvram_protect);
for (i = 0; i < 15; i++)
qemu_put_be32(f, s->rem_speed[i]);
@@ -672,44 +676,6 @@ static void rc4030_save(QEMUFile *f, void *opaque)
qemu_put_be32(f, s->itr);
}
-void rc4030_dma_memory_rw(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write)
-{
- rc4030State *s = opaque;
- hwaddr entry_addr;
- hwaddr phys_addr;
- dma_pagetable_entry entry;
- int index;
- int ncpy, i;
-
- i = 0;
- for (;;) {
- if (i == len) {
- break;
- }
-
- ncpy = DMA_PAGESIZE - (addr & (DMA_PAGESIZE - 1));
- if (ncpy > len - i)
- ncpy = len - i;
-
- /* Get DMA translation table entry */
- index = addr / DMA_PAGESIZE;
- if (index >= s->dma_tl_limit / sizeof(dma_pagetable_entry)) {
- break;
- }
- entry_addr = s->dma_tl_base + index * sizeof(dma_pagetable_entry);
- /* XXX: not sure. should we really use only lowest bits? */
- entry_addr &= 0x7fffffff;
- cpu_physical_memory_read(entry_addr, &entry, sizeof(entry));
-
- /* Read/write data at right place */
- phys_addr = entry.frame + (addr & (DMA_PAGESIZE - 1));
- cpu_physical_memory_rw(phys_addr, &buf[i], ncpy, is_write);
-
- i += ncpy;
- addr += ncpy;
- }
-}
-
static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_write)
{
rc4030State *s = opaque;
@@ -733,32 +699,11 @@ static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_wri
dma_addr = s->dma_regs[n][DMA_REG_ADDRESS];
/* Read/write data at right place */
- rc4030_dma_memory_rw(opaque, dma_addr, buf, len, is_write);
+ address_space_rw(&s->dma_as, dma_addr, MEMTXATTRS_UNSPECIFIED,
+ buf, len, is_write);
s->dma_regs[n][DMA_REG_ENABLE] |= DMA_FLAG_TC_INTR;
s->dma_regs[n][DMA_REG_COUNT] -= len;
-
-#ifdef DEBUG_RC4030_DMA
- {
- int i, j;
- printf("rc4030 dma: Copying %d bytes %s host %p\n",
- len, is_write ? "from" : "to", buf);
- for (i = 0; i < len; i += 16) {
- int n = 16;
- if (n > len - i) {
- n = len - i;
- }
- for (j = 0; j < n; j++)
- printf("%02x ", buf[i + j]);
- while (j++ < 16)
- printf(" ");
- printf("| ");
- for (j = 0; j < n; j++)
- printf("%c", isprint(buf[i + j]) ? buf[i + j] : '.');
- printf("\n");
- }
- }
-#endif
}
struct rc4030DMAState {
@@ -795,31 +740,102 @@ static rc4030_dma *rc4030_allocate_dmas(void *opaque, int n)
return s;
}
-void *rc4030_init(qemu_irq timer, qemu_irq jazz_bus,
- qemu_irq **irqs, rc4030_dma **dmas,
- MemoryRegion *sysmem)
+static void rc4030_initfn(Object *obj)
{
- rc4030State *s;
+ DeviceState *dev = DEVICE(obj);
+ rc4030State *s = RC4030(obj);
+ SysBusDevice *sysbus = SYS_BUS_DEVICE(obj);
- s = g_malloc0(sizeof(rc4030State));
+ qdev_init_gpio_in(dev, rc4030_irq_jazz_request, 16);
- *irqs = qemu_allocate_irqs(rc4030_irq_jazz_request, s, 16);
- *dmas = rc4030_allocate_dmas(s, 4);
+ sysbus_init_irq(sysbus, &s->timer_irq);
+ sysbus_init_irq(sysbus, &s->jazz_bus_irq);
- s->periodic_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, rc4030_periodic_timer, s);
- s->timer_irq = timer;
- s->jazz_bus_irq = jazz_bus;
-
- qemu_register_reset(rc4030_reset, s);
register_savevm(NULL, "rc4030", 0, 2, rc4030_save, rc4030_load, s);
- rc4030_reset(s);
+
+ sysbus_init_mmio(sysbus, &s->iomem_chipset);
+ sysbus_init_mmio(sysbus, &s->iomem_jazzio);
+}
+
+static void rc4030_realize(DeviceState *dev, Error **errp)
+{
+ rc4030State *s = RC4030(dev);
+ Object *o = OBJECT(dev);
+ int i;
+
+ s->periodic_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ rc4030_periodic_timer, s);
memory_region_init_io(&s->iomem_chipset, NULL, &rc4030_ops, s,
"rc4030.chipset", 0x300);
- memory_region_add_subregion(sysmem, 0x80000000, &s->iomem_chipset);
memory_region_init_io(&s->iomem_jazzio, NULL, &jazzio_ops, s,
"rc4030.jazzio", 0x00001000);
- memory_region_add_subregion(sysmem, 0xf0000000, &s->iomem_jazzio);
- return s;
+ memory_region_init_rom_device(&s->dma_tt, o,
+ &rc4030_dma_tt_ops, s, "dma-table",
+ MAX_TL_ENTRIES * sizeof(dma_pagetable_entry),
+ NULL);
+ memory_region_init(&s->dma_tt_alias, o, "dma-table-alias", 0);
+ memory_region_init(&s->dma_mr, o, "dma", INT32_MAX);
+ for (i = 0; i < MAX_TL_ENTRIES; ++i) {
+ memory_region_init_alias(&s->dma_mrs[i], o, "dma-alias",
+ get_system_memory(), 0, DMA_PAGESIZE);
+ memory_region_set_enabled(&s->dma_mrs[i], false);
+ memory_region_add_subregion(&s->dma_mr, i * DMA_PAGESIZE,
+ &s->dma_mrs[i]);
+ }
+ address_space_init(&s->dma_as, &s->dma_mr, "rc4030-dma");
+}
+
+static void rc4030_unrealize(DeviceState *dev, Error **errp)
+{
+ rc4030State *s = RC4030(dev);
+ int i;
+
+ timer_free(s->periodic_timer);
+
+ address_space_destroy(&s->dma_as);
+ object_unparent(OBJECT(&s->dma_tt));
+ object_unparent(OBJECT(&s->dma_tt_alias));
+ object_unparent(OBJECT(&s->dma_mr));
+ for (i = 0; i < MAX_TL_ENTRIES; ++i) {
+ memory_region_del_subregion(&s->dma_mr, &s->dma_mrs[i]);
+ object_unparent(OBJECT(&s->dma_mrs[i]));
+ }
+}
+
+static void rc4030_class_init(ObjectClass *klass, void *class_data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = rc4030_realize;
+ dc->unrealize = rc4030_unrealize;
+ dc->reset = rc4030_reset;
+}
+
+static const TypeInfo rc4030_info = {
+ .name = TYPE_RC4030,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(rc4030State),
+ .instance_init = rc4030_initfn,
+ .class_init = rc4030_class_init,
+};
+
+static void rc4030_register_types(void)
+{
+ type_register_static(&rc4030_info);
+}
+
+type_init(rc4030_register_types)
+
+DeviceState *rc4030_init(rc4030_dma **dmas, MemoryRegion **dma_mr)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, TYPE_RC4030);
+ qdev_init_nofail(dev);
+
+ *dmas = rc4030_allocate_dmas(dev, 4);
+ *dma_mr = &RC4030(dev)->dma_mr;
+ return dev;
}
diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c
index bd03e99975..4ba730b476 100644
--- a/hw/gpio/pl061.c
+++ b/hw/gpio/pl061.c
@@ -173,7 +173,7 @@ static uint64_t pl061_read(void *opaque, hwaddr offset,
case 0x414: /* Raw interrupt status */
return s->istate;
case 0x418: /* Masked interrupt status */
- return s->istate | s->im;
+ return s->istate & s->im;
case 0x420: /* Alternate function select */
return s->afsel;
case 0x500: /* 2mA drive */
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 8e88aded9f..b71e942567 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -615,6 +615,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
}
}
aml_append(parent_scope, method);
+ qobject_decref(bsel);
}
/*
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index a93972fb41..3f0d435da9 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -164,27 +164,6 @@ uint64_t cpu_get_tsc(CPUX86State *env)
return cpu_get_ticks();
}
-/* SMM support */
-
-static cpu_set_smm_t smm_set;
-static void *smm_arg;
-
-void cpu_smm_register(cpu_set_smm_t callback, void *arg)
-{
- assert(smm_set == NULL);
- assert(smm_arg == NULL);
- smm_set = callback;
- smm_arg = arg;
-}
-
-void cpu_smm_update(CPUX86State *env)
-{
- if (smm_set && smm_arg && CPU(x86_env_get_cpu(env)) == first_cpu) {
- smm_set(!!(env->hflags & HF_SMM_MASK), smm_arg);
- }
-}
-
-
/* IRQ handling */
int cpu_get_pic_interrupt(CPUX86State *env)
{
@@ -1007,7 +986,6 @@ static X86CPU *pc_new_cpu(const char *cpu_model, int64_t apic_id,
}
qdev_set_parent_bus(DEVICE(cpu), qdev_get_child_bus(icc_bridge, "icc"));
- object_unref(OBJECT(cpu));
object_property_set_int(OBJECT(cpu), apic_id, "apic-id", &local_err);
object_property_set_bool(OBJECT(cpu), true, "realized", &local_err);
@@ -1026,7 +1004,9 @@ static const char *current_cpu_model;
void pc_hot_add_cpu(const int64_t id, Error **errp)
{
DeviceState *icc_bridge;
+ X86CPU *cpu;
int64_t apic_id = x86_cpu_apic_id_from_index(id);
+ Error *local_err = NULL;
if (id < 0) {
error_setg(errp, "Invalid CPU id: %" PRIi64, id);
@@ -1054,7 +1034,12 @@ void pc_hot_add_cpu(const int64_t id, Error **errp)
icc_bridge = DEVICE(object_resolve_path_type("icc-bridge",
TYPE_ICC_BRIDGE, NULL));
- pc_new_cpu(current_cpu_model, apic_id, icc_bridge, errp);
+ cpu = pc_new_cpu(current_cpu_model, apic_id, icc_bridge, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ object_unref(OBJECT(cpu));
}
void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge)
@@ -1088,6 +1073,7 @@ void pc_cpus_init(const char *cpu_model, DeviceState *icc_bridge)
error_report_err(error);
exit(1);
}
+ object_unref(OBJECT(cpu));
}
/* map APIC MMIO area if CPU has APIC */
@@ -1365,9 +1351,9 @@ FWCfgState *pc_memory_init(MachineState *machine,
return fw_cfg;
}
-qemu_irq *pc_allocate_cpu_irq(void)
+qemu_irq pc_allocate_cpu_irq(void)
{
- return qemu_allocate_irqs(pic_irq_request, NULL, 1);
+ return qemu_allocate_irq(pic_irq_request, NULL, 0);
}
DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus)
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 0688f0c52f..e142f75649 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -52,6 +52,7 @@
#ifdef CONFIG_XEN
# include <xen/hvm/hvm_info_table.h>
#endif
+#include "migration/migration.h"
#define MAX_IDE_BUS 2
@@ -86,10 +87,9 @@ static void pc_init1(MachineState *machine)
ISABus *isa_bus;
PCII440FXState *i440fx_state;
int piix3_devfn = -1;
- qemu_irq *cpu_irq;
qemu_irq *gsi;
qemu_irq *i8259;
- qemu_irq *smi_irq;
+ qemu_irq smi_irq;
GSIState *gsi_state;
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
BusState *idebus[MAX_IDE_BUS];
@@ -219,13 +219,13 @@ static void pc_init1(MachineState *machine)
} else if (xen_enabled()) {
i8259 = xen_interrupt_controller_init();
} else {
- cpu_irq = pc_allocate_cpu_irq();
- i8259 = i8259_init(isa_bus, cpu_irq[0]);
+ i8259 = i8259_init(isa_bus, pc_allocate_cpu_irq());
}
for (i = 0; i < ISA_NUM_IRQS; i++) {
gsi_state->i8259_irq[i] = i8259[i];
}
+ g_free(i8259);
if (pci_enabled) {
ioapic_init_gsi(gsi_state, "i440fx");
}
@@ -283,10 +283,10 @@ static void pc_init1(MachineState *machine)
DeviceState *piix4_pm;
I2CBus *smbus;
- smi_irq = qemu_allocate_irqs(pc_acpi_smi_interrupt, first_cpu, 1);
+ smi_irq = qemu_allocate_irq(pc_acpi_smi_interrupt, first_cpu, 0);
/* TODO: Populate SPD eeprom data. */
smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100,
- gsi[9], *smi_irq,
+ gsi[9], smi_irq,
kvm_enabled(), &piix4_pm);
smbus_eeprom_init(smbus, 8, NULL, 0);
@@ -306,6 +306,7 @@ static void pc_init1(MachineState *machine)
static void pc_compat_2_3(MachineState *machine)
{
+ savevm_skip_section_footers();
}
static void pc_compat_2_2(MachineState *machine)
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 66220b352b..082cd93bb2 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -45,6 +45,7 @@
#include "hw/usb.h"
#include "hw/cpu/icc_bus.h"
#include "qemu/error-report.h"
+#include "migration/migration.h"
/* ICH9 AHCI has 6 ports */
#define MAX_SATA_PORTS 6
@@ -79,7 +80,6 @@ static void pc_q35_init(MachineState *machine)
GSIState *gsi_state;
ISABus *isa_bus;
int pci_enabled = 1;
- qemu_irq *cpu_irq;
qemu_irq *gsi;
qemu_irq *i8259;
int i;
@@ -230,8 +230,7 @@ static void pc_q35_init(MachineState *machine)
} else if (xen_enabled()) {
i8259 = xen_interrupt_controller_init();
} else {
- cpu_irq = pc_allocate_cpu_irq();
- i8259 = i8259_init(isa_bus, cpu_irq[0]);
+ i8259 = i8259_init(isa_bus, pc_allocate_cpu_irq());
}
for (i = 0; i < ISA_NUM_IRQS; i++) {
@@ -291,6 +290,7 @@ static void pc_q35_init(MachineState *machine)
static void pc_compat_2_3(MachineState *machine)
{
+ savevm_skip_section_footers();
}
static void pc_compat_2_2(MachineState *machine)
@@ -403,6 +403,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->no_floppy = 0;
m->alias = NULL;
SET_MACHINE_COMPAT(m, PC_COMPAT_2_3);
}
diff --git a/hw/ide/core.c b/hw/ide/core.c
index fcb908061c..1efd98af63 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -2561,6 +2561,7 @@ static const VMStateDescription vmstate_ide_atapi_gesn_state = {
.name ="ide_drive/atapi/gesn_state",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = ide_atapi_gesn_needed,
.fields = (VMStateField[]) {
VMSTATE_BOOL(events.new_media, IDEState),
VMSTATE_BOOL(events.eject_request, IDEState),
@@ -2572,6 +2573,7 @@ static const VMStateDescription vmstate_ide_tray_state = {
.name = "ide_drive/tray_state",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = ide_tray_state_needed,
.fields = (VMStateField[]) {
VMSTATE_BOOL(tray_open, IDEState),
VMSTATE_BOOL(tray_locked, IDEState),
@@ -2585,6 +2587,7 @@ static const VMStateDescription vmstate_ide_drive_pio_state = {
.minimum_version_id = 1,
.pre_save = ide_drive_pio_pre_save,
.post_load = ide_drive_pio_post_load,
+ .needed = ide_drive_pio_state_needed,
.fields = (VMStateField[]) {
VMSTATE_INT32(req_nb_sectors, IDEState),
VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1,
@@ -2626,19 +2629,11 @@ const VMStateDescription vmstate_ide_drive = {
VMSTATE_UINT8_V(cdrom_changed, IDEState, 3),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection []) {
- {
- .vmsd = &vmstate_ide_drive_pio_state,
- .needed = ide_drive_pio_state_needed,
- }, {
- .vmsd = &vmstate_ide_tray_state,
- .needed = ide_tray_state_needed,
- }, {
- .vmsd = &vmstate_ide_atapi_gesn_state,
- .needed = ide_atapi_gesn_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_ide_drive_pio_state,
+ &vmstate_ide_tray_state,
+ &vmstate_ide_atapi_gesn_state,
+ NULL
}
};
@@ -2646,6 +2641,7 @@ static const VMStateDescription vmstate_ide_error_status = {
.name ="ide_bus/error",
.version_id = 2,
.minimum_version_id = 1,
+ .needed = ide_error_needed,
.fields = (VMStateField[]) {
VMSTATE_INT32(error_status, IDEBus),
VMSTATE_INT64_V(retry_sector_num, IDEBus, 2),
@@ -2664,13 +2660,9 @@ const VMStateDescription vmstate_ide_bus = {
VMSTATE_UINT8(unit, IDEBus),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection []) {
- {
- .vmsd = &vmstate_ide_error_status,
- .needed = ide_error_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_ide_error_status,
+ NULL
}
};
diff --git a/hw/ide/macio.c b/hw/ide/macio.c
index 585a27bd6c..dd52d50732 100644
--- a/hw/ide/macio.c
+++ b/hw/ide/macio.c
@@ -51,8 +51,15 @@ static const int debug_macio = 0;
#define MACIO_PAGE_SIZE 4096
+/*
+ * Unaligned DMA read/write access functions required for OS X/Darwin which
+ * don't perform DMA transactions on sector boundaries. These functions are
+ * modelled on bdrv_co_do_preadv()/bdrv_co_do_pwritev() and so should be
+ * easy to remove if the unaligned block APIs are ever exposed.
+ */
+
static void pmac_dma_read(BlockBackend *blk,
- int64_t sector_num, int nb_sectors,
+ int64_t offset, unsigned int bytes,
void (*cb)(void *opaque, int ret), void *opaque)
{
DBDMA_io *io = opaque;
@@ -60,76 +67,48 @@ static void pmac_dma_read(BlockBackend *blk,
IDEState *s = idebus_active_if(&m->bus);
dma_addr_t dma_addr, dma_len;
void *mem;
- int nsector, remainder;
+ int64_t sector_num;
+ int nsector;
+ uint64_t align = BDRV_SECTOR_SIZE;
+ size_t head_bytes, tail_bytes;
qemu_iovec_destroy(&io->iov);
qemu_iovec_init(&io->iov, io->len / MACIO_PAGE_SIZE + 1);
- if (io->remainder_len > 0) {
- /* Return remainder of request */
- int transfer = MIN(io->remainder_len, io->len);
+ sector_num = (offset >> 9);
+ nsector = (io->len >> 9);
- MACIO_DPRINTF("--- DMA read pop - bounce addr: %p addr: %"
- HWADDR_PRIx " remainder_len: %x\n",
- &io->remainder + (0x200 - transfer), io->addr,
- io->remainder_len);
+ MACIO_DPRINTF("--- DMA read transfer (0x%" HWADDR_PRIx ",0x%x): "
+ "sector_num: %" PRId64 ", nsector: %d\n", io->addr, io->len,
+ sector_num, nsector);
- cpu_physical_memory_write(io->addr,
- &io->remainder + (0x200 - transfer),
- transfer);
+ dma_addr = io->addr;
+ dma_len = io->len;
+ mem = dma_memory_map(&address_space_memory, dma_addr, &dma_len,
+ DMA_DIRECTION_FROM_DEVICE);
- io->remainder_len -= transfer;
- io->len -= transfer;
- io->addr += transfer;
+ if (offset & (align - 1)) {
+ head_bytes = offset & (align - 1);
- s->io_buffer_index += transfer;
- s->io_buffer_size -= transfer;
+ MACIO_DPRINTF("--- DMA unaligned head: sector %" PRId64 ", "
+ "discarding %zu bytes\n", sector_num, head_bytes);
- if (io->remainder_len != 0) {
- /* Still waiting for remainder */
- return;
- }
+ qemu_iovec_add(&io->iov, &io->head_remainder, head_bytes);
- if (io->len == 0) {
- MACIO_DPRINTF("--- finished all read processing; go and finish\n");
- cb(opaque, 0);
- return;
- }
+ bytes += offset & (align - 1);
+ offset = offset & ~(align - 1);
}
- if (s->drive_kind == IDE_CD) {
- sector_num = (int64_t)(s->lba << 2) + (s->io_buffer_index >> 9);
- } else {
- sector_num = ide_get_sector(s) + (s->io_buffer_index >> 9);
- }
+ qemu_iovec_add(&io->iov, mem, io->len);
- nsector = ((io->len + 0x1ff) >> 9);
- remainder = (nsector << 9) - io->len;
+ if ((offset + bytes) & (align - 1)) {
+ tail_bytes = (offset + bytes) & (align - 1);
- MACIO_DPRINTF("--- DMA read transfer - addr: %" HWADDR_PRIx " len: %x\n",
- io->addr, io->len);
+ MACIO_DPRINTF("--- DMA unaligned tail: sector %" PRId64 ", "
+ "discarding bytes %zu\n", sector_num, tail_bytes);
- dma_addr = io->addr;
- dma_len = io->len;
- mem = dma_memory_map(&address_space_memory, dma_addr, &dma_len,
- DMA_DIRECTION_FROM_DEVICE);
-
- if (!remainder) {
- MACIO_DPRINTF("--- DMA read aligned - addr: %" HWADDR_PRIx
- " len: %x\n", io->addr, io->len);
- qemu_iovec_add(&io->iov, mem, io->len);
- } else {
- MACIO_DPRINTF("--- DMA read unaligned - addr: %" HWADDR_PRIx
- " len: %x\n", io->addr, io->len);
- qemu_iovec_add(&io->iov, mem, io->len);
-
- MACIO_DPRINTF("--- DMA read push - bounce addr: %p "
- "remainder_len: %x\n",
- &io->remainder + 0x200 - remainder, remainder);
- qemu_iovec_add(&io->iov, &io->remainder + 0x200 - remainder,
- remainder);
-
- io->remainder_len = remainder;
+ qemu_iovec_add(&io->iov, &io->tail_remainder, align - tail_bytes);
+ bytes = ROUND_UP(bytes, align);
}
s->io_buffer_size -= io->len;
@@ -137,15 +116,15 @@ static void pmac_dma_read(BlockBackend *blk,
io->len = 0;
- MACIO_DPRINTF("--- Block read transfer - sector_num: %"PRIx64" "
- "nsector: %x\n",
- sector_num, nsector);
+ MACIO_DPRINTF("--- Block read transfer - sector_num: %" PRIx64 " "
+ "nsector: %x\n", (offset >> 9), (bytes >> 9));
- m->aiocb = blk_aio_readv(blk, sector_num, &io->iov, nsector, cb, io);
+ m->aiocb = blk_aio_readv(blk, (offset >> 9), &io->iov, (bytes >> 9),
+ cb, io);
}
static void pmac_dma_write(BlockBackend *blk,
- int64_t sector_num, int nb_sectors,
+ int64_t offset, int bytes,
void (*cb)(void *opaque, int ret), void *opaque)
{
DBDMA_io *io = opaque;
@@ -153,90 +132,80 @@ static void pmac_dma_write(BlockBackend *blk,
IDEState *s = idebus_active_if(&m->bus);
dma_addr_t dma_addr, dma_len;
void *mem;
- int nsector, remainder;
- int extra = 0;
+ int64_t sector_num;
+ int nsector;
+ uint64_t align = BDRV_SECTOR_SIZE;
+ size_t head_bytes, tail_bytes;
+ bool unaligned_head = false, unaligned_tail = false;
qemu_iovec_destroy(&io->iov);
qemu_iovec_init(&io->iov, io->len / MACIO_PAGE_SIZE + 1);
- if (io->remainder_len > 0) {
- /* Return remainder of request */
- int transfer = MIN(io->remainder_len, io->len);
+ sector_num = (offset >> 9);
+ nsector = (io->len >> 9);
- MACIO_DPRINTF("--- processing write remainder %x\n", transfer);
- cpu_physical_memory_read(io->addr,
- &io->remainder + (0x200 - transfer),
- transfer);
+ MACIO_DPRINTF("--- DMA write transfer (0x%" HWADDR_PRIx ",0x%x): "
+ "sector_num: %" PRId64 ", nsector: %d\n", io->addr, io->len,
+ sector_num, nsector);
- io->remainder_len -= transfer;
- io->len -= transfer;
- io->addr += transfer;
+ dma_addr = io->addr;
+ dma_len = io->len;
+ mem = dma_memory_map(&address_space_memory, dma_addr, &dma_len,
+ DMA_DIRECTION_TO_DEVICE);
- s->io_buffer_index += transfer;
- s->io_buffer_size -= transfer;
+ if (offset & (align - 1)) {
+ head_bytes = offset & (align - 1);
+ sector_num = ((offset & ~(align - 1)) >> 9);
- if (io->remainder_len != 0) {
- /* Still waiting for remainder */
- return;
- }
+ MACIO_DPRINTF("--- DMA unaligned head: pre-reading head sector %"
+ PRId64 "\n", sector_num);
- MACIO_DPRINTF("--> prepending bounce buffer with size 0x200\n");
+ blk_pread(s->blk, (sector_num << 9), &io->head_remainder, align);
- /* Sector transfer complete - prepend to request */
- qemu_iovec_add(&io->iov, &io->remainder, 0x200);
- extra = 1;
- }
+ qemu_iovec_add(&io->iov, &io->head_remainder, head_bytes);
+ qemu_iovec_add(&io->iov, mem, io->len);
- if (s->drive_kind == IDE_CD) {
- sector_num = (int64_t)(s->lba << 2) + (s->io_buffer_index >> 9);
- } else {
- sector_num = ide_get_sector(s) + (s->io_buffer_index >> 9);
+ bytes += offset & (align - 1);
+ offset = offset & ~(align - 1);
+
+ unaligned_head = true;
}
- nsector = (io->len >> 9);
- remainder = io->len - (nsector << 9);
+ if ((offset + bytes) & (align - 1)) {
+ tail_bytes = (offset + bytes) & (align - 1);
+ sector_num = (((offset + bytes) & ~(align - 1)) >> 9);
- MACIO_DPRINTF("--- DMA write transfer - addr: %" HWADDR_PRIx " len: %x\n",
- io->addr, io->len);
- MACIO_DPRINTF("xxx remainder: %x\n", remainder);
- MACIO_DPRINTF("xxx sector_num: %"PRIx64" nsector: %x\n",
- sector_num, nsector);
+ MACIO_DPRINTF("--- DMA unaligned tail: pre-reading tail sector %"
+ PRId64 "\n", sector_num);
- dma_addr = io->addr;
- dma_len = io->len;
- mem = dma_memory_map(&address_space_memory, dma_addr, &dma_len,
- DMA_DIRECTION_TO_DEVICE);
+ blk_pread(s->blk, (sector_num << 9), &io->tail_remainder, align);
- if (!remainder) {
- MACIO_DPRINTF("--- DMA write aligned - addr: %" HWADDR_PRIx
- " len: %x\n", io->addr, io->len);
- qemu_iovec_add(&io->iov, mem, io->len);
- } else {
- /* Write up to last complete sector */
- MACIO_DPRINTF("--- DMA write unaligned - addr: %" HWADDR_PRIx
- " len: %x\n", io->addr, (nsector << 9));
- qemu_iovec_add(&io->iov, mem, (nsector << 9));
+ if (!unaligned_head) {
+ qemu_iovec_add(&io->iov, mem, io->len);
+ }
+
+ qemu_iovec_add(&io->iov, &io->tail_remainder + tail_bytes,
+ align - tail_bytes);
- MACIO_DPRINTF("--- DMA write read - bounce addr: %p "
- "remainder_len: %x\n", &io->remainder, remainder);
- cpu_physical_memory_read(io->addr + (nsector << 9), &io->remainder,
- remainder);
+ bytes = ROUND_UP(bytes, align);
- io->remainder_len = 0x200 - remainder;
+ unaligned_tail = true;
+ }
- MACIO_DPRINTF("xxx remainder_len: %x\n", io->remainder_len);
+ if (!unaligned_head && !unaligned_tail) {
+ qemu_iovec_add(&io->iov, mem, io->len);
}
- s->io_buffer_size -= ((nsector + extra) << 9);
- s->io_buffer_index += ((nsector + extra) << 9);
+ s->io_buffer_size -= io->len;
+ s->io_buffer_index += io->len;
io->len = 0;
- MACIO_DPRINTF("--- Block write transfer - sector_num: %"PRIx64" "
- "nsector: %x\n", sector_num, nsector + extra);
+ MACIO_DPRINTF("--- Block write transfer - sector_num: %" PRIx64 " "
+ "nsector: %x\n", (offset >> 9), (bytes >> 9));
- m->aiocb = blk_aio_writev(blk, sector_num, &io->iov, nsector + extra, cb,
- io);
+ m->aiocb = blk_aio_writev(blk, (offset >> 9), &io->iov, (bytes >> 9),
+ cb, io);
}
static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
@@ -244,19 +213,12 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
DBDMA_io *io = opaque;
MACIOIDEState *m = io->opaque;
IDEState *s = idebus_active_if(&m->bus);
- int64_t sector_num;
- int nsector, remainder;
+ int64_t offset;
- MACIO_DPRINTF("\ns is %p\n", s);
- MACIO_DPRINTF("io_buffer_index: %x\n", s->io_buffer_index);
- MACIO_DPRINTF("io_buffer_size: %x packet_transfer_size: %x\n",
- s->io_buffer_size, s->packet_transfer_size);
- MACIO_DPRINTF("lba: %x\n", s->lba);
- MACIO_DPRINTF("io_addr: %" HWADDR_PRIx " io_len: %x\n", io->addr,
- io->len);
+ MACIO_DPRINTF("pmac_ide_atapi_transfer_cb\n");
if (ret < 0) {
- MACIO_DPRINTF("THERE WAS AN ERROR! %d\n", ret);
+ MACIO_DPRINTF("DMA error: %d\n", ret);
ide_atapi_io_error(s, ret);
goto done;
}
@@ -270,6 +232,7 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
}
if (s->io_buffer_size <= 0) {
+ MACIO_DPRINTF("End of IDE transfer\n");
ide_atapi_cmd_ok(s);
m->dma_active = false;
goto done;
@@ -289,19 +252,13 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
goto done;
}
- /* Calculate number of sectors */
- sector_num = (int64_t)(s->lba << 2) + (s->io_buffer_index >> 9);
- nsector = (io->len + 0x1ff) >> 9;
- remainder = io->len & 0x1ff;
-
- MACIO_DPRINTF("nsector: %d remainder: %x\n", nsector, remainder);
- MACIO_DPRINTF("sector: %"PRIx64" %zx\n", sector_num, io->iov.size / 512);
+ /* Calculate current offset */
+ offset = (int64_t)(s->lba << 11) + s->io_buffer_index;
- pmac_dma_read(s->blk, sector_num, nsector, pmac_ide_atapi_transfer_cb, io);
+ pmac_dma_read(s->blk, offset, io->len, pmac_ide_atapi_transfer_cb, io);
return;
done:
- MACIO_DPRINTF("done DMA\n\n");
block_acct_done(blk_get_stats(s->blk), &s->acct);
io->dma_end(opaque);
@@ -313,16 +270,14 @@ static void pmac_ide_transfer_cb(void *opaque, int ret)
DBDMA_io *io = opaque;
MACIOIDEState *m = io->opaque;
IDEState *s = idebus_active_if(&m->bus);
- int64_t sector_num;
- int nsector, remainder;
+ int64_t offset;
MACIO_DPRINTF("pmac_ide_transfer_cb\n");
if (ret < 0) {
- MACIO_DPRINTF("DMA error\n");
+ MACIO_DPRINTF("DMA error: %d\n", ret);
m->aiocb = NULL;
ide_dma_error(s);
- io->remainder_len = 0;
goto done;
}
@@ -335,7 +290,7 @@ static void pmac_ide_transfer_cb(void *opaque, int ret)
}
if (s->io_buffer_size <= 0) {
- MACIO_DPRINTF("end of transfer\n");
+ MACIO_DPRINTF("End of IDE transfer\n");
s->status = READY_STAT | SEEK_STAT;
ide_set_irq(s->bus);
m->dma_active = false;
@@ -348,24 +303,16 @@ static void pmac_ide_transfer_cb(void *opaque, int ret)
}
/* Calculate number of sectors */
- sector_num = ide_get_sector(s) + (s->io_buffer_index >> 9);
- nsector = (io->len + 0x1ff) >> 9;
- remainder = io->len & 0x1ff;
-
- s->nsector -= nsector;
-
- MACIO_DPRINTF("nsector: %d remainder: %x\n", nsector, remainder);
- MACIO_DPRINTF("sector: %"PRIx64" %x\n", sector_num, nsector);
+ offset = (ide_get_sector(s) << 9) + s->io_buffer_index;
switch (s->dma_cmd) {
case IDE_DMA_READ:
- pmac_dma_read(s->blk, sector_num, nsector, pmac_ide_transfer_cb, io);
+ pmac_dma_read(s->blk, offset, io->len, pmac_ide_transfer_cb, io);
break;
case IDE_DMA_WRITE:
- pmac_dma_write(s->blk, sector_num, nsector, pmac_ide_transfer_cb, io);
+ pmac_dma_write(s->blk, offset, io->len, pmac_ide_transfer_cb, io);
break;
case IDE_DMA_TRIM:
- MACIO_DPRINTF("TRIM command issued!");
break;
}
@@ -561,15 +508,12 @@ static void ide_dbdma_start(IDEDMA *dma, IDEState *s,
BlockCompletionFunc *cb)
{
MACIOIDEState *m = container_of(dma, MACIOIDEState, dma);
- DBDMAState *dbdma = m->dbdma;
- DBDMA_io *io;
- int i;
s->io_buffer_index = 0;
if (s->drive_kind == IDE_CD) {
s->io_buffer_size = s->packet_transfer_size;
} else {
- s->io_buffer_size = s->nsector * 0x200;
+ s->io_buffer_size = s->nsector * BDRV_SECTOR_SIZE;
}
MACIO_DPRINTF("\n\n------------ IDE transfer\n");
@@ -578,15 +522,6 @@ static void ide_dbdma_start(IDEDMA *dma, IDEState *s,
MACIO_DPRINTF("lba: %x size: %x\n", s->lba, s->io_buffer_size);
MACIO_DPRINTF("-------------------------\n");
- for (i = 0; i < DBDMA_CHANNELS; i++) {
- io = &dbdma->channels[i].io;
-
- if (io->opaque == m) {
- io->remainder_len = 0;
- }
- }
-
- MACIO_DPRINTF("\n");
m->dma_active = true;
DBDMA_kick(m->dbdma);
}
diff --git a/hw/ide/pci.c b/hw/ide/pci.c
index 1b3d1c12ad..4afd0cfe8c 100644
--- a/hw/ide/pci.c
+++ b/hw/ide/pci.c
@@ -350,6 +350,7 @@ static const VMStateDescription vmstate_bmdma_current = {
.name = "ide bmdma_current",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = ide_bmdma_current_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT32(cur_addr, BMDMAState),
VMSTATE_UINT32(cur_prd_last, BMDMAState),
@@ -363,6 +364,7 @@ static const VMStateDescription vmstate_bmdma_status = {
.name ="ide bmdma/status",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = ide_bmdma_status_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8(status, BMDMAState),
VMSTATE_END_OF_LIST()
@@ -383,16 +385,10 @@ static const VMStateDescription vmstate_bmdma = {
VMSTATE_UINT8(migration_retry_unit, BMDMAState),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection []) {
- {
- .vmsd = &vmstate_bmdma_current,
- .needed = ide_bmdma_current_needed,
- }, {
- .vmsd = &vmstate_bmdma_status,
- .needed = ide_bmdma_status_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_bmdma_current,
+ &vmstate_bmdma_status,
+ NULL
}
};
@@ -452,8 +448,6 @@ static const struct IDEDMAOps bmdma_ops = {
void bmdma_init(IDEBus *bus, BMDMAState *bm, PCIIDEState *d)
{
- qemu_irq *irq;
-
if (bus->dma == &bm->dma) {
return;
}
@@ -461,8 +455,7 @@ void bmdma_init(IDEBus *bus, BMDMAState *bm, PCIIDEState *d)
bm->dma.ops = &bmdma_ops;
bus->dma = &bm->dma;
bm->irq = bus->irq;
- irq = qemu_allocate_irqs(bmdma_irq, bm, 1);
- bus->irq = *irq;
+ bus->irq = qemu_allocate_irq(bmdma_irq, bm, 0);
bm->pci_dev = d;
}
diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c
index 9b9a7d7a8a..ddac69df6f 100644
--- a/hw/input/pckbd.c
+++ b/hw/input/pckbd.c
@@ -391,23 +391,24 @@ static int kbd_outport_post_load(void *opaque, int version_id)
return 0;
}
+static bool kbd_outport_needed(void *opaque)
+{
+ KBDState *s = opaque;
+ return s->outport != kbd_outport_default(s);
+}
+
static const VMStateDescription vmstate_kbd_outport = {
.name = "pckbd_outport",
.version_id = 1,
.minimum_version_id = 1,
.post_load = kbd_outport_post_load,
+ .needed = kbd_outport_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8(outport, KBDState),
VMSTATE_END_OF_LIST()
}
};
-static bool kbd_outport_needed(void *opaque)
-{
- KBDState *s = opaque;
- return s->outport != kbd_outport_default(s);
-}
-
static int kbd_post_load(void *opaque, int version_id)
{
KBDState *s = opaque;
@@ -430,12 +431,9 @@ static const VMStateDescription vmstate_kbd = {
VMSTATE_UINT8(pending, KBDState),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_kbd_outport,
- .needed = kbd_outport_needed,
- },
- VMSTATE_END_OF_LIST()
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_kbd_outport,
+ NULL
}
};
diff --git a/hw/input/ps2.c b/hw/input/ps2.c
index 4baeea2b56..fdbe565e62 100644
--- a/hw/input/ps2.c
+++ b/hw/input/ps2.c
@@ -677,6 +677,7 @@ static const VMStateDescription vmstate_ps2_keyboard_ledstate = {
.version_id = 3,
.minimum_version_id = 2,
.post_load = ps2_kbd_ledstate_post_load,
+ .needed = ps2_keyboard_ledstate_needed,
.fields = (VMStateField[]) {
VMSTATE_INT32(ledstate, PS2KbdState),
VMSTATE_END_OF_LIST()
@@ -717,13 +718,9 @@ static const VMStateDescription vmstate_ps2_keyboard = {
VMSTATE_INT32_V(scancode_set, PS2KbdState,3),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection []) {
- {
- .vmsd = &vmstate_ps2_keyboard_ledstate,
- .needed = ps2_keyboard_ledstate_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_ps2_keyboard_ledstate,
+ NULL
}
};
diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
index 843864a3ef..092d8a80ac 100644
--- a/hw/intc/Makefile.objs
+++ b/hw/intc/Makefile.objs
@@ -11,6 +11,7 @@ common-obj-$(CONFIG_SLAVIO) += slavio_intctl.o
common-obj-$(CONFIG_IOAPIC) += ioapic_common.o
common-obj-$(CONFIG_ARM_GIC) += arm_gic_common.o
common-obj-$(CONFIG_ARM_GIC) += arm_gic.o
+common-obj-$(CONFIG_ARM_GIC) += arm_gicv2m.o
common-obj-$(CONFIG_OPENPIC) += openpic.o
obj-$(CONFIG_APIC) += apic.o apic_common.o
diff --git a/hw/intc/apic.c b/hw/intc/apic.c
index 0f97b47925..77b639cce8 100644
--- a/hw/intc/apic.c
+++ b/hw/intc/apic.c
@@ -370,13 +370,14 @@ static int apic_irq_pending(APICCommonState *s)
static void apic_update_irq(APICCommonState *s)
{
CPUState *cpu;
+ DeviceState *dev = (DeviceState *)s;
cpu = CPU(s->cpu);
if (!qemu_cpu_is_self(cpu)) {
cpu_interrupt(cpu, CPU_INTERRUPT_POLL);
} else if (apic_irq_pending(s) > 0) {
cpu_interrupt(cpu, CPU_INTERRUPT_HARD);
- } else if (!apic_accept_pic_intr(&s->busdev.qdev) || !pic_get_output(isa_pic)) {
+ } else if (!apic_accept_pic_intr(dev) || !pic_get_output(isa_pic)) {
cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD);
}
}
@@ -549,10 +550,12 @@ static void apic_deliver(DeviceState *dev, uint8_t dest, uint8_t dest_mode,
static bool apic_check_pic(APICCommonState *s)
{
- if (!apic_accept_pic_intr(&s->busdev.qdev) || !pic_get_output(isa_pic)) {
+ DeviceState *dev = (DeviceState *)s;
+
+ if (!apic_accept_pic_intr(dev) || !pic_get_output(isa_pic)) {
return false;
}
- apic_deliver_pic_intr(&s->busdev.qdev, 1);
+ apic_deliver_pic_intr(dev, 1);
return true;
}
diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c
index d595d63a51..0032b97c5f 100644
--- a/hw/intc/apic_common.c
+++ b/hw/intc/apic_common.c
@@ -369,6 +369,7 @@ static const VMStateDescription vmstate_apic_common_sipi = {
.name = "apic_sipi",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = apic_common_sipi_needed,
.fields = (VMStateField[]) {
VMSTATE_INT32(sipi_vector, APICCommonState),
VMSTATE_INT32(wait_for_sipi, APICCommonState),
@@ -408,12 +409,9 @@ static const VMStateDescription vmstate_apic_common = {
APICCommonState), /* open-coded timer state */
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_apic_common_sipi,
- .needed = apic_common_sipi_needed,
- },
- VMSTATE_END_OF_LIST()
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_apic_common_sipi,
+ NULL
}
};
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index c1d2e704ec..454bfd7df5 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -71,7 +71,7 @@ void gic_update(GICState *s)
|| !(s->cpu_ctlr[cpu] & (GICC_CTLR_EN_GRP0 | GICC_CTLR_EN_GRP1))) {
qemu_irq_lower(s->parent_irq[cpu]);
qemu_irq_lower(s->parent_fiq[cpu]);
- return;
+ continue;
}
best_prio = 0x100;
best_irq = 1023;
diff --git a/hw/intc/arm_gicv2m.c b/hw/intc/arm_gicv2m.c
new file mode 100644
index 0000000000..43d1976c49
--- /dev/null
+++ b/hw/intc/arm_gicv2m.c
@@ -0,0 +1,192 @@
+/*
+ * GICv2m extension for MSI/MSI-x support with a GICv2-based system
+ *
+ * Copyright (C) 2015 Linaro, All rights reserved.
+ *
+ * Author: Christoffer Dall <christoffer.dall@linaro.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements an emulated GICv2m widget as described in the ARM
+ * Server Base System Architecture (SBSA) specification Version 2.2
+ * (ARM-DEN-0029 v2.2) pages 35-39 without any optional implementation defined
+ * identification registers and with a single non-secure MSI register frame.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/pci/msi.h"
+
+#define TYPE_ARM_GICV2M "arm-gicv2m"
+#define ARM_GICV2M(obj) OBJECT_CHECK(ARMGICv2mState, (obj), TYPE_ARM_GICV2M)
+
+#define GICV2M_NUM_SPI_MAX 128
+
+#define V2M_MSI_TYPER 0x008
+#define V2M_MSI_SETSPI_NS 0x040
+#define V2M_MSI_IIDR 0xFCC
+#define V2M_IIDR0 0xFD0
+#define V2M_IIDR11 0xFFC
+
+#define PRODUCT_ID_QEMU 0x51 /* ASCII code Q */
+
+typedef struct ARMGICv2mState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion iomem;
+ qemu_irq spi[GICV2M_NUM_SPI_MAX];
+
+ uint32_t base_spi;
+ uint32_t num_spi;
+} ARMGICv2mState;
+
+static void gicv2m_set_irq(void *opaque, int irq)
+{
+ ARMGICv2mState *s = (ARMGICv2mState *)opaque;
+
+ qemu_irq_pulse(s->spi[irq]);
+}
+
+static uint64_t gicv2m_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ ARMGICv2mState *s = (ARMGICv2mState *)opaque;
+ uint32_t val;
+
+ if (size != 4) {
+ qemu_log_mask(LOG_GUEST_ERROR, "gicv2m_read: bad size %u\n", size);
+ return 0;
+ }
+
+ switch (offset) {
+ case V2M_MSI_TYPER:
+ val = (s->base_spi + 32) << 16;
+ val |= s->num_spi;
+ return val;
+ case V2M_MSI_IIDR:
+ /* We don't have any valid implementor so we leave that field as zero
+ * and we return 0 in the arch revision as per the spec.
+ */
+ return (PRODUCT_ID_QEMU << 20);
+ case V2M_IIDR0 ... V2M_IIDR11:
+ /* We do not implement any optional identification registers and the
+ * mandatory MSI_PIDR2 register reads as 0x0, so we capture all
+ * implementation defined registers here.
+ */
+ return 0;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "gicv2m_read: Bad offset %x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void gicv2m_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ ARMGICv2mState *s = (ARMGICv2mState *)opaque;
+
+ if (size != 2 && size != 4) {
+ qemu_log_mask(LOG_GUEST_ERROR, "gicv2m_write: bad size %u\n", size);
+ return;
+ }
+
+ switch (offset) {
+ case V2M_MSI_SETSPI_NS: {
+ int spi;
+
+ spi = (value & 0x3ff) - (s->base_spi + 32);
+ if (spi >= 0 && spi < s->num_spi) {
+ gicv2m_set_irq(s, spi);
+ }
+ return;
+ }
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "gicv2m_write: Bad offset %x\n", (int)offset);
+ }
+}
+
+static const MemoryRegionOps gicv2m_ops = {
+ .read = gicv2m_read,
+ .write = gicv2m_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void gicv2m_realize(DeviceState *dev, Error **errp)
+{
+ ARMGICv2mState *s = ARM_GICV2M(dev);
+ int i;
+
+ if (s->num_spi > GICV2M_NUM_SPI_MAX) {
+ error_setg(errp,
+ "requested %u SPIs exceeds GICv2m frame maximum %d",
+ s->num_spi, GICV2M_NUM_SPI_MAX);
+ return;
+ }
+
+ if (s->base_spi + 32 > 1020 - s->num_spi) {
+ error_setg(errp,
+ "requested base SPI %u+%u exceeds max. number 1020",
+ s->base_spi + 32, s->num_spi);
+ return;
+ }
+
+ for (i = 0; i < s->num_spi; i++) {
+ sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->spi[i]);
+ }
+
+ msi_supported = true;
+ kvm_gsi_direct_mapping = true;
+ kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled();
+}
+
+static void gicv2m_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ ARMGICv2mState *s = ARM_GICV2M(obj);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &gicv2m_ops, s,
+ "gicv2m", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static Property gicv2m_properties[] = {
+ DEFINE_PROP_UINT32("base-spi", ARMGICv2mState, base_spi, 0),
+ DEFINE_PROP_UINT32("num-spi", ARMGICv2mState, num_spi, 64),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void gicv2m_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->props = gicv2m_properties;
+ dc->realize = gicv2m_realize;
+}
+
+static const TypeInfo gicv2m_info = {
+ .name = TYPE_ARM_GICV2M,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(ARMGICv2mState),
+ .instance_init = gicv2m_init,
+ .class_init = gicv2m_class_init,
+};
+
+static void gicv2m_register_types(void)
+{
+ type_register_static(&gicv2m_info);
+}
+
+type_init(gicv2m_register_types)
diff --git a/hw/intc/exynos4210_gic.c b/hw/intc/exynos4210_gic.c
index 0590d5dfb8..b2a4950bc3 100644
--- a/hw/intc/exynos4210_gic.c
+++ b/hw/intc/exynos4210_gic.c
@@ -213,9 +213,6 @@ void exynos4210_init_board_irqs(Exynos4210Irq *s)
uint32_t grp, bit, irq_id, n;
for (n = 0; n < EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ; n++) {
- s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
- s->ext_combiner_irq[n]);
-
irq_id = 0;
if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 4) ||
n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4)) {
@@ -230,8 +227,10 @@ void exynos4210_init_board_irqs(Exynos4210Irq *s)
if (irq_id) {
s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
s->ext_gic_irq[irq_id-32]);
+ } else {
+ s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
+ s->ext_combiner_irq[n]);
}
-
}
for (; n < EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; n++) {
/* these IDs are passed to Internal Combiner and External GIC */
diff --git a/hw/isa/i82378.c b/hw/isa/i82378.c
index 9da9dfc4da..fcf97d86ac 100644
--- a/hw/isa/i82378.c
+++ b/hw/isa/i82378.c
@@ -65,7 +65,6 @@ static void i82378_realize(PCIDevice *pci, Error **errp)
uint8_t *pci_conf;
ISABus *isabus;
ISADevice *isa;
- qemu_irq *out0_irq;
pci_conf = pci->config;
pci_set_word(pci_conf + PCI_COMMAND,
@@ -88,11 +87,9 @@ static void i82378_realize(PCIDevice *pci, Error **errp)
All devices accept byte access only, except timer
*/
- /* Workaround the fact that i8259 is not qdev'ified... */
- out0_irq = qemu_allocate_irqs(i82378_request_out0_irq, s, 1);
-
/* 2 82C59 (irq) */
- s->i8259 = i8259_init(isabus, *out0_irq);
+ s->i8259 = i8259_init(isabus,
+ qemu_allocate_irq(i82378_request_out0_irq, s, 0));
isa_bus_irqs(isabus, s->i8259);
/* 1 82C54 (pit) */
diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c
index 825aa627df..43e0cd8ddd 100644
--- a/hw/isa/isa-bus.c
+++ b/hw/isa/isa-bus.c
@@ -21,6 +21,7 @@
#include "hw/sysbus.h"
#include "sysemu/sysemu.h"
#include "hw/isa/isa.h"
+#include "hw/i386/pc.h"
static ISABus *isabus;
@@ -178,6 +179,9 @@ ISADevice *isa_vga_init(ISABus *bus)
case VGA_VMWARE:
fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __func__);
return NULL;
+ case VGA_VIRTIO:
+ fprintf(stderr, "%s: virtio-vga: no PCI bus\n", __func__);
+ return NULL;
case VGA_NONE:
default:
return NULL;
@@ -267,3 +271,28 @@ MemoryRegion *isa_address_space_io(ISADevice *dev)
}
type_init(isabus_register_types)
+
+static void parallel_init(ISABus *bus, int index, CharDriverState *chr)
+{
+ DeviceState *dev;
+ ISADevice *isadev;
+
+ isadev = isa_create(bus, "isa-parallel");
+ dev = DEVICE(isadev);
+ qdev_prop_set_uint32(dev, "index", index);
+ qdev_prop_set_chr(dev, "chardev", chr);
+ qdev_init_nofail(dev);
+}
+
+void parallel_hds_isa_init(ISABus *bus, int n)
+{
+ int i;
+
+ assert(n <= MAX_PARALLEL_PORTS);
+
+ for (i = 0; i < n; i++) {
+ if (parallel_hds[i]) {
+ parallel_init(bus, i, parallel_hds[i]);
+ }
+ }
+}
diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index bc9afc60ab..b3e0b1fd52 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -360,11 +360,8 @@ static void ich9_set_sci(void *opaque, int irq_num, int level)
void ich9_lpc_pm_init(PCIDevice *lpc_pci)
{
ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci);
- qemu_irq *sci_irq;
-
- sci_irq = qemu_allocate_irqs(ich9_set_sci, lpc, 1);
- ich9_pm_init(lpc_pci, &lpc->pm, sci_irq[0]);
+ ich9_pm_init(lpc_pci, &lpc->pm, qemu_allocate_irq(ich9_set_sci, lpc, 0));
ich9_lpc_reset(&lpc->d.qdev);
}
@@ -410,12 +407,28 @@ static void ich9_lpc_rcba_update(ICH9LPCState *lpc, uint32_t rbca_old)
}
}
+/* config:GEN_PMCON* */
+static void
+ich9_lpc_pmcon_update(ICH9LPCState *lpc)
+{
+ uint16_t gen_pmcon_1 = pci_get_word(lpc->d.config + ICH9_LPC_GEN_PMCON_1);
+ uint16_t wmask;
+
+ if (gen_pmcon_1 & ICH9_LPC_GEN_PMCON_1_SMI_LOCK) {
+ wmask = pci_get_word(lpc->d.wmask + ICH9_LPC_GEN_PMCON_1);
+ wmask &= ~ICH9_LPC_GEN_PMCON_1_SMI_LOCK;
+ pci_set_word(lpc->d.wmask + ICH9_LPC_GEN_PMCON_1, wmask);
+ lpc->pm.smi_en_wmask &= ~1;
+ }
+}
+
static int ich9_lpc_post_load(void *opaque, int version_id)
{
ICH9LPCState *lpc = opaque;
ich9_lpc_pmbase_update(lpc);
ich9_lpc_rcba_update(lpc, 0 /* disabled ICH9_LPC_RBCA_EN */);
+ ich9_lpc_pmcon_update(lpc);
return 0;
}
@@ -438,6 +451,9 @@ static void ich9_lpc_config_write(PCIDevice *d,
if (ranges_overlap(addr, len, ICH9_LPC_PIRQE_ROUT, 4)) {
pci_bus_fire_intx_routing_notifier(lpc->d.bus);
}
+ if (ranges_overlap(addr, len, ICH9_LPC_GEN_PMCON_1, 8)) {
+ ich9_lpc_pmcon_update(lpc);
+ }
}
static void ich9_lpc_reset(DeviceState *qdev)
@@ -634,6 +650,7 @@ static const VMStateDescription vmstate_ich9_rst_cnt = {
.name = "ICH9LPC/rst_cnt",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = ich9_rst_cnt_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8(rst_cnt, ICH9LPCState),
VMSTATE_END_OF_LIST()
@@ -653,12 +670,9 @@ static const VMStateDescription vmstate_ich9_lpc = {
VMSTATE_UINT32(sci_level, ICH9LPCState),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_ich9_rst_cnt,
- .needed = ich9_rst_cnt_needed
- },
- { 0 }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_ich9_rst_cnt,
+ NULL
}
};
diff --git a/hw/lm32/lm32_boards.c b/hw/lm32/lm32_boards.c
index 14d0efcdd9..70f48d3b1d 100644
--- a/hw/lm32/lm32_boards.c
+++ b/hw/lm32/lm32_boards.c
@@ -78,7 +78,7 @@ static void lm32_evr_init(MachineState *machine)
DriveInfo *dinfo;
MemoryRegion *address_space_mem = get_system_memory();
MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
- qemu_irq *cpu_irq, irq[32];
+ qemu_irq irq[32];
ResetInfo *reset_info;
int i;
@@ -123,8 +123,7 @@ static void lm32_evr_init(MachineState *machine)
1, 2, 0x01, 0x7e, 0x43, 0x00, 0x555, 0x2aa, 1);
/* create irq lines */
- cpu_irq = qemu_allocate_irqs(cpu_irq_handler, cpu, 1);
- env->pic_state = lm32_pic_init(*cpu_irq);
+ env->pic_state = lm32_pic_init(qemu_allocate_irq(cpu_irq_handler, cpu, 0));
for (i = 0; i < 32; i++) {
irq[i] = qdev_get_gpio_in(env->pic_state, i);
}
@@ -173,7 +172,7 @@ static void lm32_uclinux_init(MachineState *machine)
DriveInfo *dinfo;
MemoryRegion *address_space_mem = get_system_memory();
MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
- qemu_irq *cpu_irq, irq[32];
+ qemu_irq irq[32];
HWSetup *hw;
ResetInfo *reset_info;
int i;
@@ -225,8 +224,7 @@ static void lm32_uclinux_init(MachineState *machine)
1, 2, 0x01, 0x7e, 0x43, 0x00, 0x555, 0x2aa, 1);
/* create irq lines */
- cpu_irq = qemu_allocate_irqs(cpu_irq_handler, env, 1);
- env->pic_state = lm32_pic_init(*cpu_irq);
+ env->pic_state = lm32_pic_init(qemu_allocate_irq(cpu_irq_handler, env, 0));
for (i = 0; i < 32; i++) {
irq[i] = qdev_get_gpio_in(env->pic_state, i);
}
diff --git a/hw/lm32/milkymist.c b/hw/lm32/milkymist.c
index e0cec7dc41..e755f5b24f 100644
--- a/hw/lm32/milkymist.c
+++ b/hw/lm32/milkymist.c
@@ -86,7 +86,7 @@ milkymist_init(MachineState *machine)
DriveInfo *dinfo;
MemoryRegion *address_space_mem = get_system_memory();
MemoryRegion *phys_sdram = g_new(MemoryRegion, 1);
- qemu_irq irq[32], *cpu_irq;
+ qemu_irq irq[32];
int i;
char *bios_filename;
ResetInfo *reset_info;
@@ -130,8 +130,7 @@ milkymist_init(MachineState *machine)
2, 0x00, 0x89, 0x00, 0x1d, 1);
/* create irq lines */
- cpu_irq = qemu_allocate_irqs(cpu_irq_handler, cpu, 1);
- env->pic_state = lm32_pic_init(*cpu_irq);
+ env->pic_state = lm32_pic_init(qemu_allocate_irq(cpu_irq_handler, cpu, 0));
for (i = 0; i < 32; i++) {
irq[i] = qdev_get_gpio_in(env->pic_state, i);
}
diff --git a/hw/mips/Makefile.objs b/hw/mips/Makefile.objs
index 0a652f8521..9633f3a57d 100644
--- a/hw/mips/Makefile.objs
+++ b/hw/mips/Makefile.objs
@@ -1,4 +1,5 @@
-obj-y += mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o
+obj-y += mips_r4k.o mips_malta.o mips_mipssim.o
obj-y += addr.o cputimer.o mips_int.o
+obj-$(CONFIG_JAZZ) += mips_jazz.o
obj-$(CONFIG_FULONG) += mips_fulong2e.o
obj-y += gt64xxx_pci.o
diff --git a/hw/mips/mips_jazz.c b/hw/mips/mips_jazz.c
index 2c153e092f..9d60633efb 100644
--- a/hw/mips/mips_jazz.c
+++ b/hw/mips/mips_jazz.c
@@ -135,16 +135,16 @@ static void mips_jazz_init(MachineState *machine,
MIPSCPU *cpu;
CPUClass *cc;
CPUMIPSState *env;
- qemu_irq *rc4030, *i8259;
+ qemu_irq *i8259;
rc4030_dma *dmas;
- void* rc4030_opaque;
+ MemoryRegion *rc4030_dma_mr;
MemoryRegion *isa_mem = g_new(MemoryRegion, 1);
MemoryRegion *isa_io = g_new(MemoryRegion, 1);
MemoryRegion *rtc = g_new(MemoryRegion, 1);
MemoryRegion *i8042 = g_new(MemoryRegion, 1);
MemoryRegion *dma_dummy = g_new(MemoryRegion, 1);
NICInfo *nd;
- DeviceState *dev;
+ DeviceState *dev, *rc4030;
SysBusDevice *sysbus;
ISABus *isa_bus;
ISADevice *pit;
@@ -157,12 +157,7 @@ static void mips_jazz_init(MachineState *machine,
/* init CPUs */
if (cpu_model == NULL) {
-#ifdef TARGET_MIPS64
cpu_model = "R4000";
-#else
- /* FIXME: All wrong, this maybe should be R3000 for the older JAZZs. */
- cpu_model = "24Kf";
-#endif
}
cpu = cpu_mips_init(cpu_model);
if (cpu == NULL) {
@@ -218,8 +213,14 @@ static void mips_jazz_init(MachineState *machine,
cpu_mips_clock_init(env);
/* Chipset */
- rc4030_opaque = rc4030_init(env->irq[6], env->irq[3], &rc4030, &dmas,
- address_space);
+ rc4030 = rc4030_init(&dmas, &rc4030_dma_mr);
+ sysbus = SYS_BUS_DEVICE(rc4030);
+ sysbus_connect_irq(sysbus, 0, env->irq[6]);
+ sysbus_connect_irq(sysbus, 1, env->irq[3]);
+ memory_region_add_subregion(address_space, 0x80000000,
+ sysbus_mmio_get_region(sysbus, 0));
+ memory_region_add_subregion(address_space, 0xf0000000,
+ sysbus_mmio_get_region(sysbus, 1));
memory_region_init_io(dma_dummy, NULL, &dma_dummy_ops, NULL, "dummy_dma", 0x1000);
memory_region_add_subregion(address_space, 0x8000d000, dma_dummy);
@@ -246,7 +247,7 @@ static void mips_jazz_init(MachineState *machine,
sysbus = SYS_BUS_DEVICE(dev);
sysbus_mmio_map(sysbus, 0, 0x60080000);
sysbus_mmio_map(sysbus, 1, 0x40000000);
- sysbus_connect_irq(sysbus, 0, rc4030[3]);
+ sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(rc4030, 3));
{
/* Simple ROM, so user doesn't have to provide one */
MemoryRegion *rom_mr = g_new(MemoryRegion, 1);
@@ -272,8 +273,17 @@ static void mips_jazz_init(MachineState *machine,
if (!nd->model)
nd->model = g_strdup("dp83932");
if (strcmp(nd->model, "dp83932") == 0) {
- dp83932_init(nd, 0x80001000, 2, get_system_memory(), rc4030[4],
- rc4030_opaque, rc4030_dma_memory_rw);
+ qemu_check_nic_model(nd, "dp83932");
+
+ dev = qdev_create(NULL, "dp8393x");
+ qdev_set_nic_properties(dev, nd);
+ qdev_prop_set_uint8(dev, "it_shift", 2);
+ qdev_prop_set_ptr(dev, "dma_mr", rc4030_dma_mr);
+ qdev_init_nofail(dev);
+ sysbus = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(sysbus, 0, 0x80001000);
+ sysbus_mmio_map(sysbus, 1, 0x8000b000);
+ sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(rc4030, 4));
break;
} else if (is_help_option(nd->model)) {
fprintf(stderr, "qemu: Supported NICs: dp83932\n");
@@ -287,7 +297,7 @@ static void mips_jazz_init(MachineState *machine,
/* SCSI adapter */
esp_init(0x80002000, 0,
rc4030_dma_read, rc4030_dma_write, dmas[0],
- rc4030[5], &esp_reset, &dma_enable);
+ qdev_get_gpio_in(rc4030, 5), &esp_reset, &dma_enable);
/* Floppy */
if (drive_get_max_bus(IF_FLOPPY) >= MAX_FD) {
@@ -297,7 +307,7 @@ static void mips_jazz_init(MachineState *machine,
for (n = 0; n < MAX_FD; n++) {
fds[n] = drive_get(IF_FLOPPY, 0, n);
}
- fdctrl_init_sysbus(rc4030[1], 0, 0x80003000, fds);
+ fdctrl_init_sysbus(qdev_get_gpio_in(rc4030, 1), 0, 0x80003000, fds);
/* Real time clock */
rtc_init(isa_bus, 1980, NULL);
@@ -305,23 +315,26 @@ static void mips_jazz_init(MachineState *machine,
memory_region_add_subregion(address_space, 0x80004000, rtc);
/* Keyboard (i8042) */
- i8042_mm_init(rc4030[6], rc4030[7], i8042, 0x1000, 0x1);
+ i8042_mm_init(qdev_get_gpio_in(rc4030, 6), qdev_get_gpio_in(rc4030, 7),
+ i8042, 0x1000, 0x1);
memory_region_add_subregion(address_space, 0x80005000, i8042);
/* Serial ports */
if (serial_hds[0]) {
- serial_mm_init(address_space, 0x80006000, 0, rc4030[8], 8000000/16,
+ serial_mm_init(address_space, 0x80006000, 0,
+ qdev_get_gpio_in(rc4030, 8), 8000000/16,
serial_hds[0], DEVICE_NATIVE_ENDIAN);
}
if (serial_hds[1]) {
- serial_mm_init(address_space, 0x80007000, 0, rc4030[9], 8000000/16,
+ serial_mm_init(address_space, 0x80007000, 0,
+ qdev_get_gpio_in(rc4030, 9), 8000000/16,
serial_hds[1], DEVICE_NATIVE_ENDIAN);
}
/* Parallel port */
if (parallel_hds[0])
- parallel_mm_init(address_space, 0x80008000, 0, rc4030[0],
- parallel_hds[0]);
+ parallel_mm_init(address_space, 0x80008000, 0,
+ qdev_get_gpio_in(rc4030, 0), parallel_hds[0]);
/* FIXME: missing Jazz sound at 0x8000c000, rc4030[2] */
diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c
index 5140882c00..786a8f0638 100644
--- a/hw/mips/mips_malta.c
+++ b/hw/mips/mips_malta.c
@@ -97,7 +97,7 @@ typedef struct {
static ISADevice *pit;
static struct _loaderparams {
- int ram_size;
+ int ram_size, ram_low_size;
const char *kernel_filename;
const char *kernel_cmdline;
const char *initrd_filename;
@@ -641,8 +641,8 @@ static void write_bootloader (CPUMIPSState *env, uint8_t *base,
stl_p(p++, 0x34a50000 | (ENVP_ADDR & 0xffff)); /* ori a1, a1, low(ENVP_ADDR) */
stl_p(p++, 0x3c060000 | (((ENVP_ADDR + 8) >> 16) & 0xffff)); /* lui a2, high(ENVP_ADDR + 8) */
stl_p(p++, 0x34c60000 | ((ENVP_ADDR + 8) & 0xffff)); /* ori a2, a2, low(ENVP_ADDR + 8) */
- stl_p(p++, 0x3c070000 | (loaderparams.ram_size >> 16)); /* lui a3, high(ram_size) */
- stl_p(p++, 0x34e70000 | (loaderparams.ram_size & 0xffff)); /* ori a3, a3, low(ram_size) */
+ stl_p(p++, 0x3c070000 | (loaderparams.ram_low_size >> 16)); /* lui a3, high(ram_low_size) */
+ stl_p(p++, 0x34e70000 | (loaderparams.ram_low_size & 0xffff)); /* ori a3, a3, low(ram_low_size) */
/* Load BAR registers as done by YAMON */
stl_p(p++, 0x3c09b400); /* lui t1, 0xb400 */
@@ -851,8 +851,10 @@ static int64_t load_kernel (void)
}
prom_set(prom_buf, prom_index++, "memsize");
- prom_set(prom_buf, prom_index++, "%i",
- MIN(loaderparams.ram_size, 256 << 20));
+ prom_set(prom_buf, prom_index++, "%u", loaderparams.ram_low_size);
+
+ prom_set(prom_buf, prom_index++, "ememsize");
+ prom_set(prom_buf, prom_index++, "%u", loaderparams.ram_size);
prom_set(prom_buf, prom_index++, "modetty0");
prom_set(prom_buf, prom_index++, "38400n8r");
@@ -1054,7 +1056,8 @@ void mips_malta_init(MachineState *machine)
}
/* Write a small bootloader to the flash location. */
- loaderparams.ram_size = ram_low_size;
+ loaderparams.ram_size = ram_size;
+ loaderparams.ram_low_size = ram_low_size;
loaderparams.kernel_filename = kernel_filename;
loaderparams.kernel_cmdline = kernel_cmdline;
loaderparams.initrd_filename = initrd_filename;
diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c
index 063ad80412..e9037b0c39 100644
--- a/hw/misc/macio/macio.c
+++ b/hw/misc/macio/macio.c
@@ -126,17 +126,18 @@ static void macio_bar_setup(MacIOState *macio_state)
}
}
-static int macio_common_initfn(PCIDevice *d)
+static void macio_common_realize(PCIDevice *d, Error **errp)
{
MacIOState *s = MACIO(d);
SysBusDevice *sysbus_dev;
- int ret;
+ Error *err = NULL;
d->config[0x3d] = 0x01; // interrupt on pin 1
- ret = qdev_init(DEVICE(&s->cuda));
- if (ret < 0) {
- return ret;
+ object_property_set_bool(OBJECT(&s->cuda), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
}
sysbus_dev = SYS_BUS_DEVICE(&s->cuda);
memory_region_add_subregion(&s->bar, 0x16000,
@@ -144,12 +145,11 @@ static int macio_common_initfn(PCIDevice *d)
macio_bar_setup(s);
pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar);
-
- return 0;
}
-static int macio_initfn_ide(MacIOState *s, MACIOIDEState *ide, qemu_irq irq0,
- qemu_irq irq1, int dmaid)
+static void macio_realize_ide(MacIOState *s, MACIOIDEState *ide,
+ qemu_irq irq0, qemu_irq irq1, int dmaid,
+ Error **errp)
{
SysBusDevice *sysbus_dev;
@@ -157,27 +157,31 @@ static int macio_initfn_ide(MacIOState *s, MACIOIDEState *ide, qemu_irq irq0,
sysbus_connect_irq(sysbus_dev, 0, irq0);
sysbus_connect_irq(sysbus_dev, 1, irq1);
macio_ide_register_dma(ide, s->dbdma, dmaid);
- return qdev_init(DEVICE(ide));
+ object_property_set_bool(OBJECT(ide), true, "realized", errp);
}
-static int macio_oldworld_initfn(PCIDevice *d)
+static void macio_oldworld_realize(PCIDevice *d, Error **errp)
{
MacIOState *s = MACIO(d);
OldWorldMacIOState *os = OLDWORLD_MACIO(d);
+ Error *err = NULL;
SysBusDevice *sysbus_dev;
int i;
int cur_irq = 0;
- int ret = macio_common_initfn(d);
- if (ret < 0) {
- return ret;
+
+ macio_common_realize(d, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
}
sysbus_dev = SYS_BUS_DEVICE(&s->cuda);
sysbus_connect_irq(sysbus_dev, 0, os->irqs[cur_irq++]);
- ret = qdev_init(DEVICE(&os->nvram));
- if (ret < 0) {
- return ret;
+ object_property_set_bool(OBJECT(&os->nvram), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
}
sysbus_dev = SYS_BUS_DEVICE(&os->nvram);
memory_region_add_subregion(&s->bar, 0x60000,
@@ -194,13 +198,12 @@ static int macio_oldworld_initfn(PCIDevice *d)
qemu_irq irq0 = os->irqs[cur_irq++];
qemu_irq irq1 = os->irqs[cur_irq++];
- ret = macio_initfn_ide(s, &os->ide[i], irq0, irq1, 0x16 + (i * 4));
- if (ret < 0) {
- return ret;
+ macio_realize_ide(s, &os->ide[i], irq0, irq1, 0x16 + (i * 4), &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
}
}
-
- return 0;
}
static void macio_init_ide(MacIOState *s, MACIOIDEState *ide, size_t ide_size,
@@ -268,17 +271,20 @@ static const MemoryRegionOps timer_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
-static int macio_newworld_initfn(PCIDevice *d)
+static void macio_newworld_realize(PCIDevice *d, Error **errp)
{
MacIOState *s = MACIO(d);
NewWorldMacIOState *ns = NEWWORLD_MACIO(d);
+ Error *err = NULL;
SysBusDevice *sysbus_dev;
MemoryRegion *timer_memory = NULL;
int i;
int cur_irq = 0;
- int ret = macio_common_initfn(d);
- if (ret < 0) {
- return ret;
+
+ macio_common_realize(d, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
}
sysbus_dev = SYS_BUS_DEVICE(&s->cuda);
@@ -294,9 +300,10 @@ static int macio_newworld_initfn(PCIDevice *d)
qemu_irq irq0 = ns->irqs[cur_irq++];
qemu_irq irq1 = ns->irqs[cur_irq++];
- ret = macio_initfn_ide(s, &ns->ide[i], irq0, irq1, 0x16 + (i * 4));
- if (ret < 0) {
- return ret;
+ macio_realize_ide(s, &ns->ide[i], irq0, irq1, 0x16 + (i * 4), &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
}
}
@@ -305,8 +312,6 @@ static int macio_newworld_initfn(PCIDevice *d)
memory_region_init_io(timer_memory, OBJECT(s), &timer_ops, NULL, "timer",
0x1000);
memory_region_add_subregion(&s->bar, 0x15000, timer_memory);
-
- return 0;
}
static void macio_newworld_init(Object *obj)
@@ -352,7 +357,7 @@ static void macio_oldworld_class_init(ObjectClass *oc, void *data)
PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc);
DeviceClass *dc = DEVICE_CLASS(oc);
- pdc->init = macio_oldworld_initfn;
+ pdc->realize = macio_oldworld_realize;
pdc->device_id = PCI_DEVICE_ID_APPLE_343S1201;
dc->vmsd = &vmstate_macio_oldworld;
}
@@ -372,7 +377,7 @@ static void macio_newworld_class_init(ObjectClass *oc, void *data)
PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc);
DeviceClass *dc = DEVICE_CLASS(oc);
- pdc->init = macio_newworld_initfn;
+ pdc->realize = macio_newworld_realize;
pdc->device_id = PCI_DEVICE_ID_APPLE_UNI_N_KEYL;
dc->vmsd = &vmstate_macio_newworld;
}
diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
index 7b91c4e51d..98801739ef 100644
--- a/hw/net/Makefile.objs
+++ b/hw/net/Makefile.objs
@@ -39,3 +39,4 @@ obj-$(CONFIG_ETSEC) += fsl_etsec/etsec.o fsl_etsec/registers.o \
common-obj-$(CONFIG_ROCKER) += rocker/rocker.o rocker/rocker_fp.o \
rocker/rocker_desc.o rocker/rocker_world.o \
rocker/rocker_of_dpa.o
+obj-$(call lnot,$(CONFIG_ROCKER)) += rocker/qmp-norocker.o
diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c
index dafe91421b..494a346cf6 100644
--- a/hw/net/cadence_gem.c
+++ b/hw/net/cadence_gem.c
@@ -155,7 +155,7 @@
#define GEM_NWCFG_BCAST_REJ 0x00000020 /* Reject broadcast packets */
#define GEM_NWCFG_PROMISC 0x00000010 /* Accept all packets */
-#define GEM_DMACFG_RBUFSZ_M 0x007F0000 /* DMA RX Buffer Size mask */
+#define GEM_DMACFG_RBUFSZ_M 0x00FF0000 /* DMA RX Buffer Size mask */
#define GEM_DMACFG_RBUFSZ_S 16 /* DMA RX Buffer Size shift */
#define GEM_DMACFG_RBUFSZ_MUL 64 /* DMA RX Buffer Size multiplier */
#define GEM_DMACFG_TXCSUM_OFFL 0x00000800 /* Transmit checksum offload */
diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c
index 7ce13d2b46..cd889bce86 100644
--- a/hw/net/dp8393x.c
+++ b/hw/net/dp8393x.c
@@ -17,20 +17,15 @@
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
-#include "hw/hw.h"
-#include "qemu/timer.h"
+#include "hw/sysbus.h"
+#include "hw/devices.h"
#include "net/net.h"
-#include "hw/mips/mips.h"
+#include "qemu/timer.h"
+#include <zlib.h>
//#define DEBUG_SONIC
-/* Calculate CRCs properly on Rx packets */
-#define SONIC_CALCULATE_RXCRC
-
-#if defined(SONIC_CALCULATE_RXCRC)
-/* For crc32 */
-#include <zlib.h>
-#endif
+#define SONIC_PROM_SIZE 0x1000
#ifdef DEBUG_SONIC
#define DPRINTF(fmt, ...) \
@@ -145,9 +140,14 @@ do { printf("sonic ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
#define SONIC_ISR_PINT 0x0800
#define SONIC_ISR_LCD 0x1000
+#define TYPE_DP8393X "dp8393x"
+#define DP8393X(obj) OBJECT_CHECK(dp8393xState, (obj), TYPE_DP8393X)
+
typedef struct dp8393xState {
+ SysBusDevice parent_obj;
+
/* Hardware */
- int it_shift;
+ uint8_t it_shift;
qemu_irq irq;
#ifdef DEBUG_SONIC
int irq_level;
@@ -156,8 +156,8 @@ typedef struct dp8393xState {
int64_t wt_last_update;
NICConf conf;
NICState *nic;
- MemoryRegion *address_space;
MemoryRegion mmio;
+ MemoryRegion prom;
/* Registers */
uint8_t cam[16][6];
@@ -168,8 +168,8 @@ typedef struct dp8393xState {
int loopback_packet;
/* Memory access */
- void (*memory_rw)(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write);
- void* mem_opaque;
+ void *dma_mr;
+ AddressSpace as;
} dp8393xState;
static void dp8393x_update_irq(dp8393xState *s)
@@ -190,7 +190,7 @@ static void dp8393x_update_irq(dp8393xState *s)
qemu_set_irq(s->irq, level);
}
-static void do_load_cam(dp8393xState *s)
+static void dp8393x_do_load_cam(dp8393xState *s)
{
uint16_t data[8];
int width, size;
@@ -201,9 +201,9 @@ static void do_load_cam(dp8393xState *s)
while (s->regs[SONIC_CDC] & 0x1f) {
/* Fill current entry */
- s->memory_rw(s->mem_opaque,
+ address_space_rw(&s->as,
(s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP],
- (uint8_t *)data, size, 0);
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
s->cam[index][0] = data[1 * width] & 0xff;
s->cam[index][1] = data[1 * width] >> 8;
s->cam[index][2] = data[2 * width] & 0xff;
@@ -220,9 +220,9 @@ static void do_load_cam(dp8393xState *s)
}
/* Read CAM enable */
- s->memory_rw(s->mem_opaque,
+ address_space_rw(&s->as,
(s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP],
- (uint8_t *)data, size, 0);
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
s->regs[SONIC_CE] = data[0 * width];
DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]);
@@ -232,7 +232,7 @@ static void do_load_cam(dp8393xState *s)
dp8393x_update_irq(s);
}
-static void do_read_rra(dp8393xState *s)
+static void dp8393x_do_read_rra(dp8393xState *s)
{
uint16_t data[8];
int width, size;
@@ -240,9 +240,9 @@ static void do_read_rra(dp8393xState *s)
/* Read memory */
width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
size = sizeof(uint16_t) * 4 * width;
- s->memory_rw(s->mem_opaque,
+ address_space_rw(&s->as,
(s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP],
- (uint8_t *)data, size, 0);
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
/* Update SONIC registers */
s->regs[SONIC_CRBA0] = data[0 * width];
@@ -272,7 +272,7 @@ static void do_read_rra(dp8393xState *s)
s->regs[SONIC_CR] &= ~SONIC_CR_RRRA;
}
-static void do_software_reset(dp8393xState *s)
+static void dp8393x_do_software_reset(dp8393xState *s)
{
timer_del(s->watchdog);
@@ -280,7 +280,7 @@ static void do_software_reset(dp8393xState *s)
s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS;
}
-static void set_next_tick(dp8393xState *s)
+static void dp8393x_set_next_tick(dp8393xState *s)
{
uint32_t ticks;
int64_t delay;
@@ -296,7 +296,7 @@ static void set_next_tick(dp8393xState *s)
timer_mod(s->watchdog, s->wt_last_update + delay);
}
-static void update_wt_regs(dp8393xState *s)
+static void dp8393x_update_wt_regs(dp8393xState *s)
{
int64_t elapsed;
uint32_t val;
@@ -311,33 +311,33 @@ static void update_wt_regs(dp8393xState *s)
val -= elapsed / 5000000;
s->regs[SONIC_WT1] = (val >> 16) & 0xffff;
s->regs[SONIC_WT0] = (val >> 0) & 0xffff;
- set_next_tick(s);
+ dp8393x_set_next_tick(s);
}
-static void do_start_timer(dp8393xState *s)
+static void dp8393x_do_start_timer(dp8393xState *s)
{
s->regs[SONIC_CR] &= ~SONIC_CR_STP;
- set_next_tick(s);
+ dp8393x_set_next_tick(s);
}
-static void do_stop_timer(dp8393xState *s)
+static void dp8393x_do_stop_timer(dp8393xState *s)
{
s->regs[SONIC_CR] &= ~SONIC_CR_ST;
- update_wt_regs(s);
+ dp8393x_update_wt_regs(s);
}
-static void do_receiver_enable(dp8393xState *s)
+static void dp8393x_do_receiver_enable(dp8393xState *s)
{
s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS;
}
-static void do_receiver_disable(dp8393xState *s)
+static void dp8393x_do_receiver_disable(dp8393xState *s)
{
s->regs[SONIC_CR] &= ~SONIC_CR_RXEN;
}
-static void do_transmit_packets(dp8393xState *s)
+static void dp8393x_do_transmit_packets(dp8393xState *s)
{
NetClientState *nc = qemu_get_queue(s->nic);
uint16_t data[12];
@@ -353,9 +353,9 @@ static void do_transmit_packets(dp8393xState *s)
(s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_CTDA]);
size = sizeof(uint16_t) * 6 * width;
s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA];
- s->memory_rw(s->mem_opaque,
+ address_space_rw(&s->as,
((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * width,
- (uint8_t *)data, size, 0);
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
tx_len = 0;
/* Update registers */
@@ -379,18 +379,18 @@ static void do_transmit_packets(dp8393xState *s)
if (tx_len + len > sizeof(s->tx_buffer)) {
len = sizeof(s->tx_buffer) - tx_len;
}
- s->memory_rw(s->mem_opaque,
+ address_space_rw(&s->as,
(s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0],
- &s->tx_buffer[tx_len], len, 0);
+ MEMTXATTRS_UNSPECIFIED, &s->tx_buffer[tx_len], len, 0);
tx_len += len;
i++;
if (i != s->regs[SONIC_TFC]) {
/* Read next fragment details */
size = sizeof(uint16_t) * 3 * width;
- s->memory_rw(s->mem_opaque,
+ address_space_rw(&s->as,
((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * i) * width,
- (uint8_t *)data, size, 0);
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
s->regs[SONIC_TSA0] = data[0 * width];
s->regs[SONIC_TSA1] = data[1 * width];
s->regs[SONIC_TFS] = data[2 * width];
@@ -422,16 +422,16 @@ static void do_transmit_packets(dp8393xState *s)
/* Write status */
data[0 * width] = s->regs[SONIC_TCR] & 0x0fff; /* status */
size = sizeof(uint16_t) * width;
- s->memory_rw(s->mem_opaque,
+ address_space_rw(&s->as,
(s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA],
- (uint8_t *)data, size, 1);
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1);
if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) {
/* Read footer of packet */
size = sizeof(uint16_t) * width;
- s->memory_rw(s->mem_opaque,
+ address_space_rw(&s->as,
((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * s->regs[SONIC_TFC]) * width,
- (uint8_t *)data, size, 0);
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
s->regs[SONIC_CTDA] = data[0 * width] & ~0x1;
if (data[0 * width] & 0x1) {
/* EOL detected */
@@ -446,12 +446,12 @@ static void do_transmit_packets(dp8393xState *s)
dp8393x_update_irq(s);
}
-static void do_halt_transmission(dp8393xState *s)
+static void dp8393x_do_halt_transmission(dp8393xState *s)
{
/* Nothing to do */
}
-static void do_command(dp8393xState *s, uint16_t command)
+static void dp8393x_do_command(dp8393xState *s, uint16_t command)
{
if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) {
s->regs[SONIC_CR] &= ~SONIC_CR_RST;
@@ -461,34 +461,36 @@ static void do_command(dp8393xState *s, uint16_t command)
s->regs[SONIC_CR] |= (command & SONIC_CR_MASK);
if (command & SONIC_CR_HTX)
- do_halt_transmission(s);
+ dp8393x_do_halt_transmission(s);
if (command & SONIC_CR_TXP)
- do_transmit_packets(s);
+ dp8393x_do_transmit_packets(s);
if (command & SONIC_CR_RXDIS)
- do_receiver_disable(s);
+ dp8393x_do_receiver_disable(s);
if (command & SONIC_CR_RXEN)
- do_receiver_enable(s);
+ dp8393x_do_receiver_enable(s);
if (command & SONIC_CR_STP)
- do_stop_timer(s);
+ dp8393x_do_stop_timer(s);
if (command & SONIC_CR_ST)
- do_start_timer(s);
+ dp8393x_do_start_timer(s);
if (command & SONIC_CR_RST)
- do_software_reset(s);
+ dp8393x_do_software_reset(s);
if (command & SONIC_CR_RRRA)
- do_read_rra(s);
+ dp8393x_do_read_rra(s);
if (command & SONIC_CR_LCAM)
- do_load_cam(s);
+ dp8393x_do_load_cam(s);
}
-static uint16_t read_register(dp8393xState *s, int reg)
+static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size)
{
+ dp8393xState *s = opaque;
+ int reg = addr >> s->it_shift;
uint16_t val = 0;
switch (reg) {
/* Update data before reading it */
case SONIC_WT0:
case SONIC_WT1:
- update_wt_regs(s);
+ dp8393x_update_wt_regs(s);
val = s->regs[reg];
break;
/* Accept read to some registers only when in reset mode */
@@ -510,14 +512,18 @@ static uint16_t read_register(dp8393xState *s, int reg)
return val;
}
-static void write_register(dp8393xState *s, int reg, uint16_t val)
+static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned int size)
{
- DPRINTF("write 0x%04x to reg %s\n", val, reg_names[reg]);
+ dp8393xState *s = opaque;
+ int reg = addr >> s->it_shift;
+
+ DPRINTF("write 0x%04x to reg %s\n", (uint16_t)data, reg_names[reg]);
switch (reg) {
/* Command register */
case SONIC_CR:
- do_command(s, val);
+ dp8393x_do_command(s, data);
break;
/* Prevent write to read-only registers */
case SONIC_CAP2:
@@ -530,37 +536,37 @@ static void write_register(dp8393xState *s, int reg, uint16_t val)
/* Accept write to some registers only when in reset mode */
case SONIC_DCR:
if (s->regs[SONIC_CR] & SONIC_CR_RST) {
- s->regs[reg] = val & 0xbfff;
+ s->regs[reg] = data & 0xbfff;
} else {
DPRINTF("writing to DCR invalid\n");
}
break;
case SONIC_DCR2:
if (s->regs[SONIC_CR] & SONIC_CR_RST) {
- s->regs[reg] = val & 0xf017;
+ s->regs[reg] = data & 0xf017;
} else {
DPRINTF("writing to DCR2 invalid\n");
}
break;
/* 12 lower bytes are Read Only */
case SONIC_TCR:
- s->regs[reg] = val & 0xf000;
+ s->regs[reg] = data & 0xf000;
break;
/* 9 lower bytes are Read Only */
case SONIC_RCR:
- s->regs[reg] = val & 0xffe0;
+ s->regs[reg] = data & 0xffe0;
break;
/* Ignore most significant bit */
case SONIC_IMR:
- s->regs[reg] = val & 0x7fff;
+ s->regs[reg] = data & 0x7fff;
dp8393x_update_irq(s);
break;
/* Clear bits by writing 1 to them */
case SONIC_ISR:
- val &= s->regs[reg];
- s->regs[reg] &= ~val;
- if (val & SONIC_ISR_RBE) {
- do_read_rra(s);
+ data &= s->regs[reg];
+ s->regs[reg] &= ~data;
+ if (data & SONIC_ISR_RBE) {
+ dp8393x_do_read_rra(s);
}
dp8393x_update_irq(s);
break;
@@ -569,24 +575,32 @@ static void write_register(dp8393xState *s, int reg, uint16_t val)
case SONIC_REA:
case SONIC_RRP:
case SONIC_RWP:
- s->regs[reg] = val & 0xfffe;
+ s->regs[reg] = data & 0xfffe;
break;
/* Invert written value for some registers */
case SONIC_CRCT:
case SONIC_FAET:
case SONIC_MPT:
- s->regs[reg] = val ^ 0xffff;
+ s->regs[reg] = data ^ 0xffff;
break;
/* All other registers have no special contrainst */
default:
- s->regs[reg] = val;
+ s->regs[reg] = data;
}
if (reg == SONIC_WT0 || reg == SONIC_WT1) {
- set_next_tick(s);
+ dp8393x_set_next_tick(s);
}
}
+static const MemoryRegionOps dp8393x_ops = {
+ .read = dp8393x_read,
+ .write = dp8393x_write,
+ .impl.min_access_size = 2,
+ .impl.max_access_size = 2,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
static void dp8393x_watchdog(void *opaque)
{
dp8393xState *s = opaque;
@@ -597,84 +611,14 @@ static void dp8393x_watchdog(void *opaque)
s->regs[SONIC_WT1] = 0xffff;
s->regs[SONIC_WT0] = 0xffff;
- set_next_tick(s);
+ dp8393x_set_next_tick(s);
/* Signal underflow */
s->regs[SONIC_ISR] |= SONIC_ISR_TC;
dp8393x_update_irq(s);
}
-static uint32_t dp8393x_readw(void *opaque, hwaddr addr)
-{
- dp8393xState *s = opaque;
- int reg;
-
- if ((addr & ((1 << s->it_shift) - 1)) != 0) {
- return 0;
- }
-
- reg = addr >> s->it_shift;
- return read_register(s, reg);
-}
-
-static uint32_t dp8393x_readb(void *opaque, hwaddr addr)
-{
- uint16_t v = dp8393x_readw(opaque, addr & ~0x1);
- return (v >> (8 * (addr & 0x1))) & 0xff;
-}
-
-static uint32_t dp8393x_readl(void *opaque, hwaddr addr)
-{
- uint32_t v;
- v = dp8393x_readw(opaque, addr);
- v |= dp8393x_readw(opaque, addr + 2) << 16;
- return v;
-}
-
-static void dp8393x_writew(void *opaque, hwaddr addr, uint32_t val)
-{
- dp8393xState *s = opaque;
- int reg;
-
- if ((addr & ((1 << s->it_shift) - 1)) != 0) {
- return;
- }
-
- reg = addr >> s->it_shift;
-
- write_register(s, reg, (uint16_t)val);
-}
-
-static void dp8393x_writeb(void *opaque, hwaddr addr, uint32_t val)
-{
- uint16_t old_val = dp8393x_readw(opaque, addr & ~0x1);
-
- switch (addr & 3) {
- case 0:
- val = val | (old_val & 0xff00);
- break;
- case 1:
- val = (val << 8) | (old_val & 0x00ff);
- break;
- }
- dp8393x_writew(opaque, addr & ~0x1, val);
-}
-
-static void dp8393x_writel(void *opaque, hwaddr addr, uint32_t val)
-{
- dp8393x_writew(opaque, addr, val & 0xffff);
- dp8393x_writew(opaque, addr + 2, (val >> 16) & 0xffff);
-}
-
-static const MemoryRegionOps dp8393x_ops = {
- .old_mmio = {
- .read = { dp8393x_readb, dp8393x_readw, dp8393x_readl, },
- .write = { dp8393x_writeb, dp8393x_writew, dp8393x_writel, },
- },
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int nic_can_receive(NetClientState *nc)
+static int dp8393x_can_receive(NetClientState *nc)
{
dp8393xState *s = qemu_get_nic_opaque(nc);
@@ -685,7 +629,8 @@ static int nic_can_receive(NetClientState *nc)
return 1;
}
-static int receive_filter(dp8393xState *s, const uint8_t * buf, int size)
+static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf,
+ int size)
{
static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
int i;
@@ -723,7 +668,8 @@ static int receive_filter(dp8393xState *s, const uint8_t * buf, int size)
return -1;
}
-static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
+static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf,
+ size_t size)
{
dp8393xState *s = qemu_get_nic_opaque(nc);
uint16_t data[10];
@@ -737,7 +683,7 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER |
SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC);
- packet_type = receive_filter(s, buf, size);
+ packet_type = dp8393x_receive_filter(s, buf, size);
if (packet_type < 0) {
DPRINTF("packet not for netcard\n");
return -1;
@@ -750,7 +696,8 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
/* Are we still in resource exhaustion? */
size = sizeof(uint16_t) * 1 * width;
address = ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width;
- s->memory_rw(s->mem_opaque, address, (uint8_t*)data, size, 0);
+ address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED,
+ (uint8_t *)data, size, 0);
if (data[0 * width] & 0x1) {
/* Still EOL ; stop reception */
return -1;
@@ -764,18 +711,16 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0];
/* Calculate the ethernet checksum */
-#ifdef SONIC_CALCULATE_RXCRC
checksum = cpu_to_le32(crc32(0, buf, rx_len));
-#else
- checksum = 0;
-#endif
/* Put packet into RBA */
DPRINTF("Receive packet at %08x\n", (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]);
address = (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0];
- s->memory_rw(s->mem_opaque, address, (uint8_t*)buf, rx_len, 1);
+ address_space_rw(&s->as, address,
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)buf, rx_len, 1);
address += rx_len;
- s->memory_rw(s->mem_opaque, address, (uint8_t*)&checksum, 4, 1);
+ address_space_rw(&s->as, address,
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)&checksum, 4, 1);
rx_len += 4;
s->regs[SONIC_CRBA1] = address >> 16;
s->regs[SONIC_CRBA0] = address & 0xffff;
@@ -803,29 +748,30 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
data[3 * width] = s->regs[SONIC_TRBA1]; /* pkt_ptr1 */
data[4 * width] = s->regs[SONIC_RSC]; /* seq_no */
size = sizeof(uint16_t) * 5 * width;
- s->memory_rw(s->mem_opaque, (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA], (uint8_t *)data, size, 1);
+ address_space_rw(&s->as, (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA],
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1);
/* Move to next descriptor */
size = sizeof(uint16_t) * width;
- s->memory_rw(s->mem_opaque,
+ address_space_rw(&s->as,
((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width,
- (uint8_t *)data, size, 0);
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
s->regs[SONIC_LLFA] = data[0 * width];
if (s->regs[SONIC_LLFA] & 0x1) {
/* EOL detected */
s->regs[SONIC_ISR] |= SONIC_ISR_RDE;
} else {
data[0 * width] = 0; /* in_use */
- s->memory_rw(s->mem_opaque,
+ address_space_rw(&s->as,
((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 6 * width,
- (uint8_t *)data, size, 1);
+ MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, sizeof(uint16_t), 1);
s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff);
if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) {
/* Read next RRA */
- do_read_rra(s);
+ dp8393x_do_read_rra(s);
}
}
@@ -835,11 +781,12 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
return size;
}
-static void nic_reset(void *opaque)
+static void dp8393x_reset(DeviceState *dev)
{
- dp8393xState *s = opaque;
+ dp8393xState *s = DP8393X(dev);
timer_del(s->watchdog);
+ memset(s->regs, 0, sizeof(s->regs));
s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS;
s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR);
s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT);
@@ -862,39 +809,91 @@ static void nic_reset(void *opaque)
static NetClientInfo net_dp83932_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
- .can_receive = nic_can_receive,
- .receive = nic_receive,
+ .can_receive = dp8393x_can_receive,
+ .receive = dp8393x_receive,
};
-void dp83932_init(NICInfo *nd, hwaddr base, int it_shift,
- MemoryRegion *address_space,
- qemu_irq irq, void* mem_opaque,
- void (*memory_rw)(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write))
+static void dp8393x_instance_init(Object *obj)
{
- dp8393xState *s;
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ dp8393xState *s = DP8393X(obj);
+
+ sysbus_init_mmio(sbd, &s->mmio);
+ sysbus_init_mmio(sbd, &s->prom);
+ sysbus_init_irq(sbd, &s->irq);
+}
- qemu_check_nic_model(nd, "dp83932");
+static void dp8393x_realize(DeviceState *dev, Error **errp)
+{
+ dp8393xState *s = DP8393X(dev);
+ int i, checksum;
+ uint8_t *prom;
- s = g_malloc0(sizeof(dp8393xState));
+ address_space_init(&s->as, s->dma_mr, "dp8393x");
+ memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s,
+ "dp8393x-regs", 0x40 << s->it_shift);
+
+ s->nic = qemu_new_nic(&net_dp83932_info, &s->conf,
+ object_get_typename(OBJECT(dev)), dev->id, s);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
- s->address_space = address_space;
- s->mem_opaque = mem_opaque;
- s->memory_rw = memory_rw;
- s->it_shift = it_shift;
- s->irq = irq;
s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s);
s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */
- s->conf.macaddr = nd->macaddr;
- s->conf.peers.ncs[0] = nd->netdev;
+ memory_region_init_rom_device(&s->prom, OBJECT(dev), NULL, NULL,
+ "dp8393x-prom", SONIC_PROM_SIZE, NULL);
+ prom = memory_region_get_ram_ptr(&s->prom);
+ checksum = 0;
+ for (i = 0; i < 6; i++) {
+ prom[i] = s->conf.macaddr.a[i];
+ checksum += prom[i];
+ if (checksum > 0xff) {
+ checksum = (checksum + 1) & 0xff;
+ }
+ }
+ prom[7] = 0xff - checksum;
+}
- s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, nd->model, nd->name, s);
+static const VMStateDescription vmstate_dp8393x = {
+ .name = "dp8393x",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField []) {
+ VMSTATE_BUFFER_UNSAFE(cam, dp8393xState, 0, 16 * 6),
+ VMSTATE_UINT16_ARRAY(regs, dp8393xState, 0x40),
+ VMSTATE_END_OF_LIST()
+ }
+};
- qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
- qemu_register_reset(nic_reset, s);
- nic_reset(s);
+static Property dp8393x_properties[] = {
+ DEFINE_NIC_PROPERTIES(dp8393xState, conf),
+ DEFINE_PROP_PTR("dma_mr", dp8393xState, dma_mr),
+ DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void dp8393x_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
- memory_region_init_io(&s->mmio, NULL, &dp8393x_ops, s,
- "dp8393x", 0x40 << it_shift);
- memory_region_add_subregion(address_space, base, &s->mmio);
+ set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
+ dc->realize = dp8393x_realize;
+ dc->reset = dp8393x_reset;
+ dc->vmsd = &vmstate_dp8393x;
+ dc->props = dp8393x_properties;
}
+
+static const TypeInfo dp8393x_info = {
+ .name = TYPE_DP8393X,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(dp8393xState),
+ .instance_init = dp8393x_instance_init,
+ .class_init = dp8393x_class_init,
+};
+
+static void dp8393x_register_types(void)
+{
+ type_register_static(&dp8393x_info);
+}
+
+type_init(dp8393x_register_types)
diff --git a/hw/net/e1000.c b/hw/net/e1000.c
index 091d61acc3..bab8e2abfb 100644
--- a/hw/net/e1000.c
+++ b/hw/net/e1000.c
@@ -1370,6 +1370,7 @@ static const VMStateDescription vmstate_e1000_mit_state = {
.name = "e1000/mit_state",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = e1000_mit_state_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT32(mac_reg[RDTR], E1000State),
VMSTATE_UINT32(mac_reg[RADV], E1000State),
@@ -1457,13 +1458,9 @@ static const VMStateDescription vmstate_e1000 = {
VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, VFTA, 128),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_e1000_mit_state,
- .needed = e1000_mit_state_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_e1000_mit_state,
+ NULL
}
};
diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c
index bdfd38f4ca..68b9981983 100644
--- a/hw/net/pcnet.c
+++ b/hw/net/pcnet.c
@@ -1241,6 +1241,14 @@ static void pcnet_transmit(PCNetState *s)
}
bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT);
+
+ /* if multi-tmd packet outsizes s->buffer then skip it silently.
+ Note: this is not what real hw does */
+ if (s->xmit_pos + bcnt > sizeof(s->buffer)) {
+ s->xmit_pos = -1;
+ goto txdone;
+ }
+
s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr),
s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s));
s->xmit_pos += bcnt;
diff --git a/hw/net/rocker/qmp-norocker.c b/hw/net/rocker/qmp-norocker.c
new file mode 100644
index 0000000000..f253747361
--- /dev/null
+++ b/hw/net/rocker/qmp-norocker.c
@@ -0,0 +1,50 @@
+/*
+ * QMP Target options - Commands handled based on a target config
+ * versus a host config
+ *
+ * Copyright (c) 2015 David Ahern <dsahern@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "qemu-common.h"
+#include "qmp-commands.h"
+#include "qapi/qmp/qerror.h"
+
+RockerSwitch *qmp_query_rocker(const char *name, Error **errp)
+{
+ error_set(errp, QERR_FEATURE_DISABLED, "rocker");
+ return NULL;
+};
+
+RockerPortList *qmp_query_rocker_ports(const char *name, Error **errp)
+{
+ error_set(errp, QERR_FEATURE_DISABLED, "rocker");
+ return NULL;
+};
+
+RockerOfDpaFlowList *qmp_query_rocker_of_dpa_flows(const char *name,
+ bool has_tbl_id,
+ uint32_t tbl_id,
+ Error **errp)
+{
+ error_set(errp, QERR_FEATURE_DISABLED, "rocker");
+ return NULL;
+};
+
+RockerOfDpaGroupList *qmp_query_rocker_of_dpa_groups(const char *name,
+ bool has_type,
+ uint8_t type,
+ Error **errp)
+{
+ error_set(errp, QERR_FEATURE_DISABLED, "rocker");
+ return NULL;
+};
diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c
index 55b6c46157..4d25842509 100644
--- a/hw/net/rocker/rocker.c
+++ b/hw/net/rocker/rocker.c
@@ -94,6 +94,51 @@ World *rocker_get_world(Rocker *r, enum rocker_world_type type)
return NULL;
}
+RockerSwitch *qmp_query_rocker(const char *name, Error **errp)
+{
+ RockerSwitch *rocker = g_malloc0(sizeof(*rocker));
+ Rocker *r;
+
+ r = rocker_find(name);
+ if (!r) {
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "rocker %s not found", name);
+ return NULL;
+ }
+
+ rocker->name = g_strdup(r->name);
+ rocker->id = r->switch_id;
+ rocker->ports = r->fp_ports;
+
+ return rocker;
+}
+
+RockerPortList *qmp_query_rocker_ports(const char *name, Error **errp)
+{
+ RockerPortList *list = NULL;
+ Rocker *r;
+ int i;
+
+ r = rocker_find(name);
+ if (!r) {
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "rocker %s not found", name);
+ return NULL;
+ }
+
+ for (i = r->fp_ports - 1; i >= 0; i--) {
+ RockerPortList *info = g_malloc0(sizeof(*info));
+ info->value = g_malloc0(sizeof(*info->value));
+ struct fp_port *port = r->fp_port[i];
+
+ fp_port_get_info(port, info);
+ info->next = list;
+ list = info;
+ }
+
+ return list;
+}
+
uint32_t rocker_fp_ports(Rocker *r)
{
return r->fp_ports;
@@ -238,6 +283,7 @@ static int cmd_get_port_settings(Rocker *r,
uint8_t duplex;
uint8_t autoneg;
uint8_t learning;
+ char *phys_name;
MACAddr macaddr;
enum rocker_world_type mode;
size_t tlv_size;
@@ -265,6 +311,7 @@ static int cmd_get_port_settings(Rocker *r,
fp_port_get_macaddr(fp_port, &macaddr);
mode = world_type(fp_port_get_world(fp_port));
learning = fp_port_get_learning(fp_port);
+ phys_name = fp_port_get_name(fp_port);
tlv_size = rocker_tlv_total_size(0) + /* nest */
rocker_tlv_total_size(sizeof(uint32_t)) + /* pport */
@@ -273,7 +320,8 @@ static int cmd_get_port_settings(Rocker *r,
rocker_tlv_total_size(sizeof(uint8_t)) + /* autoneg */
rocker_tlv_total_size(sizeof(macaddr.a)) + /* macaddr */
rocker_tlv_total_size(sizeof(uint8_t)) + /* mode */
- rocker_tlv_total_size(sizeof(uint8_t)); /* learning */
+ rocker_tlv_total_size(sizeof(uint8_t)) + /* learning */
+ rocker_tlv_total_size(strlen(phys_name));
if (tlv_size > desc_buf_size(info)) {
return -ROCKER_EMSGSIZE;
@@ -290,6 +338,8 @@ static int cmd_get_port_settings(Rocker *r,
rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_MODE, mode);
rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING,
learning);
+ rocker_tlv_put(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_PHYS_NAME,
+ strlen(phys_name), phys_name);
rocker_tlv_nest_end(buf, &pos, nest);
return desc_set_buf(info, tlv_size);
@@ -1277,6 +1327,22 @@ static int pci_rocker_init(PCIDevice *dev)
goto err_duplicate;
}
+ /* Rocker name is passed in port name requests to OS with the intention
+ * that the name is used in interface names. Limit the length of the
+ * rocker name to avoid naming problems in the OS. Also, adding the
+ * port number as p# and unganged breakout b#, where # is at most 2
+ * digits, so leave room for it too (-1 for string terminator, -3 for
+ * p# and -3 for b#)
+ */
+#define ROCKER_IFNAMSIZ 16
+#define MAX_ROCKER_NAME_LEN (ROCKER_IFNAMSIZ - 1 - 3 - 3)
+ if (strlen(r->name) > MAX_ROCKER_NAME_LEN) {
+ fprintf(stderr,
+ "rocker: name too long; please shorten to at most %d chars\n",
+ MAX_ROCKER_NAME_LEN);
+ return -EINVAL;
+ }
+
if (memcmp(&r->fp_start_macaddr, &zero, sizeof(zero)) == 0) {
memcpy(&r->fp_start_macaddr, &dflt, sizeof(dflt));
r->fp_start_macaddr.a[4] += (sw_index++);
diff --git a/hw/net/rocker/rocker_fp.c b/hw/net/rocker/rocker_fp.c
index 2f1e3b348a..d8d934c396 100644
--- a/hw/net/rocker/rocker_fp.c
+++ b/hw/net/rocker/rocker_fp.c
@@ -41,11 +41,26 @@ struct fp_port {
NICConf conf;
};
+char *fp_port_get_name(FpPort *port)
+{
+ return port->name;
+}
+
bool fp_port_get_link_up(FpPort *port)
{
return !qemu_get_queue(port->nic)->link_down;
}
+void fp_port_get_info(FpPort *port, RockerPortList *info)
+{
+ info->value->name = g_strdup(port->name);
+ info->value->enabled = port->enabled;
+ info->value->link_up = fp_port_get_link_up(port);
+ info->value->speed = port->speed;
+ info->value->duplex = port->duplex;
+ info->value->autoneg = port->autoneg;
+}
+
void fp_port_get_macaddr(FpPort *port, MACAddr *macaddr)
{
memcpy(macaddr->a, port->conf.macaddr.a, sizeof(macaddr->a));
@@ -173,8 +188,19 @@ bool fp_port_enabled(FpPort *port)
return port->enabled;
}
+static void fp_port_set_link(FpPort *port, bool up)
+{
+ NetClientState *nc = qemu_get_queue(port->nic);
+
+ if (up == nc->link_down) {
+ nc->link_down = !up;
+ nc->info->link_status_changed(nc);
+ }
+}
+
void fp_port_enable(FpPort *port)
{
+ fp_port_set_link(port, true);
port->enabled = true;
DPRINTF("port %d enabled\n", port->index);
}
@@ -182,6 +208,7 @@ void fp_port_enable(FpPort *port)
void fp_port_disable(FpPort *port)
{
port->enabled = false;
+ fp_port_set_link(port, false);
DPRINTF("port %d disabled\n", port->index);
}
@@ -201,7 +228,7 @@ FpPort *fp_port_alloc(Rocker *r, char *sw_name,
/* front-panel switch port names are 1-based */
- port->name = g_strdup_printf("%s.%d", sw_name, port->pport);
+ port->name = g_strdup_printf("%sp%d", sw_name, port->pport);
memcpy(port->conf.macaddr.a, start_mac, sizeof(port->conf.macaddr.a));
port->conf.macaddr.a[5] += index;
diff --git a/hw/net/rocker/rocker_fp.h b/hw/net/rocker/rocker_fp.h
index a5f28f120d..ab80fd833c 100644
--- a/hw/net/rocker/rocker_fp.h
+++ b/hw/net/rocker/rocker_fp.h
@@ -26,7 +26,9 @@ typedef struct fp_port FpPort;
int fp_port_eg(FpPort *port, const struct iovec *iov, int iovcnt);
+char *fp_port_get_name(FpPort *port);
bool fp_port_get_link_up(FpPort *port);
+void fp_port_get_info(FpPort *port, RockerPortList *info);
void fp_port_get_macaddr(FpPort *port, MACAddr *macaddr);
void fp_port_set_macaddr(FpPort *port, MACAddr *macaddr);
uint8_t fp_port_get_learning(FpPort *port);
diff --git a/hw/net/rocker/rocker_hw.h b/hw/net/rocker/rocker_hw.h
index c9c85a75bd..fe639badd4 100644
--- a/hw/net/rocker/rocker_hw.h
+++ b/hw/net/rocker/rocker_hw.h
@@ -179,6 +179,7 @@ enum {
ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR, /* binary */
ROCKER_TLV_CMD_PORT_SETTINGS_MODE, /* u8 */
ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING, /* u8 */
+ ROCKER_TLV_CMD_PORT_SETTINGS_PHYS_NAME, /* binary */
__ROCKER_TLV_CMD_PORT_SETTINGS_MAX,
ROCKER_TLV_CMD_PORT_SETTINGS_MAX = __ROCKER_TLV_CMD_PORT_SETTINGS_MAX - 1,
diff --git a/hw/net/rocker/rocker_of_dpa.c b/hw/net/rocker/rocker_of_dpa.c
index 1bcb7af5ef..b25a17d6d7 100644
--- a/hw/net/rocker/rocker_of_dpa.c
+++ b/hw/net/rocker/rocker_of_dpa.c
@@ -2302,6 +2302,318 @@ static void of_dpa_uninit(World *world)
g_hash_table_destroy(of_dpa->flow_tbl);
}
+struct of_dpa_flow_fill_context {
+ RockerOfDpaFlowList *list;
+ uint32_t tbl_id;
+};
+
+static void of_dpa_flow_fill(void *cookie, void *value, void *user_data)
+{
+ struct of_dpa_flow *flow = value;
+ struct of_dpa_flow_key *key = &flow->key;
+ struct of_dpa_flow_key *mask = &flow->mask;
+ struct of_dpa_flow_fill_context *flow_context = user_data;
+ RockerOfDpaFlowList *new;
+ RockerOfDpaFlow *nflow;
+ RockerOfDpaFlowKey *nkey;
+ RockerOfDpaFlowMask *nmask;
+ RockerOfDpaFlowAction *naction;
+
+ if (flow_context->tbl_id != -1 &&
+ flow_context->tbl_id != key->tbl_id) {
+ return;
+ }
+
+ new = g_malloc0(sizeof(*new));
+ nflow = new->value = g_malloc0(sizeof(*nflow));
+ nkey = nflow->key = g_malloc0(sizeof(*nkey));
+ nmask = nflow->mask = g_malloc0(sizeof(*nmask));
+ naction = nflow->action = g_malloc0(sizeof(*naction));
+
+ nflow->cookie = flow->cookie;
+ nflow->hits = flow->stats.hits;
+ nkey->priority = flow->priority;
+ nkey->tbl_id = key->tbl_id;
+
+ if (key->in_pport || mask->in_pport) {
+ nkey->has_in_pport = true;
+ nkey->in_pport = key->in_pport;
+ }
+
+ if (nkey->has_in_pport && mask->in_pport != 0xffffffff) {
+ nmask->has_in_pport = true;
+ nmask->in_pport = mask->in_pport;
+ }
+
+ if (key->eth.vlan_id || mask->eth.vlan_id) {
+ nkey->has_vlan_id = true;
+ nkey->vlan_id = ntohs(key->eth.vlan_id);
+ }
+
+ if (nkey->has_vlan_id && mask->eth.vlan_id != 0xffff) {
+ nmask->has_vlan_id = true;
+ nmask->vlan_id = ntohs(mask->eth.vlan_id);
+ }
+
+ if (key->tunnel_id || mask->tunnel_id) {
+ nkey->has_tunnel_id = true;
+ nkey->tunnel_id = key->tunnel_id;
+ }
+
+ if (nkey->has_tunnel_id && mask->tunnel_id != 0xffffffff) {
+ nmask->has_tunnel_id = true;
+ nmask->tunnel_id = mask->tunnel_id;
+ }
+
+ if (memcmp(key->eth.src.a, zero_mac.a, ETH_ALEN) ||
+ memcmp(mask->eth.src.a, zero_mac.a, ETH_ALEN)) {
+ nkey->has_eth_src = true;
+ nkey->eth_src = qemu_mac_strdup_printf(key->eth.src.a);
+ }
+
+ if (nkey->has_eth_src && memcmp(mask->eth.src.a, ff_mac.a, ETH_ALEN)) {
+ nmask->has_eth_src = true;
+ nmask->eth_src = qemu_mac_strdup_printf(mask->eth.src.a);
+ }
+
+ if (memcmp(key->eth.dst.a, zero_mac.a, ETH_ALEN) ||
+ memcmp(mask->eth.dst.a, zero_mac.a, ETH_ALEN)) {
+ nkey->has_eth_dst = true;
+ nkey->eth_dst = qemu_mac_strdup_printf(key->eth.dst.a);
+ }
+
+ if (nkey->has_eth_dst && memcmp(mask->eth.dst.a, ff_mac.a, ETH_ALEN)) {
+ nmask->has_eth_dst = true;
+ nmask->eth_dst = qemu_mac_strdup_printf(mask->eth.dst.a);
+ }
+
+ if (key->eth.type) {
+
+ nkey->has_eth_type = true;
+ nkey->eth_type = ntohs(key->eth.type);
+
+ switch (ntohs(key->eth.type)) {
+ case 0x0800:
+ case 0x86dd:
+ if (key->ip.proto || mask->ip.proto) {
+ nkey->has_ip_proto = true;
+ nkey->ip_proto = key->ip.proto;
+ }
+ if (nkey->has_ip_proto && mask->ip.proto != 0xff) {
+ nmask->has_ip_proto = true;
+ nmask->ip_proto = mask->ip.proto;
+ }
+ if (key->ip.tos || mask->ip.tos) {
+ nkey->has_ip_tos = true;
+ nkey->ip_tos = key->ip.tos;
+ }
+ if (nkey->has_ip_tos && mask->ip.tos != 0xff) {
+ nmask->has_ip_tos = true;
+ nmask->ip_tos = mask->ip.tos;
+ }
+ break;
+ }
+
+ switch (ntohs(key->eth.type)) {
+ case 0x0800:
+ if (key->ipv4.addr.dst || mask->ipv4.addr.dst) {
+ char *dst = inet_ntoa(*(struct in_addr *)&key->ipv4.addr.dst);
+ int dst_len = of_dpa_mask2prefix(mask->ipv4.addr.dst);
+ nkey->has_ip_dst = true;
+ nkey->ip_dst = g_strdup_printf("%s/%d", dst, dst_len);
+ }
+ break;
+ }
+ }
+
+ if (flow->action.goto_tbl) {
+ naction->has_goto_tbl = true;
+ naction->goto_tbl = flow->action.goto_tbl;
+ }
+
+ if (flow->action.write.group_id) {
+ naction->has_group_id = true;
+ naction->group_id = flow->action.write.group_id;
+ }
+
+ if (flow->action.apply.new_vlan_id) {
+ naction->has_new_vlan_id = true;
+ naction->new_vlan_id = flow->action.apply.new_vlan_id;
+ }
+
+ new->next = flow_context->list;
+ flow_context->list = new;
+}
+
+RockerOfDpaFlowList *qmp_query_rocker_of_dpa_flows(const char *name,
+ bool has_tbl_id,
+ uint32_t tbl_id,
+ Error **errp)
+{
+ struct rocker *r;
+ struct world *w;
+ struct of_dpa *of_dpa;
+ struct of_dpa_flow_fill_context fill_context = {
+ .list = NULL,
+ .tbl_id = tbl_id,
+ };
+
+ r = rocker_find(name);
+ if (!r) {
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "rocker %s not found", name);
+ return NULL;
+ }
+
+ w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA);
+ if (!w) {
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "rocker %s doesn't have OF-DPA world", name);
+ return NULL;
+ }
+
+ of_dpa = world_private(w);
+
+ g_hash_table_foreach(of_dpa->flow_tbl, of_dpa_flow_fill, &fill_context);
+
+ return fill_context.list;
+}
+
+struct of_dpa_group_fill_context {
+ RockerOfDpaGroupList *list;
+ uint8_t type;
+};
+
+static void of_dpa_group_fill(void *key, void *value, void *user_data)
+{
+ struct of_dpa_group *group = value;
+ struct of_dpa_group_fill_context *flow_context = user_data;
+ RockerOfDpaGroupList *new;
+ RockerOfDpaGroup *ngroup;
+ struct uint32List *id;
+ int i;
+
+ if (flow_context->type != 9 &&
+ flow_context->type != ROCKER_GROUP_TYPE_GET(group->id)) {
+ return;
+ }
+
+ new = g_malloc0(sizeof(*new));
+ ngroup = new->value = g_malloc0(sizeof(*ngroup));
+
+ ngroup->id = group->id;
+
+ ngroup->type = ROCKER_GROUP_TYPE_GET(group->id);
+
+ switch (ngroup->type) {
+ case ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE:
+ ngroup->has_vlan_id = true;
+ ngroup->vlan_id = ROCKER_GROUP_VLAN_GET(group->id);
+ ngroup->has_pport = true;
+ ngroup->pport = ROCKER_GROUP_PORT_GET(group->id);
+ ngroup->has_out_pport = true;
+ ngroup->out_pport = group->l2_interface.out_pport;
+ ngroup->has_pop_vlan = true;
+ ngroup->pop_vlan = group->l2_interface.pop_vlan;
+ break;
+ case ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE:
+ ngroup->has_index = true;
+ ngroup->index = ROCKER_GROUP_INDEX_LONG_GET(group->id);
+ ngroup->has_group_id = true;
+ ngroup->group_id = group->l2_rewrite.group_id;
+ if (group->l2_rewrite.vlan_id) {
+ ngroup->has_set_vlan_id = true;
+ ngroup->set_vlan_id = ntohs(group->l2_rewrite.vlan_id);
+ }
+ break;
+ if (memcmp(group->l2_rewrite.src_mac.a, zero_mac.a, ETH_ALEN)) {
+ ngroup->has_set_eth_src = true;
+ ngroup->set_eth_src =
+ qemu_mac_strdup_printf(group->l2_rewrite.src_mac.a);
+ }
+ if (memcmp(group->l2_rewrite.dst_mac.a, zero_mac.a, ETH_ALEN)) {
+ ngroup->has_set_eth_dst = true;
+ ngroup->set_eth_dst =
+ qemu_mac_strdup_printf(group->l2_rewrite.dst_mac.a);
+ }
+ case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD:
+ case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST:
+ ngroup->has_vlan_id = true;
+ ngroup->vlan_id = ROCKER_GROUP_VLAN_GET(group->id);
+ ngroup->has_index = true;
+ ngroup->index = ROCKER_GROUP_INDEX_GET(group->id);
+ for (i = 0; i < group->l2_flood.group_count; i++) {
+ ngroup->has_group_ids = true;
+ id = g_malloc0(sizeof(*id));
+ id->value = group->l2_flood.group_ids[i];
+ id->next = ngroup->group_ids;
+ ngroup->group_ids = id;
+ }
+ break;
+ case ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST:
+ ngroup->has_index = true;
+ ngroup->index = ROCKER_GROUP_INDEX_LONG_GET(group->id);
+ ngroup->has_group_id = true;
+ ngroup->group_id = group->l3_unicast.group_id;
+ if (group->l3_unicast.vlan_id) {
+ ngroup->has_set_vlan_id = true;
+ ngroup->set_vlan_id = ntohs(group->l3_unicast.vlan_id);
+ }
+ if (memcmp(group->l3_unicast.src_mac.a, zero_mac.a, ETH_ALEN)) {
+ ngroup->has_set_eth_src = true;
+ ngroup->set_eth_src =
+ qemu_mac_strdup_printf(group->l3_unicast.src_mac.a);
+ }
+ if (memcmp(group->l3_unicast.dst_mac.a, zero_mac.a, ETH_ALEN)) {
+ ngroup->has_set_eth_dst = true;
+ ngroup->set_eth_dst =
+ qemu_mac_strdup_printf(group->l3_unicast.dst_mac.a);
+ }
+ if (group->l3_unicast.ttl_check) {
+ ngroup->has_ttl_check = true;
+ ngroup->ttl_check = group->l3_unicast.ttl_check;
+ }
+ break;
+ }
+
+ new->next = flow_context->list;
+ flow_context->list = new;
+}
+
+RockerOfDpaGroupList *qmp_query_rocker_of_dpa_groups(const char *name,
+ bool has_type,
+ uint8_t type,
+ Error **errp)
+{
+ struct rocker *r;
+ struct world *w;
+ struct of_dpa *of_dpa;
+ struct of_dpa_group_fill_context fill_context = {
+ .list = NULL,
+ .type = type,
+ };
+
+ r = rocker_find(name);
+ if (!r) {
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "rocker %s not found", name);
+ return NULL;
+ }
+
+ w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA);
+ if (!w) {
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "rocker %s doesn't have OF-DPA world", name);
+ return NULL;
+ }
+
+ of_dpa = world_private(w);
+
+ g_hash_table_foreach(of_dpa->group_tbl, of_dpa_group_fill, &fill_context);
+
+ return fill_context.list;
+}
+
static WorldOps of_dpa_ops = {
.init = of_dpa_init,
.uninit = of_dpa_uninit,
diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c
index f868108dfe..e0db4727ae 100644
--- a/hw/net/rtl8139.c
+++ b/hw/net/rtl8139.c
@@ -3240,6 +3240,7 @@ static const VMStateDescription vmstate_rtl8139_hotplug_ready ={
.name = "rtl8139/hotplug_ready",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = rtl8139_hotplug_ready_needed,
.fields = (VMStateField[]) {
VMSTATE_END_OF_LIST()
}
@@ -3335,13 +3336,9 @@ static const VMStateDescription vmstate_rtl8139 = {
VMSTATE_UINT32_V(cplus_enabled, RTL8139State, 4),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection []) {
- {
- .vmsd = &vmstate_rtl8139_hotplug_ready,
- .needed = rtl8139_hotplug_ready_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_rtl8139_hotplug_ready,
+ NULL
}
};
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
index 34ffafd5a6..104a0f599b 100644
--- a/hw/net/vmxnet3.c
+++ b/hw/net/vmxnet3.c
@@ -2226,6 +2226,7 @@ static const VMStateDescription vmxstate_vmxnet3_mcast_list = {
.version_id = 1,
.minimum_version_id = 1,
.pre_load = vmxnet3_mcast_list_pre_load,
+ .needed = vmxnet3_mc_list_needed,
.fields = (VMStateField[]) {
VMSTATE_VBUFFER_UINT32(mcast_list, VMXNET3State, 0, NULL, 0,
mcast_list_buff_size),
@@ -2470,14 +2471,9 @@ static const VMStateDescription vmstate_vmxnet3 = {
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmxstate_vmxnet3_mcast_list,
- .needed = vmxnet3_mc_list_needed
- },
- {
- /* empty element. */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmxstate_vmxnet3_mcast_list,
+ NULL
}
};
diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c
index 68eff77983..88481b78c4 100644
--- a/hw/nvram/fw_cfg.c
+++ b/hw/nvram/fw_cfg.c
@@ -46,7 +46,6 @@ typedef struct FWCfgEntry {
uint32_t len;
uint8_t *data;
void *callback_opaque;
- FWCfgCallback callback;
FWCfgReadCallback read_callback;
} FWCfgEntry;
@@ -232,19 +231,7 @@ static void fw_cfg_reboot(FWCfgState *s)
static void fw_cfg_write(FWCfgState *s, uint8_t value)
{
- int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
- FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
-
- trace_fw_cfg_write(s, value);
-
- if (s->cur_entry & FW_CFG_WRITE_CHANNEL && e->callback &&
- s->cur_offset < e->len) {
- e->data[s->cur_offset++] = value;
- if (s->cur_offset == e->len) {
- e->callback(e->callback_opaque, e->data);
- s->cur_offset = 0;
- }
- }
+ /* nothing, write support removed in QEMU v2.4+ */
}
static int fw_cfg_select(FWCfgState *s, uint16_t key)
@@ -436,6 +423,7 @@ static void fw_cfg_add_bytes_read_callback(FWCfgState *s, uint16_t key,
key &= FW_CFG_ENTRY_MASK;
assert(key < FW_CFG_MAX_ENTRY && len < UINT32_MAX);
+ assert(s->entries[arch][key].data == NULL); /* avoid key conflict */
s->entries[arch][key].data = data;
s->entries[arch][key].len = (uint32_t)len;
@@ -458,7 +446,6 @@ static void *fw_cfg_modify_bytes_read(FWCfgState *s, uint16_t key,
s->entries[arch][key].data = data;
s->entries[arch][key].len = len;
s->entries[arch][key].callback_opaque = NULL;
- s->entries[arch][key].callback = NULL;
return ptr;
}
@@ -484,6 +471,16 @@ void fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value)
fw_cfg_add_bytes(s, key, copy, sizeof(value));
}
+void fw_cfg_modify_i16(FWCfgState *s, uint16_t key, uint16_t value)
+{
+ uint16_t *copy, *old;
+
+ copy = g_malloc(sizeof(value));
+ *copy = cpu_to_le16(value);
+ old = fw_cfg_modify_bytes_read(s, key, copy, sizeof(value));
+ g_free(old);
+}
+
void fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value)
{
uint32_t *copy;
@@ -502,23 +499,6 @@ void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value)
fw_cfg_add_bytes(s, key, copy, sizeof(value));
}
-void fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback,
- void *callback_opaque, void *data, size_t len)
-{
- int arch = !!(key & FW_CFG_ARCH_LOCAL);
-
- assert(key & FW_CFG_WRITE_CHANNEL);
-
- key &= FW_CFG_ENTRY_MASK;
-
- assert(key < FW_CFG_MAX_ENTRY && len <= UINT32_MAX);
-
- s->entries[arch][key].data = data;
- s->entries[arch][key].len = (uint32_t)len;
- s->entries[arch][key].callback_opaque = callback_opaque;
- s->entries[arch][key].callback = callback;
-}
-
void fw_cfg_add_file_callback(FWCfgState *s, const char *filename,
FWCfgReadCallback callback, void *callback_opaque,
void *data, size_t len)
@@ -535,18 +515,19 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename,
index = be32_to_cpu(s->files->count);
assert(index < FW_CFG_FILE_SLOTS);
- fw_cfg_add_bytes_read_callback(s, FW_CFG_FILE_FIRST + index,
- callback, callback_opaque, data, len);
-
pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name),
filename);
for (i = 0; i < index; i++) {
if (strcmp(s->files->f[index].name, s->files->f[i].name) == 0) {
- trace_fw_cfg_add_file_dupe(s, s->files->f[index].name);
- return;
+ error_report("duplicate fw_cfg file name: %s",
+ s->files->f[index].name);
+ exit(1);
}
}
+ fw_cfg_add_bytes_read_callback(s, FW_CFG_FILE_FIRST + index,
+ callback, callback_opaque, data, len);
+
s->files->f[index].size = cpu_to_be32(len);
s->files->f[index].select = cpu_to_be16(FW_CFG_FILE_FIRST + index);
trace_fw_cfg_add_file(s, index, s->files->f[index].name, len);
diff --git a/hw/pci-host/pam.c b/hw/pci-host/pam.c
index 8272de3f28..17d826cba5 100644
--- a/hw/pci-host/pam.c
+++ b/hw/pci-host/pam.c
@@ -31,26 +31,6 @@
#include "sysemu/sysemu.h"
#include "hw/pci-host/pam.h"
-void smram_update(MemoryRegion *smram_region, uint8_t smram,
- uint8_t smm_enabled)
-{
- bool smram_enabled;
-
- smram_enabled = ((smm_enabled && (smram & SMRAM_G_SMRAME)) ||
- (smram & SMRAM_D_OPEN));
- memory_region_set_enabled(smram_region, !smram_enabled);
-}
-
-void smram_set_smm(uint8_t *host_smm_enabled, int smm, uint8_t smram,
- MemoryRegion *smram_region)
-{
- uint8_t smm_enabled = (smm != 0);
- if (*host_smm_enabled != smm_enabled) {
- *host_smm_enabled = smm_enabled;
- smram_update(smram_region, smram, *host_smm_enabled);
- }
-}
-
void init_pam(DeviceState *dev, MemoryRegion *ram_memory,
MemoryRegion *system_memory, MemoryRegion *pci_address_space,
PAMMemoryRegion *mem, uint32_t start, uint32_t size)
diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c
index 723836fb0e..ed2424c4cd 100644
--- a/hw/pci-host/piix.c
+++ b/hw/pci-host/piix.c
@@ -105,7 +105,7 @@ struct PCII440FXState {
MemoryRegion *ram_memory;
PAMMemoryRegion pam_regions[13];
MemoryRegion smram_region;
- uint8_t smm_enabled;
+ MemoryRegion smram, low_smram;
};
@@ -138,18 +138,10 @@ static void i440fx_update_memory_mappings(PCII440FXState *d)
pam_update(&d->pam_regions[i], i,
pd->config[I440FX_PAM + ((i + 1) / 2)]);
}
- smram_update(&d->smram_region, pd->config[I440FX_SMRAM], d->smm_enabled);
- memory_region_transaction_commit();
-}
-
-static void i440fx_set_smm(int val, void *arg)
-{
- PCII440FXState *d = arg;
- PCIDevice *pd = PCI_DEVICE(d);
-
- memory_region_transaction_begin();
- smram_set_smm(&d->smm_enabled, val, pd->config[I440FX_SMRAM],
- &d->smram_region);
+ memory_region_set_enabled(&d->smram_region,
+ !(pd->config[I440FX_SMRAM] & SMRAM_D_OPEN));
+ memory_region_set_enabled(&d->smram,
+ pd->config[I440FX_SMRAM] & SMRAM_G_SMRAME);
memory_region_transaction_commit();
}
@@ -172,12 +164,13 @@ static int i440fx_load_old(QEMUFile* f, void *opaque, int version_id)
PCII440FXState *d = opaque;
PCIDevice *pd = PCI_DEVICE(d);
int ret, i;
+ uint8_t smm_enabled;
ret = pci_device_load(pd, f);
if (ret < 0)
return ret;
i440fx_update_memory_mappings(d);
- qemu_get_8s(f, &d->smm_enabled);
+ qemu_get_8s(f, &smm_enabled);
if (version_id == 2) {
for (i = 0; i < PIIX_NUM_PIRQS; i++) {
@@ -205,7 +198,10 @@ static const VMStateDescription vmstate_i440fx = {
.post_load = i440fx_post_load,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(parent_obj, PCII440FXState),
- VMSTATE_UINT8(smm_enabled, PCII440FXState),
+ /* Used to be smm_enabled, which was basically always zero because
+ * SeaBIOS hardly uses SMM. SMRAM is now handled by CPU code.
+ */
+ VMSTATE_UNUSED(1),
VMSTATE_END_OF_LIST()
}
};
@@ -297,11 +293,7 @@ static void i440fx_pcihost_realize(DeviceState *dev, Error **errp)
static void i440fx_realize(PCIDevice *dev, Error **errp)
{
- PCII440FXState *d = I440FX_PCI_DEVICE(dev);
-
dev->config[I440FX_SMRAM] = 0x02;
-
- cpu_smm_register(&i440fx_set_smm, d);
}
PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
@@ -346,11 +338,23 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
pc_pci_as_mapping_init(OBJECT(f), f->system_memory,
f->pci_address_space);
+ /* if *disabled* show SMRAM to all CPUs */
memory_region_init_alias(&f->smram_region, OBJECT(d), "smram-region",
f->pci_address_space, 0xa0000, 0x20000);
memory_region_add_subregion_overlap(f->system_memory, 0xa0000,
&f->smram_region, 1);
- memory_region_set_enabled(&f->smram_region, false);
+ memory_region_set_enabled(&f->smram_region, true);
+
+ /* smram, as seen by SMM CPUs */
+ memory_region_init(&f->smram, OBJECT(d), "smram", 1ull << 32);
+ memory_region_set_enabled(&f->smram, true);
+ memory_region_init_alias(&f->low_smram, OBJECT(d), "smram-low",
+ f->ram_memory, 0xa0000, 0x20000);
+ memory_region_set_enabled(&f->low_smram, true);
+ memory_region_add_subregion(&f->smram, 0xa0000, &f->low_smram);
+ object_property_add_const_link(qdev_get_machine(), "smram",
+ OBJECT(&f->smram), &error_abort);
+
init_pam(dev, f->ram_memory, f->system_memory, f->pci_address_space,
&f->pam_regions[0], PAM_BIOS_BASE, PAM_BIOS_SIZE);
for (i = 0; i < 12; ++i) {
@@ -578,6 +582,7 @@ static const VMStateDescription vmstate_piix3_rcr = {
.name = "PIIX3/rcr",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = piix3_rcr_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8(rcr, PIIX3State),
VMSTATE_END_OF_LIST()
@@ -596,12 +601,9 @@ static const VMStateDescription vmstate_piix3 = {
PIIX_NUM_PIRQS, 3),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_piix3_rcr,
- .needed = piix3_rcr_needed,
- },
- { 0 }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_piix3_rcr,
+ NULL
}
};
diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c
index c8827cc000..bd7409456f 100644
--- a/hw/pci-host/q35.c
+++ b/hw/pci-host/q35.c
@@ -198,6 +198,28 @@ static const TypeInfo q35_host_info = {
* MCH D0:F0
*/
+static uint64_t tseg_blackhole_read(void *ptr, hwaddr reg, unsigned size)
+{
+ return 0xffffffff;
+}
+
+static void tseg_blackhole_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned width)
+{
+ /* nothing */
+}
+
+static const MemoryRegionOps tseg_blackhole_ops = {
+ .read = tseg_blackhole_read,
+ .write = tseg_blackhole_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .impl.min_access_size = 4,
+ .impl.max_access_size = 4,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
/* PCIe MMCFG */
static void mch_update_pciexbar(MCHPCIState *mch)
{
@@ -266,21 +288,70 @@ static void mch_update_pam(MCHPCIState *mch)
static void mch_update_smram(MCHPCIState *mch)
{
PCIDevice *pd = PCI_DEVICE(mch);
+ bool h_smrame = (pd->config[MCH_HOST_BRIDGE_ESMRAMC] & MCH_HOST_BRIDGE_ESMRAMC_H_SMRAME);
+ uint32_t tseg_size;
+
+ /* implement SMRAM.D_LCK */
+ if (pd->config[MCH_HOST_BRIDGE_SMRAM] & MCH_HOST_BRIDGE_SMRAM_D_LCK) {
+ pd->config[MCH_HOST_BRIDGE_SMRAM] &= ~MCH_HOST_BRIDGE_SMRAM_D_OPEN;
+ pd->wmask[MCH_HOST_BRIDGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_WMASK_LCK;
+ pd->wmask[MCH_HOST_BRIDGE_ESMRAMC] = MCH_HOST_BRIDGE_ESMRAMC_WMASK_LCK;
+ }
memory_region_transaction_begin();
- smram_update(&mch->smram_region, pd->config[MCH_HOST_BRIDGE_SMRAM],
- mch->smm_enabled);
- memory_region_transaction_commit();
-}
-static void mch_set_smm(int smm, void *arg)
-{
- MCHPCIState *mch = arg;
- PCIDevice *pd = PCI_DEVICE(mch);
+ if (pd->config[MCH_HOST_BRIDGE_SMRAM] & SMRAM_D_OPEN) {
+ /* Hide (!) low SMRAM if H_SMRAME = 1 */
+ memory_region_set_enabled(&mch->smram_region, h_smrame);
+ /* Show high SMRAM if H_SMRAME = 1 */
+ memory_region_set_enabled(&mch->open_high_smram, h_smrame);
+ } else {
+ /* Hide high SMRAM and low SMRAM */
+ memory_region_set_enabled(&mch->smram_region, true);
+ memory_region_set_enabled(&mch->open_high_smram, false);
+ }
+
+ if (pd->config[MCH_HOST_BRIDGE_SMRAM] & SMRAM_G_SMRAME) {
+ memory_region_set_enabled(&mch->low_smram, !h_smrame);
+ memory_region_set_enabled(&mch->high_smram, h_smrame);
+ } else {
+ memory_region_set_enabled(&mch->low_smram, false);
+ memory_region_set_enabled(&mch->high_smram, false);
+ }
+
+ if (pd->config[MCH_HOST_BRIDGE_ESMRAMC] & MCH_HOST_BRIDGE_ESMRAMC_T_EN) {
+ switch (pd->config[MCH_HOST_BRIDGE_ESMRAMC] &
+ MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK) {
+ case MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_1MB:
+ tseg_size = 1024 * 1024;
+ break;
+ case MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_2MB:
+ tseg_size = 1024 * 1024 * 2;
+ break;
+ case MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_8MB:
+ tseg_size = 1024 * 1024 * 8;
+ break;
+ default:
+ tseg_size = 0;
+ break;
+ }
+ } else {
+ tseg_size = 0;
+ }
+ memory_region_del_subregion(mch->system_memory, &mch->tseg_blackhole);
+ memory_region_set_enabled(&mch->tseg_blackhole, tseg_size);
+ memory_region_set_size(&mch->tseg_blackhole, tseg_size);
+ memory_region_add_subregion_overlap(mch->system_memory,
+ mch->below_4g_mem_size - tseg_size,
+ &mch->tseg_blackhole, 1);
+
+ memory_region_set_enabled(&mch->tseg_window, tseg_size);
+ memory_region_set_size(&mch->tseg_window, tseg_size);
+ memory_region_set_address(&mch->tseg_window,
+ mch->below_4g_mem_size - tseg_size);
+ memory_region_set_alias_offset(&mch->tseg_window,
+ mch->below_4g_mem_size - tseg_size);
- memory_region_transaction_begin();
- smram_set_smm(&mch->smm_enabled, smm, pd->config[MCH_HOST_BRIDGE_SMRAM],
- &mch->smram_region);
memory_region_transaction_commit();
}
@@ -289,7 +360,6 @@ static void mch_write_config(PCIDevice *d,
{
MCHPCIState *mch = MCH_PCI_DEVICE(d);
- /* XXX: implement SMRAM.D_LOCK */
pci_default_write_config(d, address, val, len);
if (ranges_overlap(address, len, MCH_HOST_BRIDGE_PAM0,
@@ -329,7 +399,10 @@ static const VMStateDescription vmstate_mch = {
.post_load = mch_post_load,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(parent_obj, MCHPCIState),
- VMSTATE_UINT8(smm_enabled, MCHPCIState),
+ /* Used to be smm_enabled, which was basically always zero because
+ * SeaBIOS hardly uses SMM. SMRAM is now handled by CPU code.
+ */
+ VMSTATE_UNUSED(1),
VMSTATE_END_OF_LIST()
}
};
@@ -343,6 +416,9 @@ static void mch_reset(DeviceState *qdev)
MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT);
d->config[MCH_HOST_BRIDGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_DEFAULT;
+ d->config[MCH_HOST_BRIDGE_ESMRAMC] = MCH_HOST_BRIDGE_ESMRAMC_DEFAULT;
+ d->wmask[MCH_HOST_BRIDGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_WMASK;
+ d->wmask[MCH_HOST_BRIDGE_ESMRAMC] = MCH_HOST_BRIDGE_ESMRAMC_WMASK;
mch_update(mch);
}
@@ -399,13 +475,47 @@ static void mch_realize(PCIDevice *d, Error **errp)
pc_pci_as_mapping_init(OBJECT(mch), mch->system_memory,
mch->pci_address_space);
- /* smram */
- cpu_smm_register(&mch_set_smm, mch);
+ /* if *disabled* show SMRAM to all CPUs */
memory_region_init_alias(&mch->smram_region, OBJECT(mch), "smram-region",
mch->pci_address_space, 0xa0000, 0x20000);
memory_region_add_subregion_overlap(mch->system_memory, 0xa0000,
&mch->smram_region, 1);
- memory_region_set_enabled(&mch->smram_region, false);
+ memory_region_set_enabled(&mch->smram_region, true);
+
+ memory_region_init_alias(&mch->open_high_smram, OBJECT(mch), "smram-open-high",
+ mch->ram_memory, 0xa0000, 0x20000);
+ memory_region_add_subregion_overlap(mch->system_memory, 0xfeda0000,
+ &mch->open_high_smram, 1);
+ memory_region_set_enabled(&mch->open_high_smram, false);
+
+ /* smram, as seen by SMM CPUs */
+ memory_region_init(&mch->smram, OBJECT(mch), "smram", 1ull << 32);
+ memory_region_set_enabled(&mch->smram, true);
+ memory_region_init_alias(&mch->low_smram, OBJECT(mch), "smram-low",
+ mch->ram_memory, 0xa0000, 0x20000);
+ memory_region_set_enabled(&mch->low_smram, true);
+ memory_region_add_subregion(&mch->smram, 0xa0000, &mch->low_smram);
+ memory_region_init_alias(&mch->high_smram, OBJECT(mch), "smram-high",
+ mch->ram_memory, 0xa0000, 0x20000);
+ memory_region_set_enabled(&mch->high_smram, true);
+ memory_region_add_subregion(&mch->smram, 0xfeda0000, &mch->high_smram);
+
+ memory_region_init_io(&mch->tseg_blackhole, OBJECT(mch),
+ &tseg_blackhole_ops, NULL,
+ "tseg-blackhole", 0);
+ memory_region_set_enabled(&mch->tseg_blackhole, false);
+ memory_region_add_subregion_overlap(mch->system_memory,
+ mch->below_4g_mem_size,
+ &mch->tseg_blackhole, 1);
+
+ memory_region_init_alias(&mch->tseg_window, OBJECT(mch), "tseg-window",
+ mch->ram_memory, mch->below_4g_mem_size, 0);
+ memory_region_set_enabled(&mch->tseg_window, false);
+ memory_region_add_subregion(&mch->smram, mch->below_4g_mem_size,
+ &mch->tseg_window);
+ object_property_add_const_link(qdev_get_machine(), "smram",
+ OBJECT(&mch->smram), &error_abort);
+
init_pam(DEVICE(mch), mch->ram_memory, mch->system_memory,
mch->pci_address_space, &mch->pam_regions[0],
PAM_BIOS_BASE, PAM_BIOS_SIZE);
diff --git a/hw/pci/msi.c b/hw/pci/msi.c
index c111dbaff6..f9c0484420 100644
--- a/hw/pci/msi.c
+++ b/hw/pci/msi.c
@@ -21,10 +21,6 @@
#include "hw/pci/msi.h"
#include "qemu/range.h"
-/* Eventually those constants should go to Linux pci_regs.h */
-#define PCI_MSI_PENDING_32 0x10
-#define PCI_MSI_PENDING_64 0x14
-
/* PCI_MSI_ADDRESS_LO */
#define PCI_MSI_ADDRESS_LO_MASK (~0x3)
diff --git a/hw/pci/pci-stub.c b/hw/pci/pci-stub.c
index 5e564c3a87..f8f237e823 100644
--- a/hw/pci/pci-stub.c
+++ b/hw/pci/pci-stub.c
@@ -29,19 +29,7 @@ PciInfoList *qmp_query_pci(Error **errp)
return NULL;
}
-static void pci_error_message(Monitor *mon)
+void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict)
{
monitor_printf(mon, "PCI devices not supported\n");
}
-
-int hmp_pcie_aer_inject_error(Monitor *mon,
- const QDict *qdict, QObject **ret_data)
-{
- pci_error_message(mon);
- return -ENOSYS;
-}
-
-void pcie_aer_inject_error_print(Monitor *mon, const QObject *data)
-{
- pci_error_message(mon);
-}
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 4989408ece..21580433d4 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -147,7 +147,7 @@ static uint16_t pci_default_sub_device_id = PCI_SUBDEVICE_ID_QEMU;
static QLIST_HEAD(, PCIHostState) pci_host_bridges;
-static int pci_bar(PCIDevice *d, int reg)
+int pci_bar(PCIDevice *d, int reg)
{
uint8_t type;
@@ -1698,6 +1698,8 @@ PCIDevice *pci_vga_init(PCIBus *bus)
return pci_create_simple(bus, -1, "VGA");
case VGA_VMWARE:
return pci_create_simple(bus, -1, "vmware-svga");
+ case VGA_VIRTIO:
+ return pci_create_simple(bus, -1, "virtio-vga");
case VGA_NONE:
default: /* Other non-PCI types. Checking for unsupported types is already
done in vl.c. */
diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c
index b48c09cd11..c8dea8ed9c 100644
--- a/hw/pci/pcie_aer.c
+++ b/hw/pci/pcie_aer.c
@@ -815,21 +815,6 @@ const VMStateDescription vmstate_pcie_aer_log = {
}
};
-void pcie_aer_inject_error_print(Monitor *mon, const QObject *data)
-{
- QDict *qdict;
- int devfn;
- assert(qobject_type(data) == QTYPE_QDICT);
- qdict = qobject_to_qdict(data);
-
- devfn = (int)qdict_get_int(qdict, "devfn");
- monitor_printf(mon, "OK id: %s root bus: %s, bus: %x devfn: %x.%x\n",
- qdict_get_str(qdict, "id"),
- qdict_get_str(qdict, "root_bus"),
- (int) qdict_get_int(qdict, "bus"),
- PCI_SLOT(devfn), PCI_FUNC(devfn));
-}
-
typedef struct PCIEAERErrorName {
const char *name;
uint32_t val;
@@ -962,8 +947,8 @@ static int pcie_aer_parse_error_string(const char *error_name,
return -EINVAL;
}
-int hmp_pcie_aer_inject_error(Monitor *mon,
- const QDict *qdict, QObject **ret_data)
+static int do_pcie_aer_inject_error(Monitor *mon,
+ const QDict *qdict, QObject **ret_data)
{
const char *id = qdict_get_str(qdict, "id");
const char *error_name;
@@ -1035,3 +1020,23 @@ int hmp_pcie_aer_inject_error(Monitor *mon,
return 0;
}
+
+void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict)
+{
+ QObject *data;
+ int devfn;
+
+ if (do_pcie_aer_inject_error(mon, qdict, &data) < 0) {
+ return;
+ }
+
+ assert(qobject_type(data) == QTYPE_QDICT);
+ qdict = qobject_to_qdict(data);
+
+ devfn = (int)qdict_get_int(qdict, "devfn");
+ monitor_printf(mon, "OK id: %s root bus: %s, bus: %x devfn: %x.%x\n",
+ qdict_get_str(qdict, "id"),
+ qdict_get_str(qdict, "root_bus"),
+ (int) qdict_get_int(qdict, "bus"),
+ PCI_SLOT(devfn), PCI_FUNC(devfn));
+}
diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index 437955d1d5..c8ab06e7f3 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -3,7 +3,7 @@ obj-y += ppc.o ppc_booke.o
# IBM pSeries (sPAPR)
obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o
obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
-obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o
+obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o
ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
obj-y += spapr_pci_vfio.o
endif
diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
index c10e1b57b6..d300846c3d 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -1030,6 +1030,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
exit(1);
}
}
+ g_free(filename);
/* Reserve space for dtb */
dt_base = (loadaddr + bios_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK;
diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c
index a365bf9223..0f3e34122a 100644
--- a/hw/ppc/mac_newworld.c
+++ b/hw/ppc/mac_newworld.c
@@ -119,7 +119,7 @@ static const MemoryRegionOps unin_ops = {
static void fw_cfg_boot_set(void *opaque, const char *boot_device,
Error **errp)
{
- fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
+ fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
}
static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c
index f26133dedd..99879dd2d5 100644
--- a/hw/ppc/mac_oldworld.c
+++ b/hw/ppc/mac_oldworld.c
@@ -52,7 +52,7 @@
static void fw_cfg_boot_set(void *opaque, const char *boot_device,
Error **errp)
{
- fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
+ fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
}
static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c
index 7f52662d76..998ee2d16b 100644
--- a/hw/ppc/prep.c
+++ b/hw/ppc/prep.c
@@ -528,7 +528,6 @@ static void ppc_prep_init(MachineState *machine)
PCIDevice *pci;
ISABus *isa_bus;
ISADevice *isa;
- qemu_irq *cpu_exit_irq;
int ppc_boot_device;
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
@@ -625,11 +624,11 @@ static void ppc_prep_init(MachineState *machine)
/* PCI -> ISA bridge */
pci = pci_create_simple(pci_bus, PCI_DEVFN(1, 0), "i82378");
- cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
cpu = POWERPC_CPU(first_cpu);
qdev_connect_gpio_out(&pci->qdev, 0,
cpu->env.irq_inputs[PPC6xx_INPUT_INT]);
- qdev_connect_gpio_out(&pci->qdev, 1, *cpu_exit_irq);
+ qdev_connect_gpio_out(&pci->qdev, 1,
+ qemu_allocate_irq(cpu_request_exit, NULL, 0));
sysbus_connect_irq(&pcihost->busdev, 0, qdev_get_gpio_in(&pci->qdev, 9));
sysbus_connect_irq(&pcihost->busdev, 1, qdev_get_gpio_in(&pci->qdev, 11));
sysbus_connect_irq(&pcihost->busdev, 2, qdev_get_gpio_in(&pci->qdev, 9));
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index a15fa3c965..f174e5a0f3 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -533,6 +533,8 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base,
refpoints, sizeof(refpoints))));
_FDT((fdt_property_cell(fdt, "rtas-error-log-max", RTAS_ERROR_LOG_MAX)));
+ _FDT((fdt_property_cell(fdt, "rtas-event-scan-rate",
+ RTAS_EVENT_SCAN_RATE)));
/*
* According to PAPR, rtas ibm,os-term does not guarantee a return
@@ -794,8 +796,8 @@ static void spapr_finalize_fdt(sPAPREnvironment *spapr,
_FDT((fdt_pack(fdt)));
if (fdt_totalsize(fdt) > FDT_MAX_SIZE) {
- hw_error("FDT too big ! 0x%x bytes (max is 0x%x)\n",
- fdt_totalsize(fdt), FDT_MAX_SIZE);
+ error_report("FDT too big ! 0x%x bytes (max is 0x%x)",
+ fdt_totalsize(fdt), FDT_MAX_SIZE);
exit(1);
}
@@ -899,7 +901,7 @@ static int spapr_check_htab_fd(sPAPREnvironment *spapr)
spapr->htab_fd = kvmppc_get_htab_fd(false);
if (spapr->htab_fd < 0) {
error_report("Unable to open fd for reading hash table from KVM: "
- "%s", strerror(errno));
+ "%s", strerror(errno));
rc = -1;
}
spapr->htab_fd_stale = false;
@@ -1419,7 +1421,7 @@ static void ppc_spapr_init(MachineState *machine)
rma_alloc_size = kvmppc_alloc_rma(&rma);
if (rma_alloc_size == -1) {
- hw_error("qemu: Unable to create RMA\n");
+ error_report("Unable to create RMA");
exit(1);
}
@@ -1504,6 +1506,11 @@ static void ppc_spapr_init(MachineState *machine)
qemu_register_reset(spapr_cpu_reset, cpu);
}
+ if (kvm_enabled()) {
+ /* Enable H_LOGICAL_CI_* so SLOF can talk to in-kernel devices */
+ kvmppc_enable_logical_ci_hcalls();
+ }
+
/* allocate RAM */
spapr->ram_limit = ram_size;
memory_region_allocate_system_memory(ram, NULL, "ppc_spapr.ram",
@@ -1520,18 +1527,18 @@ static void ppc_spapr_init(MachineState *machine)
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "spapr-rtas.bin");
if (!filename) {
- hw_error("Could not find LPAR rtas '%s'\n", "spapr-rtas.bin");
+ error_report("Could not find LPAR rtas '%s'", "spapr-rtas.bin");
exit(1);
}
spapr->rtas_size = get_image_size(filename);
spapr->rtas_blob = g_malloc(spapr->rtas_size);
if (load_image_size(filename, spapr->rtas_blob, spapr->rtas_size) < 0) {
- hw_error("qemu: could not load LPAR rtas '%s'\n", filename);
+ error_report("Could not load LPAR rtas '%s'", filename);
exit(1);
}
if (spapr->rtas_size > RTAS_MAX_SIZE) {
- hw_error("RTAS too big ! 0x%zx bytes (max is 0x%x)\n",
- (size_t)spapr->rtas_size, RTAS_MAX_SIZE);
+ error_report("RTAS too big ! 0x%zx bytes (max is 0x%x)",
+ (size_t)spapr->rtas_size, RTAS_MAX_SIZE);
exit(1);
}
g_free(filename);
@@ -1641,12 +1648,12 @@ static void ppc_spapr_init(MachineState *machine)
}
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
if (!filename) {
- hw_error("Could not find LPAR rtas '%s'\n", bios_name);
+ error_report("Could not find LPAR firmware '%s'", bios_name);
exit(1);
}
fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE);
- if (fw_size < 0) {
- hw_error("qemu: could not load LPAR rtas '%s'\n", filename);
+ if (fw_size <= 0) {
+ error_report("Could not load LPAR firmware '%s'", filename);
exit(1);
}
g_free(filename);
@@ -1660,9 +1667,14 @@ static void ppc_spapr_init(MachineState *machine)
/* Prepare the device tree */
spapr->fdt_skel = spapr_create_fdt_skel(initrd_base, initrd_size,
kernel_size, kernel_le,
- kernel_cmdline, spapr->epow_irq);
+ kernel_cmdline,
+ spapr->check_exception_irq);
assert(spapr->fdt_skel != NULL);
+ /* used by RTAS */
+ QTAILQ_INIT(&spapr->ccs_list);
+ qemu_register_reset(spapr_ccs_reset_hook, spapr);
+
qemu_register_boot_set(spapr_boot_set, spapr);
}
@@ -1794,6 +1806,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
mc->max_cpus = MAX_CPUS;
mc->no_parallel = 1;
mc->default_boot_order = "";
+ mc->default_ram_size = 512 * M_BYTE;
mc->kvm_type = spapr_kvm_type;
mc->has_dynamic_sysbus = true;
@@ -1816,7 +1829,12 @@ static const TypeInfo spapr_machine_info = {
};
#define SPAPR_COMPAT_2_3 \
- HW_COMPAT_2_3
+ HW_COMPAT_2_3 \
+ {\
+ .driver = "spapr-pci-host-bridge",\
+ .property = "dynamic-reconfiguration",\
+ .value = "off",\
+ },
#define SPAPR_COMPAT_2_2 \
SPAPR_COMPAT_2_3 \
@@ -1905,10 +1923,15 @@ static const TypeInfo spapr_machine_2_2_info = {
static void spapr_machine_2_3_class_init(ObjectClass *oc, void *data)
{
+ static GlobalProperty compat_props[] = {
+ SPAPR_COMPAT_2_3
+ { /* end of list */ }
+ };
MachineClass *mc = MACHINE_CLASS(oc);
mc->name = "pseries-2.3";
mc->desc = "pSeries Logical Partition (PAPR compliant) v2.3";
+ mc->compat_props = compat_props;
}
static const TypeInfo spapr_machine_2_3_info = {
diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
new file mode 100644
index 0000000000..ef985381cb
--- /dev/null
+++ b/hw/ppc/spapr_drc.c
@@ -0,0 +1,744 @@
+/*
+ * QEMU SPAPR Dynamic Reconfiguration Connector Implementation
+ *
+ * Copyright IBM Corp. 2014
+ *
+ * Authors:
+ * Michael Roth <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "hw/ppc/spapr_drc.h"
+#include "qom/object.h"
+#include "hw/qdev.h"
+#include "qapi/visitor.h"
+#include "qemu/error-report.h"
+
+/* #define DEBUG_SPAPR_DRC */
+
+#ifdef DEBUG_SPAPR_DRC
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#define DPRINTFN(fmt, ...) \
+ do { DPRINTF(fmt, ## __VA_ARGS__); fprintf(stderr, "\n"); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+ do { } while (0)
+#define DPRINTFN(fmt, ...) \
+ do { } while (0)
+#endif
+
+#define DRC_CONTAINER_PATH "/dr-connector"
+#define DRC_INDEX_TYPE_SHIFT 28
+#define DRC_INDEX_ID_MASK (~(~0 << DRC_INDEX_TYPE_SHIFT))
+
+static sPAPRDRConnectorTypeShift get_type_shift(sPAPRDRConnectorType type)
+{
+ uint32_t shift = 0;
+
+ /* make sure this isn't SPAPR_DR_CONNECTOR_TYPE_ANY, or some
+ * other wonky value.
+ */
+ g_assert(is_power_of_2(type));
+
+ while (type != (1 << shift)) {
+ shift++;
+ }
+ return shift;
+}
+
+static uint32_t get_index(sPAPRDRConnector *drc)
+{
+ /* no set format for a drc index: it only needs to be globally
+ * unique. this is how we encode the DRC type on bare-metal
+ * however, so might as well do that here
+ */
+ return (get_type_shift(drc->type) << DRC_INDEX_TYPE_SHIFT) |
+ (drc->id & DRC_INDEX_ID_MASK);
+}
+
+static int set_isolation_state(sPAPRDRConnector *drc,
+ sPAPRDRIsolationState state)
+{
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+
+ DPRINTFN("drc: %x, set_isolation_state: %x", get_index(drc), state);
+
+ drc->isolation_state = state;
+
+ if (drc->isolation_state == SPAPR_DR_ISOLATION_STATE_ISOLATED) {
+ /* if we're awaiting release, but still in an unconfigured state,
+ * it's likely the guest is still in the process of configuring
+ * the device and is transitioning the devices to an ISOLATED
+ * state as a part of that process. so we only complete the
+ * removal when this transition happens for a device in a
+ * configured state, as suggested by the state diagram from
+ * PAPR+ 2.7, 13.4
+ */
+ if (drc->awaiting_release) {
+ if (drc->configured) {
+ DPRINTFN("finalizing device removal");
+ drck->detach(drc, DEVICE(drc->dev), drc->detach_cb,
+ drc->detach_cb_opaque, NULL);
+ } else {
+ DPRINTFN("deferring device removal on unconfigured device\n");
+ }
+ }
+ drc->configured = false;
+ }
+
+ return 0;
+}
+
+static int set_indicator_state(sPAPRDRConnector *drc,
+ sPAPRDRIndicatorState state)
+{
+ DPRINTFN("drc: %x, set_indicator_state: %x", get_index(drc), state);
+ drc->indicator_state = state;
+ return 0;
+}
+
+static int set_allocation_state(sPAPRDRConnector *drc,
+ sPAPRDRAllocationState state)
+{
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+
+ DPRINTFN("drc: %x, set_allocation_state: %x", get_index(drc), state);
+
+ if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI) {
+ drc->allocation_state = state;
+ if (drc->awaiting_release &&
+ drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) {
+ DPRINTFN("finalizing device removal");
+ drck->detach(drc, DEVICE(drc->dev), drc->detach_cb,
+ drc->detach_cb_opaque, NULL);
+ }
+ }
+ return 0;
+}
+
+static uint32_t get_type(sPAPRDRConnector *drc)
+{
+ return drc->type;
+}
+
+static const char *get_name(sPAPRDRConnector *drc)
+{
+ return drc->name;
+}
+
+static const void *get_fdt(sPAPRDRConnector *drc, int *fdt_start_offset)
+{
+ if (fdt_start_offset) {
+ *fdt_start_offset = drc->fdt_start_offset;
+ }
+ return drc->fdt;
+}
+
+static void set_configured(sPAPRDRConnector *drc)
+{
+ DPRINTFN("drc: %x, set_configured", get_index(drc));
+
+ if (drc->isolation_state != SPAPR_DR_ISOLATION_STATE_UNISOLATED) {
+ /* guest should be not configuring an isolated device */
+ DPRINTFN("drc: %x, set_configured: skipping isolated device",
+ get_index(drc));
+ return;
+ }
+ drc->configured = true;
+}
+
+/*
+ * dr-entity-sense sensor value
+ * returned via get-sensor-state RTAS calls
+ * as expected by state diagram in PAPR+ 2.7, 13.4
+ * based on the current allocation/indicator/power states
+ * for the DR connector.
+ */
+static sPAPRDREntitySense entity_sense(sPAPRDRConnector *drc)
+{
+ sPAPRDREntitySense state;
+
+ if (drc->dev) {
+ if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI &&
+ drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) {
+ /* for logical DR, we return a state of UNUSABLE
+ * iff the allocation state UNUSABLE.
+ * Otherwise, report the state as USABLE/PRESENT,
+ * as we would for PCI.
+ */
+ state = SPAPR_DR_ENTITY_SENSE_UNUSABLE;
+ } else {
+ /* this assumes all PCI devices are assigned to
+ * a 'live insertion' power domain, where QEMU
+ * manages power state automatically as opposed
+ * to the guest. present, non-PCI resources are
+ * unaffected by power state.
+ */
+ state = SPAPR_DR_ENTITY_SENSE_PRESENT;
+ }
+ } else {
+ if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) {
+ /* PCI devices, and only PCI devices, use EMPTY
+ * in cases where we'd otherwise use UNUSABLE
+ */
+ state = SPAPR_DR_ENTITY_SENSE_EMPTY;
+ } else {
+ state = SPAPR_DR_ENTITY_SENSE_UNUSABLE;
+ }
+ }
+
+ DPRINTFN("drc: %x, entity_sense: %x", get_index(drc), state);
+ return state;
+}
+
+static void prop_get_index(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ uint32_t value = (uint32_t)drck->get_index(drc);
+ visit_type_uint32(v, &value, name, errp);
+}
+
+static void prop_get_type(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ uint32_t value = (uint32_t)drck->get_type(drc);
+ visit_type_uint32(v, &value, name, errp);
+}
+
+static char *prop_get_name(Object *obj, Error **errp)
+{
+ sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ return g_strdup(drck->get_name(drc));
+}
+
+static void prop_get_entity_sense(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ uint32_t value = (uint32_t)drck->entity_sense(drc);
+ visit_type_uint32(v, &value, name, errp);
+}
+
+static void prop_get_fdt(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
+ int fdt_offset_next, fdt_offset, fdt_depth;
+ void *fdt;
+
+ if (!drc->fdt) {
+ return;
+ }
+
+ fdt = drc->fdt;
+ fdt_offset = drc->fdt_start_offset;
+ fdt_depth = 0;
+
+ do {
+ const char *name = NULL;
+ const struct fdt_property *prop = NULL;
+ int prop_len = 0, name_len = 0;
+ uint32_t tag;
+
+ tag = fdt_next_tag(fdt, fdt_offset, &fdt_offset_next);
+ switch (tag) {
+ case FDT_BEGIN_NODE:
+ fdt_depth++;
+ name = fdt_get_name(fdt, fdt_offset, &name_len);
+ visit_start_struct(v, NULL, NULL, name, 0, NULL);
+ break;
+ case FDT_END_NODE:
+ /* shouldn't ever see an FDT_END_NODE before FDT_BEGIN_NODE */
+ g_assert(fdt_depth > 0);
+ visit_end_struct(v, NULL);
+ fdt_depth--;
+ break;
+ case FDT_PROP: {
+ int i;
+ prop = fdt_get_property_by_offset(fdt, fdt_offset, &prop_len);
+ name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
+ visit_start_list(v, name, NULL);
+ for (i = 0; i < prop_len; i++) {
+ visit_type_uint8(v, (uint8_t *)&prop->data[i], NULL, NULL);
+
+ }
+ visit_end_list(v, NULL);
+ break;
+ }
+ default:
+ error_setg(&error_abort, "device FDT in unexpected state: %d", tag);
+ }
+ fdt_offset = fdt_offset_next;
+ } while (fdt_depth != 0);
+}
+
+static void attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt,
+ int fdt_start_offset, bool coldplug, Error **errp)
+{
+ DPRINTFN("drc: %x, attach", get_index(drc));
+
+ if (drc->isolation_state != SPAPR_DR_ISOLATION_STATE_ISOLATED) {
+ error_setg(errp, "an attached device is still awaiting release");
+ return;
+ }
+ if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) {
+ g_assert(drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_USABLE);
+ }
+ g_assert(fdt || coldplug);
+
+ /* NOTE: setting initial isolation state to UNISOLATED means we can't
+ * detach unless guest has a userspace/kernel that moves this state
+ * back to ISOLATED in response to an unplug event, or this is done
+ * manually by the admin prior. if we force things while the guest
+ * may be accessing the device, we can easily crash the guest, so we
+ * we defer completion of removal in such cases to the reset() hook.
+ */
+ if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) {
+ drc->isolation_state = SPAPR_DR_ISOLATION_STATE_UNISOLATED;
+ }
+ drc->indicator_state = SPAPR_DR_INDICATOR_STATE_ACTIVE;
+
+ drc->dev = d;
+ drc->fdt = fdt;
+ drc->fdt_start_offset = fdt_start_offset;
+ drc->configured = false;
+
+ object_property_add_link(OBJECT(drc), "device",
+ object_get_typename(OBJECT(drc->dev)),
+ (Object **)(&drc->dev),
+ NULL, 0, NULL);
+}
+
+static void detach(sPAPRDRConnector *drc, DeviceState *d,
+ spapr_drc_detach_cb *detach_cb,
+ void *detach_cb_opaque, Error **errp)
+{
+ DPRINTFN("drc: %x, detach", get_index(drc));
+
+ drc->detach_cb = detach_cb;
+ drc->detach_cb_opaque = detach_cb_opaque;
+
+ if (drc->isolation_state != SPAPR_DR_ISOLATION_STATE_ISOLATED) {
+ DPRINTFN("awaiting transition to isolated state before removal");
+ drc->awaiting_release = true;
+ return;
+ }
+
+ if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI &&
+ drc->allocation_state != SPAPR_DR_ALLOCATION_STATE_UNUSABLE) {
+ DPRINTFN("awaiting transition to unusable state before removal");
+ drc->awaiting_release = true;
+ return;
+ }
+
+ drc->indicator_state = SPAPR_DR_INDICATOR_STATE_INACTIVE;
+
+ if (drc->detach_cb) {
+ drc->detach_cb(drc->dev, drc->detach_cb_opaque);
+ }
+
+ drc->awaiting_release = false;
+ g_free(drc->fdt);
+ drc->fdt = NULL;
+ drc->fdt_start_offset = 0;
+ object_property_del(OBJECT(drc), "device", NULL);
+ drc->dev = NULL;
+ drc->detach_cb = NULL;
+ drc->detach_cb_opaque = NULL;
+}
+
+static bool release_pending(sPAPRDRConnector *drc)
+{
+ return drc->awaiting_release;
+}
+
+static void reset(DeviceState *d)
+{
+ sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d);
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+
+ DPRINTFN("drc reset: %x", drck->get_index(drc));
+ /* immediately upon reset we can safely assume DRCs whose devices
+ * are pending removal can be safely removed, and that they will
+ * subsequently be left in an ISOLATED state. move the DRC to this
+ * state in these cases (which will in turn complete any pending
+ * device removals)
+ */
+ if (drc->awaiting_release) {
+ drck->set_isolation_state(drc, SPAPR_DR_ISOLATION_STATE_ISOLATED);
+ /* generally this should also finalize the removal, but if the device
+ * hasn't yet been configured we normally defer removal under the
+ * assumption that this transition is taking place as part of device
+ * configuration. so check if we're still waiting after this, and
+ * force removal if we are
+ */
+ if (drc->awaiting_release) {
+ drck->detach(drc, DEVICE(drc->dev), drc->detach_cb,
+ drc->detach_cb_opaque, NULL);
+ }
+
+ /* non-PCI devices may be awaiting a transition to UNUSABLE */
+ if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI &&
+ drc->awaiting_release) {
+ drck->set_allocation_state(drc, SPAPR_DR_ALLOCATION_STATE_UNUSABLE);
+ }
+ }
+}
+
+static void realize(DeviceState *d, Error **errp)
+{
+ sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d);
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ Object *root_container;
+ char link_name[256];
+ gchar *child_name;
+ Error *err = NULL;
+
+ DPRINTFN("drc realize: %x", drck->get_index(drc));
+ /* NOTE: we do this as part of realize/unrealize due to the fact
+ * that the guest will communicate with the DRC via RTAS calls
+ * referencing the global DRC index. By unlinking the DRC
+ * from DRC_CONTAINER_PATH/<drc_index> we effectively make it
+ * inaccessible by the guest, since lookups rely on this path
+ * existing in the composition tree
+ */
+ root_container = container_get(object_get_root(), DRC_CONTAINER_PATH);
+ snprintf(link_name, sizeof(link_name), "%x", drck->get_index(drc));
+ child_name = object_get_canonical_path_component(OBJECT(drc));
+ DPRINTFN("drc child name: %s", child_name);
+ object_property_add_alias(root_container, link_name,
+ drc->owner, child_name, &err);
+ if (err) {
+ error_report("%s", error_get_pretty(err));
+ error_free(err);
+ object_unref(OBJECT(drc));
+ }
+ DPRINTFN("drc realize complete");
+}
+
+static void unrealize(DeviceState *d, Error **errp)
+{
+ sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d);
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ Object *root_container;
+ char name[256];
+ Error *err = NULL;
+
+ DPRINTFN("drc unrealize: %x", drck->get_index(drc));
+ root_container = container_get(object_get_root(), DRC_CONTAINER_PATH);
+ snprintf(name, sizeof(name), "%x", drck->get_index(drc));
+ object_property_del(root_container, name, &err);
+ if (err) {
+ error_report("%s", error_get_pretty(err));
+ error_free(err);
+ object_unref(OBJECT(drc));
+ }
+}
+
+sPAPRDRConnector *spapr_dr_connector_new(Object *owner,
+ sPAPRDRConnectorType type,
+ uint32_t id)
+{
+ sPAPRDRConnector *drc =
+ SPAPR_DR_CONNECTOR(object_new(TYPE_SPAPR_DR_CONNECTOR));
+
+ g_assert(type);
+
+ drc->type = type;
+ drc->id = id;
+ drc->owner = owner;
+ object_property_add_child(owner, "dr-connector[*]", OBJECT(drc), NULL);
+ object_property_set_bool(OBJECT(drc), true, "realized", NULL);
+
+ /* human-readable name for a DRC to encode into the DT
+ * description. this is mainly only used within a guest in place
+ * of the unique DRC index.
+ *
+ * in the case of VIO/PCI devices, it corresponds to a
+ * "location code" that maps a logical device/function (DRC index)
+ * to a physical (or virtual in the case of VIO) location in the
+ * system by chaining together the "location label" for each
+ * encapsulating component.
+ *
+ * since this is more to do with diagnosing physical hardware
+ * issues than guest compatibility, we choose location codes/DRC
+ * names that adhere to the documented format, but avoid encoding
+ * the entire topology information into the label/code, instead
+ * just using the location codes based on the labels for the
+ * endpoints (VIO/PCI adaptor connectors), which is basically
+ * just "C" followed by an integer ID.
+ *
+ * DRC names as documented by PAPR+ v2.7, 13.5.2.4
+ * location codes as documented by PAPR+ v2.7, 12.3.1.5
+ */
+ switch (drc->type) {
+ case SPAPR_DR_CONNECTOR_TYPE_CPU:
+ drc->name = g_strdup_printf("CPU %d", id);
+ break;
+ case SPAPR_DR_CONNECTOR_TYPE_PHB:
+ drc->name = g_strdup_printf("PHB %d", id);
+ break;
+ case SPAPR_DR_CONNECTOR_TYPE_VIO:
+ case SPAPR_DR_CONNECTOR_TYPE_PCI:
+ drc->name = g_strdup_printf("C%d", id);
+ break;
+ case SPAPR_DR_CONNECTOR_TYPE_LMB:
+ drc->name = g_strdup_printf("LMB %d", id);
+ break;
+ default:
+ g_assert(false);
+ }
+
+ /* PCI slot always start in a USABLE state, and stay there */
+ if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) {
+ drc->allocation_state = SPAPR_DR_ALLOCATION_STATE_USABLE;
+ }
+
+ return drc;
+}
+
+static void spapr_dr_connector_instance_init(Object *obj)
+{
+ sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj);
+
+ object_property_add_uint32_ptr(obj, "isolation-state",
+ &drc->isolation_state, NULL);
+ object_property_add_uint32_ptr(obj, "indicator-state",
+ &drc->indicator_state, NULL);
+ object_property_add_uint32_ptr(obj, "allocation-state",
+ &drc->allocation_state, NULL);
+ object_property_add_uint32_ptr(obj, "id", &drc->id, NULL);
+ object_property_add(obj, "index", "uint32", prop_get_index,
+ NULL, NULL, NULL, NULL);
+ object_property_add(obj, "connector_type", "uint32", prop_get_type,
+ NULL, NULL, NULL, NULL);
+ object_property_add_str(obj, "name", prop_get_name, NULL, NULL);
+ object_property_add(obj, "entity-sense", "uint32", prop_get_entity_sense,
+ NULL, NULL, NULL, NULL);
+ object_property_add(obj, "fdt", "struct", prop_get_fdt,
+ NULL, NULL, NULL, NULL);
+}
+
+static void spapr_dr_connector_class_init(ObjectClass *k, void *data)
+{
+ DeviceClass *dk = DEVICE_CLASS(k);
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k);
+
+ dk->reset = reset;
+ dk->realize = realize;
+ dk->unrealize = unrealize;
+ drck->set_isolation_state = set_isolation_state;
+ drck->set_indicator_state = set_indicator_state;
+ drck->set_allocation_state = set_allocation_state;
+ drck->get_index = get_index;
+ drck->get_type = get_type;
+ drck->get_name = get_name;
+ drck->get_fdt = get_fdt;
+ drck->set_configured = set_configured;
+ drck->entity_sense = entity_sense;
+ drck->attach = attach;
+ drck->detach = detach;
+ drck->release_pending = release_pending;
+}
+
+static const TypeInfo spapr_dr_connector_info = {
+ .name = TYPE_SPAPR_DR_CONNECTOR,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(sPAPRDRConnector),
+ .instance_init = spapr_dr_connector_instance_init,
+ .class_size = sizeof(sPAPRDRConnectorClass),
+ .class_init = spapr_dr_connector_class_init,
+};
+
+static void spapr_drc_register_types(void)
+{
+ type_register_static(&spapr_dr_connector_info);
+}
+
+type_init(spapr_drc_register_types)
+
+/* helper functions for external users */
+
+sPAPRDRConnector *spapr_dr_connector_by_index(uint32_t index)
+{
+ Object *obj;
+ char name[256];
+
+ snprintf(name, sizeof(name), "%s/%x", DRC_CONTAINER_PATH, index);
+ obj = object_resolve_path(name, NULL);
+
+ return !obj ? NULL : SPAPR_DR_CONNECTOR(obj);
+}
+
+sPAPRDRConnector *spapr_dr_connector_by_id(sPAPRDRConnectorType type,
+ uint32_t id)
+{
+ return spapr_dr_connector_by_index(
+ (get_type_shift(type) << DRC_INDEX_TYPE_SHIFT) |
+ (id & DRC_INDEX_ID_MASK));
+}
+
+/* generate a string the describes the DRC to encode into the
+ * device tree.
+ *
+ * as documented by PAPR+ v2.7, 13.5.2.6 and C.6.1
+ */
+static const char *spapr_drc_get_type_str(sPAPRDRConnectorType type)
+{
+ switch (type) {
+ case SPAPR_DR_CONNECTOR_TYPE_CPU:
+ return "CPU";
+ case SPAPR_DR_CONNECTOR_TYPE_PHB:
+ return "PHB";
+ case SPAPR_DR_CONNECTOR_TYPE_VIO:
+ return "SLOT";
+ case SPAPR_DR_CONNECTOR_TYPE_PCI:
+ return "28";
+ case SPAPR_DR_CONNECTOR_TYPE_LMB:
+ return "MEM";
+ default:
+ g_assert(false);
+ }
+
+ return NULL;
+}
+
+/**
+ * spapr_drc_populate_dt
+ *
+ * @fdt: libfdt device tree
+ * @path: path in the DT to generate properties
+ * @owner: parent Object/DeviceState for which to generate DRC
+ * descriptions for
+ * @drc_type_mask: mask of sPAPRDRConnectorType values corresponding
+ * to the types of DRCs to generate entries for
+ *
+ * generate OF properties to describe DRC topology/indices to guests
+ *
+ * as documented in PAPR+ v2.1, 13.5.2
+ */
+int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner,
+ uint32_t drc_type_mask)
+{
+ Object *root_container;
+ ObjectProperty *prop;
+ uint32_t drc_count = 0;
+ GArray *drc_indexes, *drc_power_domains;
+ GString *drc_names, *drc_types;
+ int ret;
+
+ /* the first entry of each properties is a 32-bit integer encoding
+ * the number of elements in the array. we won't know this until
+ * we complete the iteration through all the matching DRCs, but
+ * reserve the space now and set the offsets accordingly so we
+ * can fill them in later.
+ */
+ drc_indexes = g_array_new(false, true, sizeof(uint32_t));
+ drc_indexes = g_array_set_size(drc_indexes, 1);
+ drc_power_domains = g_array_new(false, true, sizeof(uint32_t));
+ drc_power_domains = g_array_set_size(drc_power_domains, 1);
+ drc_names = g_string_set_size(g_string_new(NULL), sizeof(uint32_t));
+ drc_types = g_string_set_size(g_string_new(NULL), sizeof(uint32_t));
+
+ /* aliases for all DRConnector objects will be rooted in QOM
+ * composition tree at DRC_CONTAINER_PATH
+ */
+ root_container = container_get(object_get_root(), DRC_CONTAINER_PATH);
+
+ QTAILQ_FOREACH(prop, &root_container->properties, node) {
+ Object *obj;
+ sPAPRDRConnector *drc;
+ sPAPRDRConnectorClass *drck;
+ uint32_t drc_index, drc_power_domain;
+
+ if (!strstart(prop->type, "link<", NULL)) {
+ continue;
+ }
+
+ obj = object_property_get_link(root_container, prop->name, NULL);
+ drc = SPAPR_DR_CONNECTOR(obj);
+ drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+
+ if (owner && (drc->owner != owner)) {
+ continue;
+ }
+
+ if ((drc->type & drc_type_mask) == 0) {
+ continue;
+ }
+
+ drc_count++;
+
+ /* ibm,drc-indexes */
+ drc_index = cpu_to_be32(drck->get_index(drc));
+ g_array_append_val(drc_indexes, drc_index);
+
+ /* ibm,drc-power-domains */
+ drc_power_domain = cpu_to_be32(-1);
+ g_array_append_val(drc_power_domains, drc_power_domain);
+
+ /* ibm,drc-names */
+ drc_names = g_string_append(drc_names, drck->get_name(drc));
+ drc_names = g_string_insert_len(drc_names, -1, "\0", 1);
+
+ /* ibm,drc-types */
+ drc_types = g_string_append(drc_types,
+ spapr_drc_get_type_str(drc->type));
+ drc_types = g_string_insert_len(drc_types, -1, "\0", 1);
+ }
+
+ /* now write the drc count into the space we reserved at the
+ * beginning of the arrays previously
+ */
+ *(uint32_t *)drc_indexes->data = cpu_to_be32(drc_count);
+ *(uint32_t *)drc_power_domains->data = cpu_to_be32(drc_count);
+ *(uint32_t *)drc_names->str = cpu_to_be32(drc_count);
+ *(uint32_t *)drc_types->str = cpu_to_be32(drc_count);
+
+ ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-indexes",
+ drc_indexes->data,
+ drc_indexes->len * sizeof(uint32_t));
+ if (ret) {
+ fprintf(stderr, "Couldn't create ibm,drc-indexes property\n");
+ goto out;
+ }
+
+ ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-power-domains",
+ drc_power_domains->data,
+ drc_power_domains->len * sizeof(uint32_t));
+ if (ret) {
+ fprintf(stderr, "Couldn't finalize ibm,drc-power-domains property\n");
+ goto out;
+ }
+
+ ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-names",
+ drc_names->str, drc_names->len);
+ if (ret) {
+ fprintf(stderr, "Couldn't finalize ibm,drc-names property\n");
+ goto out;
+ }
+
+ ret = fdt_setprop(fdt, fdt_offset, "ibm,drc-types",
+ drc_types->str, drc_types->len);
+ if (ret) {
+ fprintf(stderr, "Couldn't finalize ibm,drc-types property\n");
+ goto out;
+ }
+
+out:
+ g_array_free(drc_indexes, true);
+ g_array_free(drc_power_domains, true);
+ g_string_free(drc_names, true);
+ g_string_free(drc_types, true);
+
+ return ret;
+}
diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c
index 283e96bca1..fda9e3590a 100644
--- a/hw/ppc/spapr_events.c
+++ b/hw/ppc/spapr_events.c
@@ -32,6 +32,9 @@
#include "hw/ppc/spapr.h"
#include "hw/ppc/spapr_vio.h"
+#include "hw/pci/pci.h"
+#include "hw/pci-host/spapr.h"
+#include "hw/ppc/spapr_drc.h"
#include <libfdt.h>
@@ -77,6 +80,7 @@ struct rtas_error_log {
#define RTAS_LOG_TYPE_ECC_UNCORR 0x00000009
#define RTAS_LOG_TYPE_ECC_CORR 0x0000000a
#define RTAS_LOG_TYPE_EPOW 0x00000040
+#define RTAS_LOG_TYPE_HOTPLUG 0x000000e5
uint32_t extended_length;
} QEMU_PACKED;
@@ -166,6 +170,38 @@ struct epow_log_full {
struct rtas_event_log_v6_epow epow;
} QEMU_PACKED;
+struct rtas_event_log_v6_hp {
+#define RTAS_LOG_V6_SECTION_ID_HOTPLUG 0x4850 /* HP */
+ struct rtas_event_log_v6_section_header hdr;
+ uint8_t hotplug_type;
+#define RTAS_LOG_V6_HP_TYPE_CPU 1
+#define RTAS_LOG_V6_HP_TYPE_MEMORY 2
+#define RTAS_LOG_V6_HP_TYPE_SLOT 3
+#define RTAS_LOG_V6_HP_TYPE_PHB 4
+#define RTAS_LOG_V6_HP_TYPE_PCI 5
+ uint8_t hotplug_action;
+#define RTAS_LOG_V6_HP_ACTION_ADD 1
+#define RTAS_LOG_V6_HP_ACTION_REMOVE 2
+ uint8_t hotplug_identifier;
+#define RTAS_LOG_V6_HP_ID_DRC_NAME 1
+#define RTAS_LOG_V6_HP_ID_DRC_INDEX 2
+#define RTAS_LOG_V6_HP_ID_DRC_COUNT 3
+ uint8_t reserved;
+ union {
+ uint32_t index;
+ uint32_t count;
+ char name[1];
+ } drc;
+} QEMU_PACKED;
+
+struct hp_log_full {
+ struct rtas_error_log hdr;
+ struct rtas_event_log_v6 v6hdr;
+ struct rtas_event_log_v6_maina maina;
+ struct rtas_event_log_v6_mainb mainb;
+ struct rtas_event_log_v6_hp hp;
+} QEMU_PACKED;
+
#define EVENT_MASK_INTERNAL_ERRORS 0x80000000
#define EVENT_MASK_EPOW 0x40000000
#define EVENT_MASK_HOTPLUG 0x10000000
@@ -181,67 +217,105 @@ struct epow_log_full {
} \
} while (0)
-void spapr_events_fdt_skel(void *fdt, uint32_t epow_irq)
+void spapr_events_fdt_skel(void *fdt, uint32_t check_exception_irq)
{
- uint32_t epow_irq_ranges[] = {cpu_to_be32(epow_irq), cpu_to_be32(1)};
- uint32_t epow_interrupts[] = {cpu_to_be32(epow_irq), 0};
+ uint32_t irq_ranges[] = {cpu_to_be32(check_exception_irq), cpu_to_be32(1)};
+ uint32_t interrupts[] = {cpu_to_be32(check_exception_irq), 0};
_FDT((fdt_begin_node(fdt, "event-sources")));
_FDT((fdt_property(fdt, "interrupt-controller", NULL, 0)));
_FDT((fdt_property_cell(fdt, "#interrupt-cells", 2)));
_FDT((fdt_property(fdt, "interrupt-ranges",
- epow_irq_ranges, sizeof(epow_irq_ranges))));
+ irq_ranges, sizeof(irq_ranges))));
_FDT((fdt_begin_node(fdt, "epow-events")));
- _FDT((fdt_property(fdt, "interrupts",
- epow_interrupts, sizeof(epow_interrupts))));
+ _FDT((fdt_property(fdt, "interrupts", interrupts, sizeof(interrupts))));
_FDT((fdt_end_node(fdt)));
_FDT((fdt_end_node(fdt)));
}
-static struct epow_log_full *pending_epow;
-static uint32_t next_plid;
+static void rtas_event_log_queue(int log_type, void *data, bool exception)
+{
+ sPAPREventLogEntry *entry = g_new(sPAPREventLogEntry, 1);
-static void spapr_powerdown_req(Notifier *n, void *opaque)
+ g_assert(data);
+ entry->log_type = log_type;
+ entry->exception = exception;
+ entry->data = data;
+ QTAILQ_INSERT_TAIL(&spapr->pending_events, entry, next);
+}
+
+static sPAPREventLogEntry *rtas_event_log_dequeue(uint32_t event_mask,
+ bool exception)
{
- sPAPREnvironment *spapr = container_of(n, sPAPREnvironment, epow_notifier);
- struct rtas_error_log *hdr;
- struct rtas_event_log_v6 *v6hdr;
- struct rtas_event_log_v6_maina *maina;
- struct rtas_event_log_v6_mainb *mainb;
- struct rtas_event_log_v6_epow *epow;
- struct tm tm;
- int year;
+ sPAPREventLogEntry *entry = NULL;
- if (pending_epow) {
- /* For now, we just throw away earlier events if two come
- * along before any are consumed. This is sufficient for our
- * powerdown messages, but we'll need more if we do more
- * general error/event logging */
- g_free(pending_epow);
+ /* we only queue EPOW events atm. */
+ if ((event_mask & EVENT_MASK_EPOW) == 0) {
+ return NULL;
}
- pending_epow = g_malloc0(sizeof(*pending_epow));
- hdr = &pending_epow->hdr;
- v6hdr = &pending_epow->v6hdr;
- maina = &pending_epow->maina;
- mainb = &pending_epow->mainb;
- epow = &pending_epow->epow;
- hdr->summary = cpu_to_be32(RTAS_LOG_VERSION_6
- | RTAS_LOG_SEVERITY_EVENT
- | RTAS_LOG_DISPOSITION_NOT_RECOVERED
- | RTAS_LOG_OPTIONAL_PART_PRESENT
- | RTAS_LOG_TYPE_EPOW);
- hdr->extended_length = cpu_to_be32(sizeof(*pending_epow)
- - sizeof(pending_epow->hdr));
+ QTAILQ_FOREACH(entry, &spapr->pending_events, next) {
+ if (entry->exception != exception) {
+ continue;
+ }
+
+ /* EPOW and hotplug events are surfaced in the same manner */
+ if (entry->log_type == RTAS_LOG_TYPE_EPOW ||
+ entry->log_type == RTAS_LOG_TYPE_HOTPLUG) {
+ break;
+ }
+ }
+
+ if (entry) {
+ QTAILQ_REMOVE(&spapr->pending_events, entry, next);
+ }
+ return entry;
+}
+
+static bool rtas_event_log_contains(uint32_t event_mask, bool exception)
+{
+ sPAPREventLogEntry *entry = NULL;
+
+ /* we only queue EPOW events atm. */
+ if ((event_mask & EVENT_MASK_EPOW) == 0) {
+ return false;
+ }
+
+ QTAILQ_FOREACH(entry, &spapr->pending_events, next) {
+ if (entry->exception != exception) {
+ continue;
+ }
+
+ /* EPOW and hotplug events are surfaced in the same manner */
+ if (entry->log_type == RTAS_LOG_TYPE_EPOW ||
+ entry->log_type == RTAS_LOG_TYPE_HOTPLUG) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static uint32_t next_plid;
+
+static void spapr_init_v6hdr(struct rtas_event_log_v6 *v6hdr)
+{
v6hdr->b0 = RTAS_LOG_V6_B0_VALID | RTAS_LOG_V6_B0_NEW_LOG
| RTAS_LOG_V6_B0_BIGENDIAN;
v6hdr->b2 = RTAS_LOG_V6_B2_POWERPC_FORMAT
| RTAS_LOG_V6_B2_LOG_FORMAT_PLATFORM_EVENT;
v6hdr->company = cpu_to_be32(RTAS_LOG_V6_COMPANY_IBM);
+}
+
+static void spapr_init_maina(struct rtas_event_log_v6_maina *maina,
+ int section_count)
+{
+ struct tm tm;
+ int year;
maina->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINA);
maina->hdr.section_length = cpu_to_be16(sizeof(*maina));
@@ -256,8 +330,37 @@ static void spapr_powerdown_req(Notifier *n, void *opaque)
| (to_bcd(tm.tm_min) << 16)
| (to_bcd(tm.tm_sec) << 8));
maina->creator_id = 'H'; /* Hypervisor */
- maina->section_count = 3; /* Main-A, Main-B and EPOW */
+ maina->section_count = section_count;
maina->plid = next_plid++;
+}
+
+static void spapr_powerdown_req(Notifier *n, void *opaque)
+{
+ sPAPREnvironment *spapr = container_of(n, sPAPREnvironment, epow_notifier);
+ struct rtas_error_log *hdr;
+ struct rtas_event_log_v6 *v6hdr;
+ struct rtas_event_log_v6_maina *maina;
+ struct rtas_event_log_v6_mainb *mainb;
+ struct rtas_event_log_v6_epow *epow;
+ struct epow_log_full *new_epow;
+
+ new_epow = g_malloc0(sizeof(*new_epow));
+ hdr = &new_epow->hdr;
+ v6hdr = &new_epow->v6hdr;
+ maina = &new_epow->maina;
+ mainb = &new_epow->mainb;
+ epow = &new_epow->epow;
+
+ hdr->summary = cpu_to_be32(RTAS_LOG_VERSION_6
+ | RTAS_LOG_SEVERITY_EVENT
+ | RTAS_LOG_DISPOSITION_NOT_RECOVERED
+ | RTAS_LOG_OPTIONAL_PART_PRESENT
+ | RTAS_LOG_TYPE_EPOW);
+ hdr->extended_length = cpu_to_be32(sizeof(*new_epow)
+ - sizeof(new_epow->hdr));
+
+ spapr_init_v6hdr(v6hdr);
+ spapr_init_maina(maina, 3 /* Main-A, Main-B and EPOW */);
mainb->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINB);
mainb->hdr.section_length = cpu_to_be16(sizeof(*mainb));
@@ -274,7 +377,80 @@ static void spapr_powerdown_req(Notifier *n, void *opaque)
epow->event_modifier = RTAS_LOG_V6_EPOW_MODIFIER_NORMAL;
epow->extended_modifier = RTAS_LOG_V6_EPOW_XMODIFIER_PARTITION_SPECIFIC;
- qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->epow_irq));
+ rtas_event_log_queue(RTAS_LOG_TYPE_EPOW, new_epow, true);
+
+ qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
+}
+
+static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action)
+{
+ struct hp_log_full *new_hp;
+ struct rtas_error_log *hdr;
+ struct rtas_event_log_v6 *v6hdr;
+ struct rtas_event_log_v6_maina *maina;
+ struct rtas_event_log_v6_mainb *mainb;
+ struct rtas_event_log_v6_hp *hp;
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ sPAPRDRConnectorType drc_type = drck->get_type(drc);
+
+ new_hp = g_malloc0(sizeof(struct hp_log_full));
+ hdr = &new_hp->hdr;
+ v6hdr = &new_hp->v6hdr;
+ maina = &new_hp->maina;
+ mainb = &new_hp->mainb;
+ hp = &new_hp->hp;
+
+ hdr->summary = cpu_to_be32(RTAS_LOG_VERSION_6
+ | RTAS_LOG_SEVERITY_EVENT
+ | RTAS_LOG_DISPOSITION_NOT_RECOVERED
+ | RTAS_LOG_OPTIONAL_PART_PRESENT
+ | RTAS_LOG_INITIATOR_HOTPLUG
+ | RTAS_LOG_TYPE_HOTPLUG);
+ hdr->extended_length = cpu_to_be32(sizeof(*new_hp)
+ - sizeof(new_hp->hdr));
+
+ spapr_init_v6hdr(v6hdr);
+ spapr_init_maina(maina, 3 /* Main-A, Main-B, HP */);
+
+ mainb->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINB);
+ mainb->hdr.section_length = cpu_to_be16(sizeof(*mainb));
+ mainb->subsystem_id = 0x80; /* External environment */
+ mainb->event_severity = 0x00; /* Informational / non-error */
+ mainb->event_subtype = 0x00; /* Normal shutdown */
+
+ hp->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_HOTPLUG);
+ hp->hdr.section_length = cpu_to_be16(sizeof(*hp));
+ hp->hdr.section_version = 1; /* includes extended modifier */
+ hp->hotplug_action = hp_action;
+
+
+ switch (drc_type) {
+ case SPAPR_DR_CONNECTOR_TYPE_PCI:
+ hp->drc.index = cpu_to_be32(drck->get_index(drc));
+ hp->hotplug_identifier = RTAS_LOG_V6_HP_ID_DRC_INDEX;
+ hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PCI;
+ break;
+ default:
+ /* we shouldn't be signaling hotplug events for resources
+ * that don't support them
+ */
+ g_assert(false);
+ return;
+ }
+
+ rtas_event_log_queue(RTAS_LOG_TYPE_HOTPLUG, new_hp, true);
+
+ qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
+}
+
+void spapr_hotplug_req_add_event(sPAPRDRConnector *drc)
+{
+ spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_ADD);
+}
+
+void spapr_hotplug_req_remove_event(sPAPRDRConnector *drc)
+{
+ spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_REMOVE);
}
static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr,
@@ -282,8 +458,10 @@ static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr,
target_ulong args,
uint32_t nret, target_ulong rets)
{
- uint32_t mask, buf, len;
+ uint32_t mask, buf, len, event_len;
uint64_t xinfo;
+ sPAPREventLogEntry *event;
+ struct rtas_error_log *hdr;
if ((nargs < 6) || (nargs > 7) || nret != 1) {
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
@@ -298,25 +476,85 @@ static void check_exception(PowerPCCPU *cpu, sPAPREnvironment *spapr,
xinfo |= (uint64_t)rtas_ld(args, 6) << 32;
}
- if ((mask & EVENT_MASK_EPOW) && pending_epow) {
- if (sizeof(*pending_epow) < len) {
- len = sizeof(*pending_epow);
- }
+ event = rtas_event_log_dequeue(mask, true);
+ if (!event) {
+ goto out_no_events;
+ }
+
+ hdr = event->data;
+ event_len = be32_to_cpu(hdr->extended_length) + sizeof(*hdr);
+
+ if (event_len < len) {
+ len = event_len;
+ }
+
+ cpu_physical_memory_write(buf, event->data, len);
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+ g_free(event->data);
+ g_free(event);
+
+ /* according to PAPR+, the IRQ must be left asserted, or re-asserted, if
+ * there are still pending events to be fetched via check-exception. We
+ * do the latter here, since our code relies on edge-triggered
+ * interrupts.
+ */
+ if (rtas_event_log_contains(mask, true)) {
+ qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq));
+ }
- cpu_physical_memory_write(buf, pending_epow, len);
- g_free(pending_epow);
- pending_epow = NULL;
- rtas_st(rets, 0, RTAS_OUT_SUCCESS);
- } else {
- rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND);
+ return;
+
+out_no_events:
+ rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND);
+}
+
+static void event_scan(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ uint32_t mask, buf, len, event_len;
+ sPAPREventLogEntry *event;
+ struct rtas_error_log *hdr;
+
+ if (nargs != 4 || nret != 1) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
}
+
+ mask = rtas_ld(args, 0);
+ buf = rtas_ld(args, 2);
+ len = rtas_ld(args, 3);
+
+ event = rtas_event_log_dequeue(mask, false);
+ if (!event) {
+ goto out_no_events;
+ }
+
+ hdr = event->data;
+ event_len = be32_to_cpu(hdr->extended_length) + sizeof(*hdr);
+
+ if (event_len < len) {
+ len = event_len;
+ }
+
+ cpu_physical_memory_write(buf, event->data, len);
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+ g_free(event->data);
+ g_free(event);
+ return;
+
+out_no_events:
+ rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND);
}
void spapr_events_init(sPAPREnvironment *spapr)
{
- spapr->epow_irq = xics_alloc(spapr->icp, 0, 0, false);
+ QTAILQ_INIT(&spapr->pending_events);
+ spapr->check_exception_irq = xics_alloc(spapr->icp, 0, 0, false);
spapr->epow_notifier.notify = spapr_powerdown_req;
qemu_register_powerdown_notifier(&spapr->epow_notifier);
spapr_rtas_register(RTAS_CHECK_EXCEPTION, "check-exception",
check_exception);
+ spapr_rtas_register(RTAS_EVENT_SCAN, "event-scan", event_scan);
}
diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c
index f3990fdc32..8cd9dba9ac 100644
--- a/hw/ppc/spapr_iommu.c
+++ b/hw/ppc/spapr_iommu.c
@@ -41,7 +41,7 @@ enum sPAPRTCEAccess {
static QLIST_HEAD(spapr_tce_tables, sPAPRTCETable) spapr_tce_tables;
-static sPAPRTCETable *spapr_tce_find_by_liobn(uint32_t liobn)
+sPAPRTCETable *spapr_tce_find_by_liobn(target_ulong liobn)
{
sPAPRTCETable *tcet;
@@ -52,7 +52,7 @@ static sPAPRTCETable *spapr_tce_find_by_liobn(uint32_t liobn)
}
QLIST_FOREACH(tcet, &spapr_tce_tables, list) {
- if (tcet->liobn == liobn) {
+ if (tcet->liobn == (uint32_t)liobn) {
return tcet;
}
}
@@ -126,11 +126,11 @@ static MemoryRegionIOMMUOps spapr_iommu_ops = {
static int spapr_tce_table_realize(DeviceState *dev)
{
sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev);
+ uint64_t window_size = (uint64_t)tcet->nb_table << tcet->page_shift;
- if (kvm_enabled()) {
+ if (kvm_enabled() && !(window_size >> 32)) {
tcet->table = kvmppc_create_spapr_tce(tcet->liobn,
- tcet->nb_table <<
- tcet->page_shift,
+ window_size,
&tcet->fd,
tcet->vfio_accel);
}
@@ -161,6 +161,7 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn,
bool vfio_accel)
{
sPAPRTCETable *tcet;
+ char tmp[64];
if (spapr_tce_find_by_liobn(liobn)) {
fprintf(stderr, "Attempted to create TCE table with duplicate"
@@ -179,7 +180,8 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn,
tcet->nb_table = nb_table;
tcet->vfio_accel = vfio_accel;
- object_property_add_child(OBJECT(owner), "tce-table", OBJECT(tcet), NULL);
+ snprintf(tmp, sizeof(tmp), "tce-table-%x", liobn);
+ object_property_add_child(OBJECT(owner), tmp, OBJECT(tcet), NULL);
object_property_set_bool(OBJECT(tcet), true, "realized", NULL);
@@ -247,7 +249,7 @@ static target_ulong h_put_tce_indirect(PowerPCCPU *cpu,
target_ulong ioba1 = ioba;
target_ulong tce_list = args[2];
target_ulong npages = args[3];
- target_ulong ret = H_PARAMETER;
+ target_ulong ret = H_PARAMETER, tce = 0;
sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn);
CPUState *cs = CPU(cpu);
hwaddr page_mask, page_size;
@@ -267,7 +269,7 @@ static target_ulong h_put_tce_indirect(PowerPCCPU *cpu,
for (i = 0; i < npages; ++i, ioba += page_size) {
target_ulong off = (tce_list & ~SPAPR_TCE_RW) +
i * sizeof(target_ulong);
- target_ulong tce = ldq_phys(cs->as, off);
+ tce = ldq_be_phys(cs->as, off);
ret = put_tce_emu(tcet, ioba, tce);
if (ret) {
@@ -277,11 +279,11 @@ static target_ulong h_put_tce_indirect(PowerPCCPU *cpu,
/* Trace last successful or the first problematic entry */
i = i ? (i - 1) : 0;
- trace_spapr_iommu_indirect(liobn, ioba1, tce_list, i,
- ldq_phys(cs->as,
- tce_list + i * sizeof(target_ulong)),
- ret);
-
+ if (SPAPR_IS_PCI_LIOBN(liobn)) {
+ trace_spapr_iommu_pci_indirect(liobn, ioba1, tce_list, i, tce, ret);
+ } else {
+ trace_spapr_iommu_indirect(liobn, ioba1, tce_list, i, tce, ret);
+ }
return ret;
}
@@ -315,7 +317,11 @@ static target_ulong h_stuff_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr,
break;
}
}
- trace_spapr_iommu_stuff(liobn, ioba, tce_value, npages, ret);
+ if (SPAPR_IS_PCI_LIOBN(liobn)) {
+ trace_spapr_iommu_pci_stuff(liobn, ioba, tce_value, npages, ret);
+ } else {
+ trace_spapr_iommu_stuff(liobn, ioba, tce_value, npages, ret);
+ }
return ret;
}
@@ -336,7 +342,11 @@ static target_ulong h_put_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr,
ret = put_tce_emu(tcet, ioba, tce);
}
- trace_spapr_iommu_put(liobn, ioba, tce, ret);
+ if (SPAPR_IS_PCI_LIOBN(liobn)) {
+ trace_spapr_iommu_pci_put(liobn, ioba, tce, ret);
+ } else {
+ trace_spapr_iommu_put(liobn, ioba, tce, ret);
+ }
return ret;
}
@@ -376,7 +386,11 @@ static target_ulong h_get_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr,
args[0] = tce;
}
}
- trace_spapr_iommu_get(liobn, ioba, ret, tce);
+ if (SPAPR_IS_PCI_LIOBN(liobn)) {
+ trace_spapr_iommu_pci_get(liobn, ioba, ret, tce);
+ } else {
+ trace_spapr_iommu_get(liobn, ioba, ret, tce);
+ }
return ret;
}
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index 05f4faca6e..4df3a33db4 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -33,8 +33,11 @@
#include <libfdt.h>
#include "trace.h"
#include "qemu/error-report.h"
+#include "qapi/qmp/qerror.h"
#include "hw/pci/pci_bus.h"
+#include "hw/ppc/spapr_drc.h"
+#include "sysemu/device_tree.h"
/* Copied from the kernel arch/powerpc/platforms/pseries/msi.c */
#define RTAS_QUERY_FN 0
@@ -47,7 +50,15 @@
#define RTAS_TYPE_MSI 1
#define RTAS_TYPE_MSIX 2
-static sPAPRPHBState *find_phb(sPAPREnvironment *spapr, uint64_t buid)
+#define _FDT(exp) \
+ do { \
+ int ret = (exp); \
+ if (ret < 0) { \
+ return ret; \
+ } \
+ } while (0)
+
+sPAPRPHBState *spapr_pci_find_phb(sPAPREnvironment *spapr, uint64_t buid)
{
sPAPRPHBState *sphb;
@@ -61,10 +72,10 @@ static sPAPRPHBState *find_phb(sPAPREnvironment *spapr, uint64_t buid)
return NULL;
}
-static PCIDevice *find_dev(sPAPREnvironment *spapr, uint64_t buid,
- uint32_t config_addr)
+PCIDevice *spapr_pci_find_dev(sPAPREnvironment *spapr, uint64_t buid,
+ uint32_t config_addr)
{
- sPAPRPHBState *sphb = find_phb(spapr, buid);
+ sPAPRPHBState *sphb = spapr_pci_find_phb(spapr, buid);
PCIHostState *phb = PCI_HOST_BRIDGE(sphb);
int bus_num = (config_addr >> 16) & 0xFF;
int devfn = (config_addr >> 8) & 0xFF;
@@ -95,7 +106,7 @@ static void finish_read_pci_config(sPAPREnvironment *spapr, uint64_t buid,
return;
}
- pci_dev = find_dev(spapr, buid, addr);
+ pci_dev = spapr_pci_find_dev(spapr, buid, addr);
addr = rtas_pci_cfgaddr(addr);
if (!pci_dev || (addr % size) || (addr >= pci_config_size(pci_dev))) {
@@ -162,7 +173,7 @@ static void finish_write_pci_config(sPAPREnvironment *spapr, uint64_t buid,
return;
}
- pci_dev = find_dev(spapr, buid, addr);
+ pci_dev = spapr_pci_find_dev(spapr, buid, addr);
addr = rtas_pci_cfgaddr(addr);
if (!pci_dev || (addr % size) || (addr >= pci_config_size(pci_dev))) {
@@ -280,9 +291,9 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
}
/* Fins sPAPRPHBState */
- phb = find_phb(spapr, buid);
+ phb = spapr_pci_find_phb(spapr, buid);
if (phb) {
- pdev = find_dev(spapr, buid, config_addr);
+ pdev = spapr_pci_find_dev(spapr, buid, config_addr);
}
if (!phb || !pdev) {
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
@@ -381,9 +392,9 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu,
spapr_pci_msi *msi;
/* Find sPAPRPHBState */
- phb = find_phb(spapr, buid);
+ phb = spapr_pci_find_phb(spapr, buid);
if (phb) {
- pdev = find_dev(spapr, buid, config_addr);
+ pdev = spapr_pci_find_dev(spapr, buid, config_addr);
}
if (!phb || !pdev) {
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
@@ -426,7 +437,7 @@ static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu,
addr = rtas_ld(args, 0);
option = rtas_ld(args, 3);
- sphb = find_phb(spapr, buid);
+ sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
@@ -461,7 +472,7 @@ static void rtas_ibm_get_config_addr_info2(PowerPCCPU *cpu,
}
buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
- sphb = find_phb(spapr, buid);
+ sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
@@ -479,7 +490,7 @@ static void rtas_ibm_get_config_addr_info2(PowerPCCPU *cpu,
switch (option) {
case RTAS_GET_PE_ADDR:
addr = rtas_ld(args, 0);
- pdev = find_dev(spapr, buid, addr);
+ pdev = spapr_pci_find_dev(spapr, buid, addr);
if (!pdev) {
goto param_error_exit;
}
@@ -516,7 +527,7 @@ static void rtas_ibm_read_slot_reset_state2(PowerPCCPU *cpu,
}
buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
- sphb = find_phb(spapr, buid);
+ sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
@@ -562,7 +573,7 @@ static void rtas_ibm_set_slot_reset(PowerPCCPU *cpu,
buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
option = rtas_ld(args, 3);
- sphb = find_phb(spapr, buid);
+ sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
@@ -596,7 +607,7 @@ static void rtas_ibm_configure_pe(PowerPCCPU *cpu,
}
buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
- sphb = find_phb(spapr, buid);
+ sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
@@ -631,7 +642,7 @@ static void rtas_ibm_slot_error_detail(PowerPCCPU *cpu,
}
buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
- sphb = find_phb(spapr, buid);
+ sphb = spapr_pci_find_phb(spapr, buid);
if (!sphb) {
goto param_error_exit;
}
@@ -731,6 +742,372 @@ static AddressSpace *spapr_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn)
return &phb->iommu_as;
}
+/* Macros to operate with address in OF binding to PCI */
+#define b_x(x, p, l) (((x) & ((1<<(l))-1)) << (p))
+#define b_n(x) b_x((x), 31, 1) /* 0 if relocatable */
+#define b_p(x) b_x((x), 30, 1) /* 1 if prefetchable */
+#define b_t(x) b_x((x), 29, 1) /* 1 if the address is aliased */
+#define b_ss(x) b_x((x), 24, 2) /* the space code */
+#define b_bbbbbbbb(x) b_x((x), 16, 8) /* bus number */
+#define b_ddddd(x) b_x((x), 11, 5) /* device number */
+#define b_fff(x) b_x((x), 8, 3) /* function number */
+#define b_rrrrrrrr(x) b_x((x), 0, 8) /* register number */
+
+/* for 'reg'/'assigned-addresses' OF properties */
+#define RESOURCE_CELLS_SIZE 2
+#define RESOURCE_CELLS_ADDRESS 3
+
+typedef struct ResourceFields {
+ uint32_t phys_hi;
+ uint32_t phys_mid;
+ uint32_t phys_lo;
+ uint32_t size_hi;
+ uint32_t size_lo;
+} QEMU_PACKED ResourceFields;
+
+typedef struct ResourceProps {
+ ResourceFields reg[8];
+ ResourceFields assigned[7];
+ uint32_t reg_len;
+ uint32_t assigned_len;
+} ResourceProps;
+
+/* fill in the 'reg'/'assigned-resources' OF properties for
+ * a PCI device. 'reg' describes resource requirements for a
+ * device's IO/MEM regions, 'assigned-addresses' describes the
+ * actual resource assignments.
+ *
+ * the properties are arrays of ('phys-addr', 'size') pairs describing
+ * the addressable regions of the PCI device, where 'phys-addr' is a
+ * RESOURCE_CELLS_ADDRESS-tuple of 32-bit integers corresponding to
+ * (phys.hi, phys.mid, phys.lo), and 'size' is a
+ * RESOURCE_CELLS_SIZE-tuple corresponding to (size.hi, size.lo).
+ *
+ * phys.hi = 0xYYXXXXZZ, where:
+ * 0xYY = npt000ss
+ * ||| |
+ * ||| +-- space code: 1 if IO region, 2 if MEM region
+ * ||+------ for non-relocatable IO: 1 if aliased
+ * || for relocatable IO: 1 if below 64KB
+ * || for MEM: 1 if below 1MB
+ * |+------- 1 if region is prefetchable
+ * +-------- 1 if region is non-relocatable
+ * 0xXXXX = bbbbbbbb dddddfff, encoding bus, slot, and function
+ * bits respectively
+ * 0xZZ = rrrrrrrr, the register number of the BAR corresponding
+ * to the region
+ *
+ * phys.mid and phys.lo correspond respectively to the hi/lo portions
+ * of the actual address of the region.
+ *
+ * how the phys-addr/size values are used differ slightly between
+ * 'reg' and 'assigned-addresses' properties. namely, 'reg' has
+ * an additional description for the config space region of the
+ * device, and in the case of QEMU has n=0 and phys.mid=phys.lo=0
+ * to describe the region as relocatable, with an address-mapping
+ * that corresponds directly to the PHB's address space for the
+ * resource. 'assigned-addresses' always has n=1 set with an absolute
+ * address assigned for the resource. in general, 'assigned-addresses'
+ * won't be populated, since addresses for PCI devices are generally
+ * unmapped initially and left to the guest to assign.
+ *
+ * note also that addresses defined in these properties are, at least
+ * for PAPR guests, relative to the PHBs IO/MEM windows, and
+ * correspond directly to the addresses in the BARs.
+ *
+ * in accordance with PCI Bus Binding to Open Firmware,
+ * IEEE Std 1275-1994, section 4.1.1, as implemented by PAPR+ v2.7,
+ * Appendix C.
+ */
+static void populate_resource_props(PCIDevice *d, ResourceProps *rp)
+{
+ int bus_num = pci_bus_num(PCI_BUS(qdev_get_parent_bus(DEVICE(d))));
+ uint32_t dev_id = (b_bbbbbbbb(bus_num) |
+ b_ddddd(PCI_SLOT(d->devfn)) |
+ b_fff(PCI_FUNC(d->devfn)));
+ ResourceFields *reg, *assigned;
+ int i, reg_idx = 0, assigned_idx = 0;
+
+ /* config space region */
+ reg = &rp->reg[reg_idx++];
+ reg->phys_hi = cpu_to_be32(dev_id);
+ reg->phys_mid = 0;
+ reg->phys_lo = 0;
+ reg->size_hi = 0;
+ reg->size_lo = 0;
+
+ for (i = 0; i < PCI_NUM_REGIONS; i++) {
+ if (!d->io_regions[i].size) {
+ continue;
+ }
+
+ reg = &rp->reg[reg_idx++];
+
+ reg->phys_hi = cpu_to_be32(dev_id | b_rrrrrrrr(pci_bar(d, i)));
+ if (d->io_regions[i].type & PCI_BASE_ADDRESS_SPACE_IO) {
+ reg->phys_hi |= cpu_to_be32(b_ss(1));
+ } else {
+ reg->phys_hi |= cpu_to_be32(b_ss(2));
+ }
+ reg->phys_mid = 0;
+ reg->phys_lo = 0;
+ reg->size_hi = cpu_to_be32(d->io_regions[i].size >> 32);
+ reg->size_lo = cpu_to_be32(d->io_regions[i].size);
+
+ if (d->io_regions[i].addr == PCI_BAR_UNMAPPED) {
+ continue;
+ }
+
+ assigned = &rp->assigned[assigned_idx++];
+ assigned->phys_hi = cpu_to_be32(reg->phys_hi | b_n(1));
+ assigned->phys_mid = cpu_to_be32(d->io_regions[i].addr >> 32);
+ assigned->phys_lo = cpu_to_be32(d->io_regions[i].addr);
+ assigned->size_hi = reg->size_hi;
+ assigned->size_lo = reg->size_lo;
+ }
+
+ rp->reg_len = reg_idx * sizeof(ResourceFields);
+ rp->assigned_len = assigned_idx * sizeof(ResourceFields);
+}
+
+static int spapr_populate_pci_child_dt(PCIDevice *dev, void *fdt, int offset,
+ int phb_index, int drc_index,
+ const char *drc_name)
+{
+ ResourceProps rp;
+ bool is_bridge = false;
+ int pci_status;
+
+ if (pci_default_read_config(dev, PCI_HEADER_TYPE, 1) ==
+ PCI_HEADER_TYPE_BRIDGE) {
+ is_bridge = true;
+ }
+
+ /* in accordance with PAPR+ v2.7 13.6.3, Table 181 */
+ _FDT(fdt_setprop_cell(fdt, offset, "vendor-id",
+ pci_default_read_config(dev, PCI_VENDOR_ID, 2)));
+ _FDT(fdt_setprop_cell(fdt, offset, "device-id",
+ pci_default_read_config(dev, PCI_DEVICE_ID, 2)));
+ _FDT(fdt_setprop_cell(fdt, offset, "revision-id",
+ pci_default_read_config(dev, PCI_REVISION_ID, 1)));
+ _FDT(fdt_setprop_cell(fdt, offset, "class-code",
+ pci_default_read_config(dev, PCI_CLASS_DEVICE, 2)
+ << 8));
+ if (pci_default_read_config(dev, PCI_INTERRUPT_PIN, 1)) {
+ _FDT(fdt_setprop_cell(fdt, offset, "interrupts",
+ pci_default_read_config(dev, PCI_INTERRUPT_PIN, 1)));
+ }
+
+ if (!is_bridge) {
+ _FDT(fdt_setprop_cell(fdt, offset, "min-grant",
+ pci_default_read_config(dev, PCI_MIN_GNT, 1)));
+ _FDT(fdt_setprop_cell(fdt, offset, "max-latency",
+ pci_default_read_config(dev, PCI_MAX_LAT, 1)));
+ }
+
+ if (pci_default_read_config(dev, PCI_SUBSYSTEM_ID, 2)) {
+ _FDT(fdt_setprop_cell(fdt, offset, "subsystem-id",
+ pci_default_read_config(dev, PCI_SUBSYSTEM_ID, 2)));
+ }
+
+ if (pci_default_read_config(dev, PCI_SUBSYSTEM_VENDOR_ID, 2)) {
+ _FDT(fdt_setprop_cell(fdt, offset, "subsystem-vendor-id",
+ pci_default_read_config(dev, PCI_SUBSYSTEM_VENDOR_ID, 2)));
+ }
+
+ _FDT(fdt_setprop_cell(fdt, offset, "cache-line-size",
+ pci_default_read_config(dev, PCI_CACHE_LINE_SIZE, 1)));
+
+ /* the following fdt cells are masked off the pci status register */
+ pci_status = pci_default_read_config(dev, PCI_STATUS, 2);
+ _FDT(fdt_setprop_cell(fdt, offset, "devsel-speed",
+ PCI_STATUS_DEVSEL_MASK & pci_status));
+
+ if (pci_status & PCI_STATUS_FAST_BACK) {
+ _FDT(fdt_setprop(fdt, offset, "fast-back-to-back", NULL, 0));
+ }
+ if (pci_status & PCI_STATUS_66MHZ) {
+ _FDT(fdt_setprop(fdt, offset, "66mhz-capable", NULL, 0));
+ }
+ if (pci_status & PCI_STATUS_UDF) {
+ _FDT(fdt_setprop(fdt, offset, "udf-supported", NULL, 0));
+ }
+
+ /* NOTE: this is normally generated by firmware via path/unit name,
+ * but in our case we must set it manually since it does not get
+ * processed by OF beforehand
+ */
+ _FDT(fdt_setprop_string(fdt, offset, "name", "pci"));
+ _FDT(fdt_setprop(fdt, offset, "ibm,loc-code", drc_name, strlen(drc_name)));
+ _FDT(fdt_setprop_cell(fdt, offset, "ibm,my-drc-index", drc_index));
+
+ _FDT(fdt_setprop_cell(fdt, offset, "#address-cells",
+ RESOURCE_CELLS_ADDRESS));
+ _FDT(fdt_setprop_cell(fdt, offset, "#size-cells",
+ RESOURCE_CELLS_SIZE));
+ _FDT(fdt_setprop_cell(fdt, offset, "ibm,req#msi-x",
+ RESOURCE_CELLS_SIZE));
+
+ populate_resource_props(dev, &rp);
+ _FDT(fdt_setprop(fdt, offset, "reg", (uint8_t *)rp.reg, rp.reg_len));
+ _FDT(fdt_setprop(fdt, offset, "assigned-addresses",
+ (uint8_t *)rp.assigned, rp.assigned_len));
+
+ return 0;
+}
+
+/* create OF node for pci device and required OF DT properties */
+static void *spapr_create_pci_child_dt(sPAPRPHBState *phb, PCIDevice *dev,
+ int drc_index, const char *drc_name,
+ int *dt_offset)
+{
+ void *fdt;
+ int offset, ret, fdt_size;
+ int slot = PCI_SLOT(dev->devfn);
+ int func = PCI_FUNC(dev->devfn);
+ char nodename[512];
+
+ fdt = create_device_tree(&fdt_size);
+ if (func != 0) {
+ sprintf(nodename, "pci@%d,%d", slot, func);
+ } else {
+ sprintf(nodename, "pci@%d", slot);
+ }
+ offset = fdt_add_subnode(fdt, 0, nodename);
+ ret = spapr_populate_pci_child_dt(dev, fdt, offset, phb->index, drc_index,
+ drc_name);
+ g_assert(!ret);
+
+ *dt_offset = offset;
+ return fdt;
+}
+
+static void spapr_phb_add_pci_device(sPAPRDRConnector *drc,
+ sPAPRPHBState *phb,
+ PCIDevice *pdev,
+ Error **errp)
+{
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ DeviceState *dev = DEVICE(pdev);
+ int drc_index = drck->get_index(drc);
+ const char *drc_name = drck->get_name(drc);
+ void *fdt = NULL;
+ int fdt_start_offset = 0;
+
+ /* boot-time devices get their device tree node created by SLOF, but for
+ * hotplugged devices we need QEMU to generate it so the guest can fetch
+ * it via RTAS
+ */
+ if (dev->hotplugged) {
+ fdt = spapr_create_pci_child_dt(phb, pdev, drc_index, drc_name,
+ &fdt_start_offset);
+ }
+
+ drck->attach(drc, DEVICE(pdev),
+ fdt, fdt_start_offset, !dev->hotplugged, errp);
+ if (*errp) {
+ g_free(fdt);
+ }
+}
+
+static void spapr_phb_remove_pci_device_cb(DeviceState *dev, void *opaque)
+{
+ /* some version guests do not wait for completion of a device
+ * cleanup (generally done asynchronously by the kernel) before
+ * signaling to QEMU that the device is safe, but instead sleep
+ * for some 'safe' period of time. unfortunately on a busy host
+ * this sleep isn't guaranteed to be long enough, resulting in
+ * bad things like IRQ lines being left asserted during final
+ * device removal. to deal with this we call reset just prior
+ * to finalizing the device, which will put the device back into
+ * an 'idle' state, as the device cleanup code expects.
+ */
+ pci_device_reset(PCI_DEVICE(dev));
+ object_unparent(OBJECT(dev));
+}
+
+static void spapr_phb_remove_pci_device(sPAPRDRConnector *drc,
+ sPAPRPHBState *phb,
+ PCIDevice *pdev,
+ Error **errp)
+{
+ sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+
+ drck->detach(drc, DEVICE(pdev), spapr_phb_remove_pci_device_cb, phb, errp);
+}
+
+static sPAPRDRConnector *spapr_phb_get_pci_drc(sPAPRPHBState *phb,
+ PCIDevice *pdev)
+{
+ uint32_t busnr = pci_bus_num(PCI_BUS(qdev_get_parent_bus(DEVICE(pdev))));
+ return spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_PCI,
+ (phb->index << 16) |
+ (busnr << 8) |
+ pdev->devfn);
+}
+
+static void spapr_phb_hot_plug_child(HotplugHandler *plug_handler,
+ DeviceState *plugged_dev, Error **errp)
+{
+ sPAPRPHBState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler));
+ PCIDevice *pdev = PCI_DEVICE(plugged_dev);
+ sPAPRDRConnector *drc = spapr_phb_get_pci_drc(phb, pdev);
+ Error *local_err = NULL;
+
+ /* if DR is disabled we don't need to do anything in the case of
+ * hotplug or coldplug callbacks
+ */
+ if (!phb->dr_enabled) {
+ /* if this is a hotplug operation initiated by the user
+ * we need to let them know it's not enabled
+ */
+ if (plugged_dev->hotplugged) {
+ error_set(errp, QERR_BUS_NO_HOTPLUG,
+ object_get_typename(OBJECT(phb)));
+ }
+ return;
+ }
+
+ g_assert(drc);
+
+ spapr_phb_add_pci_device(drc, phb, pdev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ if (plugged_dev->hotplugged) {
+ spapr_hotplug_req_add_event(drc);
+ }
+}
+
+static void spapr_phb_hot_unplug_child(HotplugHandler *plug_handler,
+ DeviceState *plugged_dev, Error **errp)
+{
+ sPAPRPHBState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler));
+ PCIDevice *pdev = PCI_DEVICE(plugged_dev);
+ sPAPRDRConnectorClass *drck;
+ sPAPRDRConnector *drc = spapr_phb_get_pci_drc(phb, pdev);
+ Error *local_err = NULL;
+
+ if (!phb->dr_enabled) {
+ error_set(errp, QERR_BUS_NO_HOTPLUG,
+ object_get_typename(OBJECT(phb)));
+ return;
+ }
+
+ g_assert(drc);
+
+ drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ if (!drck->release_pending(drc)) {
+ spapr_phb_remove_pci_device(drc, phb, pdev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ spapr_hotplug_req_remove_event(drc);
+ }
+}
+
static void spapr_phb_realize(DeviceState *dev, Error **errp)
{
SysBusDevice *s = SYS_BUS_DEVICE(dev);
@@ -742,12 +1119,12 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
PCIBus *bus;
uint64_t msi_window_size = 4096;
- if (sphb->index != -1) {
+ if (sphb->index != (uint32_t)-1) {
hwaddr windows_base;
- if ((sphb->buid != -1) || (sphb->dma_liobn != -1)
- || (sphb->mem_win_addr != -1)
- || (sphb->io_win_addr != -1)) {
+ if ((sphb->buid != (uint64_t)-1) || (sphb->dma_liobn != (uint32_t)-1)
+ || (sphb->mem_win_addr != (hwaddr)-1)
+ || (sphb->io_win_addr != (hwaddr)-1)) {
error_setg(errp, "Either \"index\" or other parameters must"
" be specified for PAPR PHB, not both");
return;
@@ -760,7 +1137,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
}
sphb->buid = SPAPR_PCI_BASE_BUID + sphb->index;
- sphb->dma_liobn = SPAPR_PCI_BASE_LIOBN + sphb->index;
+ sphb->dma_liobn = SPAPR_PCI_LIOBN(sphb->index, 0);
windows_base = SPAPR_PCI_WINDOW_BASE
+ sphb->index * SPAPR_PCI_WINDOW_SPACING;
@@ -768,27 +1145,27 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
sphb->io_win_addr = windows_base + SPAPR_PCI_IO_WIN_OFF;
}
- if (sphb->buid == -1) {
+ if (sphb->buid == (uint64_t)-1) {
error_setg(errp, "BUID not specified for PHB");
return;
}
- if (sphb->dma_liobn == -1) {
+ if (sphb->dma_liobn == (uint32_t)-1) {
error_setg(errp, "LIOBN not specified for PHB");
return;
}
- if (sphb->mem_win_addr == -1) {
+ if (sphb->mem_win_addr == (hwaddr)-1) {
error_setg(errp, "Memory window address not specified for PHB");
return;
}
- if (sphb->io_win_addr == -1) {
+ if (sphb->io_win_addr == (hwaddr)-1) {
error_setg(errp, "IO window address not specified for PHB");
return;
}
- if (find_phb(spapr, sphb->buid)) {
+ if (spapr_pci_find_phb(spapr, sphb->buid)) {
error_setg(errp, "PCI host bridges must have unique BUIDs");
return;
}
@@ -824,6 +1201,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
&sphb->memspace, &sphb->iospace,
PCI_DEVFN(0, 0), PCI_NUM_PINS, TYPE_PCI_BUS);
phb->bus = bus;
+ qbus_set_hotplug_handler(BUS(phb->bus), DEVICE(sphb), NULL);
/*
* Initialize PHB address space.
@@ -880,6 +1258,15 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
sphb->lsi_table[i].irq = irq;
}
+ /* allocate connectors for child PCI devices */
+ if (sphb->dr_enabled) {
+ for (i = 0; i < PCI_SLOT_MAX * 8; i++) {
+ spapr_dr_connector_new(OBJECT(phb),
+ SPAPR_DR_CONNECTOR_TYPE_PCI,
+ (sphb->index << 16) | i);
+ }
+ }
+
if (!info->finish_realize) {
error_setg(errp, "finish_realize not defined");
return;
@@ -893,11 +1280,11 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
static void spapr_phb_finish_realize(sPAPRPHBState *sphb, Error **errp)
{
sPAPRTCETable *tcet;
+ uint32_t nb_table;
+ nb_table = SPAPR_PCI_DMA32_SIZE >> SPAPR_TCE_PAGE_SHIFT;
tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn,
- 0,
- SPAPR_TCE_PAGE_SHIFT,
- 0x40000000 >> SPAPR_TCE_PAGE_SHIFT, false);
+ 0, SPAPR_TCE_PAGE_SHIFT, nb_table, false);
if (!tcet) {
error_setg(errp, "Unable to create TCE table for %s",
sphb->dtbusname);
@@ -936,6 +1323,8 @@ static Property spapr_phb_properties[] = {
DEFINE_PROP_UINT64("io_win_addr", sPAPRPHBState, io_win_addr, -1),
DEFINE_PROP_UINT64("io_win_size", sPAPRPHBState, io_win_size,
SPAPR_PCI_IO_WIN_SIZE),
+ DEFINE_PROP_BOOL("dynamic-reconfiguration", sPAPRPHBState, dr_enabled,
+ true),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1049,6 +1438,7 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data)
PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
sPAPRPHBClass *spc = SPAPR_PCI_HOST_BRIDGE_CLASS(klass);
+ HotplugHandlerClass *hp = HOTPLUG_HANDLER_CLASS(klass);
hc->root_bus_path = spapr_phb_root_bus_path;
dc->realize = spapr_phb_realize;
@@ -1058,6 +1448,8 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data)
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
dc->cannot_instantiate_with_device_add_yet = false;
spc->finish_realize = spapr_phb_finish_realize;
+ hp->plug = spapr_phb_hot_plug_child;
+ hp->unplug = spapr_phb_hot_unplug_child;
}
static const TypeInfo spapr_phb_info = {
@@ -1066,6 +1458,10 @@ static const TypeInfo spapr_phb_info = {
.instance_size = sizeof(sPAPRPHBState),
.class_init = spapr_phb_class_init,
.class_size = sizeof(sPAPRPHBClass),
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_HOTPLUG_HANDLER },
+ { }
+ }
};
PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index)
@@ -1079,45 +1475,11 @@ PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index)
return PCI_HOST_BRIDGE(dev);
}
-/* Macros to operate with address in OF binding to PCI */
-#define b_x(x, p, l) (((x) & ((1<<(l))-1)) << (p))
-#define b_n(x) b_x((x), 31, 1) /* 0 if relocatable */
-#define b_p(x) b_x((x), 30, 1) /* 1 if prefetchable */
-#define b_t(x) b_x((x), 29, 1) /* 1 if the address is aliased */
-#define b_ss(x) b_x((x), 24, 2) /* the space code */
-#define b_bbbbbbbb(x) b_x((x), 16, 8) /* bus number */
-#define b_ddddd(x) b_x((x), 11, 5) /* device number */
-#define b_fff(x) b_x((x), 8, 3) /* function number */
-#define b_rrrrrrrr(x) b_x((x), 0, 8) /* register number */
-
-typedef struct sPAPRTCEDT {
- void *fdt;
- int node_off;
-} sPAPRTCEDT;
-
-static int spapr_phb_children_dt(Object *child, void *opaque)
-{
- sPAPRTCEDT *p = opaque;
- sPAPRTCETable *tcet;
-
- tcet = (sPAPRTCETable *) object_dynamic_cast(child, TYPE_SPAPR_TCE_TABLE);
- if (!tcet) {
- return 0;
- }
-
- spapr_dma_dt(p->fdt, p->node_off, "ibm,dma-window",
- tcet->liobn, tcet->bus_offset,
- tcet->nb_table << tcet->page_shift);
- /* Stop after the first window */
-
- return 1;
-}
-
int spapr_populate_pci_dt(sPAPRPHBState *phb,
uint32_t xics_phandle,
void *fdt)
{
- int bus_off, i, j;
+ int bus_off, i, j, ret;
char nodename[256];
uint32_t bus_range[] = { cpu_to_be32(0), cpu_to_be32(0xff) };
const uint64_t mmiosize = memory_region_size(&phb->memwindow);
@@ -1151,6 +1513,7 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
uint32_t interrupt_map_mask[] = {
cpu_to_be32(b_ddddd(-1)|b_fff(0)), 0x0, 0x0, cpu_to_be32(-1)};
uint32_t interrupt_map[PCI_SLOT_MAX * PCI_NUM_PINS][7];
+ sPAPRTCETable *tcet;
/* Start populating the FDT */
sprintf(nodename, "pci@%" PRIx64, phb->buid);
@@ -1159,14 +1522,6 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
return bus_off;
}
-#define _FDT(exp) \
- do { \
- int ret = (exp); \
- if (ret < 0) { \
- return ret; \
- } \
- } while (0)
-
/* Write PHB properties */
_FDT(fdt_setprop_string(fdt, bus_off, "device_type", "pci"));
_FDT(fdt_setprop_string(fdt, bus_off, "compatible", "IBM,Logical_PHB"));
@@ -1203,8 +1558,16 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
_FDT(fdt_setprop(fdt, bus_off, "interrupt-map", &interrupt_map,
sizeof(interrupt_map)));
- object_child_foreach(OBJECT(phb), spapr_phb_children_dt,
- &((sPAPRTCEDT){ .fdt = fdt, .node_off = bus_off }));
+ tcet = spapr_tce_find_by_liobn(SPAPR_PCI_LIOBN(phb->index, 0));
+ spapr_dma_dt(fdt, bus_off, "ibm,dma-window",
+ tcet->liobn, tcet->bus_offset,
+ tcet->nb_table << tcet->page_shift);
+
+ ret = spapr_drc_populate_dt(fdt, bus_off, OBJECT(phb),
+ SPAPR_DR_CONNECTOR_TYPE_PCI);
+ if (ret) {
+ return ret;
+ }
return 0;
}
diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c
index 0f1ae55828..fa28d43f81 100644
--- a/hw/ppc/spapr_rtas.c
+++ b/hw/ppc/spapr_rtas.c
@@ -35,6 +35,55 @@
#include "qapi-event.h"
#include <libfdt.h>
+#include "hw/ppc/spapr_drc.h"
+
+/* #define DEBUG_SPAPR */
+
+#ifdef DEBUG_SPAPR
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+ do { } while (0)
+#endif
+
+static sPAPRConfigureConnectorState *spapr_ccs_find(sPAPREnvironment *spapr,
+ uint32_t drc_index)
+{
+ sPAPRConfigureConnectorState *ccs = NULL;
+
+ QTAILQ_FOREACH(ccs, &spapr->ccs_list, next) {
+ if (ccs->drc_index == drc_index) {
+ break;
+ }
+ }
+
+ return ccs;
+}
+
+static void spapr_ccs_add(sPAPREnvironment *spapr,
+ sPAPRConfigureConnectorState *ccs)
+{
+ g_assert(!spapr_ccs_find(spapr, ccs->drc_index));
+ QTAILQ_INSERT_HEAD(&spapr->ccs_list, ccs, next);
+}
+
+static void spapr_ccs_remove(sPAPREnvironment *spapr,
+ sPAPRConfigureConnectorState *ccs)
+{
+ QTAILQ_REMOVE(&spapr->ccs_list, ccs, next);
+ g_free(ccs);
+}
+
+void spapr_ccs_reset_hook(void *opaque)
+{
+ sPAPREnvironment *spapr = opaque;
+ sPAPRConfigureConnectorState *ccs, *ccs_tmp;
+
+ QTAILQ_FOREACH_SAFE(ccs, &spapr->ccs_list, next, ccs_tmp) {
+ spapr_ccs_remove(spapr, ccs);
+ }
+}
static void rtas_display_character(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t token, uint32_t nargs,
@@ -245,6 +294,308 @@ static void rtas_ibm_os_term(PowerPCCPU *cpu,
rtas_st(rets, 0, ret);
}
+static void rtas_set_power_level(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args, uint32_t nret,
+ target_ulong rets)
+{
+ int32_t power_domain;
+
+ if (nargs != 2 || nret != 2) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ /* we currently only use a single, "live insert" powerdomain for
+ * hotplugged/dlpar'd resources, so the power is always live/full (100)
+ */
+ power_domain = rtas_ld(args, 0);
+ if (power_domain != -1) {
+ rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED);
+ return;
+ }
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+ rtas_st(rets, 1, 100);
+}
+
+static void rtas_get_power_level(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args, uint32_t nret,
+ target_ulong rets)
+{
+ int32_t power_domain;
+
+ if (nargs != 1 || nret != 2) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ /* we currently only use a single, "live insert" powerdomain for
+ * hotplugged/dlpar'd resources, so the power is always live/full (100)
+ */
+ power_domain = rtas_ld(args, 0);
+ if (power_domain != -1) {
+ rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED);
+ return;
+ }
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+ rtas_st(rets, 1, 100);
+}
+
+static bool sensor_type_is_dr(uint32_t sensor_type)
+{
+ switch (sensor_type) {
+ case RTAS_SENSOR_TYPE_ISOLATION_STATE:
+ case RTAS_SENSOR_TYPE_DR:
+ case RTAS_SENSOR_TYPE_ALLOCATION_STATE:
+ return true;
+ }
+
+ return false;
+}
+
+static void rtas_set_indicator(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args, uint32_t nret,
+ target_ulong rets)
+{
+ uint32_t sensor_type;
+ uint32_t sensor_index;
+ uint32_t sensor_state;
+ sPAPRDRConnector *drc;
+ sPAPRDRConnectorClass *drck;
+
+ if (nargs != 3 || nret != 1) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ sensor_type = rtas_ld(args, 0);
+ sensor_index = rtas_ld(args, 1);
+ sensor_state = rtas_ld(args, 2);
+
+ if (!sensor_type_is_dr(sensor_type)) {
+ goto out_unimplemented;
+ }
+
+ /* if this is a DR sensor we can assume sensor_index == drc_index */
+ drc = spapr_dr_connector_by_index(sensor_index);
+ if (!drc) {
+ DPRINTF("rtas_set_indicator: invalid sensor/DRC index: %xh\n",
+ sensor_index);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+ drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+
+ switch (sensor_type) {
+ case RTAS_SENSOR_TYPE_ISOLATION_STATE:
+ /* if the guest is configuring a device attached to this
+ * DRC, we should reset the configuration state at this
+ * point since it may no longer be reliable (guest released
+ * device and needs to start over, or unplug occurred so
+ * the FDT is no longer valid)
+ */
+ if (sensor_state == SPAPR_DR_ISOLATION_STATE_ISOLATED) {
+ sPAPRConfigureConnectorState *ccs = spapr_ccs_find(spapr,
+ sensor_index);
+ if (ccs) {
+ spapr_ccs_remove(spapr, ccs);
+ }
+ }
+ drck->set_isolation_state(drc, sensor_state);
+ break;
+ case RTAS_SENSOR_TYPE_DR:
+ drck->set_indicator_state(drc, sensor_state);
+ break;
+ case RTAS_SENSOR_TYPE_ALLOCATION_STATE:
+ drck->set_allocation_state(drc, sensor_state);
+ break;
+ default:
+ goto out_unimplemented;
+ }
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+ return;
+
+out_unimplemented:
+ /* currently only DR-related sensors are implemented */
+ DPRINTF("rtas_set_indicator: sensor/indicator not implemented: %d\n",
+ sensor_type);
+ rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED);
+}
+
+static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPREnvironment *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args, uint32_t nret,
+ target_ulong rets)
+{
+ uint32_t sensor_type;
+ uint32_t sensor_index;
+ sPAPRDRConnector *drc;
+ sPAPRDRConnectorClass *drck;
+ uint32_t entity_sense;
+
+ if (nargs != 2 || nret != 2) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ sensor_type = rtas_ld(args, 0);
+ sensor_index = rtas_ld(args, 1);
+
+ if (sensor_type != RTAS_SENSOR_TYPE_ENTITY_SENSE) {
+ /* currently only DR-related sensors are implemented */
+ DPRINTF("rtas_get_sensor_state: sensor/indicator not implemented: %d\n",
+ sensor_type);
+ rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED);
+ return;
+ }
+
+ drc = spapr_dr_connector_by_index(sensor_index);
+ if (!drc) {
+ DPRINTF("rtas_get_sensor_state: invalid sensor/DRC index: %xh\n",
+ sensor_index);
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+ drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ entity_sense = drck->entity_sense(drc);
+
+ rtas_st(rets, 0, RTAS_OUT_SUCCESS);
+ rtas_st(rets, 1, entity_sense);
+}
+
+/* configure-connector work area offsets, int32_t units for field
+ * indexes, bytes for field offset/len values.
+ *
+ * as documented by PAPR+ v2.7, 13.5.3.5
+ */
+#define CC_IDX_NODE_NAME_OFFSET 2
+#define CC_IDX_PROP_NAME_OFFSET 2
+#define CC_IDX_PROP_LEN 3
+#define CC_IDX_PROP_DATA_OFFSET 4
+#define CC_VAL_DATA_OFFSET ((CC_IDX_PROP_DATA_OFFSET + 1) * 4)
+#define CC_WA_LEN 4096
+
+static void rtas_ibm_configure_connector(PowerPCCPU *cpu,
+ sPAPREnvironment *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args, uint32_t nret,
+ target_ulong rets)
+{
+ uint64_t wa_addr;
+ uint64_t wa_offset;
+ uint32_t drc_index;
+ sPAPRDRConnector *drc;
+ sPAPRDRConnectorClass *drck;
+ sPAPRConfigureConnectorState *ccs;
+ sPAPRDRCCResponse resp = SPAPR_DR_CC_RESPONSE_CONTINUE;
+ int rc;
+ const void *fdt;
+
+ if (nargs != 2 || nret != 1) {
+ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
+ return;
+ }
+
+ wa_addr = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 0);
+
+ drc_index = rtas_ld(wa_addr, 0);
+ drc = spapr_dr_connector_by_index(drc_index);
+ if (!drc) {
+ DPRINTF("rtas_ibm_configure_connector: invalid DRC index: %xh\n",
+ drc_index);
+ rc = RTAS_OUT_PARAM_ERROR;
+ goto out;
+ }
+
+ drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ fdt = drck->get_fdt(drc, NULL);
+
+ ccs = spapr_ccs_find(spapr, drc_index);
+ if (!ccs) {
+ ccs = g_new0(sPAPRConfigureConnectorState, 1);
+ (void)drck->get_fdt(drc, &ccs->fdt_offset);
+ ccs->drc_index = drc_index;
+ spapr_ccs_add(spapr, ccs);
+ }
+
+ do {
+ uint32_t tag;
+ const char *name;
+ const struct fdt_property *prop;
+ int fdt_offset_next, prop_len;
+
+ tag = fdt_next_tag(fdt, ccs->fdt_offset, &fdt_offset_next);
+
+ switch (tag) {
+ case FDT_BEGIN_NODE:
+ ccs->fdt_depth++;
+ name = fdt_get_name(fdt, ccs->fdt_offset, NULL);
+
+ /* provide the name of the next OF node */
+ wa_offset = CC_VAL_DATA_OFFSET;
+ rtas_st(wa_addr, CC_IDX_NODE_NAME_OFFSET, wa_offset);
+ rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset,
+ (uint8_t *)name, strlen(name) + 1);
+ resp = SPAPR_DR_CC_RESPONSE_NEXT_CHILD;
+ break;
+ case FDT_END_NODE:
+ ccs->fdt_depth--;
+ if (ccs->fdt_depth == 0) {
+ /* done sending the device tree, don't need to track
+ * the state anymore
+ */
+ drck->set_configured(drc);
+ spapr_ccs_remove(spapr, ccs);
+ ccs = NULL;
+ resp = SPAPR_DR_CC_RESPONSE_SUCCESS;
+ } else {
+ resp = SPAPR_DR_CC_RESPONSE_PREV_PARENT;
+ }
+ break;
+ case FDT_PROP:
+ prop = fdt_get_property_by_offset(fdt, ccs->fdt_offset,
+ &prop_len);
+ name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
+
+ /* provide the name of the next OF property */
+ wa_offset = CC_VAL_DATA_OFFSET;
+ rtas_st(wa_addr, CC_IDX_PROP_NAME_OFFSET, wa_offset);
+ rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset,
+ (uint8_t *)name, strlen(name) + 1);
+
+ /* provide the length and value of the OF property. data gets
+ * placed immediately after NULL terminator of the OF property's
+ * name string
+ */
+ wa_offset += strlen(name) + 1,
+ rtas_st(wa_addr, CC_IDX_PROP_LEN, prop_len);
+ rtas_st(wa_addr, CC_IDX_PROP_DATA_OFFSET, wa_offset);
+ rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset,
+ (uint8_t *)((struct fdt_property *)prop)->data,
+ prop_len);
+ resp = SPAPR_DR_CC_RESPONSE_NEXT_PROPERTY;
+ break;
+ case FDT_END:
+ resp = SPAPR_DR_CC_RESPONSE_ERROR;
+ default:
+ /* keep seeking for an actionable tag */
+ break;
+ }
+ if (ccs) {
+ ccs->fdt_offset = fdt_offset_next;
+ }
+ } while (resp == SPAPR_DR_CC_RESPONSE_CONTINUE);
+
+ rc = resp;
+out:
+ rtas_st(rets, 0, rc);
+}
+
static struct rtas_call {
const char *name;
spapr_rtas_fn fn;
@@ -370,6 +721,16 @@ static void core_rtas_register_types(void)
rtas_ibm_set_system_parameter);
spapr_rtas_register(RTAS_IBM_OS_TERM, "ibm,os-term",
rtas_ibm_os_term);
+ spapr_rtas_register(RTAS_SET_POWER_LEVEL, "set-power-level",
+ rtas_set_power_level);
+ spapr_rtas_register(RTAS_GET_POWER_LEVEL, "get-power-level",
+ rtas_get_power_level);
+ spapr_rtas_register(RTAS_SET_INDICATOR, "set-indicator",
+ rtas_set_indicator);
+ spapr_rtas_register(RTAS_GET_SENSOR_STATE, "get-sensor-state",
+ rtas_get_sensor_state);
+ spapr_rtas_register(RTAS_IBM_CONFIGURE_CONNECTOR, "ibm,configure-connector",
+ rtas_ibm_configure_connector);
}
type_init(core_rtas_register_types)
diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c
index 1360b97ab0..174033dd41 100644
--- a/hw/ppc/spapr_vio.c
+++ b/hw/ppc/spapr_vio.c
@@ -469,7 +469,7 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp)
}
if (pc->rtce_window_size) {
- uint32_t liobn = SPAPR_VIO_BASE_LIOBN | dev->reg;
+ uint32_t liobn = SPAPR_VIO_LIOBN(dev->reg);
memory_region_init(&dev->mrroot, OBJECT(dev), "iommu-spapr-root",
ram_size);
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index 8a565f657a..c574988c36 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -216,6 +216,7 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data)
mc->no_sdcard = 1;
mc->use_sclp = 1;
mc->max_cpus = 255;
+ mc->is_default = 1;
nc->nmi_monitor_handler = s390_nmi;
}
diff --git a/hw/s390x/s390-virtio.c b/hw/s390x/s390-virtio.c
index 59750dbfcd..00ea793651 100644
--- a/hw/s390x/s390-virtio.c
+++ b/hw/s390x/s390-virtio.c
@@ -345,7 +345,6 @@ static void s390_machine_class_init(ObjectClass *oc, void *data)
mc->no_floppy = 1;
mc->no_cdrom = 1;
mc->no_sdcard = 1;
- mc->is_default = 1;
nc->nmi_monitor_handler = s390_nmi;
}
diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
index 3ef0055126..e32ada9bf1 100644
--- a/hw/s390x/virtio-ccw.c
+++ b/hw/s390x/virtio-ccw.c
@@ -1,8 +1,9 @@
/*
* virtio ccw target implementation
*
- * Copyright 2012,2014 IBM Corp.
+ * Copyright 2012,2015 IBM Corp.
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ * Pierre Morel <pmorel@linux.vnet.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or (at
* your option) any later version. See the COPYING file in the top-level
@@ -1314,6 +1315,7 @@ static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f)
{
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
SubchDev *s = dev->sch;
+ VirtIODevice *vdev = virtio_ccw_get_vdev(s);
subch_device_save(s, f);
if (dev->indicators != NULL) {
@@ -1337,6 +1339,7 @@ static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f)
qemu_put_be32(f, 0);
qemu_put_be64(f, 0UL);
}
+ qemu_put_be16(f, vdev->config_vector);
qemu_put_be64(f, dev->routes.adapter.ind_offset);
qemu_put_byte(f, dev->thinint_isc);
}
@@ -1345,6 +1348,7 @@ static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f)
{
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
SubchDev *s = dev->sch;
+ VirtIODevice *vdev = virtio_ccw_get_vdev(s);
int len;
s->driver_data = dev;
@@ -1370,6 +1374,7 @@ static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f)
qemu_get_be64(f);
dev->summary_indicator = NULL;
}
+ qemu_get_be16s(f, &vdev->config_vector);
dev->routes.adapter.ind_offset = qemu_get_be64(f);
dev->thinint_isc = qemu_get_byte(f);
if (s->thinint_active) {
@@ -1396,6 +1401,10 @@ static void virtio_ccw_device_plugged(DeviceState *d, Error **errp)
return;
}
+ if (!kvm_eventfds_enabled()) {
+ dev->flags &= ~VIRTIO_CCW_FLAG_USE_IOEVENTFD;
+ }
+
sch->id.cu_model = virtio_bus_get_vdev_id(&dev->bus);
css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid,
@@ -1734,6 +1743,56 @@ static const TypeInfo virtio_ccw_bus_info = {
.class_init = virtio_ccw_bus_class_init,
};
+#ifdef CONFIG_VIRTFS
+static Property virtio_ccw_9p_properties[] = {
+ DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id),
+ DEFINE_PROP_BIT("ioeventfd", VirtioCcwDevice, flags,
+ VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_ccw_9p_realize(VirtioCcwDevice *ccw_dev, Error **errp)
+{
+ V9fsCCWState *dev = VIRTIO_9P_CCW(ccw_dev);
+ DeviceState *vdev = DEVICE(&dev->vdev);
+ Error *err = NULL;
+
+ qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus));
+ object_property_set_bool(OBJECT(vdev), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ }
+}
+
+static void virtio_ccw_9p_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
+
+ k->exit = virtio_ccw_exit;
+ k->realize = virtio_ccw_9p_realize;
+ dc->reset = virtio_ccw_reset;
+ dc->props = virtio_ccw_9p_properties;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
+}
+
+static void virtio_ccw_9p_instance_init(Object *obj)
+{
+ V9fsCCWState *dev = VIRTIO_9P_CCW(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VIRTIO_9P);
+}
+
+static const TypeInfo virtio_ccw_9p_info = {
+ .name = TYPE_VIRTIO_9P_CCW,
+ .parent = TYPE_VIRTIO_CCW_DEVICE,
+ .instance_size = sizeof(V9fsCCWState),
+ .instance_init = virtio_ccw_9p_instance_init,
+ .class_init = virtio_ccw_9p_class_init,
+};
+#endif
+
static void virtio_ccw_register(void)
{
type_register_static(&virtio_ccw_bus_info);
@@ -1749,6 +1808,9 @@ static void virtio_ccw_register(void)
#endif
type_register_static(&virtio_ccw_rng);
type_register_static(&virtual_css_bridge_info);
+#ifdef CONFIG_VIRTFS
+ type_register_static(&virtio_ccw_9p_info);
+#endif
}
type_init(virtio_ccw_register)
diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h
index ad3af7626a..d729263960 100644
--- a/hw/s390x/virtio-ccw.h
+++ b/hw/s390x/virtio-ccw.h
@@ -1,8 +1,9 @@
/*
* virtio ccw target definitions
*
- * Copyright 2012 IBM Corp.
+ * Copyright 2012,2015 IBM Corp.
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ * Pierre Morel <pmorel@linux.vnet.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or (at
* your option) any later version. See the COPYING file in the top-level
@@ -189,4 +190,19 @@ typedef struct VirtIORNGCcw {
VirtualCssBus *virtual_css_bus_init(void);
void virtio_ccw_device_update_status(SubchDev *sch);
VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch);
+
+#ifdef CONFIG_VIRTFS
+#include "hw/9pfs/virtio-9p.h"
+
+#define TYPE_VIRTIO_9P_CCW "virtio-9p-ccw"
+#define VIRTIO_9P_CCW(obj) \
+ OBJECT_CHECK(V9fsCCWState, (obj), TYPE_VIRTIO_9P_CCW)
+
+typedef struct V9fsCCWState {
+ VirtioCcwDevice parent_obj;
+ V9fsState vdev;
+} V9fsCCWState;
+
+#endif /* CONFIG_VIRTFS */
+
#endif
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
index bd2c0e4caa..f50b2f08af 100644
--- a/hw/scsi/scsi-bus.c
+++ b/hw/scsi/scsi-bus.c
@@ -1968,6 +1968,7 @@ static const VMStateDescription vmstate_scsi_sense_state = {
.name = "SCSIDevice/sense",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = scsi_sense_state_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8_SUB_ARRAY(sense, SCSIDevice,
SCSI_SENSE_BUF_SIZE_OLD,
@@ -1998,13 +1999,9 @@ const VMStateDescription vmstate_scsi_device = {
},
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection []) {
- {
- .vmsd = &vmstate_scsi_sense_state,
- .needed = scsi_sense_state_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_scsi_sense_state,
+ NULL
}
};
diff --git a/hw/sd/pxa2xx_mmci.c b/hw/sd/pxa2xx_mmci.c
index ac3ab39bea..d1fe6d58e8 100644
--- a/hw/sd/pxa2xx_mmci.c
+++ b/hw/sd/pxa2xx_mmci.c
@@ -48,7 +48,6 @@ struct PXA2xxMMCIState {
int resp_len;
int cmdreq;
- int ac_width;
};
#define MMC_STRPCL 0x00 /* MMC Clock Start/Stop register */
@@ -215,7 +214,7 @@ static void pxa2xx_mmci_wakequeues(PXA2xxMMCIState *s)
pxa2xx_mmci_fifo_update(s);
}
-static uint32_t pxa2xx_mmci_read(void *opaque, hwaddr offset)
+static uint64_t pxa2xx_mmci_read(void *opaque, hwaddr offset, unsigned size)
{
PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
uint32_t ret;
@@ -257,8 +256,8 @@ static uint32_t pxa2xx_mmci_read(void *opaque, hwaddr offset)
return 0;
case MMC_RXFIFO:
ret = 0;
- while (s->ac_width -- && s->rx_len) {
- ret |= s->rx_fifo[s->rx_start ++] << (s->ac_width << 3);
+ while (size-- && s->rx_len) {
+ ret |= s->rx_fifo[s->rx_start++] << (size << 3);
s->rx_start &= 0x1f;
s->rx_len --;
}
@@ -277,7 +276,7 @@ static uint32_t pxa2xx_mmci_read(void *opaque, hwaddr offset)
}
static void pxa2xx_mmci_write(void *opaque,
- hwaddr offset, uint32_t value)
+ hwaddr offset, uint64_t value, unsigned size)
{
PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
@@ -370,9 +369,9 @@ static void pxa2xx_mmci_write(void *opaque,
break;
case MMC_TXFIFO:
- while (s->ac_width -- && s->tx_len < 0x20)
+ while (size-- && s->tx_len < 0x20)
s->tx_fifo[(s->tx_start + (s->tx_len ++)) & 0x1f] =
- (value >> (s->ac_width << 3)) & 0xff;
+ (value >> (size << 3)) & 0xff;
s->intreq &= ~INT_TXFIFO_REQ;
pxa2xx_mmci_fifo_update(s);
break;
@@ -386,60 +385,9 @@ static void pxa2xx_mmci_write(void *opaque,
}
}
-static uint32_t pxa2xx_mmci_readb(void *opaque, hwaddr offset)
-{
- PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
- s->ac_width = 1;
- return pxa2xx_mmci_read(opaque, offset);
-}
-
-static uint32_t pxa2xx_mmci_readh(void *opaque, hwaddr offset)
-{
- PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
- s->ac_width = 2;
- return pxa2xx_mmci_read(opaque, offset);
-}
-
-static uint32_t pxa2xx_mmci_readw(void *opaque, hwaddr offset)
-{
- PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
- s->ac_width = 4;
- return pxa2xx_mmci_read(opaque, offset);
-}
-
-static void pxa2xx_mmci_writeb(void *opaque,
- hwaddr offset, uint32_t value)
-{
- PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
- s->ac_width = 1;
- pxa2xx_mmci_write(opaque, offset, value);
-}
-
-static void pxa2xx_mmci_writeh(void *opaque,
- hwaddr offset, uint32_t value)
-{
- PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
- s->ac_width = 2;
- pxa2xx_mmci_write(opaque, offset, value);
-}
-
-static void pxa2xx_mmci_writew(void *opaque,
- hwaddr offset, uint32_t value)
-{
- PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
- s->ac_width = 4;
- pxa2xx_mmci_write(opaque, offset, value);
-}
-
static const MemoryRegionOps pxa2xx_mmci_ops = {
- .old_mmio = {
- .read = { pxa2xx_mmci_readb,
- pxa2xx_mmci_readh,
- pxa2xx_mmci_readw, },
- .write = { pxa2xx_mmci_writeb,
- pxa2xx_mmci_writeh,
- pxa2xx_mmci_writew, },
- },
+ .read = pxa2xx_mmci_read,
+ .write = pxa2xx_mmci_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c
index 4221060308..5e22ed79b2 100644
--- a/hw/sh4/r2d.c
+++ b/hw/sh4/r2d.c
@@ -127,7 +127,7 @@ static void r2d_fpga_irq_set(void *opaque, int n, int level)
update_irl(fpga);
}
-static uint32_t r2d_fpga_read(void *opaque, hwaddr addr)
+static uint64_t r2d_fpga_read(void *opaque, hwaddr addr, unsigned int size)
{
r2d_fpga_t *s = opaque;
@@ -146,7 +146,7 @@ static uint32_t r2d_fpga_read(void *opaque, hwaddr addr)
}
static void
-r2d_fpga_write(void *opaque, hwaddr addr, uint32_t value)
+r2d_fpga_write(void *opaque, hwaddr addr, uint64_t value, unsigned int size)
{
r2d_fpga_t *s = opaque;
@@ -170,10 +170,10 @@ r2d_fpga_write(void *opaque, hwaddr addr, uint32_t value)
}
static const MemoryRegionOps r2d_fpga_ops = {
- .old_mmio = {
- .read = { r2d_fpga_read, r2d_fpga_read, NULL, },
- .write = { r2d_fpga_write, r2d_fpga_write, NULL, },
- },
+ .read = r2d_fpga_read,
+ .write = r2d_fpga_write,
+ .impl.min_access_size = 2,
+ .impl.max_access_size = 2,
.endianness = DEVICE_NATIVE_ENDIAN,
};
diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c
index a69bf2da45..68ac4d8bba 100644
--- a/hw/sparc/sun4m.c
+++ b/hw/sparc/sun4m.c
@@ -124,7 +124,7 @@ void DMA_register_channel (int nchan,
static void fw_cfg_boot_set(void *opaque, const char *boot_device,
Error **errp)
{
- fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
+ fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
}
static void nvram_init(Nvram *nvram, uint8_t *macaddr,
@@ -897,7 +897,6 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef,
espdma_irq, ledma_irq;
qemu_irq esp_reset, dma_enable;
qemu_irq fdc_tc;
- qemu_irq *cpu_halt;
unsigned long kernel_size;
DriveInfo *fd[MAX_FD];
FWCfgState *fw_cfg;
@@ -1024,9 +1023,8 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef,
escc_init(hwdef->serial_base, slavio_irq[15], slavio_irq[15],
serial_hds[0], serial_hds[1], ESCC_CLOCK, 1);
- cpu_halt = qemu_allocate_irqs(cpu_halt_signal, NULL, 1);
if (hwdef->apc_base) {
- apc_init(hwdef->apc_base, cpu_halt[0]);
+ apc_init(hwdef->apc_base, qemu_allocate_irq(cpu_halt_signal, NULL, 0));
}
if (hwdef->fd_base) {
@@ -1036,7 +1034,7 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef,
sun4m_fdctrl_init(slavio_irq[22], hwdef->fd_base, fd,
&fdc_tc);
} else {
- fdc_tc = *qemu_allocate_irqs(dummy_fdc_tc, NULL, 1);
+ fdc_tc = qemu_allocate_irq(dummy_fdc_tc, NULL, 0);
}
slavio_misc_init(hwdef->slavio_base, hwdef->aux1_base, hwdef->aux2_base,
diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c
index 6f34e87935..30cfa0e0a0 100644
--- a/hw/sparc64/sun4u.c
+++ b/hw/sparc64/sun4u.c
@@ -127,7 +127,7 @@ void DMA_register_channel (int nchan,
static void fw_cfg_boot_set(void *opaque, const char *boot_device,
Error **errp)
{
- fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
+ fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
}
static int sun4u_NVRAM_set_params(Nvram *nvram, uint16_t NVRAM_size,
diff --git a/hw/timer/arm_timer.c b/hw/timer/arm_timer.c
index 145291016b..d53f39ad62 100644
--- a/hw/timer/arm_timer.c
+++ b/hw/timer/arm_timer.c
@@ -280,14 +280,12 @@ static int sp804_init(SysBusDevice *sbd)
{
DeviceState *dev = DEVICE(sbd);
SP804State *s = SP804(dev);
- qemu_irq *qi;
- qi = qemu_allocate_irqs(sp804_set_irq, s, 2);
sysbus_init_irq(sbd, &s->irq);
s->timer[0] = arm_timer_init(s->freq0);
s->timer[1] = arm_timer_init(s->freq1);
- s->timer[0]->irq = qi[0];
- s->timer[1]->irq = qi[1];
+ s->timer[0]->irq = qemu_allocate_irq(sp804_set_irq, s, 0);
+ s->timer[1]->irq = qemu_allocate_irq(sp804_set_irq, s, 1);
memory_region_init_io(&s->iomem, OBJECT(s), &sp804_ops, s,
"sp804", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c
index b6b8a2063d..b50071ef93 100644
--- a/hw/timer/hpet.c
+++ b/hw/timer/hpet.c
@@ -283,6 +283,7 @@ static const VMStateDescription vmstate_hpet_rtc_irq_level = {
.name = "hpet/rtc_irq_level",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = hpet_rtc_irq_level_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8(rtc_irq_level, HPETState),
VMSTATE_END_OF_LIST()
@@ -322,13 +323,9 @@ static const VMStateDescription vmstate_hpet = {
vmstate_hpet_timer, HPETTimer),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_hpet_rtc_irq_level,
- .needed = hpet_rtc_irq_level_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_hpet_rtc_irq_level,
+ NULL
}
};
diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c
index f2b77fa118..32048258c9 100644
--- a/hw/timer/mc146818rtc.c
+++ b/hw/timer/mc146818rtc.c
@@ -733,22 +733,23 @@ static int rtc_post_load(void *opaque, int version_id)
return 0;
}
+static bool rtc_irq_reinject_on_ack_count_needed(void *opaque)
+{
+ RTCState *s = (RTCState *)opaque;
+ return s->irq_reinject_on_ack_count != 0;
+}
+
static const VMStateDescription vmstate_rtc_irq_reinject_on_ack_count = {
.name = "mc146818rtc/irq_reinject_on_ack_count",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = rtc_irq_reinject_on_ack_count_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT16(irq_reinject_on_ack_count, RTCState),
VMSTATE_END_OF_LIST()
}
};
-static bool rtc_irq_reinject_on_ack_count_needed(void *opaque)
-{
- RTCState *s = (RTCState *)opaque;
- return s->irq_reinject_on_ack_count != 0;
-}
-
static const VMStateDescription vmstate_rtc = {
.name = "mc146818rtc",
.version_id = 3,
@@ -770,13 +771,9 @@ static const VMStateDescription vmstate_rtc = {
VMSTATE_UINT64_V(next_alarm_time, RTCState, 3),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_rtc_irq_reinject_on_ack_count,
- .needed = rtc_irq_reinject_on_ack_count_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_rtc_irq_reinject_on_ack_count,
+ NULL
}
};
diff --git a/hw/unicore32/puv3.c b/hw/unicore32/puv3.c
index cc9a21a712..703e29d6d3 100644
--- a/hw/unicore32/puv3.c
+++ b/hw/unicore32/puv3.c
@@ -40,15 +40,15 @@ static void puv3_intc_cpu_handler(void *opaque, int irq, int level)
static void puv3_soc_init(CPUUniCore32State *env)
{
- qemu_irq *cpu_intc, irqs[PUV3_IRQS_NR];
+ qemu_irq cpu_intc, irqs[PUV3_IRQS_NR];
DeviceState *dev;
MemoryRegion *i8042 = g_new(MemoryRegion, 1);
int i;
/* Initialize interrupt controller */
- cpu_intc = qemu_allocate_irqs(puv3_intc_cpu_handler,
- uc32_env_get_cpu(env), 1);
- dev = sysbus_create_simple("puv3_intc", PUV3_INTC_BASE, *cpu_intc);
+ cpu_intc = qemu_allocate_irq(puv3_intc_cpu_handler,
+ uc32_env_get_cpu(env), 0);
+ dev = sysbus_create_simple("puv3_intc", PUV3_INTC_BASE, cpu_intc);
for (i = 0; i < PUV3_IRQS_NR; i++) {
irqs[i] = qdev_get_gpio_in(dev, i);
}
diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c
index 1a22c9c0cb..7d65818064 100644
--- a/hw/usb/hcd-ohci.c
+++ b/hw/usb/hcd-ohci.c
@@ -2034,6 +2034,7 @@ static const VMStateDescription vmstate_ohci_eof_timer = {
.version_id = 1,
.minimum_version_id = 1,
.pre_load = ohci_eof_timer_pre_load,
+ .needed = ohci_eof_timer_needed,
.fields = (VMStateField[]) {
VMSTATE_TIMER_PTR(eof_timer, OHCIState),
VMSTATE_END_OF_LIST()
@@ -2081,13 +2082,9 @@ static const VMStateDescription vmstate_ohci_state = {
VMSTATE_BOOL(async_complete, OHCIState),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection []) {
- {
- .vmsd = &vmstate_ohci_eof_timer,
- .needed = ohci_eof_timer_needed,
- } , {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_ohci_eof_timer,
+ NULL
}
};
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index 242a654583..6b4218c037 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -2257,40 +2257,42 @@ static const VMStateInfo usbredir_ep_bufpq_vmstate_info = {
/* For endp_data migration */
+static bool usbredir_bulk_receiving_needed(void *priv)
+{
+ struct endp_data *endp = priv;
+
+ return endp->bulk_receiving_started;
+}
+
static const VMStateDescription usbredir_bulk_receiving_vmstate = {
.name = "usb-redir-ep/bulk-receiving",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = usbredir_bulk_receiving_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8(bulk_receiving_started, struct endp_data),
VMSTATE_END_OF_LIST()
}
};
-static bool usbredir_bulk_receiving_needed(void *priv)
+static bool usbredir_stream_needed(void *priv)
{
struct endp_data *endp = priv;
- return endp->bulk_receiving_started;
+ return endp->max_streams;
}
static const VMStateDescription usbredir_stream_vmstate = {
.name = "usb-redir-ep/stream-state",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = usbredir_stream_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT32(max_streams, struct endp_data),
VMSTATE_END_OF_LIST()
}
};
-static bool usbredir_stream_needed(void *priv)
-{
- struct endp_data *endp = priv;
-
- return endp->max_streams;
-}
-
static const VMStateDescription usbredir_ep_vmstate = {
.name = "usb-redir-ep",
.version_id = 1,
@@ -2318,16 +2320,10 @@ static const VMStateDescription usbredir_ep_vmstate = {
VMSTATE_INT32(bufpq_target_size, struct endp_data),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &usbredir_bulk_receiving_vmstate,
- .needed = usbredir_bulk_receiving_needed,
- }, {
- .vmsd = &usbredir_stream_vmstate,
- .needed = usbredir_stream_needed,
- }, {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &usbredir_bulk_receiving_vmstate,
+ &usbredir_stream_vmstate,
+ NULL
}
};
diff --git a/hw/vfio/Makefile.objs b/hw/vfio/Makefile.objs
index e31f30ec09..d540c9d140 100644
--- a/hw/vfio/Makefile.objs
+++ b/hw/vfio/Makefile.objs
@@ -1,4 +1,6 @@
ifeq ($(CONFIG_LINUX), y)
obj-$(CONFIG_SOFTMMU) += common.o
obj-$(CONFIG_PCI) += pci.o
+obj-$(CONFIG_SOFTMMU) += platform.o
+obj-$(CONFIG_SOFTMMU) += calxeda-xgmac.o
endif
diff --git a/hw/vfio/calxeda-xgmac.c b/hw/vfio/calxeda-xgmac.c
new file mode 100644
index 0000000000..eb914f0d0b
--- /dev/null
+++ b/hw/vfio/calxeda-xgmac.c
@@ -0,0 +1,55 @@
+/*
+ * calxeda xgmac VFIO device
+ *
+ * Copyright Linaro Limited, 2014
+ *
+ * Authors:
+ * Eric Auger <eric.auger@linaro.org>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "hw/vfio/vfio-calxeda-xgmac.h"
+
+static void calxeda_xgmac_realize(DeviceState *dev, Error **errp)
+{
+ VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev);
+ VFIOCalxedaXgmacDeviceClass *k = VFIO_CALXEDA_XGMAC_DEVICE_GET_CLASS(dev);
+
+ vdev->compat = g_strdup("calxeda,hb-xgmac");
+
+ k->parent_realize(dev, errp);
+}
+
+static const VMStateDescription vfio_platform_calxeda_xgmac_vmstate = {
+ .name = TYPE_VFIO_CALXEDA_XGMAC,
+ .unmigratable = 1,
+};
+
+static void vfio_calxeda_xgmac_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VFIOCalxedaXgmacDeviceClass *vcxc =
+ VFIO_CALXEDA_XGMAC_DEVICE_CLASS(klass);
+ vcxc->parent_realize = dc->realize;
+ dc->realize = calxeda_xgmac_realize;
+ dc->desc = "VFIO Calxeda XGMAC";
+ dc->vmsd = &vfio_platform_calxeda_xgmac_vmstate;
+}
+
+static const TypeInfo vfio_calxeda_xgmac_dev_info = {
+ .name = TYPE_VFIO_CALXEDA_XGMAC,
+ .parent = TYPE_VFIO_PLATFORM,
+ .instance_size = sizeof(VFIOCalxedaXgmacDevice),
+ .class_init = vfio_calxeda_xgmac_class_init,
+ .class_size = sizeof(VFIOCalxedaXgmacDeviceClass),
+};
+
+static void register_calxeda_xgmac_dev_type(void)
+{
+ type_register_static(&vfio_calxeda_xgmac_dev_info);
+}
+
+type_init(register_calxeda_xgmac_dev_type)
diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c
new file mode 100644
index 0000000000..5c678b914e
--- /dev/null
+++ b/hw/vfio/platform.c
@@ -0,0 +1,615 @@
+/*
+ * vfio based device assignment support - platform devices
+ *
+ * Copyright Linaro Limited, 2014
+ *
+ * Authors:
+ * Kim Phillips <kim.phillips@linaro.org>
+ * Eric Auger <eric.auger@linaro.org>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ * Based on vfio based PCI device assignment support:
+ * Copyright Red Hat, Inc. 2012
+ */
+
+#include <sys/ioctl.h>
+#include <linux/vfio.h>
+
+#include "hw/vfio/vfio-platform.h"
+#include "qemu/error-report.h"
+#include "qemu/range.h"
+#include "sysemu/sysemu.h"
+#include "exec/memory.h"
+#include "qemu/queue.h"
+#include "hw/sysbus.h"
+#include "trace.h"
+#include "hw/platform-bus.h"
+
+/*
+ * Functions used whatever the injection method
+ */
+
+/**
+ * vfio_init_intp - allocate, initialize the IRQ struct pointer
+ * and add it into the list of IRQs
+ * @vbasedev: the VFIO device handle
+ * @info: irq info struct retrieved from VFIO driver
+ */
+static VFIOINTp *vfio_init_intp(VFIODevice *vbasedev,
+ struct vfio_irq_info info)
+{
+ int ret;
+ VFIOPlatformDevice *vdev =
+ container_of(vbasedev, VFIOPlatformDevice, vbasedev);
+ SysBusDevice *sbdev = SYS_BUS_DEVICE(vdev);
+ VFIOINTp *intp;
+
+ intp = g_malloc0(sizeof(*intp));
+ intp->vdev = vdev;
+ intp->pin = info.index;
+ intp->flags = info.flags;
+ intp->state = VFIO_IRQ_INACTIVE;
+
+ sysbus_init_irq(sbdev, &intp->qemuirq);
+
+ /* Get an eventfd for trigger */
+ ret = event_notifier_init(&intp->interrupt, 0);
+ if (ret) {
+ g_free(intp);
+ error_report("vfio: Error: trigger event_notifier_init failed ");
+ return NULL;
+ }
+
+ QLIST_INSERT_HEAD(&vdev->intp_list, intp, next);
+ return intp;
+}
+
+/**
+ * vfio_set_trigger_eventfd - set VFIO eventfd handling
+ *
+ * @intp: IRQ struct handle
+ * @handler: handler to be called on eventfd signaling
+ *
+ * Setup VFIO signaling and attach an optional user-side handler
+ * to the eventfd
+ */
+static int vfio_set_trigger_eventfd(VFIOINTp *intp,
+ eventfd_user_side_handler_t handler)
+{
+ VFIODevice *vbasedev = &intp->vdev->vbasedev;
+ struct vfio_irq_set *irq_set;
+ int argsz, ret;
+ int32_t *pfd;
+
+ argsz = sizeof(*irq_set) + sizeof(*pfd);
+ irq_set = g_malloc0(argsz);
+ irq_set->argsz = argsz;
+ irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
+ irq_set->index = intp->pin;
+ irq_set->start = 0;
+ irq_set->count = 1;
+ pfd = (int32_t *)&irq_set->data;
+ *pfd = event_notifier_get_fd(&intp->interrupt);
+ qemu_set_fd_handler(*pfd, (IOHandler *)handler, NULL, intp);
+ ret = ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irq_set);
+ g_free(irq_set);
+ if (ret < 0) {
+ error_report("vfio: Failed to set trigger eventfd: %m");
+ qemu_set_fd_handler(*pfd, NULL, NULL, NULL);
+ }
+ return ret;
+}
+
+/*
+ * Functions only used when eventfds are handled on user-side
+ * ie. without irqfd
+ */
+
+/**
+ * vfio_mmap_set_enabled - enable/disable the fast path mode
+ * @vdev: the VFIO platform device
+ * @enabled: the target mmap state
+ *
+ * enabled = true ~ fast path = MMIO region is mmaped (no KVM TRAP);
+ * enabled = false ~ slow path = MMIO region is trapped and region callbacks
+ * are called; slow path enables to trap the device IRQ status register reset
+*/
+
+static void vfio_mmap_set_enabled(VFIOPlatformDevice *vdev, bool enabled)
+{
+ int i;
+
+ trace_vfio_platform_mmap_set_enabled(enabled);
+
+ for (i = 0; i < vdev->vbasedev.num_regions; i++) {
+ VFIORegion *region = vdev->regions[i];
+
+ memory_region_set_enabled(&region->mmap_mem, enabled);
+ }
+}
+
+/**
+ * vfio_intp_mmap_enable - timer function, restores the fast path
+ * if there is no more active IRQ
+ * @opaque: actually points to the VFIO platform device
+ *
+ * Called on mmap timer timout, this function checks whether the
+ * IRQ is still active and if not, restores the fast path.
+ * by construction a single eventfd is handled at a time.
+ * if the IRQ is still active, the timer is re-programmed.
+ */
+static void vfio_intp_mmap_enable(void *opaque)
+{
+ VFIOINTp *tmp;
+ VFIOPlatformDevice *vdev = (VFIOPlatformDevice *)opaque;
+
+ qemu_mutex_lock(&vdev->intp_mutex);
+ QLIST_FOREACH(tmp, &vdev->intp_list, next) {
+ if (tmp->state == VFIO_IRQ_ACTIVE) {
+ trace_vfio_platform_intp_mmap_enable(tmp->pin);
+ /* re-program the timer to check active status later */
+ timer_mod(vdev->mmap_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
+ vdev->mmap_timeout);
+ qemu_mutex_unlock(&vdev->intp_mutex);
+ return;
+ }
+ }
+ vfio_mmap_set_enabled(vdev, true);
+ qemu_mutex_unlock(&vdev->intp_mutex);
+}
+
+/**
+ * vfio_intp_inject_pending_lockheld - Injects a pending IRQ
+ * @opaque: opaque pointer, in practice the VFIOINTp handle
+ *
+ * The function is called on a previous IRQ completion, from
+ * vfio_platform_eoi, while the intp_mutex is locked.
+ * Also in such situation, the slow path already is set and
+ * the mmap timer was already programmed.
+ */
+static void vfio_intp_inject_pending_lockheld(VFIOINTp *intp)
+{
+ trace_vfio_platform_intp_inject_pending_lockheld(intp->pin,
+ event_notifier_get_fd(&intp->interrupt));
+
+ intp->state = VFIO_IRQ_ACTIVE;
+
+ /* trigger the virtual IRQ */
+ qemu_set_irq(intp->qemuirq, 1);
+}
+
+/**
+ * vfio_intp_interrupt - The user-side eventfd handler
+ * @opaque: opaque pointer which in practice is the VFIOINTp handle
+ *
+ * the function is entered in event handler context:
+ * the vIRQ is injected into the guest if there is no other active
+ * or pending IRQ.
+ */
+static void vfio_intp_interrupt(VFIOINTp *intp)
+{
+ int ret;
+ VFIOINTp *tmp;
+ VFIOPlatformDevice *vdev = intp->vdev;
+ bool delay_handling = false;
+
+ qemu_mutex_lock(&vdev->intp_mutex);
+ if (intp->state == VFIO_IRQ_INACTIVE) {
+ QLIST_FOREACH(tmp, &vdev->intp_list, next) {
+ if (tmp->state == VFIO_IRQ_ACTIVE ||
+ tmp->state == VFIO_IRQ_PENDING) {
+ delay_handling = true;
+ break;
+ }
+ }
+ }
+ if (delay_handling) {
+ /*
+ * the new IRQ gets a pending status and is pushed in
+ * the pending queue
+ */
+ intp->state = VFIO_IRQ_PENDING;
+ trace_vfio_intp_interrupt_set_pending(intp->pin);
+ QSIMPLEQ_INSERT_TAIL(&vdev->pending_intp_queue,
+ intp, pqnext);
+ ret = event_notifier_test_and_clear(&intp->interrupt);
+ qemu_mutex_unlock(&vdev->intp_mutex);
+ return;
+ }
+
+ trace_vfio_platform_intp_interrupt(intp->pin,
+ event_notifier_get_fd(&intp->interrupt));
+
+ ret = event_notifier_test_and_clear(&intp->interrupt);
+ if (!ret) {
+ error_report("Error when clearing fd=%d (ret = %d)\n",
+ event_notifier_get_fd(&intp->interrupt), ret);
+ }
+
+ intp->state = VFIO_IRQ_ACTIVE;
+
+ /* sets slow path */
+ vfio_mmap_set_enabled(vdev, false);
+
+ /* trigger the virtual IRQ */
+ qemu_set_irq(intp->qemuirq, 1);
+
+ /*
+ * Schedule the mmap timer which will restore fastpath when no IRQ
+ * is active anymore
+ */
+ if (vdev->mmap_timeout) {
+ timer_mod(vdev->mmap_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
+ vdev->mmap_timeout);
+ }
+ qemu_mutex_unlock(&vdev->intp_mutex);
+}
+
+/**
+ * vfio_platform_eoi - IRQ completion routine
+ * @vbasedev: the VFIO device handle
+ *
+ * De-asserts the active virtual IRQ and unmasks the physical IRQ
+ * (effective for level sensitive IRQ auto-masked by the VFIO driver).
+ * Then it handles next pending IRQ if any.
+ * eoi function is called on the first access to any MMIO region
+ * after an IRQ was triggered, trapped since slow path was set.
+ * It is assumed this access corresponds to the IRQ status
+ * register reset. With such a mechanism, a single IRQ can be
+ * handled at a time since there is no way to know which IRQ
+ * was completed by the guest (we would need additional details
+ * about the IRQ status register mask).
+ */
+static void vfio_platform_eoi(VFIODevice *vbasedev)
+{
+ VFIOINTp *intp;
+ VFIOPlatformDevice *vdev =
+ container_of(vbasedev, VFIOPlatformDevice, vbasedev);
+
+ qemu_mutex_lock(&vdev->intp_mutex);
+ QLIST_FOREACH(intp, &vdev->intp_list, next) {
+ if (intp->state == VFIO_IRQ_ACTIVE) {
+ trace_vfio_platform_eoi(intp->pin,
+ event_notifier_get_fd(&intp->interrupt));
+ intp->state = VFIO_IRQ_INACTIVE;
+
+ /* deassert the virtual IRQ */
+ qemu_set_irq(intp->qemuirq, 0);
+
+ if (intp->flags & VFIO_IRQ_INFO_AUTOMASKED) {
+ /* unmasks the physical level-sensitive IRQ */
+ vfio_unmask_single_irqindex(vbasedev, intp->pin);
+ }
+
+ /* a single IRQ can be active at a time */
+ break;
+ }
+ }
+ /* in case there are pending IRQs, handle the first one */
+ if (!QSIMPLEQ_EMPTY(&vdev->pending_intp_queue)) {
+ intp = QSIMPLEQ_FIRST(&vdev->pending_intp_queue);
+ vfio_intp_inject_pending_lockheld(intp);
+ QSIMPLEQ_REMOVE_HEAD(&vdev->pending_intp_queue, pqnext);
+ }
+ qemu_mutex_unlock(&vdev->intp_mutex);
+}
+
+/**
+ * vfio_start_eventfd_injection - starts the virtual IRQ injection using
+ * user-side handled eventfds
+ * @intp: the IRQ struct pointer
+ */
+
+static int vfio_start_eventfd_injection(VFIOINTp *intp)
+{
+ int ret;
+
+ ret = vfio_set_trigger_eventfd(intp, vfio_intp_interrupt);
+ if (ret) {
+ error_report("vfio: Error: Failed to pass IRQ fd to the driver: %m");
+ }
+ return ret;
+}
+
+/* VFIO skeleton */
+
+static void vfio_platform_compute_needs_reset(VFIODevice *vbasedev)
+{
+ vbasedev->needs_reset = true;
+}
+
+/* not implemented yet */
+static int vfio_platform_hot_reset_multi(VFIODevice *vbasedev)
+{
+ return -1;
+}
+
+/**
+ * vfio_populate_device - Allocate and populate MMIO region
+ * and IRQ structs according to driver returned information
+ * @vbasedev: the VFIO device handle
+ *
+ */
+static int vfio_populate_device(VFIODevice *vbasedev)
+{
+ VFIOINTp *intp, *tmp;
+ int i, ret = -1;
+ VFIOPlatformDevice *vdev =
+ container_of(vbasedev, VFIOPlatformDevice, vbasedev);
+
+ if (!(vbasedev->flags & VFIO_DEVICE_FLAGS_PLATFORM)) {
+ error_report("vfio: Um, this isn't a platform device");
+ return ret;
+ }
+
+ vdev->regions = g_new0(VFIORegion *, vbasedev->num_regions);
+
+ for (i = 0; i < vbasedev->num_regions; i++) {
+ struct vfio_region_info reg_info = { .argsz = sizeof(reg_info) };
+ VFIORegion *ptr;
+
+ vdev->regions[i] = g_malloc0(sizeof(VFIORegion));
+ ptr = vdev->regions[i];
+ reg_info.index = i;
+ ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, &reg_info);
+ if (ret) {
+ error_report("vfio: Error getting region %d info: %m", i);
+ goto reg_error;
+ }
+ ptr->flags = reg_info.flags;
+ ptr->size = reg_info.size;
+ ptr->fd_offset = reg_info.offset;
+ ptr->nr = i;
+ ptr->vbasedev = vbasedev;
+
+ trace_vfio_platform_populate_regions(ptr->nr,
+ (unsigned long)ptr->flags,
+ (unsigned long)ptr->size,
+ ptr->vbasedev->fd,
+ (unsigned long)ptr->fd_offset);
+ }
+
+ vdev->mmap_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
+ vfio_intp_mmap_enable, vdev);
+
+ QSIMPLEQ_INIT(&vdev->pending_intp_queue);
+
+ for (i = 0; i < vbasedev->num_irqs; i++) {
+ struct vfio_irq_info irq = { .argsz = sizeof(irq) };
+
+ irq.index = i;
+ ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_IRQ_INFO, &irq);
+ if (ret) {
+ error_printf("vfio: error getting device %s irq info",
+ vbasedev->name);
+ goto irq_err;
+ } else {
+ trace_vfio_platform_populate_interrupts(irq.index,
+ irq.count,
+ irq.flags);
+ intp = vfio_init_intp(vbasedev, irq);
+ if (!intp) {
+ error_report("vfio: Error installing IRQ %d up", i);
+ goto irq_err;
+ }
+ }
+ }
+ return 0;
+irq_err:
+ timer_del(vdev->mmap_timer);
+ QLIST_FOREACH_SAFE(intp, &vdev->intp_list, next, tmp) {
+ QLIST_REMOVE(intp, next);
+ g_free(intp);
+ }
+reg_error:
+ for (i = 0; i < vbasedev->num_regions; i++) {
+ g_free(vdev->regions[i]);
+ }
+ g_free(vdev->regions);
+ return ret;
+}
+
+/* specialized functions for VFIO Platform devices */
+static VFIODeviceOps vfio_platform_ops = {
+ .vfio_compute_needs_reset = vfio_platform_compute_needs_reset,
+ .vfio_hot_reset_multi = vfio_platform_hot_reset_multi,
+ .vfio_eoi = vfio_platform_eoi,
+};
+
+/**
+ * vfio_base_device_init - perform preliminary VFIO setup
+ * @vbasedev: the VFIO device handle
+ *
+ * Implement the VFIO command sequence that allows to discover
+ * assigned device resources: group extraction, device
+ * fd retrieval, resource query.
+ * Precondition: the device name must be initialized
+ */
+static int vfio_base_device_init(VFIODevice *vbasedev)
+{
+ VFIOGroup *group;
+ VFIODevice *vbasedev_iter;
+ char path[PATH_MAX], iommu_group_path[PATH_MAX], *group_name;
+ ssize_t len;
+ struct stat st;
+ int groupid;
+ int ret;
+
+ /* name must be set prior to the call */
+ if (!vbasedev->name || strchr(vbasedev->name, '/')) {
+ return -EINVAL;
+ }
+
+ /* Check that the host device exists */
+ g_snprintf(path, sizeof(path), "/sys/bus/platform/devices/%s/",
+ vbasedev->name);
+
+ if (stat(path, &st) < 0) {
+ error_report("vfio: error: no such host device: %s", path);
+ return -errno;
+ }
+
+ g_strlcat(path, "iommu_group", sizeof(path));
+ len = readlink(path, iommu_group_path, sizeof(iommu_group_path));
+ if (len < 0 || len >= sizeof(iommu_group_path)) {
+ error_report("vfio: error no iommu_group for device");
+ return len < 0 ? -errno : -ENAMETOOLONG;
+ }
+
+ iommu_group_path[len] = 0;
+ group_name = basename(iommu_group_path);
+
+ if (sscanf(group_name, "%d", &groupid) != 1) {
+ error_report("vfio: error reading %s: %m", path);
+ return -errno;
+ }
+
+ trace_vfio_platform_base_device_init(vbasedev->name, groupid);
+
+ group = vfio_get_group(groupid, &address_space_memory);
+ if (!group) {
+ error_report("vfio: failed to get group %d", groupid);
+ return -ENOENT;
+ }
+
+ g_snprintf(path, sizeof(path), "%s", vbasedev->name);
+
+ QLIST_FOREACH(vbasedev_iter, &group->device_list, next) {
+ if (strcmp(vbasedev_iter->name, vbasedev->name) == 0) {
+ error_report("vfio: error: device %s is already attached", path);
+ vfio_put_group(group);
+ return -EBUSY;
+ }
+ }
+ ret = vfio_get_device(group, path, vbasedev);
+ if (ret) {
+ error_report("vfio: failed to get device %s", path);
+ vfio_put_group(group);
+ return ret;
+ }
+
+ ret = vfio_populate_device(vbasedev);
+ if (ret) {
+ error_report("vfio: failed to populate device %s", path);
+ vfio_put_group(group);
+ }
+
+ return ret;
+}
+
+/**
+ * vfio_map_region - initialize the 2 memory regions for a given
+ * MMIO region index
+ * @vdev: the VFIO platform device handle
+ * @nr: the index of the region
+ *
+ * Init the top memory region and the mmapped memory region beneath
+ * VFIOPlatformDevice is used since VFIODevice is not a QOM Object
+ * and could not be passed to memory region functions
+*/
+static void vfio_map_region(VFIOPlatformDevice *vdev, int nr)
+{
+ VFIORegion *region = vdev->regions[nr];
+ uint64_t size = region->size;
+ char name[64];
+
+ if (!size) {
+ return;
+ }
+
+ g_snprintf(name, sizeof(name), "VFIO %s region %d",
+ vdev->vbasedev.name, nr);
+
+ /* A "slow" read/write mapping underlies all regions */
+ memory_region_init_io(&region->mem, OBJECT(vdev), &vfio_region_ops,
+ region, name, size);
+
+ g_strlcat(name, " mmap", sizeof(name));
+
+ if (vfio_mmap_region(OBJECT(vdev), region, &region->mem,
+ &region->mmap_mem, &region->mmap, size, 0, name)) {
+ error_report("%s unsupported. Performance may be slow", name);
+ }
+}
+
+/**
+ * vfio_platform_realize - the device realize function
+ * @dev: device state pointer
+ * @errp: error
+ *
+ * initialize the device, its memory regions and IRQ structures
+ * IRQ are started separately
+ */
+static void vfio_platform_realize(DeviceState *dev, Error **errp)
+{
+ VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev);
+ SysBusDevice *sbdev = SYS_BUS_DEVICE(dev);
+ VFIODevice *vbasedev = &vdev->vbasedev;
+ VFIOINTp *intp;
+ int i, ret;
+
+ vbasedev->type = VFIO_DEVICE_TYPE_PLATFORM;
+ vbasedev->ops = &vfio_platform_ops;
+
+ trace_vfio_platform_realize(vbasedev->name, vdev->compat);
+
+ ret = vfio_base_device_init(vbasedev);
+ if (ret) {
+ error_setg(errp, "vfio: vfio_base_device_init failed for %s",
+ vbasedev->name);
+ return;
+ }
+
+ for (i = 0; i < vbasedev->num_regions; i++) {
+ vfio_map_region(vdev, i);
+ sysbus_init_mmio(sbdev, &vdev->regions[i]->mem);
+ }
+
+ QLIST_FOREACH(intp, &vdev->intp_list, next) {
+ vfio_start_eventfd_injection(intp);
+ }
+}
+
+static const VMStateDescription vfio_platform_vmstate = {
+ .name = TYPE_VFIO_PLATFORM,
+ .unmigratable = 1,
+};
+
+static Property vfio_platform_dev_properties[] = {
+ DEFINE_PROP_STRING("host", VFIOPlatformDevice, vbasedev.name),
+ DEFINE_PROP_BOOL("x-mmap", VFIOPlatformDevice, vbasedev.allow_mmap, true),
+ DEFINE_PROP_UINT32("mmap-timeout-ms", VFIOPlatformDevice,
+ mmap_timeout, 1100),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vfio_platform_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = vfio_platform_realize;
+ dc->props = vfio_platform_dev_properties;
+ dc->vmsd = &vfio_platform_vmstate;
+ dc->desc = "VFIO-based platform device assignment";
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+}
+
+static const TypeInfo vfio_platform_dev_info = {
+ .name = TYPE_VFIO_PLATFORM,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(VFIOPlatformDevice),
+ .class_init = vfio_platform_class_init,
+ .class_size = sizeof(VFIOPlatformDeviceClass),
+ .abstract = true,
+};
+
+static void register_vfio_platform_dev_type(void)
+{
+ type_register_static(&vfio_platform_dev_info);
+}
+
+type_init(register_vfio_platform_dev_type)
diff --git a/hw/virtio/dataplane/vring.c b/hw/virtio/dataplane/vring.c
index fabb8103e9..35891856ee 100644
--- a/hw/virtio/dataplane/vring.c
+++ b/hw/virtio/dataplane/vring.c
@@ -42,7 +42,7 @@ static void *vring_map(MemoryRegion **mr, hwaddr phys, hwaddr len,
}
/* Ignore regions with dirty logging, we cannot mark them dirty */
- if (memory_region_is_logging(section.mr)) {
+ if (memory_region_get_dirty_log_mask(section.mr)) {
goto out;
}
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index b84d21cf7e..a6dcc79399 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -418,7 +418,8 @@ static void vhost_set_memory(MemoryListener *listener,
memory_listener);
hwaddr start_addr = section->offset_within_address_space;
ram_addr_t size = int128_get64(section->size);
- bool log_dirty = memory_region_is_logging(section->mr);
+ bool log_dirty =
+ memory_region_get_dirty_log_mask(section->mr) & ~(1 << DIRTY_MEMORY_MIGRATION);
int s = offsetof(struct vhost_memory, regions) +
(dev->mem->nregions + 1) * sizeof dev->mem->regions[0];
void *ram;
@@ -677,13 +678,15 @@ static void vhost_log_global_stop(MemoryListener *listener)
}
static void vhost_log_start(MemoryListener *listener,
- MemoryRegionSection *section)
+ MemoryRegionSection *section,
+ int old, int new)
{
/* FIXME: implement */
}
static void vhost_log_stop(MemoryListener *listener,
- MemoryRegionSection *section)
+ MemoryRegionSection *section,
+ int old, int new)
{
/* FIXME: implement */
}
diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h
index d962125351..96025ca205 100644
--- a/hw/virtio/virtio-pci.h
+++ b/hw/virtio/virtio-pci.h
@@ -25,6 +25,7 @@
#include "hw/virtio/virtio-bus.h"
#include "hw/virtio/virtio-9p.h"
#include "hw/virtio/virtio-input.h"
+#include "hw/virtio/virtio-gpu.h"
#ifdef CONFIG_VIRTFS
#include "hw/9pfs/virtio-9p.h"
#endif
@@ -42,6 +43,7 @@ typedef struct VHostSCSIPCI VHostSCSIPCI;
typedef struct VirtIORngPCI VirtIORngPCI;
typedef struct VirtIOInputPCI VirtIOInputPCI;
typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI;
+typedef struct VirtIOGPUPCI VirtIOGPUPCI;
/* virtio-pci-bus */
@@ -261,6 +263,18 @@ struct VirtIOInputHIDPCI {
VirtIOInputHID vdev;
};
+/*
+ * virtio-gpu-pci: This extends VirtioPCIProxy.
+ */
+#define TYPE_VIRTIO_GPU_PCI "virtio-gpu-pci"
+#define VIRTIO_GPU_PCI(obj) \
+ OBJECT_CHECK(VirtIOGPUPCI, (obj), TYPE_VIRTIO_GPU_PCI)
+
+struct VirtIOGPUPCI {
+ VirtIOPCIProxy parent_obj;
+ VirtIOGPU vdev;
+};
+
/* Virtio ABI version, if we increment this, we break the guest driver. */
#define VIRTIO_PCI_ABI_VERSION 0
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index fb49ffcb2d..ee4e07c5e7 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -1053,6 +1053,7 @@ static const VMStateDescription vmstate_virtio_device_endian = {
.name = "virtio/device_endian",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = &virtio_device_endian_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8(device_endian, VirtIODevice),
VMSTATE_END_OF_LIST()
@@ -1063,6 +1064,7 @@ static const VMStateDescription vmstate_virtio_64bit_features = {
.name = "virtio/64bit_features",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = &virtio_64bit_features_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64(guest_features, VirtIODevice),
VMSTATE_END_OF_LIST()
@@ -1077,16 +1079,10 @@ static const VMStateDescription vmstate_virtio = {
.fields = (VMStateField[]) {
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_virtio_device_endian,
- .needed = &virtio_device_endian_needed
- },
- {
- .vmsd = &vmstate_virtio_64bit_features,
- .needed = &virtio_64bit_features_needed
- },
- { 0 }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_virtio_device_endian,
+ &vmstate_virtio_64bit_features,
+ NULL
}
};
diff --git a/hw/watchdog/Makefile.objs b/hw/watchdog/Makefile.objs
index 4b0374a555..72e3ffd93c 100644
--- a/hw/watchdog/Makefile.objs
+++ b/hw/watchdog/Makefile.objs
@@ -1,3 +1,4 @@
common-obj-y += watchdog.o
common-obj-$(CONFIG_WDT_IB6300ESB) += wdt_i6300esb.o
common-obj-$(CONFIG_WDT_IB700) += wdt_ib700.o
+common-obj-$(CONFIG_WDT_DIAG288) += wdt_diag288.o
diff --git a/hw/watchdog/watchdog.c b/hw/watchdog/watchdog.c
index 54440c91c5..8d4b0eeeb0 100644
--- a/hw/watchdog/watchdog.c
+++ b/hw/watchdog/watchdog.c
@@ -27,6 +27,7 @@
#include "sysemu/sysemu.h"
#include "sysemu/watchdog.h"
#include "qapi-event.h"
+#include "hw/nmi.h"
/* Possible values for action parameter. */
#define WDT_RESET 1 /* Hard reset. */
@@ -35,6 +36,7 @@
#define WDT_PAUSE 4 /* Pause. */
#define WDT_DEBUG 5 /* Prints a message and continues running. */
#define WDT_NONE 6 /* Do nothing. */
+#define WDT_NMI 7 /* Inject nmi into the guest */
static int watchdog_action = WDT_RESET;
static QLIST_HEAD(watchdog_list, WatchdogTimerModel) watchdog_list;
@@ -95,6 +97,8 @@ int select_watchdog_action(const char *p)
watchdog_action = WDT_DEBUG;
else if (strcasecmp(p, "none") == 0)
watchdog_action = WDT_NONE;
+ else if (strcasecmp(p, "inject-nmi") == 0)
+ watchdog_action = WDT_NMI;
else
return -1;
@@ -138,5 +142,11 @@ void watchdog_perform_action(void)
case WDT_NONE:
qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_NONE, &error_abort);
break;
+
+ case WDT_NMI:
+ qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_INJECT_NMI,
+ &error_abort);
+ inject_nmi();
+ break;
}
}
diff --git a/hw/watchdog/wdt_diag288.c b/hw/watchdog/wdt_diag288.c
new file mode 100644
index 0000000000..1185e0681c
--- /dev/null
+++ b/hw/watchdog/wdt_diag288.c
@@ -0,0 +1,122 @@
+/*
+ * watchdog device diag288 support
+ *
+ * Copyright IBM, Corp. 2015
+ *
+ * Authors:
+ * Xu Wang <gesaint@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at your
+ * option) any later version. See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "sysemu/watchdog.h"
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#include "hw/watchdog/wdt_diag288.h"
+
+static WatchdogTimerModel model = {
+ .wdt_name = TYPE_WDT_DIAG288,
+ .wdt_description = "diag288 device for s390x platform",
+};
+
+static const VMStateDescription vmstate_diag288 = {
+ .name = "vmstate_diag288",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_TIMER_PTR(timer, DIAG288State),
+ VMSTATE_BOOL(enabled, DIAG288State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void wdt_diag288_reset(DeviceState *dev)
+{
+ DIAG288State *diag288 = DIAG288(dev);
+
+ diag288->enabled = false;
+ timer_del(diag288->timer);
+}
+
+static void diag288_timer_expired(void *dev)
+{
+ qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n");
+ watchdog_perform_action();
+ wdt_diag288_reset(dev);
+}
+
+static int wdt_diag288_handle_timer(DIAG288State *diag288,
+ uint64_t func, uint64_t timeout)
+{
+ switch (func) {
+ case WDT_DIAG288_INIT:
+ diag288->enabled = true;
+ /* fall through */
+ case WDT_DIAG288_CHANGE:
+ if (!diag288->enabled) {
+ return -1;
+ }
+ timer_mod(diag288->timer,
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ timeout * get_ticks_per_sec());
+ break;
+ case WDT_DIAG288_CANCEL:
+ if (!diag288->enabled) {
+ return -1;
+ }
+ diag288->enabled = false;
+ timer_del(diag288->timer);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static void wdt_diag288_realize(DeviceState *dev, Error **errp)
+{
+ DIAG288State *diag288 = DIAG288(dev);
+
+ diag288->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, diag288_timer_expired,
+ dev);
+}
+
+static void wdt_diag288_unrealize(DeviceState *dev, Error **errp)
+{
+ DIAG288State *diag288 = DIAG288(dev);
+
+ timer_del(diag288->timer);
+ timer_free(diag288->timer);
+}
+
+static void wdt_diag288_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ DIAG288Class *diag288 = DIAG288_CLASS(klass);
+
+ dc->realize = wdt_diag288_realize;
+ dc->unrealize = wdt_diag288_unrealize;
+ dc->reset = wdt_diag288_reset;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->vmsd = &vmstate_diag288;
+ diag288->handle_timer = wdt_diag288_handle_timer;
+}
+
+static const TypeInfo wdt_diag288_info = {
+ .class_init = wdt_diag288_class_init,
+ .parent = TYPE_DEVICE,
+ .name = TYPE_WDT_DIAG288,
+ .instance_size = sizeof(DIAG288State),
+ .class_size = sizeof(DIAG288Class),
+};
+
+static void wdt_diag288_register_types(void)
+{
+ watchdog_add_model(&model);
+ type_register_static(&wdt_diag288_info);
+}
+
+type_init(wdt_diag288_register_types)
diff --git a/hw/xen/xen_backend.c b/hw/xen/xen_backend.c
index b2cb22b99d..2510e2e4ff 100644
--- a/hw/xen/xen_backend.c
+++ b/hw/xen/xen_backend.c
@@ -714,9 +714,7 @@ int xen_be_init(void)
return -1;
}
- if (qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL) < 0) {
- goto err;
- }
+ qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL);
if (xen_xc == XC_HANDLER_INITIAL_VALUE) {
/* Check if xen_init() have been called */
diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c
index d095c081cc..9afcda8e21 100644
--- a/hw/xen/xen_pt.c
+++ b/hw/xen/xen_pt.c
@@ -234,11 +234,12 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr,
int index = 0;
XenPTRegGroup *reg_grp_entry = NULL;
int rc = 0;
- uint32_t read_val = 0;
+ uint32_t read_val = 0, wb_mask;
int emul_len = 0;
XenPTReg *reg_entry = NULL;
uint32_t find_addr = addr;
XenPTRegInfo *reg = NULL;
+ bool wp_flag = false;
if (xen_pt_pci_config_access_check(d, addr, len)) {
return;
@@ -271,10 +272,17 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr,
if (rc < 0) {
XEN_PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc);
memset(&read_val, 0xff, len);
+ wb_mask = 0;
+ } else {
+ wb_mask = 0xFFFFFFFF >> ((4 - len) << 3);
}
/* pass directly to the real device for passthrough type register group */
if (reg_grp_entry == NULL) {
+ if (!s->permissive) {
+ wb_mask = 0;
+ wp_flag = true;
+ }
goto out;
}
@@ -295,9 +303,17 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr,
uint32_t real_offset = reg_grp_entry->base_offset + reg->offset;
uint32_t valid_mask = 0xFFFFFFFF >> ((4 - emul_len) << 3);
uint8_t *ptr_val = NULL;
+ uint32_t wp_mask = reg->emu_mask | reg->ro_mask;
valid_mask <<= (find_addr - real_offset) << 3;
ptr_val = (uint8_t *)&val + (real_offset & 3);
+ if (!s->permissive) {
+ wp_mask |= reg->res_mask;
+ }
+ if (wp_mask == (0xFFFFFFFF >> ((4 - reg->size) << 3))) {
+ wb_mask &= ~((wp_mask >> ((find_addr - real_offset) << 3))
+ << ((len - emul_len) << 3));
+ }
/* do emulation based on register size */
switch (reg->size) {
@@ -339,6 +355,16 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr,
} else {
/* nothing to do with passthrough type register,
* continue to find next byte */
+ if (!s->permissive) {
+ wb_mask &= ~(0xff << ((len - emul_len) << 3));
+ /* Unused BARs will make it here, but we don't want to issue
+ * warnings for writes to them (bogus writes get dealt with
+ * above).
+ */
+ if (index < 0) {
+ wp_flag = true;
+ }
+ }
emul_len--;
find_addr++;
}
@@ -350,10 +376,26 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr,
memory_region_transaction_commit();
out:
- if (!(reg && reg->no_wb)) {
+ if (wp_flag && !s->permissive_warned) {
+ s->permissive_warned = true;
+ xen_pt_log(d, "Write-back to unknown field 0x%02x (partially) inhibited (0x%0*x)\n",
+ addr, len * 2, wb_mask);
+ xen_pt_log(d, "If the device doesn't work, try enabling permissive mode\n");
+ xen_pt_log(d, "(unsafe) and if it helps report the problem to xen-devel\n");
+ }
+ for (index = 0; wb_mask; index += len) {
/* unknown regs are passed through */
- rc = xen_host_pci_set_block(&s->real_device, addr,
- (uint8_t *)&val, len);
+ while (!(wb_mask & 0xff)) {
+ index++;
+ wb_mask >>= 8;
+ }
+ len = 0;
+ do {
+ len++;
+ wb_mask >>= 8;
+ } while (wb_mask & 0xff);
+ rc = xen_host_pci_set_block(&s->real_device, addr + index,
+ (uint8_t *)&val + index, len);
if (rc < 0) {
XEN_PT_ERR(d, "pci_write_block failed. return value: %d.\n", rc);
@@ -807,6 +849,7 @@ static void xen_pt_unregister_device(PCIDevice *d)
static Property xen_pci_passthrough_properties[] = {
DEFINE_PROP_PCI_HOST_DEVADDR("hostaddr", XenPCIPassthroughState, hostaddr),
+ DEFINE_PROP_BOOL("permissive", XenPCIPassthroughState, permissive, false),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h
index 942dc60cc7..4bba559763 100644
--- a/hw/xen/xen_pt.h
+++ b/hw/xen/xen_pt.h
@@ -101,12 +101,12 @@ struct XenPTRegInfo {
uint32_t offset;
uint32_t size;
uint32_t init_val;
+ /* reg reserved field mask (ON:reserved, OFF:defined) */
+ uint32_t res_mask;
/* reg read only field mask (ON:RO/ROS, OFF:other) */
uint32_t ro_mask;
/* reg emulate field mask (ON:emu, OFF:passthrough) */
uint32_t emu_mask;
- /* no write back allowed */
- uint32_t no_wb;
xen_pt_conf_reg_init init;
/* read/write function pointer
* for double_word/word/byte size */
@@ -177,6 +177,7 @@ typedef struct XenPTMSIXEntry {
uint32_t data;
uint32_t vector_ctrl;
bool updated; /* indicate whether MSI ADDR or DATA is updated */
+ bool warned; /* avoid issuing (bogus) warning more than once */
} XenPTMSIXEntry;
typedef struct XenPTMSIX {
uint32_t ctrl_offset;
@@ -196,6 +197,8 @@ struct XenPCIPassthroughState {
PCIHostDeviceAddress hostaddr;
bool is_virtfn;
+ bool permissive;
+ bool permissive_warned;
XenHostPCIDevice real_device;
XenPTRegion bases[PCI_NUM_REGIONS]; /* Access regions */
QLIST_HEAD(, XenPTRegGroup) reg_grps;
diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c
index 95a51dbbb1..f3cf069b60 100644
--- a/hw/xen/xen_pt_config_init.c
+++ b/hw/xen/xen_pt_config_init.c
@@ -95,6 +95,18 @@ XenPTReg *xen_pt_find_reg(XenPTRegGroup *reg_grp, uint32_t address)
return NULL;
}
+static uint32_t get_throughable_mask(const XenPCIPassthroughState *s,
+ const XenPTRegInfo *reg,
+ uint32_t valid_mask)
+{
+ uint32_t throughable_mask = ~(reg->emu_mask | reg->ro_mask);
+
+ if (!s->permissive) {
+ throughable_mask &= ~reg->res_mask;
+ }
+
+ return throughable_mask & valid_mask;
+}
/****************
* general register functions
@@ -157,14 +169,13 @@ static int xen_pt_byte_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint8_t writable_mask = 0;
- uint8_t throughable_mask = 0;
+ uint8_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
/* modify emulate register */
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
/* create value for writing to I/O device register */
- throughable_mask = ~reg->emu_mask & valid_mask;
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
return 0;
@@ -175,14 +186,13 @@ static int xen_pt_word_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint16_t writable_mask = 0;
- uint16_t throughable_mask = 0;
+ uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
/* modify emulate register */
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
/* create value for writing to I/O device register */
- throughable_mask = ~reg->emu_mask & valid_mask;
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
return 0;
@@ -193,14 +203,13 @@ static int xen_pt_long_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint32_t writable_mask = 0;
- uint32_t throughable_mask = 0;
+ uint32_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
/* modify emulate register */
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
/* create value for writing to I/O device register */
- throughable_mask = ~reg->emu_mask & valid_mask;
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
return 0;
@@ -292,15 +301,13 @@ static int xen_pt_cmd_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint16_t writable_mask = 0;
- uint16_t throughable_mask = 0;
+ uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
/* modify emulate register */
writable_mask = ~reg->ro_mask & valid_mask;
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
/* create value for writing to I/O device register */
- throughable_mask = ~reg->emu_mask & valid_mask;
-
if (*val & PCI_COMMAND_INTX_DISABLE) {
throughable_mask |= PCI_COMMAND_INTX_DISABLE;
} else {
@@ -454,7 +461,6 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
PCIDevice *d = &s->dev;
const PCIIORegion *r;
uint32_t writable_mask = 0;
- uint32_t throughable_mask = 0;
uint32_t bar_emu_mask = 0;
uint32_t bar_ro_mask = 0;
uint32_t r_size = 0;
@@ -511,8 +517,7 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
}
/* create value for writing to I/O device register */
- throughable_mask = ~bar_emu_mask & valid_mask;
- *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
+ *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0);
return 0;
}
@@ -526,9 +531,8 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s,
XenPTRegion *base = NULL;
PCIDevice *d = (PCIDevice *)&s->dev;
uint32_t writable_mask = 0;
- uint32_t throughable_mask = 0;
+ uint32_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
pcibus_t r_size = 0;
- uint32_t bar_emu_mask = 0;
uint32_t bar_ro_mask = 0;
r_size = d->io_regions[PCI_ROM_SLOT].size;
@@ -537,7 +541,6 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s,
r_size = xen_pt_get_emul_size(base->bar_flag, r_size);
/* set emulate mask and read-only mask */
- bar_emu_mask = reg->emu_mask;
bar_ro_mask = (reg->ro_mask | (r_size - 1)) & ~PCI_ROM_ADDRESS_ENABLE;
/* modify emulate register */
@@ -545,7 +548,6 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s,
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
/* create value for writing to I/O device register */
- throughable_mask = ~bar_emu_mask & valid_mask;
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
return 0;
@@ -580,7 +582,7 @@ static XenPTRegInfo xen_pt_emu_reg_header0[] = {
.offset = PCI_COMMAND,
.size = 2,
.init_val = 0x0000,
- .ro_mask = 0xF880,
+ .res_mask = 0xF880,
.emu_mask = 0x0743,
.init = xen_pt_common_reg_init,
.u.w.read = xen_pt_word_reg_read,
@@ -605,7 +607,8 @@ static XenPTRegInfo xen_pt_emu_reg_header0[] = {
.offset = PCI_STATUS,
.size = 2,
.init_val = 0x0000,
- .ro_mask = 0x06FF,
+ .res_mask = 0x0007,
+ .ro_mask = 0x06F8,
.emu_mask = 0x0010,
.init = xen_pt_status_reg_init,
.u.w.read = xen_pt_word_reg_read,
@@ -755,6 +758,15 @@ static XenPTRegInfo xen_pt_emu_reg_vpd[] = {
.u.b.write = xen_pt_byte_reg_write,
},
{
+ .offset = PCI_VPD_ADDR,
+ .size = 2,
+ .ro_mask = 0x0003,
+ .emu_mask = 0x0003,
+ .init = xen_pt_common_reg_init,
+ .u.w.read = xen_pt_word_reg_read,
+ .u.w.write = xen_pt_word_reg_write,
+ },
+ {
.size = 0,
},
};
@@ -873,7 +885,7 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[] = {
.offset = PCI_EXP_DEVCAP,
.size = 4,
.init_val = 0x00000000,
- .ro_mask = 0x1FFCFFFF,
+ .ro_mask = 0xFFFFFFFF,
.emu_mask = 0x10000000,
.init = xen_pt_common_reg_init,
.u.dw.read = xen_pt_long_reg_read,
@@ -890,6 +902,16 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[] = {
.u.w.read = xen_pt_word_reg_read,
.u.w.write = xen_pt_word_reg_write,
},
+ /* Device Status reg */
+ {
+ .offset = PCI_EXP_DEVSTA,
+ .size = 2,
+ .res_mask = 0xFFC0,
+ .ro_mask = 0x0030,
+ .init = xen_pt_common_reg_init,
+ .u.w.read = xen_pt_word_reg_read,
+ .u.w.write = xen_pt_word_reg_write,
+ },
/* Link Control reg */
{
.offset = PCI_EXP_LNKCTL,
@@ -901,6 +923,15 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[] = {
.u.w.read = xen_pt_word_reg_read,
.u.w.write = xen_pt_word_reg_write,
},
+ /* Link Status reg */
+ {
+ .offset = PCI_EXP_LNKSTA,
+ .size = 2,
+ .ro_mask = 0x3FFF,
+ .init = xen_pt_common_reg_init,
+ .u.w.read = xen_pt_word_reg_read,
+ .u.w.write = xen_pt_word_reg_write,
+ },
/* Device Control 2 reg */
{
.offset = 0x28,
@@ -933,39 +964,22 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[] = {
* Power Management Capability
*/
-/* read Power Management Control/Status register */
-static int xen_pt_pmcsr_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
- uint16_t *value, uint16_t valid_mask)
-{
- XenPTRegInfo *reg = cfg_entry->reg;
- uint16_t valid_emu_mask = reg->emu_mask;
-
- valid_emu_mask |= PCI_PM_CTRL_STATE_MASK | PCI_PM_CTRL_NO_SOFT_RESET;
-
- valid_emu_mask = valid_emu_mask & valid_mask;
- *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask);
-
- return 0;
-}
/* write Power Management Control/Status register */
static int xen_pt_pmcsr_reg_write(XenPCIPassthroughState *s,
XenPTReg *cfg_entry, uint16_t *val,
uint16_t dev_value, uint16_t valid_mask)
{
XenPTRegInfo *reg = cfg_entry->reg;
- uint16_t emu_mask = reg->emu_mask;
uint16_t writable_mask = 0;
- uint16_t throughable_mask = 0;
-
- emu_mask |= PCI_PM_CTRL_STATE_MASK | PCI_PM_CTRL_NO_SOFT_RESET;
+ uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
/* modify emulate register */
- writable_mask = emu_mask & ~reg->ro_mask & valid_mask;
+ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
/* create value for writing to I/O device register */
- throughable_mask = ~emu_mask & valid_mask;
- *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
+ *val = XEN_PT_MERGE_VALUE(*val, dev_value & ~PCI_PM_CTRL_PME_STATUS,
+ throughable_mask);
return 0;
}
@@ -999,10 +1013,11 @@ static XenPTRegInfo xen_pt_emu_reg_pm[] = {
.offset = PCI_PM_CTRL,
.size = 2,
.init_val = 0x0008,
- .ro_mask = 0xE1FC,
- .emu_mask = 0x8100,
+ .res_mask = 0x00F0,
+ .ro_mask = 0xE10C,
+ .emu_mask = 0x810B,
.init = xen_pt_common_reg_init,
- .u.w.read = xen_pt_pmcsr_reg_read,
+ .u.w.read = xen_pt_word_reg_read,
.u.w.write = xen_pt_pmcsr_reg_write,
},
{
@@ -1016,13 +1031,9 @@ static XenPTRegInfo xen_pt_emu_reg_pm[] = {
*/
/* Helper */
-static bool xen_pt_msgdata_check_type(uint32_t offset, uint16_t flags)
-{
- /* check the offset whether matches the type or not */
- bool is_32 = (offset == PCI_MSI_DATA_32) && !(flags & PCI_MSI_FLAGS_64BIT);
- bool is_64 = (offset == PCI_MSI_DATA_64) && (flags & PCI_MSI_FLAGS_64BIT);
- return is_32 || is_64;
-}
+#define xen_pt_msi_check_type(offset, flags, what) \
+ ((offset) == ((flags) & PCI_MSI_FLAGS_64BIT ? \
+ PCI_MSI_##what##_64 : PCI_MSI_##what##_32))
/* Message Control register */
static int xen_pt_msgctrl_reg_init(XenPCIPassthroughState *s,
@@ -1056,8 +1067,7 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s,
XenPTRegInfo *reg = cfg_entry->reg;
XenPTMSI *msi = s->msi;
uint16_t writable_mask = 0;
- uint16_t throughable_mask = 0;
- uint16_t raw_val;
+ uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
/* Currently no support for multi-vector */
if (*val & PCI_MSI_FLAGS_QSIZE) {
@@ -1070,12 +1080,10 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s,
msi->flags |= cfg_entry->data & ~PCI_MSI_FLAGS_ENABLE;
/* create value for writing to I/O device register */
- raw_val = *val;
- throughable_mask = ~reg->emu_mask & valid_mask;
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
/* update MSI */
- if (raw_val & PCI_MSI_FLAGS_ENABLE) {
+ if (*val & PCI_MSI_FLAGS_ENABLE) {
/* setup MSI pirq for the first time */
if (!msi->initialized) {
/* Init physical one */
@@ -1103,10 +1111,6 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s,
xen_pt_msi_disable(s);
}
- /* pass through MSI_ENABLE bit */
- *val &= ~PCI_MSI_FLAGS_ENABLE;
- *val |= raw_val & PCI_MSI_FLAGS_ENABLE;
-
return 0;
}
@@ -1134,7 +1138,45 @@ static int xen_pt_msgdata_reg_init(XenPCIPassthroughState *s,
uint32_t offset = reg->offset;
/* check the offset whether matches the type or not */
- if (xen_pt_msgdata_check_type(offset, flags)) {
+ if (xen_pt_msi_check_type(offset, flags, DATA)) {
+ *data = reg->init_val;
+ } else {
+ *data = XEN_PT_INVALID_REG;
+ }
+ return 0;
+}
+
+/* this function will be called twice (for 32 bit and 64 bit type) */
+/* initialize Mask register */
+static int xen_pt_mask_reg_init(XenPCIPassthroughState *s,
+ XenPTRegInfo *reg, uint32_t real_offset,
+ uint32_t *data)
+{
+ uint32_t flags = s->msi->flags;
+
+ /* check the offset whether matches the type or not */
+ if (!(flags & PCI_MSI_FLAGS_MASKBIT)) {
+ *data = XEN_PT_INVALID_REG;
+ } else if (xen_pt_msi_check_type(reg->offset, flags, MASK)) {
+ *data = reg->init_val;
+ } else {
+ *data = XEN_PT_INVALID_REG;
+ }
+ return 0;
+}
+
+/* this function will be called twice (for 32 bit and 64 bit type) */
+/* initialize Pending register */
+static int xen_pt_pending_reg_init(XenPCIPassthroughState *s,
+ XenPTRegInfo *reg, uint32_t real_offset,
+ uint32_t *data)
+{
+ uint32_t flags = s->msi->flags;
+
+ /* check the offset whether matches the type or not */
+ if (!(flags & PCI_MSI_FLAGS_MASKBIT)) {
+ *data = XEN_PT_INVALID_REG;
+ } else if (xen_pt_msi_check_type(reg->offset, flags, PENDING)) {
*data = reg->init_val;
} else {
*data = XEN_PT_INVALID_REG;
@@ -1149,7 +1191,6 @@ static int xen_pt_msgaddr32_reg_write(XenPCIPassthroughState *s,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint32_t writable_mask = 0;
- uint32_t throughable_mask = 0;
uint32_t old_addr = cfg_entry->data;
/* modify emulate register */
@@ -1158,8 +1199,7 @@ static int xen_pt_msgaddr32_reg_write(XenPCIPassthroughState *s,
s->msi->addr_lo = cfg_entry->data;
/* create value for writing to I/O device register */
- throughable_mask = ~reg->emu_mask & valid_mask;
- *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
+ *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0);
/* update MSI */
if (cfg_entry->data != old_addr) {
@@ -1177,7 +1217,6 @@ static int xen_pt_msgaddr64_reg_write(XenPCIPassthroughState *s,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint32_t writable_mask = 0;
- uint32_t throughable_mask = 0;
uint32_t old_addr = cfg_entry->data;
/* check whether the type is 64 bit or not */
@@ -1194,8 +1233,7 @@ static int xen_pt_msgaddr64_reg_write(XenPCIPassthroughState *s,
s->msi->addr_hi = cfg_entry->data;
/* create value for writing to I/O device register */
- throughable_mask = ~reg->emu_mask & valid_mask;
- *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
+ *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0);
/* update MSI */
if (cfg_entry->data != old_addr) {
@@ -1217,12 +1255,11 @@ static int xen_pt_msgdata_reg_write(XenPCIPassthroughState *s,
XenPTRegInfo *reg = cfg_entry->reg;
XenPTMSI *msi = s->msi;
uint16_t writable_mask = 0;
- uint16_t throughable_mask = 0;
uint16_t old_data = cfg_entry->data;
uint32_t offset = reg->offset;
/* check the offset whether matches the type or not */
- if (!xen_pt_msgdata_check_type(offset, msi->flags)) {
+ if (!xen_pt_msi_check_type(offset, msi->flags, DATA)) {
/* exit I/O emulator */
XEN_PT_ERR(&s->dev, "the offset does not match the 32/64 bit type!\n");
return -1;
@@ -1235,8 +1272,7 @@ static int xen_pt_msgdata_reg_write(XenPCIPassthroughState *s,
msi->data = cfg_entry->data;
/* create value for writing to I/O device register */
- throughable_mask = ~reg->emu_mask & valid_mask;
- *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
+ *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0);
/* update MSI */
if (cfg_entry->data != old_data) {
@@ -1266,8 +1302,9 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] = {
.offset = PCI_MSI_FLAGS,
.size = 2,
.init_val = 0x0000,
- .ro_mask = 0xFF8E,
- .emu_mask = 0x007F,
+ .res_mask = 0xFE00,
+ .ro_mask = 0x018E,
+ .emu_mask = 0x017E,
.init = xen_pt_msgctrl_reg_init,
.u.w.read = xen_pt_word_reg_read,
.u.w.write = xen_pt_msgctrl_reg_write,
@@ -1279,7 +1316,6 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] = {
.init_val = 0x00000000,
.ro_mask = 0x00000003,
.emu_mask = 0xFFFFFFFF,
- .no_wb = 1,
.init = xen_pt_common_reg_init,
.u.dw.read = xen_pt_long_reg_read,
.u.dw.write = xen_pt_msgaddr32_reg_write,
@@ -1291,7 +1327,6 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] = {
.init_val = 0x00000000,
.ro_mask = 0x00000000,
.emu_mask = 0xFFFFFFFF,
- .no_wb = 1,
.init = xen_pt_msgaddr64_reg_init,
.u.dw.read = xen_pt_long_reg_read,
.u.dw.write = xen_pt_msgaddr64_reg_write,
@@ -1303,7 +1338,6 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] = {
.init_val = 0x0000,
.ro_mask = 0x0000,
.emu_mask = 0xFFFF,
- .no_wb = 1,
.init = xen_pt_msgdata_reg_init,
.u.w.read = xen_pt_word_reg_read,
.u.w.write = xen_pt_msgdata_reg_write,
@@ -1315,11 +1349,54 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] = {
.init_val = 0x0000,
.ro_mask = 0x0000,
.emu_mask = 0xFFFF,
- .no_wb = 1,
.init = xen_pt_msgdata_reg_init,
.u.w.read = xen_pt_word_reg_read,
.u.w.write = xen_pt_msgdata_reg_write,
},
+ /* Mask reg (if PCI_MSI_FLAGS_MASKBIT set, for 32-bit devices) */
+ {
+ .offset = PCI_MSI_MASK_32,
+ .size = 4,
+ .init_val = 0x00000000,
+ .ro_mask = 0xFFFFFFFF,
+ .emu_mask = 0xFFFFFFFF,
+ .init = xen_pt_mask_reg_init,
+ .u.dw.read = xen_pt_long_reg_read,
+ .u.dw.write = xen_pt_long_reg_write,
+ },
+ /* Mask reg (if PCI_MSI_FLAGS_MASKBIT set, for 64-bit devices) */
+ {
+ .offset = PCI_MSI_MASK_64,
+ .size = 4,
+ .init_val = 0x00000000,
+ .ro_mask = 0xFFFFFFFF,
+ .emu_mask = 0xFFFFFFFF,
+ .init = xen_pt_mask_reg_init,
+ .u.dw.read = xen_pt_long_reg_read,
+ .u.dw.write = xen_pt_long_reg_write,
+ },
+ /* Pending reg (if PCI_MSI_FLAGS_MASKBIT set, for 32-bit devices) */
+ {
+ .offset = PCI_MSI_MASK_32 + 4,
+ .size = 4,
+ .init_val = 0x00000000,
+ .ro_mask = 0xFFFFFFFF,
+ .emu_mask = 0x00000000,
+ .init = xen_pt_pending_reg_init,
+ .u.dw.read = xen_pt_long_reg_read,
+ .u.dw.write = xen_pt_long_reg_write,
+ },
+ /* Pending reg (if PCI_MSI_FLAGS_MASKBIT set, for 64-bit devices) */
+ {
+ .offset = PCI_MSI_MASK_64 + 4,
+ .size = 4,
+ .init_val = 0x00000000,
+ .ro_mask = 0xFFFFFFFF,
+ .emu_mask = 0x00000000,
+ .init = xen_pt_pending_reg_init,
+ .u.dw.read = xen_pt_long_reg_read,
+ .u.dw.write = xen_pt_long_reg_write,
+ },
{
.size = 0,
},
@@ -1358,7 +1435,7 @@ static int xen_pt_msixctrl_reg_write(XenPCIPassthroughState *s,
{
XenPTRegInfo *reg = cfg_entry->reg;
uint16_t writable_mask = 0;
- uint16_t throughable_mask = 0;
+ uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
int debug_msix_enabled_old;
/* modify emulate register */
@@ -1366,7 +1443,6 @@ static int xen_pt_msixctrl_reg_write(XenPCIPassthroughState *s,
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
/* create value for writing to I/O device register */
- throughable_mask = ~reg->emu_mask & valid_mask;
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
/* update MSI-X */
@@ -1405,7 +1481,8 @@ static XenPTRegInfo xen_pt_emu_reg_msix[] = {
.offset = PCI_MSI_FLAGS,
.size = 2,
.init_val = 0x0000,
- .ro_mask = 0x3FFF,
+ .res_mask = 0x3800,
+ .ro_mask = 0x07FF,
.emu_mask = 0x0000,
.init = xen_pt_msixctrl_reg_init,
.u.w.read = xen_pt_word_reg_read,
diff --git a/hw/xen/xen_pt_msi.c b/hw/xen/xen_pt_msi.c
index 9ed9321c9f..68db6233dc 100644
--- a/hw/xen/xen_pt_msi.c
+++ b/hw/xen/xen_pt_msi.c
@@ -434,11 +434,10 @@ static void pci_msix_write(void *opaque, hwaddr addr,
XenPCIPassthroughState *s = opaque;
XenPTMSIX *msix = s->msix;
XenPTMSIXEntry *entry;
- int entry_nr, offset;
+ unsigned int entry_nr, offset;
entry_nr = addr / PCI_MSIX_ENTRY_SIZE;
- if (entry_nr < 0 || entry_nr >= msix->total_entries) {
- XEN_PT_ERR(&s->dev, "asked MSI-X entry '%i' invalid!\n", entry_nr);
+ if (entry_nr >= msix->total_entries) {
return;
}
entry = &msix->msix_entry[entry_nr];
@@ -460,8 +459,11 @@ static void pci_msix_write(void *opaque, hwaddr addr,
+ PCI_MSIX_ENTRY_VECTOR_CTRL;
if (msix->enabled && !(*vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT)) {
- XEN_PT_ERR(&s->dev, "Can't update msix entry %d since MSI-X is"
- " already enabled.\n", entry_nr);
+ if (!entry->warned) {
+ entry->warned = true;
+ XEN_PT_ERR(&s->dev, "Can't update msix entry %d since MSI-X is"
+ " already enabled.\n", entry_nr);
+ }
return;
}
diff --git a/include/block/aio.h b/include/block/aio.h
index d2bb423de1..b46103ece7 100644
--- a/include/block/aio.h
+++ b/include/block/aio.h
@@ -241,7 +241,7 @@ bool aio_dispatch(AioContext *ctx);
bool aio_poll(AioContext *ctx, bool blocking);
/* Register a file descriptor and associated callbacks. Behaves very similarly
- * to qemu_set_fd_handler2. Unlike qemu_set_fd_handler2, these callbacks will
+ * to qemu_set_fd_handler. Unlike qemu_set_fd_handler, these callbacks will
* be invoked when using aio_poll().
*
* Code that invokes AIO completion functions should rely on this function
diff --git a/include/block/block.h b/include/block/block.h
index f7680b6e68..07bb724f7d 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -12,6 +12,7 @@
/* block.c */
typedef struct BlockDriver BlockDriver;
typedef struct BlockJob BlockJob;
+typedef struct BdrvChildRole BdrvChildRole;
typedef struct BlockDriverInfo {
/* in bytes, 0 if irrelevant */
@@ -90,6 +91,14 @@ typedef struct HDGeometry {
#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH)
+
+/* Option names of options parsed by the block layer */
+
+#define BDRV_OPT_CACHE_WB "cache.writeback"
+#define BDRV_OPT_CACHE_DIRECT "cache.direct"
+#define BDRV_OPT_CACHE_NO_FLUSH "cache.no-flush"
+
+
#define BDRV_SECTOR_BITS 9
#define BDRV_SECTOR_SIZE (1ULL << BDRV_SECTOR_BITS)
#define BDRV_SECTOR_MASK ~(BDRV_SECTOR_SIZE - 1)
@@ -173,8 +182,9 @@ void bdrv_stats_print(Monitor *mon, const QObject *data);
void bdrv_info_stats(Monitor *mon, QObject **ret_data);
/* disk I/O throttling */
-void bdrv_io_limits_enable(BlockDriverState *bs);
+void bdrv_io_limits_enable(BlockDriverState *bs, const char *group);
void bdrv_io_limits_disable(BlockDriverState *bs);
+void bdrv_io_limits_update_group(BlockDriverState *bs, const char *group);
void bdrv_init(void);
void bdrv_init_with_whitelist(void);
@@ -195,7 +205,8 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top);
int bdrv_parse_cache_flags(const char *mode, int *flags);
int bdrv_parse_discard_flags(const char *mode, int *flags);
int bdrv_open_image(BlockDriverState **pbs, const char *filename,
- QDict *options, const char *bdref_key, int flags,
+ QDict *options, const char *bdref_key,
+ BlockDriverState* parent, const BdrvChildRole *child_role,
bool allow_none, Error **errp);
void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd);
int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp);
diff --git a/include/block/block_int.h b/include/block/block_int.h
index f004378d58..888ec09e96 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -330,6 +330,19 @@ typedef struct BdrvAioNotifier {
QLIST_ENTRY(BdrvAioNotifier) list;
} BdrvAioNotifier;
+struct BdrvChildRole {
+ int (*inherit_flags)(int parent_flags);
+};
+
+extern const BdrvChildRole child_file;
+extern const BdrvChildRole child_format;
+
+typedef struct BdrvChild {
+ BlockDriverState *bs;
+ const BdrvChildRole *role;
+ QLIST_ENTRY(BdrvChild) next;
+} BdrvChild;
+
/*
* Note: the function bdrv_append() copies and swaps contents of
* BlockDriverStates, so if you add new fields to this struct, please
@@ -379,9 +392,14 @@ struct BlockDriverState {
unsigned int serialising_in_flight;
/* I/O throttling */
- ThrottleState throttle_state;
CoQueue throttled_reqs[2];
bool io_limits_enabled;
+ /* The following fields are protected by the ThrottleGroup lock.
+ * See the ThrottleGroup documentation for details. */
+ ThrottleState *throttle_state;
+ ThrottleTimers throttle_timers;
+ unsigned pending_reqs[2];
+ QLIST_ENTRY(BlockDriverState) round_robin;
/* I/O stats (display with "info blockstats"). */
BlockAcctStats stats;
@@ -424,6 +442,12 @@ struct BlockDriverState {
/* long-running background operation */
BlockJob *job;
+ /* The node that this node inherited default options from (and a reopen on
+ * which can affect this node by changing these defaults). This is always a
+ * parent node of this node. */
+ BlockDriverState *inherits_from;
+ QLIST_HEAD(, BdrvChild) children;
+
QDict *options;
BlockdevDetectZeroesOptions detect_zeroes;
diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
new file mode 100644
index 0000000000..fab113f6d1
--- /dev/null
+++ b/include/block/throttle-groups.h
@@ -0,0 +1,46 @@
+/*
+ * QEMU block throttling group infrastructure
+ *
+ * Copyright (C) Nodalink, EURL. 2014
+ * Copyright (C) Igalia, S.L. 2015
+ *
+ * Authors:
+ * Benoît Canet <benoit.canet@nodalink.com>
+ * Alberto Garcia <berto@igalia.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 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/>.
+ */
+
+#ifndef THROTTLE_GROUPS_H
+#define THROTTLE_GROUPS_H
+
+#include "qemu/throttle.h"
+#include "block/block_int.h"
+
+const char *throttle_group_get_name(BlockDriverState *bs);
+
+void throttle_group_config(BlockDriverState *bs, ThrottleConfig *cfg);
+void throttle_group_get_config(BlockDriverState *bs, ThrottleConfig *cfg);
+
+void throttle_group_register_bs(BlockDriverState *bs, const char *groupname);
+void throttle_group_unregister_bs(BlockDriverState *bs);
+
+void coroutine_fn throttle_group_co_io_limits_intercept(BlockDriverState *bs,
+ unsigned int bytes,
+ bool is_write);
+
+void throttle_group_lock(BlockDriverState *bs);
+void throttle_group_unlock(BlockDriverState *bs);
+
+#endif
diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h
index 43428bd030..de8a7200a9 100644
--- a/include/exec/cpu-common.h
+++ b/include/exec/cpu-common.h
@@ -126,10 +126,10 @@ void cpu_flush_icache_range(hwaddr start, int len);
extern struct MemoryRegion io_mem_rom;
extern struct MemoryRegion io_mem_notdirty;
-typedef void (RAMBlockIterFunc)(void *host_addr,
+typedef int (RAMBlockIterFunc)(const char *block_name, void *host_addr,
ram_addr_t offset, ram_addr_t length, void *opaque);
-void qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque);
+int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque);
#endif
diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h
index 3f56546066..d5aecaf49e 100644
--- a/include/exec/cpu-defs.h
+++ b/include/exec/cpu-defs.h
@@ -27,6 +27,7 @@
#include <inttypes.h>
#include "qemu/osdep.h"
#include "qemu/queue.h"
+#include "tcg-target.h"
#ifndef CONFIG_USER_ONLY
#include "exec/hwaddr.h"
#endif
@@ -70,8 +71,6 @@ typedef uint64_t target_ulong;
#define TB_JMP_PAGE_MASK (TB_JMP_CACHE_SIZE - TB_JMP_PAGE_SIZE)
#if !defined(CONFIG_USER_ONLY)
-#define CPU_TLB_BITS 8
-#define CPU_TLB_SIZE (1 << CPU_TLB_BITS)
/* use a fully associative victim tlb of 8 entries */
#define CPU_VTLB_SIZE 8
@@ -81,6 +80,38 @@ typedef uint64_t target_ulong;
#define CPU_TLB_ENTRY_BITS 5
#endif
+/* TCG_TARGET_TLB_DISPLACEMENT_BITS is used in CPU_TLB_BITS to ensure that
+ * the TLB is not unnecessarily small, but still small enough for the
+ * TLB lookup instruction sequence used by the TCG target.
+ *
+ * TCG will have to generate an operand as large as the distance between
+ * env and the tlb_table[NB_MMU_MODES - 1][0].addend. For simplicity,
+ * the TCG targets just round everything up to the next power of two, and
+ * count bits. This works because: 1) the size of each TLB is a largish
+ * power of two, 2) and because the limit of the displacement is really close
+ * to a power of two, 3) the offset of tlb_table[0][0] inside env is smaller
+ * than the size of a TLB.
+ *
+ * For example, the maximum displacement 0xFFF0 on PPC and MIPS, but TCG
+ * just says "the displacement is 16 bits". TCG_TARGET_TLB_DISPLACEMENT_BITS
+ * then ensures that tlb_table at least 0x8000 bytes large ("not unnecessarily
+ * small": 2^15). The operand then will come up smaller than 0xFFF0 without
+ * any particular care, because the TLB for a single MMU mode is larger than
+ * 0x10000-0xFFF0=16 bytes. In the end, the maximum value of the operand
+ * could be something like 0xC000 (the offset of the last TLB table) plus
+ * 0x18 (the offset of the addend field in each TLB entry) plus the offset
+ * of tlb_table inside env (which is non-trivial but not huge).
+ */
+#define CPU_TLB_BITS \
+ MIN(8, \
+ TCG_TARGET_TLB_DISPLACEMENT_BITS - CPU_TLB_ENTRY_BITS - \
+ (NB_MMU_MODES <= 1 ? 0 : \
+ NB_MMU_MODES <= 2 ? 1 : \
+ NB_MMU_MODES <= 4 ? 2 : \
+ NB_MMU_MODES <= 8 ? 3 : 4))
+
+#define CPU_TLB_SIZE (1 << CPU_TLB_BITS)
+
typedef struct CPUTLBEntry {
/* bit TARGET_LONG_BITS to TARGET_PAGE_BITS : virtual address
bit TARGET_PAGE_BITS-1..4 : Nonzero for accesses that should not
diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h
index 1673287189..1239c60f23 100644
--- a/include/exec/cpu_ldst.h
+++ b/include/exec/cpu_ldst.h
@@ -263,12 +263,104 @@ uint64_t helper_ldq_cmmu(CPUArchState *env, target_ulong addr, int mmu_idx);
#undef MEMSUFFIX
#endif /* (NB_MMU_MODES >= 7) */
-#if (NB_MMU_MODES > 7)
-/* Note that supporting NB_MMU_MODES == 9 would require
- * changes to at least the ARM TCG backend.
- */
-#error "NB_MMU_MODES > 7 is not supported for now"
-#endif /* (NB_MMU_MODES > 7) */
+#if (NB_MMU_MODES >= 8) && defined(MMU_MODE7_SUFFIX)
+
+#define CPU_MMU_INDEX 7
+#define MEMSUFFIX MMU_MODE7_SUFFIX
+#define DATA_SIZE 1
+#include "exec/cpu_ldst_template.h"
+
+#define DATA_SIZE 2
+#include "exec/cpu_ldst_template.h"
+
+#define DATA_SIZE 4
+#include "exec/cpu_ldst_template.h"
+
+#define DATA_SIZE 8
+#include "exec/cpu_ldst_template.h"
+#undef CPU_MMU_INDEX
+#undef MEMSUFFIX
+#endif /* (NB_MMU_MODES >= 8) */
+
+#if (NB_MMU_MODES >= 9) && defined(MMU_MODE8_SUFFIX)
+
+#define CPU_MMU_INDEX 8
+#define MEMSUFFIX MMU_MODE8_SUFFIX
+#define DATA_SIZE 1
+#include "exec/cpu_ldst_template.h"
+
+#define DATA_SIZE 2
+#include "exec/cpu_ldst_template.h"
+
+#define DATA_SIZE 4
+#include "exec/cpu_ldst_template.h"
+
+#define DATA_SIZE 8
+#include "exec/cpu_ldst_template.h"
+#undef CPU_MMU_INDEX
+#undef MEMSUFFIX
+#endif /* (NB_MMU_MODES >= 9) */
+
+#if (NB_MMU_MODES >= 10) && defined(MMU_MODE9_SUFFIX)
+
+#define CPU_MMU_INDEX 9
+#define MEMSUFFIX MMU_MODE9_SUFFIX
+#define DATA_SIZE 1
+#include "exec/cpu_ldst_template.h"
+
+#define DATA_SIZE 2
+#include "exec/cpu_ldst_template.h"
+
+#define DATA_SIZE 4
+#include "exec/cpu_ldst_template.h"
+
+#define DATA_SIZE 8
+#include "exec/cpu_ldst_template.h"
+#undef CPU_MMU_INDEX
+#undef MEMSUFFIX
+#endif /* (NB_MMU_MODES >= 10) */
+
+#if (NB_MMU_MODES >= 11) && defined(MMU_MODE10_SUFFIX)
+
+#define CPU_MMU_INDEX 10
+#define MEMSUFFIX MMU_MODE10_SUFFIX
+#define DATA_SIZE 1
+#include "exec/cpu_ldst_template.h"
+
+#define DATA_SIZE 2
+#include "exec/cpu_ldst_template.h"
+
+#define DATA_SIZE 4
+#include "exec/cpu_ldst_template.h"
+
+#define DATA_SIZE 8
+#include "exec/cpu_ldst_template.h"
+#undef CPU_MMU_INDEX
+#undef MEMSUFFIX
+#endif /* (NB_MMU_MODES >= 11) */
+
+#if (NB_MMU_MODES >= 12) && defined(MMU_MODE11_SUFFIX)
+
+#define CPU_MMU_INDEX 11
+#define MEMSUFFIX MMU_MODE11_SUFFIX
+#define DATA_SIZE 1
+#include "exec/cpu_ldst_template.h"
+
+#define DATA_SIZE 2
+#include "exec/cpu_ldst_template.h"
+
+#define DATA_SIZE 4
+#include "exec/cpu_ldst_template.h"
+
+#define DATA_SIZE 8
+#include "exec/cpu_ldst_template.h"
+#undef CPU_MMU_INDEX
+#undef MEMSUFFIX
+#endif /* (NB_MMU_MODES >= 12) */
+
+#if (NB_MMU_MODES > 12)
+#error "NB_MMU_MODES > 12 is not supported for now"
+#endif /* (NB_MMU_MODES > 12) */
/* these access are slower, they must be as rare as possible */
#define CPU_MMU_INDEX (cpu_mmu_index(env))
@@ -307,6 +399,8 @@ uint64_t helper_ldq_cmmu(CPUArchState *env, target_ulong addr, int mmu_idx);
#undef MEMSUFFIX
#undef SOFTMMU_CODE_ACCESS
+#endif /* defined(CONFIG_USER_ONLY) */
+
/**
* tlb_vaddr_to_host:
* @env: CPUArchState
@@ -325,6 +419,9 @@ uint64_t helper_ldq_cmmu(CPUArchState *env, target_ulong addr, int mmu_idx);
static inline void *tlb_vaddr_to_host(CPUArchState *env, target_ulong addr,
int access_type, int mmu_idx)
{
+#if defined(CONFIG_USER_ONLY)
+ return g2h(vaddr);
+#else
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
CPUTLBEntry *tlbentry = &env->tlb_table[mmu_idx][index];
target_ulong tlb_addr;
@@ -357,8 +454,7 @@ static inline void *tlb_vaddr_to_host(CPUArchState *env, target_ulong addr,
haddr = addr + env->tlb_table[mmu_idx][index].addend;
return (void *)haddr;
-}
-
#endif /* defined(CONFIG_USER_ONLY) */
+}
#endif /* CPU_LDST_H */
diff --git a/include/exec/cputlb.h b/include/exec/cputlb.h
index e0da9d7ad3..360815e1b4 100644
--- a/include/exec/cputlb.h
+++ b/include/exec/cputlb.h
@@ -22,8 +22,7 @@
#if !defined(CONFIG_USER_ONLY)
/* cputlb.c */
void tlb_protect_code(ram_addr_t ram_addr);
-void tlb_unprotect_code_phys(CPUState *cpu, ram_addr_t ram_addr,
- target_ulong vaddr);
+void tlb_unprotect_code(ram_addr_t ram_addr);
void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry, uintptr_t start,
uintptr_t length);
void cpu_tlb_reset_dirty_all(ram_addr_t start1, ram_addr_t length);
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index b58cd47ced..2573e8c36e 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -90,11 +90,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
int cflags);
void cpu_exec_init(CPUArchState *env);
void QEMU_NORETURN cpu_loop_exit(CPUState *cpu);
-int page_unprotect(target_ulong address, uintptr_t pc, void *puc);
-void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
- int is_cpu_write_access);
-void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end,
- int is_cpu_write_access);
+
#if !defined(CONFIG_USER_ONLY)
bool qemu_in_vcpu_thread(void);
void cpu_reload_memory_map(CPUState *cpu);
@@ -109,6 +105,8 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
hwaddr paddr, MemTxAttrs attrs,
int prot, int mmu_idx, target_ulong size);
void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr);
+void probe_write(CPUArchState *env, target_ulong addr, int mmu_idx,
+ uintptr_t retaddr);
#else
static inline void tlb_flush_page(CPUState *cpu, target_ulong addr)
{
diff --git a/include/exec/memattrs.h b/include/exec/memattrs.h
index 96dc440423..f8537a8d91 100644
--- a/include/exec/memattrs.h
+++ b/include/exec/memattrs.h
@@ -29,7 +29,9 @@ typedef struct MemTxAttrs {
* "didn't specify" if necessary.
*/
unsigned int unspecified:1;
- /* ARM/AMBA TrustZone Secure access */
+ /* ARM/AMBA: TrustZone Secure access
+ * x86: System Management Mode access
+ */
unsigned int secure:1;
/* Memory access is usermode (unprivileged) */
unsigned int user:1;
diff --git a/include/exec/memory.h b/include/exec/memory.h
index b61c84f62a..8ae004eb06 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -206,8 +206,10 @@ struct MemoryListener {
void (*region_add)(MemoryListener *listener, MemoryRegionSection *section);
void (*region_del)(MemoryListener *listener, MemoryRegionSection *section);
void (*region_nop)(MemoryListener *listener, MemoryRegionSection *section);
- void (*log_start)(MemoryListener *listener, MemoryRegionSection *section);
- void (*log_stop)(MemoryListener *listener, MemoryRegionSection *section);
+ void (*log_start)(MemoryListener *listener, MemoryRegionSection *section,
+ int old, int new);
+ void (*log_stop)(MemoryListener *listener, MemoryRegionSection *section,
+ int old, int new);
void (*log_sync)(MemoryListener *listener, MemoryRegionSection *section);
void (*log_global_start)(MemoryListener *listener);
void (*log_global_stop)(MemoryListener *listener);
@@ -591,11 +593,23 @@ const char *memory_region_name(const MemoryRegion *mr);
/**
* memory_region_is_logging: return whether a memory region is logging writes
*
- * Returns %true if the memory region is logging writes
+ * Returns %true if the memory region is logging writes for the given client
*
* @mr: the memory region being queried
+ * @client: the client being queried
*/
-bool memory_region_is_logging(MemoryRegion *mr);
+bool memory_region_is_logging(MemoryRegion *mr, uint8_t client);
+
+/**
+ * memory_region_get_dirty_log_mask: return the clients for which a
+ * memory region is logging writes.
+ *
+ * Returns a bitmap of clients, in which the DIRTY_MEMORY_* constants
+ * are the bit indices.
+ *
+ * @mr: the memory region being queried
+ */
+uint8_t memory_region_get_dirty_log_mask(MemoryRegion *mr);
/**
* memory_region_is_rom: check whether a memory region is ROM
@@ -647,8 +661,7 @@ void memory_region_ram_resize(MemoryRegion *mr, ram_addr_t newsize,
*
* @mr: the memory region being updated.
* @log: whether dirty logging is to be enabled or disabled.
- * @client: the user of the logging information; %DIRTY_MEMORY_MIGRATION or
- * %DIRTY_MEMORY_VGA.
+ * @client: the user of the logging information; %DIRTY_MEMORY_VGA only.
*/
void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client);
diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h
index ff558a4734..c113f21140 100644
--- a/include/exec/ram_addr.h
+++ b/include/exec/ram_addr.h
@@ -41,6 +41,9 @@ void qemu_ram_free_from_ptr(ram_addr_t addr);
int qemu_ram_resize(ram_addr_t base, ram_addr_t newsize, Error **errp);
+#define DIRTY_CLIENTS_ALL ((1 << DIRTY_MEMORY_NUM) - 1)
+#define DIRTY_CLIENTS_NOCODE (DIRTY_CLIENTS_ALL & ~(1 << DIRTY_MEMORY_CODE))
+
static inline bool cpu_physical_memory_get_dirty(ram_addr_t start,
ram_addr_t length,
unsigned client)
@@ -56,7 +59,7 @@ static inline bool cpu_physical_memory_get_dirty(ram_addr_t start,
return next < end;
}
-static inline bool cpu_physical_memory_get_clean(ram_addr_t start,
+static inline bool cpu_physical_memory_all_dirty(ram_addr_t start,
ram_addr_t length,
unsigned client)
{
@@ -68,7 +71,7 @@ static inline bool cpu_physical_memory_get_clean(ram_addr_t start,
page = start >> TARGET_PAGE_BITS;
next = find_next_zero_bit(ram_list.dirty_memory[client], end, page);
- return next < end;
+ return next >= end;
}
static inline bool cpu_physical_memory_get_dirty_flag(ram_addr_t addr,
@@ -86,44 +89,52 @@ static inline bool cpu_physical_memory_is_clean(ram_addr_t addr)
return !(vga && code && migration);
}
-static inline bool cpu_physical_memory_range_includes_clean(ram_addr_t start,
- ram_addr_t length)
+static inline uint8_t cpu_physical_memory_range_includes_clean(ram_addr_t start,
+ ram_addr_t length,
+ uint8_t mask)
{
- bool vga = cpu_physical_memory_get_clean(start, length, DIRTY_MEMORY_VGA);
- bool code = cpu_physical_memory_get_clean(start, length, DIRTY_MEMORY_CODE);
- bool migration =
- cpu_physical_memory_get_clean(start, length, DIRTY_MEMORY_MIGRATION);
- return vga || code || migration;
+ uint8_t ret = 0;
+
+ if (mask & (1 << DIRTY_MEMORY_VGA) &&
+ !cpu_physical_memory_all_dirty(start, length, DIRTY_MEMORY_VGA)) {
+ ret |= (1 << DIRTY_MEMORY_VGA);
+ }
+ if (mask & (1 << DIRTY_MEMORY_CODE) &&
+ !cpu_physical_memory_all_dirty(start, length, DIRTY_MEMORY_CODE)) {
+ ret |= (1 << DIRTY_MEMORY_CODE);
+ }
+ if (mask & (1 << DIRTY_MEMORY_MIGRATION) &&
+ !cpu_physical_memory_all_dirty(start, length, DIRTY_MEMORY_MIGRATION)) {
+ ret |= (1 << DIRTY_MEMORY_MIGRATION);
+ }
+ return ret;
}
static inline void cpu_physical_memory_set_dirty_flag(ram_addr_t addr,
unsigned client)
{
assert(client < DIRTY_MEMORY_NUM);
- set_bit(addr >> TARGET_PAGE_BITS, ram_list.dirty_memory[client]);
-}
-
-static inline void cpu_physical_memory_set_dirty_range_nocode(ram_addr_t start,
- ram_addr_t length)
-{
- unsigned long end, page;
-
- end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS;
- page = start >> TARGET_PAGE_BITS;
- bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION], page, end - page);
- bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_VGA], page, end - page);
+ set_bit_atomic(addr >> TARGET_PAGE_BITS, ram_list.dirty_memory[client]);
}
static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start,
- ram_addr_t length)
+ ram_addr_t length,
+ uint8_t mask)
{
unsigned long end, page;
+ unsigned long **d = ram_list.dirty_memory;
end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS;
page = start >> TARGET_PAGE_BITS;
- bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION], page, end - page);
- bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_VGA], page, end - page);
- bitmap_set(ram_list.dirty_memory[DIRTY_MEMORY_CODE], page, end - page);
+ if (likely(mask & (1 << DIRTY_MEMORY_MIGRATION))) {
+ bitmap_set_atomic(d[DIRTY_MEMORY_MIGRATION], page, end - page);
+ }
+ if (unlikely(mask & (1 << DIRTY_MEMORY_VGA))) {
+ bitmap_set_atomic(d[DIRTY_MEMORY_VGA], page, end - page);
+ }
+ if (unlikely(mask & (1 << DIRTY_MEMORY_CODE))) {
+ bitmap_set_atomic(d[DIRTY_MEMORY_CODE], page, end - page);
+ }
xen_modified_memory(start, length);
}
@@ -149,14 +160,18 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap,
for (k = 0; k < nr; k++) {
if (bitmap[k]) {
unsigned long temp = leul_to_cpu(bitmap[k]);
+ unsigned long **d = ram_list.dirty_memory;
- ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION][page + k] |= temp;
- ram_list.dirty_memory[DIRTY_MEMORY_VGA][page + k] |= temp;
- ram_list.dirty_memory[DIRTY_MEMORY_CODE][page + k] |= temp;
+ atomic_or(&d[DIRTY_MEMORY_MIGRATION][page + k], temp);
+ atomic_or(&d[DIRTY_MEMORY_VGA][page + k], temp);
+ if (tcg_enabled()) {
+ atomic_or(&d[DIRTY_MEMORY_CODE][page + k], temp);
+ }
}
}
- xen_modified_memory(start, pages);
+ xen_modified_memory(start, pages << TARGET_PAGE_BITS);
} else {
+ uint8_t clients = tcg_enabled() ? DIRTY_CLIENTS_ALL : DIRTY_CLIENTS_NOCODE;
/*
* bitmap-traveling is faster than memory-traveling (for addr...)
* especially when most of the memory is not dirty.
@@ -171,7 +186,7 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap,
addr = page_number * TARGET_PAGE_SIZE;
ram_addr = start + addr;
cpu_physical_memory_set_dirty_range(ram_addr,
- TARGET_PAGE_SIZE * hpratio);
+ TARGET_PAGE_SIZE * hpratio, clients);
} while (c != 0);
}
}
@@ -179,29 +194,60 @@ static inline void cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap,
}
#endif /* not _WIN32 */
-static inline void cpu_physical_memory_clear_dirty_range_type(ram_addr_t start,
- ram_addr_t length,
- unsigned client)
-{
- unsigned long end, page;
-
- assert(client < DIRTY_MEMORY_NUM);
- end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS;
- page = start >> TARGET_PAGE_BITS;
- bitmap_clear(ram_list.dirty_memory[client], page, end - page);
-}
+bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start,
+ ram_addr_t length,
+ unsigned client);
static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start,
ram_addr_t length)
{
- cpu_physical_memory_clear_dirty_range_type(start, length, DIRTY_MEMORY_MIGRATION);
- cpu_physical_memory_clear_dirty_range_type(start, length, DIRTY_MEMORY_VGA);
- cpu_physical_memory_clear_dirty_range_type(start, length, DIRTY_MEMORY_CODE);
+ cpu_physical_memory_test_and_clear_dirty(start, length, DIRTY_MEMORY_MIGRATION);
+ cpu_physical_memory_test_and_clear_dirty(start, length, DIRTY_MEMORY_VGA);
+ cpu_physical_memory_test_and_clear_dirty(start, length, DIRTY_MEMORY_CODE);
}
-void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t length,
- unsigned client);
+static inline
+uint64_t cpu_physical_memory_sync_dirty_bitmap(unsigned long *dest,
+ ram_addr_t start,
+ ram_addr_t length)
+{
+ ram_addr_t addr;
+ unsigned long page = BIT_WORD(start >> TARGET_PAGE_BITS);
+ uint64_t num_dirty = 0;
+
+ /* start address is aligned at the start of a word? */
+ if (((page * BITS_PER_LONG) << TARGET_PAGE_BITS) == start) {
+ int k;
+ int nr = BITS_TO_LONGS(length >> TARGET_PAGE_BITS);
+ unsigned long *src = ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION];
+
+ for (k = page; k < page + nr; k++) {
+ if (src[k]) {
+ unsigned long bits = atomic_xchg(&src[k], 0);
+ unsigned long new_dirty;
+ new_dirty = ~dest[k];
+ dest[k] |= bits;
+ new_dirty &= bits;
+ num_dirty += ctpopl(new_dirty);
+ }
+ }
+ } else {
+ for (addr = 0; addr < length; addr += TARGET_PAGE_SIZE) {
+ if (cpu_physical_memory_test_and_clear_dirty(
+ start + addr,
+ TARGET_PAGE_SIZE,
+ DIRTY_MEMORY_MIGRATION)) {
+ long k = (start + addr) >> TARGET_PAGE_BITS;
+ if (!test_and_set_bit(k, dest)) {
+ num_dirty++;
+ }
+ }
+ }
+ }
+
+ return num_dirty;
+}
#endif
#endif
diff --git a/include/exec/user/thunk.h b/include/exec/user/thunk.h
index 87025c3b04..3b67462726 100644
--- a/include/exec/user/thunk.h
+++ b/include/exec/user/thunk.h
@@ -74,7 +74,7 @@ const argtype *thunk_convert(void *dst, const void *src,
const argtype *type_ptr, int to_host);
#ifndef NO_THUNK_TYPE_SIZE
-extern StructEntry struct_entries[];
+extern StructEntry *struct_entries;
int thunk_type_size_array(const argtype *type_ptr, int is_host);
int thunk_type_align_array(const argtype *type_ptr, int is_host);
@@ -186,4 +186,6 @@ unsigned int target_to_host_bitmask(unsigned int x86_mask,
unsigned int host_to_target_bitmask(unsigned int alpha_mask,
const bitmask_transtbl * trans_tbl);
+void thunk_init(unsigned int max_structs);
+
#endif
diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h
index 59cf277434..7b4bfb7494 100644
--- a/include/hw/acpi/acpi-defs.h
+++ b/include/hw/acpi/acpi-defs.h
@@ -197,6 +197,38 @@ enum {
};
/*
+ * Serial Port Console Redirection Table (SPCR), Rev. 1.02
+ *
+ * For .interface_type see Debug Port Table 2 (DBG2) serial port
+ * subtypes in Table 3, Rev. May 22, 2012
+ */
+struct AcpiSerialPortConsoleRedirection {
+ ACPI_TABLE_HEADER_DEF
+ uint8_t interface_type;
+ uint8_t reserved1[3];
+ struct AcpiGenericAddress base_address;
+ uint8_t interrupt_types;
+ uint8_t irq;
+ uint32_t gsi;
+ uint8_t baud;
+ uint8_t parity;
+ uint8_t stopbits;
+ uint8_t flowctrl;
+ uint8_t term_type;
+ uint8_t reserved2;
+ uint16_t pci_device_id;
+ uint16_t pci_vendor_id;
+ uint8_t pci_bus;
+ uint8_t pci_slot;
+ uint8_t pci_func;
+ uint32_t pci_flags;
+ uint8_t pci_seg;
+ uint32_t reserved3;
+} QEMU_PACKED;
+typedef struct AcpiSerialPortConsoleRedirection
+ AcpiSerialPortConsoleRedirection;
+
+/*
* ACPI 1.0 Root System Description Table (RSDT)
*/
struct AcpiRsdtDescriptorRev1
diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h
index c2d3dba0c7..77cc65cbc2 100644
--- a/include/hw/acpi/ich9.h
+++ b/include/hw/acpi/ich9.h
@@ -39,6 +39,7 @@ typedef struct ICH9LPCPMRegs {
MemoryRegion io_smi;
uint32_t smi_en;
+ uint32_t smi_en_wmask;
uint32_t smi_sts;
qemu_irq irq; /* SCI */
diff --git a/include/hw/arm/arm.h b/include/hw/arm/arm.h
index 5c940eb412..760804cc46 100644
--- a/include/hw/arm/arm.h
+++ b/include/hw/arm/arm.h
@@ -13,11 +13,21 @@
#include "exec/memory.h"
#include "hw/irq.h"
+#include "qemu/notify.h"
/* armv7m.c */
qemu_irq *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
const char *kernel_filename, const char *cpu_model);
+/*
+ * struct used as a parameter of the arm_load_kernel machine init
+ * done notifier
+ */
+typedef struct {
+ Notifier notifier; /* actual notifier */
+ ARMCPU *cpu; /* handle to the first cpu object */
+} ArmLoadKernelNotifier;
+
/* arm_boot.c */
struct arm_boot_info {
uint64_t ram_size;
@@ -64,6 +74,8 @@ struct arm_boot_info {
* the user it should implement this hook.
*/
void (*modify_dtb)(const struct arm_boot_info *info, void *fdt);
+ /* machine init done notifier executing arm_load_dtb */
+ ArmLoadKernelNotifier load_kernel_notifier;
/* Used internally by arm_boot.c */
int is_linux;
hwaddr initrd_start;
@@ -75,6 +87,22 @@ struct arm_boot_info {
*/
bool firmware_loaded;
};
+
+/**
+ * arm_load_kernel - Loads memory with everything needed to boot
+ *
+ * @cpu: handle to the first CPU object
+ * @info: handle to the boot info struct
+ * Registers a machine init done notifier that copies to memory
+ * everything needed to boot, depending on machine and user options:
+ * kernel image, boot loaders, initrd, dtb. Also registers the CPU
+ * reset handler.
+ *
+ * In case the machine file supports the platform bus device and its
+ * dynamically instantiable sysbus devices, this function must be called
+ * before sysbus-fdt arm_register_platform_bus_fdt_creator. Indeed the
+ * machine init done notifiers are called in registration reverse order.
+ */
void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info);
/* Multiplication factor to convert from system clock ticks to qemu timer
diff --git a/include/hw/arm/sysbus-fdt.h b/include/hw/arm/sysbus-fdt.h
new file mode 100644
index 0000000000..e15bb81807
--- /dev/null
+++ b/include/hw/arm/sysbus-fdt.h
@@ -0,0 +1,60 @@
+/*
+ * Dynamic sysbus device tree node generation API
+ *
+ * Copyright Linaro Limited, 2014
+ *
+ * Authors:
+ * Alex Graf <agraf@suse.de>
+ * Eric Auger <eric.auger@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef HW_ARM_SYSBUS_FDT_H
+#define HW_ARM_SYSBUS_FDT_H
+
+#include "hw/arm/arm.h"
+#include "qemu-common.h"
+#include "hw/sysbus.h"
+
+/*
+ * struct that contains dimensioning parameters of the platform bus
+ */
+typedef struct {
+ hwaddr platform_bus_base; /* start address of the bus */
+ hwaddr platform_bus_size; /* size of the bus */
+ int platform_bus_first_irq; /* first hwirq assigned to the bus */
+ int platform_bus_num_irqs; /* number of hwirq assigned to the bus */
+} ARMPlatformBusSystemParams;
+
+/*
+ * struct that contains all relevant info to build the fdt nodes of
+ * platform bus and attached dynamic sysbus devices
+ * in the future might be augmented with additional info
+ * such as PHY, CLK handles ...
+ */
+typedef struct {
+ const ARMPlatformBusSystemParams *system_params;
+ struct arm_boot_info *binfo;
+ const char *intc; /* parent interrupt controller name */
+} ARMPlatformBusFDTParams;
+
+/**
+ * arm_register_platform_bus_fdt_creator - register a machine init done
+ * notifier that creates the device tree nodes of the platform bus and
+ * associated dynamic sysbus devices
+ */
+void arm_register_platform_bus_fdt_creator(ARMPlatformBusFDTParams *fdt_params);
+
+#endif
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index ceec8b3664..d22fd8e508 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -32,6 +32,7 @@
#include "qemu-common.h"
+#define NUM_GICV2M_SPIS 64
#define NUM_VIRTIO_TRANSPORTS 32
#define ARCH_TIMER_VIRT_IRQ 11
@@ -53,6 +54,8 @@ enum {
VIRT_PCIE_MMIO,
VIRT_PCIE_PIO,
VIRT_PCIE_ECAM,
+ VIRT_GIC_V2M,
+ VIRT_PLATFORM_BUS,
};
typedef struct MemMapEntry {
diff --git a/include/hw/boards.h b/include/hw/boards.h
index ff79797ce4..6379901528 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -106,6 +106,7 @@ struct MachineClass {
const char *default_display;
GlobalProperty *compat_props;
const char *hw_version;
+ ram_addr_t default_ram_size;
HotplugHandler *(*get_hotplug_handler)(MachineState *machine,
DeviceState *dev);
diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h
index f4e522cc1f..a2cc15c915 100644
--- a/include/hw/i386/ich9.h
+++ b/include/hw/i386/ich9.h
@@ -152,6 +152,12 @@ Object *ich9_lpc_find(void);
#define ICH9_LPC_PIRQ_ROUT_MASK Q35_MASK(8, 3, 0)
#define ICH9_LPC_PIRQ_ROUT_DEFAULT 0x80
+#define ICH9_LPC_GEN_PMCON_1 0xa0
+#define ICH9_LPC_GEN_PMCON_1_SMI_LOCK (1 << 4)
+#define ICH9_LPC_GEN_PMCON_2 0xa2
+#define ICH9_LPC_GEN_PMCON_3 0xa4
+#define ICH9_LPC_GEN_PMCON_LOCK 0xa6
+
#define ICH9_LPC_RCBA 0xf0
#define ICH9_LPC_RCBA_BA_MASK Q35_MASK(32, 31, 14)
#define ICH9_LPC_RCBA_EN 0x1
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 0d501c9368..86c565147c 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -193,7 +193,7 @@ FWCfgState *pc_memory_init(MachineState *machine,
MemoryRegion *rom_memory,
MemoryRegion **ram_memory,
PcGuestInfo *guest_info);
-qemu_irq *pc_allocate_cpu_irq(void);
+qemu_irq pc_allocate_cpu_irq(void);
DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus);
void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
ISADevice **rtc_state,
@@ -210,7 +210,6 @@ void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus);
void pc_pci_device_init(PCIBus *pci_bus);
typedef void (*cpu_set_smm_t)(int smm, void *arg);
-void cpu_smm_register(cpu_set_smm_t callback, void *arg);
void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name);
diff --git a/include/hw/mips/mips.h b/include/hw/mips/mips.h
index 2a7a9c9f42..e0065ce808 100644
--- a/include/hw/mips/mips.h
+++ b/include/hw/mips/mips.h
@@ -15,18 +15,9 @@ PCIBus *bonito_init(qemu_irq *pic);
/* rc4030.c */
typedef struct rc4030DMAState *rc4030_dma;
-void rc4030_dma_memory_rw(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write);
void rc4030_dma_read(void *dma, uint8_t *buf, int len);
void rc4030_dma_write(void *dma, uint8_t *buf, int len);
-void *rc4030_init(qemu_irq timer, qemu_irq jazz_bus,
- qemu_irq **irqs, rc4030_dma **dmas,
- MemoryRegion *sysmem);
-
-/* dp8393x.c */
-void dp83932_init(NICInfo *nd, hwaddr base, int it_shift,
- MemoryRegion *address_space,
- qemu_irq irq, void* mem_opaque,
- void (*memory_rw)(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write));
+DeviceState *rc4030_init(rc4030_dma **dmas, MemoryRegion **dma_mr);
#endif
diff --git a/include/hw/nmi.h b/include/hw/nmi.h
index b541772e1d..f4cec6257d 100644
--- a/include/hw/nmi.h
+++ b/include/hw/nmi.h
@@ -45,5 +45,6 @@ typedef struct NMIClass {
} NMIClass;
void nmi_monitor_handle(int cpu_index, Error **errp);
+void inject_nmi(void);
#endif /* NMI_H */
diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h
index 6d8a8ac564..e60d3ca212 100644
--- a/include/hw/nvram/fw_cfg.h
+++ b/include/hw/nvram/fw_cfg.h
@@ -67,10 +67,9 @@ typedef void (*FWCfgReadCallback)(void *opaque, uint32_t offset);
void fw_cfg_add_bytes(FWCfgState *s, uint16_t key, void *data, size_t len);
void fw_cfg_add_string(FWCfgState *s, uint16_t key, const char *value);
void fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value);
+void fw_cfg_modify_i16(FWCfgState *s, uint16_t key, uint16_t value);
void fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value);
void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value);
-void fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback,
- void *callback_opaque, void *data, size_t len);
void fw_cfg_add_file(FWCfgState *s, const char *filename, void *data,
size_t len);
void fw_cfg_add_file_callback(FWCfgState *s, const char *filename,
diff --git a/include/hw/pci-host/pam.h b/include/hw/pci-host/pam.h
index 4d03e4bf18..6116c638f9 100644
--- a/include/hw/pci-host/pam.h
+++ b/include/hw/pci-host/pam.h
@@ -86,10 +86,6 @@ typedef struct PAMMemoryRegion {
unsigned current;
} PAMMemoryRegion;
-void smram_update(MemoryRegion *smram_region, uint8_t smram,
- uint8_t smm_enabled);
-void smram_set_smm(uint8_t *host_smm_enabled, int smm, uint8_t smram,
- MemoryRegion *smram_region);
void init_pam(DeviceState *dev, MemoryRegion *ram, MemoryRegion *system,
MemoryRegion *pci, PAMMemoryRegion *mem, uint32_t start, uint32_t size);
void pam_update(PAMMemoryRegion *mem, int idx, uint8_t val);
diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h
index 96d4cdc713..dbe6dc05b5 100644
--- a/include/hw/pci-host/q35.h
+++ b/include/hw/pci-host/q35.h
@@ -52,9 +52,10 @@ typedef struct MCHPCIState {
MemoryRegion *system_memory;
MemoryRegion *address_space_io;
PAMMemoryRegion pam_regions[13];
- MemoryRegion smram_region;
+ MemoryRegion smram_region, open_high_smram;
+ MemoryRegion smram, low_smram, high_smram;
+ MemoryRegion tseg_blackhole, tseg_window;
PcPciInfo pci_info;
- uint8_t smm_enabled;
ram_addr_t below_4g_mem_size;
ram_addr_t above_4g_mem_size;
uint64_t pci_hole64_size;
@@ -127,8 +128,7 @@ typedef struct Q35PCIHost {
#define MCH_HOST_BRIDGE_PAM_MASK ((uint8_t)0x3)
#define MCH_HOST_BRIDGE_SMRAM 0x9d
-#define MCH_HOST_BRIDGE_SMRAM_SIZE 1
-#define MCH_HOST_BRIDGE_SMRAM_DEFAULT ((uint8_t)0x2)
+#define MCH_HOST_BRIDGE_SMRAM_SIZE 2
#define MCH_HOST_BRIDGE_SMRAM_D_OPEN ((uint8_t)(1 << 6))
#define MCH_HOST_BRIDGE_SMRAM_D_CLS ((uint8_t)(1 << 5))
#define MCH_HOST_BRIDGE_SMRAM_D_LCK ((uint8_t)(1 << 4))
@@ -139,18 +139,36 @@ typedef struct Q35PCIHost {
#define MCH_HOST_BRIDGE_SMRAM_C_END 0xc0000
#define MCH_HOST_BRIDGE_SMRAM_C_SIZE 0x20000
#define MCH_HOST_BRIDGE_UPPER_SYSTEM_BIOS_END 0x100000
+#define MCH_HOST_BRIDGE_SMRAM_DEFAULT \
+ MCH_HOST_BRIDGE_SMRAM_C_BASE_SEG
+#define MCH_HOST_BRIDGE_SMRAM_WMASK \
+ (MCH_HOST_BRIDGE_SMRAM_D_OPEN | \
+ MCH_HOST_BRIDGE_SMRAM_D_CLS | \
+ MCH_HOST_BRIDGE_SMRAM_D_LCK | \
+ MCH_HOST_BRIDGE_SMRAM_G_SMRAME)
+#define MCH_HOST_BRIDGE_SMRAM_WMASK_LCK \
+ MCH_HOST_BRIDGE_SMRAM_D_CLS
#define MCH_HOST_BRIDGE_ESMRAMC 0x9e
-#define MCH_HOST_BRIDGE_ESMRAMC_H_SMRAME ((uint8_t)(1 << 6))
-#define MCH_HOST_BRIDGE_ESMRAMC_E_SMERR ((uint8_t)(1 << 5))
-#define MCH_HOST_BRIDGE_ESMRAMC_SM_CACHE ((uint8_t)(1 << 4))
-#define MCH_HOST_BRIDGE_ESMRAMC_SM_L1 ((uint8_t)(1 << 3))
-#define MCH_HOST_BRIDGE_ESMRAMC_SM_L2 ((uint8_t)(1 << 2))
+#define MCH_HOST_BRIDGE_ESMRAMC_H_SMRAME ((uint8_t)(1 << 7))
+#define MCH_HOST_BRIDGE_ESMRAMC_E_SMERR ((uint8_t)(1 << 6))
+#define MCH_HOST_BRIDGE_ESMRAMC_SM_CACHE ((uint8_t)(1 << 5))
+#define MCH_HOST_BRIDGE_ESMRAMC_SM_L1 ((uint8_t)(1 << 4))
+#define MCH_HOST_BRIDGE_ESMRAMC_SM_L2 ((uint8_t)(1 << 3))
#define MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK ((uint8_t)(0x3 << 1))
#define MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_1MB ((uint8_t)(0x0 << 1))
#define MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_2MB ((uint8_t)(0x1 << 1))
#define MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_8MB ((uint8_t)(0x2 << 1))
#define MCH_HOST_BRIDGE_ESMRAMC_T_EN ((uint8_t)1)
+#define MCH_HOST_BRIDGE_ESMRAMC_DEFAULT \
+ (MCH_HOST_BRIDGE_ESMRAMC_SM_CACHE | \
+ MCH_HOST_BRIDGE_ESMRAMC_SM_L1 | \
+ MCH_HOST_BRIDGE_ESMRAMC_SM_L2)
+#define MCH_HOST_BRIDGE_ESMRAMC_WMASK \
+ (MCH_HOST_BRIDGE_ESMRAMC_H_SMRAME | \
+ MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK | \
+ MCH_HOST_BRIDGE_ESMRAMC_T_EN)
+#define MCH_HOST_BRIDGE_ESMRAMC_WMASK_LCK 0
/* D1:F0 PCIE* port*/
#define MCH_PCIE_DEV 1
diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h
index 895d273fee..9dca38837b 100644
--- a/include/hw/pci-host/spapr.h
+++ b/include/hw/pci-host/spapr.h
@@ -71,6 +71,7 @@ struct sPAPRPHBState {
uint32_t index;
uint64_t buid;
char *dtbusname;
+ bool dr_enabled;
MemoryRegion memspace, iospace;
hwaddr mem_win_addr, mem_win_size, io_win_addr, io_win_size;
@@ -114,6 +115,8 @@ struct sPAPRPHBVFIOState {
#define SPAPR_PCI_MSI_WINDOW 0x40000000000ULL
+#define SPAPR_PCI_DMA32_SIZE 0x40000000
+
static inline qemu_irq spapr_phb_lsi_qirq(struct sPAPRPHBState *phb, int pin)
{
return xics_get_qirq(spapr->icp, phb->lsi_table[pin].irq);
@@ -129,4 +132,8 @@ void spapr_pci_msi_init(sPAPREnvironment *spapr, hwaddr addr);
void spapr_pci_rtas_init(void);
+sPAPRPHBState *spapr_pci_find_phb(sPAPREnvironment *spapr, uint64_t buid);
+PCIDevice *spapr_pci_find_dev(sPAPREnvironment *spapr, uint64_t buid,
+ uint32_t config_addr);
+
#endif /* __HW_SPAPR_PCI_H__ */
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index c2a427f4aa..d44bc84d1e 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -335,6 +335,12 @@ int pci_device_load(PCIDevice *s, QEMUFile *f);
MemoryRegion *pci_address_space(PCIDevice *dev);
MemoryRegion *pci_address_space_io(PCIDevice *dev);
+/*
+ * Should not normally be used by devices. For use by sPAPR target
+ * where QEMU emulates firmware.
+ */
+int pci_bar(PCIDevice *d, int reg);
+
typedef void (*pci_set_irq_fn)(void *opaque, int irq_num, int level);
typedef int (*pci_map_irq_fn)(PCIDevice *pci_dev, int irq_num);
typedef PCIINTxRoute (*pci_route_irq_fn)(void *opaque, int pin);
diff --git a/include/hw/pci/pci_regs.h b/include/hw/pci/pci_regs.h
index 56a404be6e..57e8c80c30 100644
--- a/include/hw/pci/pci_regs.h
+++ b/include/hw/pci/pci_regs.h
@@ -298,8 +298,10 @@
#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */
#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */
#define PCI_MSI_MASK_32 12 /* Mask bits register for 32-bit devices */
+#define PCI_MSI_PENDING_32 16 /* Pending bits register for 32-bit devices */
#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */
#define PCI_MSI_MASK_64 16 /* Mask bits register for 64-bit devices */
+#define PCI_MSI_PENDING_64 20 /* Pending bits register for 32-bit devices */
/* MSI-X registers */
#define PCI_MSIX_FLAGS 2
diff --git a/include/hw/ppc/mac_dbdma.h b/include/hw/ppc/mac_dbdma.h
index c5803279da..c6870212e9 100644
--- a/include/hw/ppc/mac_dbdma.h
+++ b/include/hw/ppc/mac_dbdma.h
@@ -40,8 +40,8 @@ struct DBDMA_io {
/* DMA is in progress, don't start another one */
bool processing;
/* unaligned last sector of a request */
- uint8_t remainder[0x200];
- int remainder_len;
+ uint8_t head_remainder[0x200];
+ uint8_t tail_remainder[0x200];
QEMUIOVector iov;
};
diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h
index af71e8b0d5..7b4b1bb3d7 100644
--- a/include/hw/ppc/spapr.h
+++ b/include/hw/ppc/spapr.h
@@ -3,10 +3,13 @@
#include "sysemu/dma.h"
#include "hw/ppc/xics.h"
+#include "hw/ppc/spapr_drc.h"
struct VIOsPAPRBus;
struct sPAPRPHBState;
struct sPAPRNVRAM;
+typedef struct sPAPRConfigureConnectorState sPAPRConfigureConnectorState;
+typedef struct sPAPREventLogEntry sPAPREventLogEntry;
#define HPTE64_V_HPTE_DIRTY 0x0000000000000040ULL
@@ -31,14 +34,18 @@ typedef struct sPAPREnvironment {
struct PPCTimebase tb;
bool has_graphics;
- uint32_t epow_irq;
+ uint32_t check_exception_irq;
Notifier epow_notifier;
+ QTAILQ_HEAD(, sPAPREventLogEntry) pending_events;
/* Migration state */
int htab_save_index;
bool htab_first_pass;
int htab_fd;
bool htab_fd_stale;
+
+ /* RTAS state */
+ QTAILQ_HEAD(, sPAPRConfigureConnectorState) ccs_list;
} sPAPREnvironment;
#define H_SUCCESS 0
@@ -430,6 +437,17 @@ int spapr_allocate_irq_block(int num, bool lsi, bool msi);
#define RTAS_SYSPARM_DIAGNOSTICS_RUN_MODE 42
#define RTAS_SYSPARM_UUID 48
+/* RTAS indicator/sensor types
+ *
+ * as defined by PAPR+ 2.7 7.3.5.4, Table 41
+ *
+ * NOTE: currently only DR-related sensors are implemented here
+ */
+#define RTAS_SENSOR_TYPE_ISOLATION_STATE 9001
+#define RTAS_SENSOR_TYPE_DR 9002
+#define RTAS_SENSOR_TYPE_ALLOCATION_STATE 9003
+#define RTAS_SENSOR_TYPE_ENTITY_SENSE RTAS_SENSOR_TYPE_ALLOCATION_STATE
+
/* Possible values for the platform-processor-diagnostics-run-mode parameter
* of the RTAS ibm,get-system-parameter call.
*/
@@ -453,6 +471,13 @@ static inline void rtas_st(target_ulong phys, int n, uint32_t val)
stl_be_phys(&address_space_memory, ppc64_phys_to_real(phys + 4*n), val);
}
+static inline void rtas_st_buffer_direct(target_ulong phys,
+ target_ulong phys_len,
+ uint8_t *buffer, uint16_t buffer_len)
+{
+ cpu_physical_memory_write(ppc64_phys_to_real(phys), buffer,
+ MIN(buffer_len, phys_len));
+}
static inline void rtas_st_buffer(target_ulong phys, target_ulong phys_len,
uint8_t *buffer, uint16_t buffer_len)
@@ -462,8 +487,7 @@ static inline void rtas_st_buffer(target_ulong phys, target_ulong phys_len,
}
stw_be_phys(&address_space_memory,
ppc64_phys_to_real(phys), buffer_len);
- cpu_physical_memory_write(ppc64_phys_to_real(phys + 2),
- buffer, MIN(buffer_len, phys_len - 2));
+ rtas_st_buffer_direct(phys + 2, phys_len - 2, buffer, buffer_len);
}
typedef void (*spapr_rtas_fn)(PowerPCCPU *cpu, sPAPREnvironment *spapr,
@@ -482,10 +506,16 @@ int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr,
#define SPAPR_TCE_PAGE_MASK (SPAPR_TCE_PAGE_SIZE - 1)
#define SPAPR_VIO_BASE_LIOBN 0x00000000
-#define SPAPR_PCI_BASE_LIOBN 0x80000000
+#define SPAPR_VIO_LIOBN(reg) (0x00000000 | (reg))
+#define SPAPR_PCI_LIOBN(phb_index, window_num) \
+ (0x80000000 | ((phb_index) << 8) | (window_num))
+#define SPAPR_IS_PCI_LIOBN(liobn) (!!((liobn) & 0x80000000))
+#define SPAPR_PCI_DMA_WINDOW_NUM(liobn) ((liobn) & 0xff)
#define RTAS_ERROR_LOG_MAX 2048
+#define RTAS_EVENT_SCAN_RATE 1
+
typedef struct sPAPRTCETable sPAPRTCETable;
#define TYPE_SPAPR_TCE_TABLE "spapr-tce-table"
@@ -507,6 +537,15 @@ struct sPAPRTCETable {
QLIST_ENTRY(sPAPRTCETable) list;
};
+sPAPRTCETable *spapr_tce_find_by_liobn(target_ulong liobn);
+
+struct sPAPREventLogEntry {
+ int log_type;
+ bool exception;
+ void *data;
+ QTAILQ_ENTRY(sPAPREventLogEntry) next;
+};
+
void spapr_events_init(sPAPREnvironment *spapr);
void spapr_events_fdt_skel(void *fdt, uint32_t epow_irq);
int spapr_h_cas_compose_response(target_ulong addr, target_ulong size);
@@ -521,6 +560,18 @@ int spapr_dma_dt(void *fdt, int node_off, const char *propname,
int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname,
sPAPRTCETable *tcet);
void spapr_pci_switch_vga(bool big_endian);
+void spapr_hotplug_req_add_event(sPAPRDRConnector *drc);
+void spapr_hotplug_req_remove_event(sPAPRDRConnector *drc);
+
+/* rtas-configure-connector state */
+struct sPAPRConfigureConnectorState {
+ uint32_t drc_index;
+ int fdt_offset;
+ int fdt_depth;
+ QTAILQ_ENTRY(sPAPRConfigureConnectorState) next;
+};
+
+void spapr_ccs_reset_hook(void *opaque);
#define TYPE_SPAPR_RTC "spapr-rtc"
diff --git a/include/hw/ppc/spapr_drc.h b/include/hw/ppc/spapr_drc.h
new file mode 100644
index 0000000000..60cda35ed2
--- /dev/null
+++ b/include/hw/ppc/spapr_drc.h
@@ -0,0 +1,201 @@
+/*
+ * QEMU SPAPR Dynamic Reconfiguration Connector Implementation
+ *
+ * Copyright IBM Corp. 2014
+ *
+ * Authors:
+ * Michael Roth <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#if !defined(__HW_SPAPR_DRC_H__)
+#define __HW_SPAPR_DRC_H__
+
+#include "qom/object.h"
+#include "hw/qdev.h"
+#include "libfdt.h"
+
+#define TYPE_SPAPR_DR_CONNECTOR "spapr-dr-connector"
+#define SPAPR_DR_CONNECTOR_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(sPAPRDRConnectorClass, obj, TYPE_SPAPR_DR_CONNECTOR)
+#define SPAPR_DR_CONNECTOR_CLASS(klass) \
+ OBJECT_CLASS_CHECK(sPAPRDRConnectorClass, klass, \
+ TYPE_SPAPR_DR_CONNECTOR)
+#define SPAPR_DR_CONNECTOR(obj) OBJECT_CHECK(sPAPRDRConnector, (obj), \
+ TYPE_SPAPR_DR_CONNECTOR)
+
+/*
+ * Various hotplug types managed by sPAPRDRConnector
+ *
+ * these are somewhat arbitrary, but to make things easier
+ * when generating DRC indexes later we've aligned the bit
+ * positions with the values used to assign DRC indexes on
+ * pSeries. we use those values as bit shifts to allow for
+ * the OR'ing of these values in various QEMU routines, but
+ * for values exposed to the guest (via DRC indexes for
+ * instance) we will use the shift amounts.
+ */
+typedef enum {
+ SPAPR_DR_CONNECTOR_TYPE_SHIFT_CPU = 1,
+ SPAPR_DR_CONNECTOR_TYPE_SHIFT_PHB = 2,
+ SPAPR_DR_CONNECTOR_TYPE_SHIFT_VIO = 3,
+ SPAPR_DR_CONNECTOR_TYPE_SHIFT_PCI = 4,
+ SPAPR_DR_CONNECTOR_TYPE_SHIFT_LMB = 8,
+} sPAPRDRConnectorTypeShift;
+
+typedef enum {
+ SPAPR_DR_CONNECTOR_TYPE_ANY = ~0,
+ SPAPR_DR_CONNECTOR_TYPE_CPU = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_CPU,
+ SPAPR_DR_CONNECTOR_TYPE_PHB = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_PHB,
+ SPAPR_DR_CONNECTOR_TYPE_VIO = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_VIO,
+ SPAPR_DR_CONNECTOR_TYPE_PCI = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_PCI,
+ SPAPR_DR_CONNECTOR_TYPE_LMB = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_LMB,
+} sPAPRDRConnectorType;
+
+/*
+ * set via set-indicator RTAS calls
+ * as documented by PAPR+ 2.7 13.5.3.4, Table 177
+ *
+ * isolated: put device under firmware control
+ * unisolated: claim OS control of device (may or may not be in use)
+ */
+typedef enum {
+ SPAPR_DR_ISOLATION_STATE_ISOLATED = 0,
+ SPAPR_DR_ISOLATION_STATE_UNISOLATED = 1
+} sPAPRDRIsolationState;
+
+/*
+ * set via set-indicator RTAS calls
+ * as documented by PAPR+ 2.7 13.5.3.4, Table 177
+ *
+ * unusable: mark device as unavailable to OS
+ * usable: mark device as available to OS
+ * exchange: (currently unused)
+ * recover: (currently unused)
+ */
+typedef enum {
+ SPAPR_DR_ALLOCATION_STATE_UNUSABLE = 0,
+ SPAPR_DR_ALLOCATION_STATE_USABLE = 1,
+ SPAPR_DR_ALLOCATION_STATE_EXCHANGE = 2,
+ SPAPR_DR_ALLOCATION_STATE_RECOVER = 3
+} sPAPRDRAllocationState;
+
+/*
+ * LED/visual indicator state
+ *
+ * set via set-indicator RTAS calls
+ * as documented by PAPR+ 2.7 13.5.3.4, Table 177,
+ * and PAPR+ 2.7 13.5.4.1, Table 180
+ *
+ * inactive: hotpluggable entity inactive and safely removable
+ * active: hotpluggable entity in use and not safely removable
+ * identify: (currently unused)
+ * action: (currently unused)
+ */
+typedef enum {
+ SPAPR_DR_INDICATOR_STATE_INACTIVE = 0,
+ SPAPR_DR_INDICATOR_STATE_ACTIVE = 1,
+ SPAPR_DR_INDICATOR_STATE_IDENTIFY = 2,
+ SPAPR_DR_INDICATOR_STATE_ACTION = 3,
+} sPAPRDRIndicatorState;
+
+/*
+ * returned via get-sensor-state RTAS calls
+ * as documented by PAPR+ 2.7 13.5.3.3, Table 175:
+ *
+ * empty: connector slot empty (e.g. empty hotpluggable PCI slot)
+ * present: connector slot populated and device available to OS
+ * unusable: device not currently available to OS
+ * exchange: (currently unused)
+ * recover: (currently unused)
+ */
+typedef enum {
+ SPAPR_DR_ENTITY_SENSE_EMPTY = 0,
+ SPAPR_DR_ENTITY_SENSE_PRESENT = 1,
+ SPAPR_DR_ENTITY_SENSE_UNUSABLE = 2,
+ SPAPR_DR_ENTITY_SENSE_EXCHANGE = 3,
+ SPAPR_DR_ENTITY_SENSE_RECOVER = 4,
+} sPAPRDREntitySense;
+
+typedef enum {
+ SPAPR_DR_CC_RESPONSE_NEXT_SIB = 1, /* currently unused */
+ SPAPR_DR_CC_RESPONSE_NEXT_CHILD = 2,
+ SPAPR_DR_CC_RESPONSE_NEXT_PROPERTY = 3,
+ SPAPR_DR_CC_RESPONSE_PREV_PARENT = 4,
+ SPAPR_DR_CC_RESPONSE_SUCCESS = 0,
+ SPAPR_DR_CC_RESPONSE_ERROR = -1,
+ SPAPR_DR_CC_RESPONSE_CONTINUE = -2,
+} sPAPRDRCCResponse;
+
+typedef void (spapr_drc_detach_cb)(DeviceState *d, void *opaque);
+
+typedef struct sPAPRDRConnector {
+ /*< private >*/
+ DeviceState parent;
+
+ sPAPRDRConnectorType type;
+ uint32_t id;
+ Object *owner;
+ const char *name;
+
+ /* sensor/indicator states */
+ uint32_t isolation_state;
+ uint32_t allocation_state;
+ uint32_t indicator_state;
+
+ /* configure-connector state */
+ void *fdt;
+ int fdt_start_offset;
+ bool configured;
+
+ bool awaiting_release;
+
+ /* device pointer, via link property */
+ DeviceState *dev;
+ spapr_drc_detach_cb *detach_cb;
+ void *detach_cb_opaque;
+} sPAPRDRConnector;
+
+typedef struct sPAPRDRConnectorClass {
+ /*< private >*/
+ DeviceClass parent;
+
+ /*< public >*/
+
+ /* accessors for guest-visible (generally via RTAS) DR state */
+ int (*set_isolation_state)(sPAPRDRConnector *drc,
+ sPAPRDRIsolationState state);
+ int (*set_indicator_state)(sPAPRDRConnector *drc,
+ sPAPRDRIndicatorState state);
+ int (*set_allocation_state)(sPAPRDRConnector *drc,
+ sPAPRDRAllocationState state);
+ uint32_t (*get_index)(sPAPRDRConnector *drc);
+ uint32_t (*get_type)(sPAPRDRConnector *drc);
+ const char *(*get_name)(sPAPRDRConnector *drc);
+
+ sPAPRDREntitySense (*entity_sense)(sPAPRDRConnector *drc);
+
+ /* QEMU interfaces for managing FDT/configure-connector */
+ const void *(*get_fdt)(sPAPRDRConnector *drc, int *fdt_start_offset);
+ void (*set_configured)(sPAPRDRConnector *drc);
+
+ /* QEMU interfaces for managing hotplug operations */
+ void (*attach)(sPAPRDRConnector *drc, DeviceState *d, void *fdt,
+ int fdt_start_offset, bool coldplug, Error **errp);
+ void (*detach)(sPAPRDRConnector *drc, DeviceState *d,
+ spapr_drc_detach_cb *detach_cb,
+ void *detach_cb_opaque, Error **errp);
+ bool (*release_pending)(sPAPRDRConnector *drc);
+} sPAPRDRConnectorClass;
+
+sPAPRDRConnector *spapr_dr_connector_new(Object *owner,
+ sPAPRDRConnectorType type,
+ uint32_t id);
+sPAPRDRConnector *spapr_dr_connector_by_index(uint32_t index);
+sPAPRDRConnector *spapr_dr_connector_by_id(sPAPRDRConnectorType type,
+ uint32_t id);
+int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner,
+ uint32_t drc_type_mask);
+
+#endif /* __HW_SPAPR_DRC_H__ */
diff --git a/include/hw/vfio/vfio-calxeda-xgmac.h b/include/hw/vfio/vfio-calxeda-xgmac.h
new file mode 100644
index 0000000000..f994775c09
--- /dev/null
+++ b/include/hw/vfio/vfio-calxeda-xgmac.h
@@ -0,0 +1,46 @@
+/*
+ * VFIO calxeda xgmac device
+ *
+ * Copyright Linaro Limited, 2014
+ *
+ * Authors:
+ * Eric Auger <eric.auger@linaro.org>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef HW_VFIO_VFIO_CALXEDA_XGMAC_H
+#define HW_VFIO_VFIO_CALXEDA_XGMAC_H
+
+#include "hw/vfio/vfio-platform.h"
+
+#define TYPE_VFIO_CALXEDA_XGMAC "vfio-calxeda-xgmac"
+
+/**
+ * This device exposes:
+ * - a single MMIO region corresponding to its register space
+ * - 3 IRQS (main and 2 power related IRQs)
+ */
+typedef struct VFIOCalxedaXgmacDevice {
+ VFIOPlatformDevice vdev;
+} VFIOCalxedaXgmacDevice;
+
+typedef struct VFIOCalxedaXgmacDeviceClass {
+ /*< private >*/
+ VFIOPlatformDeviceClass parent_class;
+ /*< public >*/
+ DeviceRealize parent_realize;
+} VFIOCalxedaXgmacDeviceClass;
+
+#define VFIO_CALXEDA_XGMAC_DEVICE(obj) \
+ OBJECT_CHECK(VFIOCalxedaXgmacDevice, (obj), TYPE_VFIO_CALXEDA_XGMAC)
+#define VFIO_CALXEDA_XGMAC_DEVICE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(VFIOCalxedaXgmacDeviceClass, (klass), \
+ TYPE_VFIO_CALXEDA_XGMAC)
+#define VFIO_CALXEDA_XGMAC_DEVICE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(VFIOCalxedaXgmacDeviceClass, (obj), \
+ TYPE_VFIO_CALXEDA_XGMAC)
+
+#endif
diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h
index 0d1fb805bb..59a321d479 100644
--- a/include/hw/vfio/vfio-common.h
+++ b/include/hw/vfio/vfio-common.h
@@ -42,6 +42,7 @@
enum {
VFIO_DEVICE_TYPE_PCI = 0,
+ VFIO_DEVICE_TYPE_PLATFORM = 1,
};
typedef struct VFIORegion {
diff --git a/include/hw/vfio/vfio-platform.h b/include/hw/vfio/vfio-platform.h
new file mode 100644
index 0000000000..26b2ad6f4e
--- /dev/null
+++ b/include/hw/vfio/vfio-platform.h
@@ -0,0 +1,75 @@
+/*
+ * vfio based device assignment support - platform devices
+ *
+ * Copyright Linaro Limited, 2014
+ *
+ * Authors:
+ * Kim Phillips <kim.phillips@linaro.org>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ * Based on vfio based PCI device assignment support:
+ * Copyright Red Hat, Inc. 2012
+ */
+
+#ifndef HW_VFIO_VFIO_PLATFORM_H
+#define HW_VFIO_VFIO_PLATFORM_H
+
+#include "hw/sysbus.h"
+#include "hw/vfio/vfio-common.h"
+#include "qemu/event_notifier.h"
+#include "qemu/queue.h"
+#include "hw/irq.h"
+
+#define TYPE_VFIO_PLATFORM "vfio-platform"
+
+enum {
+ VFIO_IRQ_INACTIVE = 0,
+ VFIO_IRQ_PENDING = 1,
+ VFIO_IRQ_ACTIVE = 2,
+ /* VFIO_IRQ_ACTIVE_AND_PENDING cannot happen with VFIO */
+};
+
+typedef struct VFIOINTp {
+ QLIST_ENTRY(VFIOINTp) next; /* entry for IRQ list */
+ QSIMPLEQ_ENTRY(VFIOINTp) pqnext; /* entry for pending IRQ queue */
+ EventNotifier interrupt; /* eventfd triggered on interrupt */
+ EventNotifier unmask; /* eventfd for unmask on QEMU bypass */
+ qemu_irq qemuirq;
+ struct VFIOPlatformDevice *vdev; /* back pointer to device */
+ int state; /* inactive, pending, active */
+ uint8_t pin; /* index */
+ uint32_t flags; /* IRQ info flags */
+} VFIOINTp;
+
+/* function type for user side eventfd handler */
+typedef void (*eventfd_user_side_handler_t)(VFIOINTp *intp);
+
+typedef struct VFIOPlatformDevice {
+ SysBusDevice sbdev;
+ VFIODevice vbasedev; /* not a QOM object */
+ VFIORegion **regions;
+ QLIST_HEAD(, VFIOINTp) intp_list; /* list of IRQs */
+ /* queue of pending IRQs */
+ QSIMPLEQ_HEAD(pending_intp_queue, VFIOINTp) pending_intp_queue;
+ char *compat; /* compatibility string */
+ uint32_t mmap_timeout; /* delay to re-enable mmaps after interrupt */
+ QEMUTimer *mmap_timer; /* allows fast-path resume after IRQ hit */
+ QemuMutex intp_mutex; /* protect the intp_list IRQ state */
+} VFIOPlatformDevice;
+
+typedef struct VFIOPlatformDeviceClass {
+ /*< private >*/
+ SysBusDeviceClass parent_class;
+ /*< public >*/
+} VFIOPlatformDeviceClass;
+
+#define VFIO_PLATFORM_DEVICE(obj) \
+ OBJECT_CHECK(VFIOPlatformDevice, (obj), TYPE_VFIO_PLATFORM)
+#define VFIO_PLATFORM_DEVICE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(VFIOPlatformDeviceClass, (klass), TYPE_VFIO_PLATFORM)
+#define VFIO_PLATFORM_DEVICE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(VFIOPlatformDeviceClass, (obj), TYPE_VFIO_PLATFORM)
+
+#endif /*HW_VFIO_VFIO_PLATFORM_H*/
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
new file mode 100644
index 0000000000..b8c9244b21
--- /dev/null
+++ b/include/hw/virtio/virtio-gpu.h
@@ -0,0 +1,145 @@
+/*
+ * Virtio GPU Device
+ *
+ * Copyright Red Hat, Inc. 2013-2014
+ *
+ * Authors:
+ * Dave Airlie <airlied@redhat.com>
+ * Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef _QEMU_VIRTIO_VGA_H
+#define _QEMU_VIRTIO_VGA_H
+
+#include "qemu/queue.h"
+#include "ui/qemu-pixman.h"
+#include "ui/console.h"
+#include "hw/virtio/virtio.h"
+#include "hw/pci/pci.h"
+
+#include "standard-headers/linux/virtio_gpu.h"
+#define TYPE_VIRTIO_GPU "virtio-gpu-device"
+#define VIRTIO_GPU(obj) \
+ OBJECT_CHECK(VirtIOGPU, (obj), TYPE_VIRTIO_GPU)
+
+#define VIRTIO_ID_GPU 16
+
+#define VIRTIO_GPU_MAX_SCANOUT 4
+
+struct virtio_gpu_simple_resource {
+ uint32_t resource_id;
+ uint32_t width;
+ uint32_t height;
+ uint32_t format;
+ struct iovec *iov;
+ unsigned int iov_cnt;
+ uint32_t scanout_bitmask;
+ pixman_image_t *image;
+ QTAILQ_ENTRY(virtio_gpu_simple_resource) next;
+};
+
+struct virtio_gpu_scanout {
+ QemuConsole *con;
+ DisplaySurface *ds;
+ uint32_t width, height;
+ int x, y;
+ int invalidate;
+ uint32_t resource_id;
+ QEMUCursor *current_cursor;
+};
+
+struct virtio_gpu_requested_state {
+ uint32_t width, height;
+ int x, y;
+};
+
+struct virtio_gpu_conf {
+ uint32_t max_outputs;
+};
+
+struct virtio_gpu_ctrl_command {
+ VirtQueueElement elem;
+ VirtQueue *vq;
+ struct virtio_gpu_ctrl_hdr cmd_hdr;
+ uint32_t error;
+ bool finished;
+ QTAILQ_ENTRY(virtio_gpu_ctrl_command) next;
+};
+
+typedef struct VirtIOGPU {
+ VirtIODevice parent_obj;
+
+ QEMUBH *ctrl_bh;
+ QEMUBH *cursor_bh;
+ VirtQueue *ctrl_vq;
+ VirtQueue *cursor_vq;
+
+ int enable;
+
+ int config_size;
+ DeviceState *qdev;
+
+ QTAILQ_HEAD(, virtio_gpu_simple_resource) reslist;
+ QTAILQ_HEAD(, virtio_gpu_ctrl_command) fenceq;
+
+ struct virtio_gpu_scanout scanout[VIRTIO_GPU_MAX_SCANOUT];
+ struct virtio_gpu_requested_state req_state[VIRTIO_GPU_MAX_SCANOUT];
+
+ struct virtio_gpu_conf conf;
+ int enabled_output_bitmask;
+ struct virtio_gpu_config virtio_config;
+
+ QEMUTimer *fence_poll;
+ QEMUTimer *print_stats;
+
+ struct {
+ uint32_t inflight;
+ uint32_t max_inflight;
+ uint32_t requests;
+ uint32_t req_3d;
+ uint32_t bytes_3d;
+ } stats;
+} VirtIOGPU;
+
+extern const GraphicHwOps virtio_gpu_ops;
+
+/* to share between PCI and VGA */
+#define DEFINE_VIRTIO_GPU_PCI_PROPERTIES(_state) \
+ DEFINE_PROP_BIT("ioeventfd", _state, flags, \
+ VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, false), \
+ DEFINE_PROP_UINT32("vectors", _state, nvectors, 3)
+
+#define DEFINE_VIRTIO_GPU_PROPERTIES(_state, _conf_field) \
+ DEFINE_PROP_UINT32("max_outputs", _state, _conf_field.max_outputs, 1)
+
+#define VIRTIO_GPU_FILL_CMD(out) do { \
+ size_t s; \
+ s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 0, \
+ &out, sizeof(out)); \
+ if (s != sizeof(out)) { \
+ qemu_log_mask(LOG_GUEST_ERROR, \
+ "%s: command size incorrect %zu vs %zu\n", \
+ __func__, s, sizeof(out)); \
+ return; \
+ } \
+ } while (0)
+
+/* virtio-gpu.c */
+void virtio_gpu_ctrl_response(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd,
+ struct virtio_gpu_ctrl_hdr *resp,
+ size_t resp_len);
+void virtio_gpu_ctrl_response_nodata(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd,
+ enum virtio_gpu_ctrl_type type);
+void virtio_gpu_get_display_info(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd);
+int virtio_gpu_create_mapping_iov(struct virtio_gpu_resource_attach_backing *ab,
+ struct virtio_gpu_ctrl_command *cmd,
+ struct iovec **iov);
+void virtio_gpu_cleanup_mapping_iov(struct iovec *iov, uint32_t count);
+
+#endif
diff --git a/include/hw/virtio/virtio-input.h b/include/hw/virtio/virtio-input.h
index a265519e1e..8134178bcd 100644
--- a/include/hw/virtio/virtio-input.h
+++ b/include/hw/virtio/virtio-input.h
@@ -14,8 +14,14 @@ typedef struct virtio_input_config virtio_input_config;
typedef struct virtio_input_event virtio_input_event;
#if defined(HOST_WORDS_BIGENDIAN)
-# define const_le32(_x) bswap32(_x)
-# define const_le16(_x) bswap32(_x)
+# define const_le32(_x) \
+ (((_x & 0x000000ffU) << 24) | \
+ ((_x & 0x0000ff00U) << 8) | \
+ ((_x & 0x00ff0000U) >> 8) | \
+ ((_x & 0xff000000U) >> 24))
+# define const_le16(_x) \
+ (((_x & 0x00ff) << 8) | \
+ ((_x & 0xff00) >> 8))
#else
# define const_le32(_x) (_x)
# define const_le16(_x) (_x)
@@ -34,10 +40,10 @@ typedef struct virtio_input_event virtio_input_event;
#define VIRTIO_INPUT_CLASS(klass) \
OBJECT_CLASS_CHECK(VirtIOInputClass, klass, TYPE_VIRTIO_INPUT)
-#define TYPE_VIRTIO_INPUT_HID "virtio-input-hid"
-#define TYPE_VIRTIO_KEYBOARD "virtio-keyboard"
-#define TYPE_VIRTIO_MOUSE "virtio-mouse"
-#define TYPE_VIRTIO_TABLET "virtio-tablet"
+#define TYPE_VIRTIO_INPUT_HID "virtio-input-hid-device"
+#define TYPE_VIRTIO_KEYBOARD "virtio-keyboard-device"
+#define TYPE_VIRTIO_MOUSE "virtio-mouse-device"
+#define TYPE_VIRTIO_TABLET "virtio-tablet-device"
#define VIRTIO_INPUT_HID(obj) \
OBJECT_CHECK(VirtIOInputHID, (obj), TYPE_VIRTIO_INPUT_HID)
diff --git a/include/hw/watchdog/wdt_diag288.h b/include/hw/watchdog/wdt_diag288.h
new file mode 100644
index 0000000000..7f3fd450dc
--- /dev/null
+++ b/include/hw/watchdog/wdt_diag288.h
@@ -0,0 +1,36 @@
+#ifndef WDT_DIAG288_H
+#define WDT_DIAG288_H
+
+#include "hw/qdev.h"
+
+#define TYPE_WDT_DIAG288 "diag288"
+#define DIAG288(obj) \
+ OBJECT_CHECK(DIAG288State, (obj), TYPE_WDT_DIAG288)
+#define DIAG288_CLASS(klass) \
+ OBJECT_CLASS_CHECK(DIAG288Class, (klass), TYPE_WDT_DIAG288)
+#define DIAG288_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(DIAG288Class, (obj), TYPE_WDT_DIAG288)
+
+#define WDT_DIAG288_INIT 0
+#define WDT_DIAG288_CHANGE 1
+#define WDT_DIAG288_CANCEL 2
+
+typedef struct DIAG288State {
+ /*< private >*/
+ DeviceState parent_obj;
+ QEMUTimer *timer;
+ bool enabled;
+
+ /*< public >*/
+} DIAG288State;
+
+typedef struct DIAG288Class {
+ /*< private >*/
+ DeviceClass parent_class;
+
+ /*< public >*/
+ int (*handle_timer)(DIAG288State *dev,
+ uint64_t func, uint64_t timeout);
+} DIAG288Class;
+
+#endif /* WDT_DIAG288_H */
diff --git a/include/migration/migration.h b/include/migration/migration.h
index a6e025a248..9387c8c9d4 100644
--- a/include/migration/migration.h
+++ b/include/migration/migration.h
@@ -34,6 +34,7 @@
#define QEMU_VM_SECTION_FULL 0x04
#define QEMU_VM_SUBSECTION 0x05
#define QEMU_VM_VMDESCRIPTION 0x06
+#define QEMU_VM_SECTION_FOOTER 0x7e
struct MigrationParams {
bool blk;
@@ -42,6 +43,20 @@ struct MigrationParams {
typedef struct MigrationState MigrationState;
+typedef QLIST_HEAD(, LoadStateEntry) LoadStateEntry_Head;
+
+/* State for the incoming migration */
+struct MigrationIncomingState {
+ QEMUFile *file;
+
+ /* See savevm.c */
+ LoadStateEntry_Head loadvm_handlers;
+};
+
+MigrationIncomingState *migration_incoming_get_current(void);
+MigrationIncomingState *migration_incoming_state_new(QEMUFile *f);
+void migration_incoming_state_destroy(void);
+
struct MigrationState
{
int64_t bandwidth_limit;
@@ -180,4 +195,6 @@ size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset,
ram_addr_t offset, size_t size,
uint64_t *bytes_sent);
+void ram_mig_init(void);
+void savevm_skip_section_footers(void);
#endif
diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h
index a01c5b817e..4f67d79227 100644
--- a/include/migration/qemu-file.h
+++ b/include/migration/qemu-file.h
@@ -157,7 +157,7 @@ static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v)
void qemu_put_be16(QEMUFile *f, unsigned int v);
void qemu_put_be32(QEMUFile *f, unsigned int v);
void qemu_put_be64(QEMUFile *f, uint64_t v);
-int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset);
+int qemu_peek_buffer(QEMUFile *f, uint8_t **buf, int size, size_t offset);
int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size);
ssize_t qemu_put_compression_data(QEMUFile *f, const uint8_t *p, size_t size,
int level);
@@ -312,4 +312,7 @@ static inline void qemu_get_sbe64s(QEMUFile *f, int64_t *pv)
{
qemu_get_be64s(f, (uint64_t *)pv);
}
+
+size_t qemu_get_counted_string(QEMUFile *f, char buf[256]);
+
#endif
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index bc7616aaa8..7153b1e145 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -120,11 +120,6 @@ typedef struct {
bool (*field_exists)(void *opaque, int version_id);
} VMStateField;
-typedef struct VMStateSubsection {
- const VMStateDescription *vmsd;
- bool (*needed)(void *opaque);
-} VMStateSubsection;
-
struct VMStateDescription {
const char *name;
int unmigratable;
@@ -135,8 +130,9 @@ struct VMStateDescription {
int (*pre_load)(void *opaque);
int (*post_load)(void *opaque, int version_id);
void (*pre_save)(void *opaque);
+ bool (*needed)(void *opaque);
VMStateField *fields;
- const VMStateSubsection *subsections;
+ const VMStateDescription **subsections;
};
extern const VMStateDescription vmstate_dummy;
@@ -812,6 +808,8 @@ extern const VMStateInfo vmstate_info_bitmap;
#define SELF_ANNOUNCE_ROUNDS 5
+void loadvm_free_handlers(MigrationIncomingState *mis);
+
int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
void *opaque, int version_id);
void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
index df67d56ec0..88644ceda7 100644
--- a/include/monitor/monitor.h
+++ b/include/monitor/monitor.h
@@ -8,7 +8,6 @@
#include "qemu/readline.h"
extern Monitor *cur_mon;
-extern Monitor *default_mon;
/* flags for monitor_init */
#define MONITOR_IS_DEFAULT 0x01
@@ -16,10 +15,7 @@ extern Monitor *default_mon;
#define MONITOR_USE_CONTROL 0x04
#define MONITOR_USE_PRETTY 0x08
-/* flags for monitor commands */
-#define MONITOR_CMD_ASYNC 0x0001
-
-int monitor_cur_is_qmp(void);
+bool monitor_cur_is_qmp(void);
void monitor_init(CharDriverState *chr, int flags);
@@ -43,8 +39,6 @@ void monitor_flush(Monitor *mon);
int monitor_set_cpu(int cpu_index);
int monitor_get_cpu_index(void);
-typedef void (MonitorCompletion)(void *opaque, QObject *ret_data);
-
void monitor_set_error(Monitor *mon, QError *qerror);
void monitor_read_command(Monitor *mon, int show_prompt);
int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h
index d68f4eb4d5..9fbf68ee0c 100644
--- a/include/qapi/qmp/qdict.h
+++ b/include/qapi/qmp/qdict.h
@@ -65,11 +65,15 @@ int64_t qdict_get_try_int(const QDict *qdict, const char *key,
int qdict_get_try_bool(const QDict *qdict, const char *key, int def_value);
const char *qdict_get_try_str(const QDict *qdict, const char *key);
+void qdict_copy_default(QDict *dst, QDict *src, const char *key);
+void qdict_set_default_str(QDict *dst, const char *key, const char *val);
+
QDict *qdict_clone_shallow(const QDict *src);
void qdict_flatten(QDict *qdict);
void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start);
void qdict_array_split(QDict *src, QList **dst);
+int qdict_array_entries(QDict *src, const char *subqdict);
void qdict_join(QDict *dest, QDict *src, bool overwrite);
diff --git a/include/qemu-common.h b/include/qemu-common.h
index 6b373ff7e3..d52d09cfb8 100644
--- a/include/qemu-common.h
+++ b/include/qemu-common.h
@@ -186,6 +186,12 @@ int64_t strtosz(const char *nptr, char **end);
int64_t strtosz_suffix(const char *nptr, char **end, const char default_suffix);
int64_t strtosz_suffix_unit(const char *nptr, char **end,
const char default_suffix, int64_t unit);
+#define K_BYTE (1ULL << 10)
+#define M_BYTE (1ULL << 20)
+#define G_BYTE (1ULL << 30)
+#define T_BYTE (1ULL << 40)
+#define P_BYTE (1ULL << 50)
+#define E_BYTE (1ULL << 60)
/* used to print char* safely */
#define STR_OR_NULL(str) ((str) ? (str) : "null")
diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h
index 98e05ca875..bd2c075343 100644
--- a/include/qemu/atomic.h
+++ b/include/qemu/atomic.h
@@ -99,7 +99,13 @@
#ifndef smp_wmb
#ifdef __ATOMIC_RELEASE
-#define smp_wmb() __atomic_thread_fence(__ATOMIC_RELEASE)
+/* __atomic_thread_fence does not include a compiler barrier; instead,
+ * the barrier is part of __atomic_load/__atomic_store's "volatile-like"
+ * semantics. If smp_wmb() is a no-op, absence of the barrier means that
+ * the compiler is free to reorder stores on each side of the barrier.
+ * Add one here, and similarly in smp_rmb() and smp_read_barrier_depends().
+ */
+#define smp_wmb() ({ barrier(); __atomic_thread_fence(__ATOMIC_RELEASE); barrier(); })
#else
#define smp_wmb() __sync_synchronize()
#endif
@@ -107,7 +113,7 @@
#ifndef smp_rmb
#ifdef __ATOMIC_ACQUIRE
-#define smp_rmb() __atomic_thread_fence(__ATOMIC_ACQUIRE)
+#define smp_rmb() ({ barrier(); __atomic_thread_fence(__ATOMIC_ACQUIRE); barrier(); })
#else
#define smp_rmb() __sync_synchronize()
#endif
@@ -115,7 +121,7 @@
#ifndef smp_read_barrier_depends
#ifdef __ATOMIC_CONSUME
-#define smp_read_barrier_depends() __atomic_thread_fence(__ATOMIC_CONSUME)
+#define smp_read_barrier_depends() ({ barrier(); __atomic_thread_fence(__ATOMIC_CONSUME); barrier(); })
#else
#define smp_read_barrier_depends() barrier()
#endif
diff --git a/include/qemu/bitmap.h b/include/qemu/bitmap.h
index f0273c965f..86dd9cd5fc 100644
--- a/include/qemu/bitmap.h
+++ b/include/qemu/bitmap.h
@@ -39,7 +39,9 @@
* bitmap_empty(src, nbits) Are all bits zero in *src?
* bitmap_full(src, nbits) Are all bits set in *src?
* bitmap_set(dst, pos, nbits) Set specified bit area
+ * bitmap_set_atomic(dst, pos, nbits) Set specified bit area with atomic ops
* bitmap_clear(dst, pos, nbits) Clear specified bit area
+ * bitmap_test_and_clear_atomic(dst, pos, nbits) Test and clear area
* bitmap_find_next_zero_area(buf, len, pos, n, mask) Find bit free area
*/
@@ -226,7 +228,9 @@ static inline int bitmap_intersects(const unsigned long *src1,
}
void bitmap_set(unsigned long *map, long i, long len);
+void bitmap_set_atomic(unsigned long *map, long i, long len);
void bitmap_clear(unsigned long *map, long start, long nr);
+bool bitmap_test_and_clear_atomic(unsigned long *map, long start, long nr);
unsigned long bitmap_find_next_zero_area(unsigned long *map,
unsigned long size,
unsigned long start,
diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h
index 8abdcf9077..8164225152 100644
--- a/include/qemu/bitops.h
+++ b/include/qemu/bitops.h
@@ -16,6 +16,7 @@
#include <assert.h>
#include "host-utils.h"
+#include "atomic.h"
#define BITS_PER_BYTE CHAR_BIT
#define BITS_PER_LONG (sizeof (unsigned long) * BITS_PER_BYTE)
@@ -39,6 +40,19 @@ static inline void set_bit(long nr, unsigned long *addr)
}
/**
+ * set_bit_atomic - Set a bit in memory atomically
+ * @nr: the bit to set
+ * @addr: the address to start counting from
+ */
+static inline void set_bit_atomic(long nr, unsigned long *addr)
+{
+ unsigned long mask = BIT_MASK(nr);
+ unsigned long *p = addr + BIT_WORD(nr);
+
+ atomic_or(p, mask);
+}
+
+/**
* clear_bit - Clears a bit in memory
* @nr: Bit to clear
* @addr: Address to start counting from
diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h
index 62c68c0f32..0f4a0fd4b2 100644
--- a/include/qemu/main-loop.h
+++ b/include/qemu/main-loop.h
@@ -96,8 +96,7 @@ AioContext *qemu_get_aio_context(void);
* that the main loop waits for.
*
* Calling qemu_notify_event is rarely necessary, because main loop
- * services (bottom halves and timers) call it themselves. One notable
- * exception occurs when using qemu_set_fd_handler2 (see below).
+ * services (bottom halves and timers) call it themselves.
*/
void qemu_notify_event(void);
@@ -172,52 +171,6 @@ typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size);
typedef int IOCanReadHandler(void *opaque);
/**
- * qemu_set_fd_handler2: Register a file descriptor with the main loop
- *
- * This function tells the main loop to wake up whenever one of the
- * following conditions is true:
- *
- * 1) if @fd_write is not %NULL, when the file descriptor is writable;
- *
- * 2) if @fd_read is not %NULL, when the file descriptor is readable.
- *
- * @fd_read_poll can be used to disable the @fd_read callback temporarily.
- * This is useful to avoid calling qemu_set_fd_handler2 every time the
- * client becomes interested in reading (or dually, stops being interested).
- * A typical example is when @fd is a listening socket and you want to bound
- * the number of active clients. Remember to call qemu_notify_event whenever
- * the condition may change from %false to %true.
- *
- * The callbacks that are set up by qemu_set_fd_handler2 are level-triggered.
- * If @fd_read does not read from @fd, or @fd_write does not write to @fd
- * until its buffers are full, they will be called again on the next
- * iteration.
- *
- * @fd: The file descriptor to be observed. Under Windows it must be
- * a #SOCKET.
- *
- * @fd_read_poll: A function that returns 1 if the @fd_read callback
- * should be fired. If the function returns 0, the main loop will not
- * end its iteration even if @fd becomes readable.
- *
- * @fd_read: A level-triggered callback that is fired if @fd is readable
- * at the beginning of a main loop iteration, or if it becomes readable
- * during one.
- *
- * @fd_write: A level-triggered callback that is fired when @fd is writable
- * at the beginning of a main loop iteration, or if it becomes writable
- * during one.
- *
- * @opaque: A pointer-sized value that is passed to @fd_read_poll,
- * @fd_read and @fd_write.
- */
-int qemu_set_fd_handler2(int fd,
- IOCanReadHandler *fd_read_poll,
- IOHandler *fd_read,
- IOHandler *fd_write,
- void *opaque);
-
-/**
* qemu_set_fd_handler: Register a file descriptor with the main loop
*
* This function tells the main loop to wake up whenever one of the
@@ -245,10 +198,10 @@ int qemu_set_fd_handler2(int fd,
*
* @opaque: A pointer-sized value that is passed to @fd_read and @fd_write.
*/
-int qemu_set_fd_handler(int fd,
- IOHandler *fd_read,
- IOHandler *fd_write,
- void *opaque);
+void qemu_set_fd_handler(int fd,
+ IOHandler *fd_read,
+ IOHandler *fd_write,
+ void *opaque);
#ifdef CONFIG_POSIX
/**
diff --git a/include/qemu/option.h b/include/qemu/option.h
index f88b545dfc..ac0e43b7e5 100644
--- a/include/qemu/option.h
+++ b/include/qemu/option.h
@@ -100,9 +100,11 @@ void qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val,
Error **errp);
void qemu_opt_set_number(QemuOpts *opts, const char *name, int64_t val,
Error **errp);
-typedef int (*qemu_opt_loopfunc)(const char *name, const char *value, void *opaque);
+typedef int (*qemu_opt_loopfunc)(void *opaque,
+ const char *name, const char *value,
+ Error **errp);
int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque,
- int abort_on_failure);
+ Error **errp);
QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id);
QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
@@ -125,10 +127,10 @@ QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict,
QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict);
void qemu_opts_absorb_qdict(QemuOpts *opts, QDict *qdict, Error **errp);
-typedef int (*qemu_opts_loopfunc)(QemuOpts *opts, void *opaque);
+typedef int (*qemu_opts_loopfunc)(void *opaque, QemuOpts *opts, Error **errp);
+int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func,
+ void *opaque, Error **errp);
void qemu_opts_print(QemuOpts *opts, const char *sep);
-int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func, void *opaque,
- int abort_on_failure);
void qemu_opts_print_help(QemuOptsList *list);
void qemu_opts_free(QemuOptsList *list);
QemuOptsList *qemu_opts_append(QemuOptsList *dst, QemuOptsList *list);
diff --git a/include/qemu/queue.h b/include/qemu/queue.h
index f781aa20a8..a8d3cb8e63 100644
--- a/include/qemu/queue.h
+++ b/include/qemu/queue.h
@@ -117,6 +117,12 @@ struct { \
} \
} while (/*CONSTCOND*/0)
+#define QLIST_FIX_HEAD_PTR(head, field) do { \
+ if ((head)->lh_first != NULL) { \
+ (head)->lh_first->field.le_prev = &(head)->lh_first; \
+ } \
+} while (/*CONSTCOND*/0)
+
#define QLIST_INSERT_AFTER(listelm, elm, field) do { \
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
(listelm)->field.le_next->field.le_prev = \
diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h
index b890613a9c..5af76f0ff4 100644
--- a/include/qemu/throttle.h
+++ b/include/qemu/throttle.h
@@ -1,10 +1,12 @@
/*
* QEMU throttling infrastructure
*
- * Copyright (C) Nodalink, SARL. 2013
+ * Copyright (C) Nodalink, EURL. 2013-2014
+ * Copyright (C) Igalia, S.L. 2015
*
- * Author:
- * Benoît Canet <benoit.canet@irqsave.net>
+ * Authors:
+ * Benoît Canet <benoit.canet@nodalink.com>
+ * Alberto Garcia <berto@igalia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -65,14 +67,17 @@ typedef struct ThrottleConfig {
typedef struct ThrottleState {
ThrottleConfig cfg; /* configuration */
int64_t previous_leak; /* timestamp of the last leak done */
- QEMUTimer * timers[2]; /* timers used to do the throttling */
+} ThrottleState;
+
+typedef struct ThrottleTimers {
+ QEMUTimer *timers[2]; /* timers used to do the throttling */
QEMUClockType clock_type; /* the clock used */
/* Callbacks */
QEMUTimerCB *read_timer_cb;
QEMUTimerCB *write_timer_cb;
void *timer_opaque;
-} ThrottleState;
+} ThrottleTimers;
/* operations on single leaky buckets */
void throttle_leak_bucket(LeakyBucket *bkt, int64_t delta);
@@ -86,20 +91,23 @@ bool throttle_compute_timer(ThrottleState *ts,
int64_t *next_timestamp);
/* init/destroy cycle */
-void throttle_init(ThrottleState *ts,
- AioContext *aio_context,
- QEMUClockType clock_type,
- void (read_timer)(void *),
- void (write_timer)(void *),
- void *timer_opaque);
+void throttle_init(ThrottleState *ts);
+
+void throttle_timers_init(ThrottleTimers *tt,
+ AioContext *aio_context,
+ QEMUClockType clock_type,
+ QEMUTimerCB *read_timer_cb,
+ QEMUTimerCB *write_timer_cb,
+ void *timer_opaque);
-void throttle_destroy(ThrottleState *ts);
+void throttle_timers_destroy(ThrottleTimers *tt);
-void throttle_detach_aio_context(ThrottleState *ts);
+void throttle_timers_detach_aio_context(ThrottleTimers *tt);
-void throttle_attach_aio_context(ThrottleState *ts, AioContext *new_context);
+void throttle_timers_attach_aio_context(ThrottleTimers *tt,
+ AioContext *new_context);
-bool throttle_have_timer(ThrottleState *ts);
+bool throttle_timers_are_initialized(ThrottleTimers *tt);
/* configuration */
bool throttle_enabled(ThrottleConfig *cfg);
@@ -108,12 +116,16 @@ bool throttle_conflicting(ThrottleConfig *cfg);
bool throttle_is_valid(ThrottleConfig *cfg);
-void throttle_config(ThrottleState *ts, ThrottleConfig *cfg);
+void throttle_config(ThrottleState *ts,
+ ThrottleTimers *tt,
+ ThrottleConfig *cfg);
void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg);
/* usage */
-bool throttle_schedule_timer(ThrottleState *ts, bool is_write);
+bool throttle_schedule_timer(ThrottleState *ts,
+ ThrottleTimers *tt,
+ bool is_write);
void throttle_account(ThrottleState *ts, bool is_write, uint64_t size);
diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
index cde3314896..6fdcbcd524 100644
--- a/include/qemu/typedefs.h
+++ b/include/qemu/typedefs.h
@@ -31,6 +31,7 @@ typedef struct I2CBus I2CBus;
typedef struct I2SCodec I2SCodec;
typedef struct ISABus ISABus;
typedef struct ISADevice ISADevice;
+typedef struct LoadStateEntry LoadStateEntry;
typedef struct MACAddr MACAddr;
typedef struct MachineClass MachineClass;
typedef struct MachineState MachineState;
@@ -38,6 +39,7 @@ typedef struct MemoryListener MemoryListener;
typedef struct MemoryMappingList MemoryMappingList;
typedef struct MemoryRegion MemoryRegion;
typedef struct MemoryRegionSection MemoryRegionSection;
+typedef struct MigrationIncomingState MigrationIncomingState;
typedef struct MigrationParams MigrationParams;
typedef struct Monitor Monitor;
typedef struct MouseTransformInfo MouseTransformInfo;
diff --git a/include/qom/object.h b/include/qom/object.h
index d2d7748f62..0505f20e71 100644
--- a/include/qom/object.h
+++ b/include/qom/object.h
@@ -1290,6 +1290,24 @@ void object_property_add_alias(Object *obj, const char *name,
Error **errp);
/**
+ * object_property_add_const_link:
+ * @obj: the object to add a property to
+ * @name: the name of the property
+ * @target: the object to be referred by the link
+ * @errp: if an error occurs, a pointer to an area to store the error
+ *
+ * Add an unmodifiable link for a property on an object. This function will
+ * add a property of type link<TYPE> where TYPE is the type of @target.
+ *
+ * The caller must ensure that @target stays alive as long as
+ * this property exists. In the case @target is a child of @obj,
+ * this will be the case. Otherwise, the caller is responsible for
+ * taking a reference.
+ */
+void object_property_add_const_link(Object *obj, const char *name,
+ Object *target, Error **errp);
+
+/**
* object_property_set_description:
* @obj: the object owning the property
* @name: the name of the property
diff --git a/include/standard-headers/linux/virtio_gpu.h b/include/standard-headers/linux/virtio_gpu.h
new file mode 100644
index 0000000000..cfcfb463fc
--- /dev/null
+++ b/include/standard-headers/linux/virtio_gpu.h
@@ -0,0 +1,204 @@
+/*
+ * Virtio GPU Device
+ *
+ * Copyright Red Hat, Inc. 2013-2014
+ *
+ * Authors:
+ * Dave Airlie <airlied@redhat.com>
+ * Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This header is BSD licensed so anyone can use the definitions
+ * to implement compatible drivers/servers:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of IBM nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef VIRTIO_GPU_HW_H
+#define VIRTIO_GPU_HW_H
+
+enum virtio_gpu_ctrl_type {
+ VIRTIO_GPU_UNDEFINED = 0,
+
+ /* 2d commands */
+ VIRTIO_GPU_CMD_GET_DISPLAY_INFO = 0x0100,
+ VIRTIO_GPU_CMD_RESOURCE_CREATE_2D,
+ VIRTIO_GPU_CMD_RESOURCE_UNREF,
+ VIRTIO_GPU_CMD_SET_SCANOUT,
+ VIRTIO_GPU_CMD_RESOURCE_FLUSH,
+ VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D,
+ VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING,
+ VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING,
+
+ /* cursor commands */
+ VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300,
+ VIRTIO_GPU_CMD_MOVE_CURSOR,
+
+ /* success responses */
+ VIRTIO_GPU_RESP_OK_NODATA = 0x1100,
+ VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
+
+ /* error responses */
+ VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200,
+ VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY,
+ VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID,
+ VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID,
+ VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID,
+ VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER,
+};
+
+#define VIRTIO_GPU_FLAG_FENCE (1 << 0)
+
+struct virtio_gpu_ctrl_hdr {
+ uint32_t type;
+ uint32_t flags;
+ uint64_t fence_id;
+ uint32_t ctx_id;
+ uint32_t padding;
+};
+
+/* data passed in the cursor vq */
+
+struct virtio_gpu_cursor_pos {
+ uint32_t scanout_id;
+ uint32_t x;
+ uint32_t y;
+ uint32_t padding;
+};
+
+/* VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_CMD_MOVE_CURSOR */
+struct virtio_gpu_update_cursor {
+ struct virtio_gpu_ctrl_hdr hdr;
+ struct virtio_gpu_cursor_pos pos; /* update & move */
+ uint32_t resource_id; /* update only */
+ uint32_t hot_x; /* update only */
+ uint32_t hot_y; /* update only */
+ uint32_t padding;
+};
+
+/* data passed in the control vq, 2d related */
+
+struct virtio_gpu_rect {
+ uint32_t x;
+ uint32_t y;
+ uint32_t width;
+ uint32_t height;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_UNREF */
+struct virtio_gpu_resource_unref {
+ struct virtio_gpu_ctrl_hdr hdr;
+ uint32_t resource_id;
+ uint32_t padding;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: create a 2d resource with a format */
+struct virtio_gpu_resource_create_2d {
+ struct virtio_gpu_ctrl_hdr hdr;
+ uint32_t resource_id;
+ uint32_t format;
+ uint32_t width;
+ uint32_t height;
+};
+
+/* VIRTIO_GPU_CMD_SET_SCANOUT */
+struct virtio_gpu_set_scanout {
+ struct virtio_gpu_ctrl_hdr hdr;
+ struct virtio_gpu_rect r;
+ uint32_t scanout_id;
+ uint32_t resource_id;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_FLUSH */
+struct virtio_gpu_resource_flush {
+ struct virtio_gpu_ctrl_hdr hdr;
+ struct virtio_gpu_rect r;
+ uint32_t resource_id;
+ uint32_t padding;
+};
+
+/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: simple transfer to_host */
+struct virtio_gpu_transfer_to_host_2d {
+ struct virtio_gpu_ctrl_hdr hdr;
+ struct virtio_gpu_rect r;
+ uint64_t offset;
+ uint32_t resource_id;
+ uint32_t padding;
+};
+
+struct virtio_gpu_mem_entry {
+ uint64_t addr;
+ uint32_t length;
+ uint32_t padding;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING */
+struct virtio_gpu_resource_attach_backing {
+ struct virtio_gpu_ctrl_hdr hdr;
+ uint32_t resource_id;
+ uint32_t nr_entries;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING */
+struct virtio_gpu_resource_detach_backing {
+ struct virtio_gpu_ctrl_hdr hdr;
+ uint32_t resource_id;
+ uint32_t padding;
+};
+
+/* VIRTIO_GPU_RESP_OK_DISPLAY_INFO */
+#define VIRTIO_GPU_MAX_SCANOUTS 16
+struct virtio_gpu_resp_display_info {
+ struct virtio_gpu_ctrl_hdr hdr;
+ struct virtio_gpu_display_one {
+ struct virtio_gpu_rect r;
+ uint32_t enabled;
+ uint32_t flags;
+ } pmodes[VIRTIO_GPU_MAX_SCANOUTS];
+};
+
+#define VIRTIO_GPU_EVENT_DISPLAY (1 << 0)
+
+struct virtio_gpu_config {
+ uint32_t events_read;
+ uint32_t events_clear;
+ uint32_t num_scanouts;
+ uint32_t reserved;
+};
+
+/* simple formats for fbcon/X use */
+enum virtio_gpu_formats {
+ VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM = 1,
+ VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM = 2,
+ VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM = 3,
+ VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM = 4,
+
+ VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM = 67,
+ VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM = 68,
+
+ VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM = 121,
+ VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM = 134,
+};
+
+#endif
diff --git a/include/standard-headers/linux/virtio_ids.h b/include/standard-headers/linux/virtio_ids.h
index 5f60aa4be5..77925f587b 100644
--- a/include/standard-headers/linux/virtio_ids.h
+++ b/include/standard-headers/linux/virtio_ids.h
@@ -39,6 +39,7 @@
#define VIRTIO_ID_9P 9 /* 9p virtio console */
#define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */
#define VIRTIO_ID_CAIF 12 /* Virtio caif */
+#define VIRTIO_ID_GPU 16 /* virtio GPU */
#define VIRTIO_ID_INPUT 18 /* virtio input */
#endif /* _LINUX_VIRTIO_IDS_H */
diff --git a/include/standard-headers/linux/virtio_ring.h b/include/standard-headers/linux/virtio_ring.h
index cc647d61fc..6fe276fafb 100644
--- a/include/standard-headers/linux/virtio_ring.h
+++ b/include/standard-headers/linux/virtio_ring.h
@@ -155,7 +155,7 @@ static inline unsigned vring_size(unsigned int num, unsigned long align)
}
/* The following is used with USED_EVENT_IDX and AVAIL_EVENT_IDX */
-/* Assuming a given event_idx value from the other size, if
+/* Assuming a given event_idx value from the other side, if
* we have just incremented index from old to new_idx,
* should we trigger an event? */
static inline int vring_need_event(uint16_t event_idx, uint16_t new_idx, uint16_t old)
diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h
index 54b36c16c4..c38892fec6 100644
--- a/include/sysemu/arch_init.h
+++ b/include/sysemu/arch_init.h
@@ -30,7 +30,6 @@ extern const uint32_t arch_type;
void select_soundhw(const char *optarg);
void do_acpitable_option(const QemuOpts *opts);
void do_smbios_option(QemuOpts *opts);
-void ram_mig_init(void);
void cpudef_init(void);
void audio_init(void);
int kvm_available(void);
diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h
index 7ca59b5070..310415025c 100644
--- a/include/sysemu/blockdev.h
+++ b/include/sysemu/blockdev.h
@@ -66,5 +66,5 @@ DriveInfo *drive_new(QemuOpts *arg, BlockInterfaceType block_default_type);
void qmp_change_blockdev(const char *device, const char *filename,
const char *format, Error **errp);
void hmp_commit(Monitor *mon, const QDict *qdict);
-int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data);
+void hmp_drive_del(Monitor *mon, const QDict *qdict);
#endif
diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h
index 4878959404..f459fbdbd4 100644
--- a/include/sysemu/kvm.h
+++ b/include/sysemu/kvm.h
@@ -287,6 +287,8 @@ void kvm_arch_init_irq_routing(KVMState *s);
int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
uint64_t address, uint32_t data);
+int kvm_arch_msi_data_to_gsi(uint32_t data);
+
int kvm_set_irq(KVMState *s, int irq, int level);
int kvm_irqchip_send_msi(KVMState *s, MSIMessage msg);
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index 4fcc20e957..0304aa761e 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -84,6 +84,7 @@ void qemu_announce_self(void);
bool qemu_savevm_state_blocked(Error **errp);
void qemu_savevm_state_begin(QEMUFile *f,
const MigrationParams *params);
+void qemu_savevm_state_header(QEMUFile *f);
int qemu_savevm_state_iterate(QEMUFile *f);
void qemu_savevm_state_complete(QEMUFile *f);
void qemu_savevm_state_cancel(void);
@@ -104,7 +105,7 @@ extern int autostart;
typedef enum {
VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB, VGA_QXL,
- VGA_TCX, VGA_CG3, VGA_DEVICE
+ VGA_TCX, VGA_CG3, VGA_DEVICE, VGA_VIRTIO,
} VGAInterfaceType;
extern int vga_interface_type;
@@ -162,9 +163,7 @@ extern unsigned int nb_prom_envs;
void hmp_drive_add(Monitor *mon, const QDict *qdict);
/* pcie aer error injection */
-void pcie_aer_inject_error_print(Monitor *mon, const QObject *data);
-int hmp_pcie_aer_inject_error(Monitor *mon,
- const QDict *qdict, QObject **ret_data);
+void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict);
/* serial ports */
diff --git a/include/ui/console.h b/include/ui/console.h
index 6f7550ef9c..de92523bbb 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -241,10 +241,6 @@ void dpy_text_resize(QemuConsole *con, int w, int h);
void dpy_mouse_set(QemuConsole *con, int x, int y, int on);
void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor);
bool dpy_cursor_define_supported(QemuConsole *con);
-void dpy_gfx_update_dirty(QemuConsole *con,
- MemoryRegion *address_space,
- uint64_t base,
- bool invalidate);
bool dpy_gfx_check_format(QemuConsole *con,
pixman_format_code_t format);
@@ -374,7 +370,7 @@ char *vnc_display_local_addr(const char *id);
int vnc_display_password(const char *id, const char *password);
int vnc_display_pw_expire(const char *id, time_t expires);
QemuOpts *vnc_parse_func(const char *str);
-int vnc_init_func(QemuOpts *opts, void *opaque);
+int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp);
#else
static inline int vnc_display_password(const char *id, const char *password)
{
diff --git a/iohandler.c b/iohandler.c
index cca614f087..826f713e9f 100644
--- a/iohandler.c
+++ b/iohandler.c
@@ -33,7 +33,6 @@
#endif
typedef struct IOHandlerRecord {
- IOCanReadHandler *fd_read_poll;
IOHandler *fd_read;
IOHandler *fd_write;
void *opaque;
@@ -46,11 +45,7 @@ typedef struct IOHandlerRecord {
static QLIST_HEAD(, IOHandlerRecord) io_handlers =
QLIST_HEAD_INITIALIZER(io_handlers);
-
-/* XXX: fd_read_poll should be suppressed, but an API change is
- necessary in the character devices to suppress fd_can_read(). */
-int qemu_set_fd_handler2(int fd,
- IOCanReadHandler *fd_read_poll,
+void qemu_set_fd_handler(int fd,
IOHandler *fd_read,
IOHandler *fd_write,
void *opaque)
@@ -75,7 +70,6 @@ int qemu_set_fd_handler2(int fd,
QLIST_INSERT_HEAD(&io_handlers, ioh, next);
found:
ioh->fd = fd;
- ioh->fd_read_poll = fd_read_poll;
ioh->fd_read = fd_read;
ioh->fd_write = fd_write;
ioh->opaque = opaque;
@@ -83,15 +77,6 @@ int qemu_set_fd_handler2(int fd,
ioh->deleted = 0;
qemu_notify_event();
}
- return 0;
-}
-
-int qemu_set_fd_handler(int fd,
- IOHandler *fd_read,
- IOHandler *fd_write,
- void *opaque)
-{
- return qemu_set_fd_handler2(fd, NULL, fd_read, fd_write, opaque);
}
void qemu_iohandler_fill(GArray *pollfds)
@@ -103,9 +88,7 @@ void qemu_iohandler_fill(GArray *pollfds)
if (ioh->deleted)
continue;
- if (ioh->fd_read &&
- (!ioh->fd_read_poll ||
- ioh->fd_read_poll(ioh->opaque) != 0)) {
+ if (ioh->fd_read) {
events |= G_IO_IN | G_IO_HUP | G_IO_ERR;
}
if (ioh->fd_write) {
diff --git a/iothread.c b/iothread.c
index 0416fc4268..878a594ef4 100644
--- a/iothread.c
+++ b/iothread.c
@@ -31,14 +31,21 @@ typedef ObjectClass IOThreadClass;
static void *iothread_run(void *opaque)
{
IOThread *iothread = opaque;
+ bool blocking;
qemu_mutex_lock(&iothread->init_done_lock);
iothread->thread_id = qemu_get_thread_id();
qemu_cond_signal(&iothread->init_done_cond);
qemu_mutex_unlock(&iothread->init_done_lock);
- while (!atomic_read(&iothread->stopping)) {
- aio_poll(iothread->ctx, true);
+ while (!iothread->stopping) {
+ aio_context_acquire(iothread->ctx);
+ blocking = true;
+ while (!iothread->stopping && aio_poll(iothread->ctx, blocking)) {
+ /* Progress was made, keep going */
+ blocking = false;
+ }
+ aio_context_release(iothread->ctx);
}
return NULL;
}
diff --git a/kvm-all.c b/kvm-all.c
index 17a3771efe..53e01d468e 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -83,7 +83,6 @@ struct KVMState
struct kvm_coalesced_mmio_ring *coalesced_mmio_ring;
bool coalesced_flush_in_progress;
int broken_set_mem_region;
- int migration_log;
int vcpu_events;
int robust_singlestep;
int debugregs;
@@ -234,9 +233,6 @@ static int kvm_set_user_memory_region(KVMState *s, KVMSlot *slot)
mem.guest_phys_addr = slot->start_addr;
mem.userspace_addr = (unsigned long)slot->ram;
mem.flags = slot->flags;
- if (s->migration_log) {
- mem.flags |= KVM_MEM_LOG_DIRTY_PAGES;
- }
if (slot->memory_size && mem.flags & KVM_MEM_READONLY) {
/* Set the slot size to 0 before setting the slot to the desired
@@ -317,10 +313,6 @@ static int kvm_slot_dirty_pages_log_change(KVMSlot *mem, bool log_dirty)
mem->flags = flags;
/* If nothing changed effectively, no need to issue ioctl */
- if (s->migration_log) {
- flags |= KVM_MEM_LOG_DIRTY_PAGES;
- }
-
if (flags == old_flags) {
return 0;
}
@@ -335,19 +327,22 @@ static int kvm_dirty_pages_log_change(hwaddr phys_addr,
KVMSlot *mem = kvm_lookup_matching_slot(s, phys_addr, phys_addr + size);
if (mem == NULL) {
- fprintf(stderr, "BUG: %s: invalid parameters " TARGET_FMT_plx "-"
- TARGET_FMT_plx "\n", __func__, phys_addr,
- (hwaddr)(phys_addr + size - 1));
- return -EINVAL;
+ return 0;
+ } else {
+ return kvm_slot_dirty_pages_log_change(mem, log_dirty);
}
- return kvm_slot_dirty_pages_log_change(mem, log_dirty);
}
static void kvm_log_start(MemoryListener *listener,
- MemoryRegionSection *section)
+ MemoryRegionSection *section,
+ int old, int new)
{
int r;
+ if (old != 0) {
+ return;
+ }
+
r = kvm_dirty_pages_log_change(section->offset_within_address_space,
int128_get64(section->size), true);
if (r < 0) {
@@ -356,10 +351,15 @@ static void kvm_log_start(MemoryListener *listener,
}
static void kvm_log_stop(MemoryListener *listener,
- MemoryRegionSection *section)
+ MemoryRegionSection *section,
+ int old, int new)
{
int r;
+ if (new != 0) {
+ return;
+ }
+
r = kvm_dirty_pages_log_change(section->offset_within_address_space,
int128_get64(section->size), false);
if (r < 0) {
@@ -367,31 +367,6 @@ static void kvm_log_stop(MemoryListener *listener,
}
}
-static int kvm_set_migration_log(bool enable)
-{
- KVMState *s = kvm_state;
- KVMSlot *mem;
- int i, err;
-
- s->migration_log = enable;
-
- for (i = 0; i < s->nr_slots; i++) {
- mem = &s->slots[i];
-
- if (!mem->memory_size) {
- continue;
- }
- if (!!(mem->flags & KVM_MEM_LOG_DIRTY_PAGES) == enable) {
- continue;
- }
- err = kvm_set_user_memory_region(s, mem);
- if (err) {
- return err;
- }
- }
- return 0;
-}
-
/* get kvm's dirty pages bitmap and update qemu's */
static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section,
unsigned long *bitmap)
@@ -663,7 +638,7 @@ static void kvm_set_phys_mem(MemoryRegionSection *section, bool add)
KVMSlot *mem, old;
int err;
MemoryRegion *mr = section->mr;
- bool log_dirty = memory_region_is_logging(mr);
+ bool log_dirty = memory_region_get_dirty_log_mask(mr) != 0;
bool writeable = !mr->readonly && !mr->rom_device;
bool readonly_flag = mr->readonly || memory_region_is_romd(mr);
hwaddr start_addr = section->offset_within_address_space;
@@ -715,7 +690,7 @@ static void kvm_set_phys_mem(MemoryRegionSection *section, bool add)
old = *mem;
- if ((mem->flags & KVM_MEM_LOG_DIRTY_PAGES) || s->migration_log) {
+ if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) {
kvm_physical_sync_dirty_bitmap(section);
}
@@ -844,22 +819,6 @@ static void kvm_log_sync(MemoryListener *listener,
}
}
-static void kvm_log_global_start(struct MemoryListener *listener)
-{
- int r;
-
- r = kvm_set_migration_log(1);
- assert(r >= 0);
-}
-
-static void kvm_log_global_stop(struct MemoryListener *listener)
-{
- int r;
-
- r = kvm_set_migration_log(0);
- assert(r >= 0);
-}
-
static void kvm_mem_ioeventfd_add(MemoryListener *listener,
MemoryRegionSection *section,
bool match_data, uint64_t data,
@@ -935,8 +894,6 @@ static MemoryListener kvm_memory_listener = {
.log_start = kvm_log_start,
.log_stop = kvm_log_stop,
.log_sync = kvm_log_sync,
- .log_global_start = kvm_log_global_start,
- .log_global_stop = kvm_log_global_stop,
.eventfd_add = kvm_mem_ioeventfd_add,
.eventfd_del = kvm_mem_ioeventfd_del,
.coalesced_mmio_add = kvm_coalesce_mmio_region,
@@ -1228,7 +1185,7 @@ int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg)
int virq;
if (kvm_gsi_direct_mapping()) {
- return msg.data & 0xffff;
+ return kvm_arch_msi_data_to_gsi(msg.data);
}
if (!kvm_gsi_routing_enabled()) {
@@ -1828,6 +1785,14 @@ int kvm_cpu_exec(CPUState *cpu)
}
fprintf(stderr, "error: kvm run failed %s\n",
strerror(-run_ret));
+#ifdef TARGET_PPC
+ if (run_ret == -EBUSY) {
+ fprintf(stderr,
+ "This is probably because your SMT is enabled.\n"
+ "VCPU can only run on primary threads with all "
+ "secondary threads offline.\n");
+ }
+#endif
ret = -1;
break;
}
diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h
index d7dcef58ae..a4ae82eb82 100644
--- a/linux-headers/asm-x86/kvm.h
+++ b/linux-headers/asm-x86/kvm.h
@@ -106,6 +106,8 @@ struct kvm_ioapic_state {
#define KVM_IRQCHIP_IOAPIC 2
#define KVM_NR_IRQCHIPS 3
+#define KVM_RUN_X86_SMM (1 << 0)
+
/* for KVM_GET_REGS and KVM_SET_REGS */
struct kvm_regs {
/* out (KVM_GET_REGS) / in (KVM_SET_REGS) */
@@ -281,6 +283,7 @@ struct kvm_reinject_control {
#define KVM_VCPUEVENT_VALID_NMI_PENDING 0x00000001
#define KVM_VCPUEVENT_VALID_SIPI_VECTOR 0x00000002
#define KVM_VCPUEVENT_VALID_SHADOW 0x00000004
+#define KVM_VCPUEVENT_VALID_SMM 0x00000008
/* Interrupt shadow states */
#define KVM_X86_SHADOW_INT_MOV_SS 0x01
@@ -309,7 +312,13 @@ struct kvm_vcpu_events {
} nmi;
__u32 sipi_vector;
__u32 flags;
- __u32 reserved[10];
+ struct {
+ __u8 smm;
+ __u8 pending;
+ __u8 smm_inside_nmi;
+ __u8 latched_init;
+ } smi;
+ __u32 reserved[9];
};
/* for KVM_GET/SET_DEBUGREGS */
@@ -345,4 +354,7 @@ struct kvm_xcrs {
struct kvm_sync_regs {
};
+#define KVM_QUIRK_LINT0_REENABLED (1 << 0)
+#define KVM_QUIRK_CD_NW_CLEARED (1 << 1)
+
#endif /* _ASM_X86_KVM_H */
diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h
index b96d9787dd..fad9e5c561 100644
--- a/linux-headers/linux/kvm.h
+++ b/linux-headers/linux/kvm.h
@@ -202,7 +202,7 @@ struct kvm_run {
__u32 exit_reason;
__u8 ready_for_interrupt_injection;
__u8 if_flag;
- __u8 padding2[2];
+ __u16 flags;
/* in (pre_kvm_run), out (post_kvm_run) */
__u64 cr8;
@@ -814,6 +814,9 @@ struct kvm_ppc_smmu_info {
#define KVM_CAP_S390_INJECT_IRQ 113
#define KVM_CAP_S390_IRQ_STATE 114
#define KVM_CAP_PPC_HWRNG 115
+#define KVM_CAP_DISABLE_QUIRKS 116
+#define KVM_CAP_X86_SMM 117
+#define KVM_CAP_MULTI_ADDRESS_SPACE 118
#ifdef KVM_CAP_IRQ_ROUTING
@@ -1199,6 +1202,8 @@ struct kvm_s390_ucas_mapping {
/* Available with KVM_CAP_S390_IRQ_STATE */
#define KVM_S390_SET_IRQ_STATE _IOW(KVMIO, 0xb5, struct kvm_s390_irq_state)
#define KVM_S390_GET_IRQ_STATE _IOW(KVMIO, 0xb6, struct kvm_s390_irq_state)
+/* Available with KVM_CAP_X86_SMM */
+#define KVM_SMI _IO(KVMIO, 0xb7)
#define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0)
#define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1)
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 0ba97062b7..17883686f0 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -1075,6 +1075,35 @@ static inline void elf_core_copy_regs(target_elf_gregset_t *regs,
#define USE_ELF_CORE_DUMP
#define ELF_EXEC_PAGESIZE 4096
+enum {
+ SH_CPU_HAS_FPU = 0x0001, /* Hardware FPU support */
+ SH_CPU_HAS_P2_FLUSH_BUG = 0x0002, /* Need to flush the cache in P2 area */
+ SH_CPU_HAS_MMU_PAGE_ASSOC = 0x0004, /* SH3: TLB way selection bit support */
+ SH_CPU_HAS_DSP = 0x0008, /* SH-DSP: DSP support */
+ SH_CPU_HAS_PERF_COUNTER = 0x0010, /* Hardware performance counters */
+ SH_CPU_HAS_PTEA = 0x0020, /* PTEA register */
+ SH_CPU_HAS_LLSC = 0x0040, /* movli.l/movco.l */
+ SH_CPU_HAS_L2_CACHE = 0x0080, /* Secondary cache / URAM */
+ SH_CPU_HAS_OP32 = 0x0100, /* 32-bit instruction support */
+ SH_CPU_HAS_PTEAEX = 0x0200, /* PTE ASID Extension support */
+};
+
+#define ELF_HWCAP get_elf_hwcap()
+
+static uint32_t get_elf_hwcap(void)
+{
+ SuperHCPU *cpu = SUPERH_CPU(thread_cpu);
+ uint32_t hwcap = 0;
+
+ hwcap |= SH_CPU_HAS_FPU;
+
+ if (cpu->env.features & SH_FEATURE_SH4A) {
+ hwcap |= SH_CPU_HAS_LLSC;
+ }
+
+ return hwcap;
+}
+
#endif
#ifdef TARGET_CRIS
@@ -1227,7 +1256,8 @@ struct exec
/* Necessary parameters */
#define TARGET_ELF_EXEC_PAGESIZE TARGET_PAGE_SIZE
-#define TARGET_ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(TARGET_ELF_EXEC_PAGESIZE-1))
+#define TARGET_ELF_PAGESTART(_v) ((_v) & \
+ ~(abi_ulong)(TARGET_ELF_EXEC_PAGESIZE-1))
#define TARGET_ELF_PAGEOFFSET(_v) ((_v) & (TARGET_ELF_EXEC_PAGESIZE-1))
#define DLINFO_ITEMS 14
diff --git a/linux-user/main.c b/linux-user/main.c
index 3f32db0afd..c855bccadc 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -215,10 +215,6 @@ void cpu_list_unlock(void)
/***********************************************************/
/* CPUX86 core interface */
-void cpu_smm_update(CPUX86State *env)
-{
-}
-
uint64_t cpu_get_tsc(CPUX86State *env)
{
return cpu_get_real_ticks();
@@ -3463,8 +3459,8 @@ CPUArchState *cpu_copy(CPUArchState *env)
/* Clone all break/watchpoints.
Note: Once we support ptrace with hw-debug register access, make sure
BP_CPU break/watchpoints are handled correctly on clone. */
- QTAILQ_INIT(&cpu->breakpoints);
- QTAILQ_INIT(&cpu->watchpoints);
+ QTAILQ_INIT(&new_cpu->breakpoints);
+ QTAILQ_INIT(&new_cpu->watchpoints);
QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
cpu_breakpoint_insert(new_cpu, bp->pc, bp->flags, NULL);
}
@@ -3929,6 +3925,8 @@ int main(int argc, char **argv, char **envp)
# else
cpu_model = "750";
# endif
+#elif defined TARGET_SH4
+ cpu_model = TYPE_SH7785_CPU;
#else
cpu_model = "any";
#endif
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index a249f0ceb6..78e1b2df43 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -30,6 +30,7 @@
#include "qemu.h"
#include "qemu-common.h"
+#include "translate-all.h"
//#define DEBUG_MMAP
@@ -574,7 +575,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
page_dump(stdout);
printf("\n");
#endif
- tb_invalidate_phys_range(start, start + len, 0);
+ tb_invalidate_phys_range(start, start + len);
mmap_unlock();
return start;
fail:
@@ -679,7 +680,7 @@ int target_munmap(abi_ulong start, abi_ulong len)
if (ret == 0) {
page_set_flags(start, start + len, 0);
- tb_invalidate_phys_range(start, start + len, 0);
+ tb_invalidate_phys_range(start, start + len);
}
mmap_unlock();
return ret;
@@ -758,7 +759,7 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
page_set_flags(old_addr, old_addr + old_size, 0);
page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
}
- tb_invalidate_phys_range(new_addr, new_addr + new_size, 0);
+ tb_invalidate_phys_range(new_addr, new_addr + new_size);
mmap_unlock();
return new_addr;
}
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 1622ad6490..f62c698948 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -1202,6 +1202,15 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh,
space += CMSG_SPACE(len);
if (space > msgh->msg_controllen) {
space -= CMSG_SPACE(len);
+ /* This is a QEMU bug, since we allocated the payload
+ * area ourselves (unlike overflow in host-to-target
+ * conversion, which is just the guest giving us a buffer
+ * that's too small). It can't happen for the payload types
+ * we currently support; if it becomes an issue in future
+ * we would need to improve our allocation strategy to
+ * something more intelligent than "twice the size of the
+ * target buffer we're reading from".
+ */
gemu_log("Host cmsg overflow\n");
break;
}
@@ -1219,17 +1228,18 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh,
int *target_fd = (int *)target_data;
int i, numfds = len / sizeof(int);
- for (i = 0; i < numfds; i++)
- fd[i] = tswap32(target_fd[i]);
+ for (i = 0; i < numfds; i++) {
+ __get_user(fd[i], target_fd + i);
+ }
} else if (cmsg->cmsg_level == SOL_SOCKET
&& cmsg->cmsg_type == SCM_CREDENTIALS) {
struct ucred *cred = (struct ucred *)data;
struct target_ucred *target_cred =
(struct target_ucred *)target_data;
- __put_user(target_cred->pid, &cred->pid);
- __put_user(target_cred->uid, &cred->uid);
- __put_user(target_cred->gid, &cred->gid);
+ __get_user(cred->pid, &target_cred->pid);
+ __get_user(cred->uid, &target_cred->uid);
+ __get_user(cred->gid, &target_cred->gid);
} else {
gemu_log("Unsupported ancillary data: %d/%d\n",
cmsg->cmsg_level, cmsg->cmsg_type);
@@ -1267,11 +1277,16 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh,
void *target_data = TARGET_CMSG_DATA(target_cmsg);
int len = cmsg->cmsg_len - CMSG_ALIGN(sizeof (struct cmsghdr));
+ int tgt_len, tgt_space;
- space += TARGET_CMSG_SPACE(len);
- if (space > msg_controllen) {
- space -= TARGET_CMSG_SPACE(len);
- gemu_log("Target cmsg overflow\n");
+ /* We never copy a half-header but may copy half-data;
+ * this is Linux's behaviour in put_cmsg(). Note that
+ * truncation here is a guest problem (which we report
+ * to the guest via the CTRUNC bit), unlike truncation
+ * in target_to_host_cmsg, which is a QEMU bug.
+ */
+ if (msg_controllen < sizeof(struct cmsghdr)) {
+ target_msgh->msg_flags |= tswap32(MSG_CTRUNC);
break;
}
@@ -1281,8 +1296,35 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh,
target_cmsg->cmsg_level = tswap32(cmsg->cmsg_level);
}
target_cmsg->cmsg_type = tswap32(cmsg->cmsg_type);
- target_cmsg->cmsg_len = tswapal(TARGET_CMSG_LEN(len));
+ tgt_len = TARGET_CMSG_LEN(len);
+
+ /* Payload types which need a different size of payload on
+ * the target must adjust tgt_len here.
+ */
+ switch (cmsg->cmsg_level) {
+ case SOL_SOCKET:
+ switch (cmsg->cmsg_type) {
+ case SO_TIMESTAMP:
+ tgt_len = sizeof(struct target_timeval);
+ break;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (msg_controllen < tgt_len) {
+ target_msgh->msg_flags |= tswap32(MSG_CTRUNC);
+ tgt_len = msg_controllen;
+ }
+
+ /* We must now copy-and-convert len bytes of payload
+ * into tgt_len bytes of destination space. Bear in mind
+ * that in both source and destination we may be dealing
+ * with a truncated value!
+ */
switch (cmsg->cmsg_level) {
case SOL_SOCKET:
switch (cmsg->cmsg_type) {
@@ -1290,10 +1332,11 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh,
{
int *fd = (int *)data;
int *target_fd = (int *)target_data;
- int i, numfds = len / sizeof(int);
+ int i, numfds = tgt_len / sizeof(int);
- for (i = 0; i < numfds; i++)
- target_fd[i] = tswap32(fd[i]);
+ for (i = 0; i < numfds; i++) {
+ __put_user(fd[i], target_fd + i);
+ }
break;
}
case SO_TIMESTAMP:
@@ -1302,12 +1345,14 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh,
struct target_timeval *target_tv =
(struct target_timeval *)target_data;
- if (len != sizeof(struct timeval))
+ if (len != sizeof(struct timeval) ||
+ tgt_len != sizeof(struct target_timeval)) {
goto unimplemented;
+ }
/* copy struct timeval to target */
- target_tv->tv_sec = tswapal(tv->tv_sec);
- target_tv->tv_usec = tswapal(tv->tv_usec);
+ __put_user(tv->tv_sec, &target_tv->tv_sec);
+ __put_user(tv->tv_usec, &target_tv->tv_usec);
break;
}
case SCM_CREDENTIALS:
@@ -1330,9 +1375,19 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh,
unimplemented:
gemu_log("Unsupported ancillary data: %d/%d\n",
cmsg->cmsg_level, cmsg->cmsg_type);
- memcpy(target_data, data, len);
+ memcpy(target_data, data, MIN(len, tgt_len));
+ if (tgt_len > len) {
+ memset(target_data + len, 0, tgt_len - len);
+ }
}
+ target_cmsg->cmsg_len = tswapal(tgt_len);
+ tgt_space = TARGET_CMSG_SPACE(tgt_len);
+ if (msg_controllen < tgt_space) {
+ tgt_space = msg_controllen;
+ }
+ msg_controllen -= tgt_space;
+ space += tgt_space;
cmsg = CMSG_NXTHDR(msgh, cmsg);
target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg);
}
@@ -3277,6 +3332,7 @@ static abi_long do_ipc(unsigned int call, abi_long first,
#define STRUCT_SPECIAL(name) STRUCT_ ## name,
enum {
#include "syscall_types.h"
+STRUCT_MAX
};
#undef STRUCT
#undef STRUCT_SPECIAL
@@ -3290,7 +3346,7 @@ enum {
typedef struct IOCTLEntry IOCTLEntry;
typedef abi_long do_ioctl_fn(const IOCTLEntry *ie, uint8_t *buf_temp,
- int fd, abi_long cmd, abi_long arg);
+ int fd, int cmd, abi_long arg);
struct IOCTLEntry {
int target_cmd;
@@ -3316,7 +3372,7 @@ struct IOCTLEntry {
/ sizeof(struct fiemap_extent))
static abi_long do_ioctl_fs_ioc_fiemap(const IOCTLEntry *ie, uint8_t *buf_temp,
- int fd, abi_long cmd, abi_long arg)
+ int fd, int cmd, abi_long arg)
{
/* The parameter for this ioctl is a struct fiemap followed
* by an array of struct fiemap_extent whose size is set
@@ -3397,7 +3453,7 @@ static abi_long do_ioctl_fs_ioc_fiemap(const IOCTLEntry *ie, uint8_t *buf_temp,
#endif
static abi_long do_ioctl_ifconf(const IOCTLEntry *ie, uint8_t *buf_temp,
- int fd, abi_long cmd, abi_long arg)
+ int fd, int cmd, abi_long arg)
{
const argtype *arg_type = ie->arg_type;
int target_size;
@@ -3491,7 +3547,7 @@ static abi_long do_ioctl_ifconf(const IOCTLEntry *ie, uint8_t *buf_temp,
}
static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd,
- abi_long cmd, abi_long arg)
+ int cmd, abi_long arg)
{
void *argptr;
struct dm_ioctl *host_dm;
@@ -3716,7 +3772,7 @@ out:
}
static abi_long do_ioctl_blkpg(const IOCTLEntry *ie, uint8_t *buf_temp, int fd,
- abi_long cmd, abi_long arg)
+ int cmd, abi_long arg)
{
void *argptr;
int target_size;
@@ -3769,7 +3825,7 @@ out:
}
static abi_long do_ioctl_rt(const IOCTLEntry *ie, uint8_t *buf_temp,
- int fd, abi_long cmd, abi_long arg)
+ int fd, int cmd, abi_long arg)
{
const argtype *arg_type = ie->arg_type;
const StructEntry *se;
@@ -3832,7 +3888,7 @@ static abi_long do_ioctl_rt(const IOCTLEntry *ie, uint8_t *buf_temp,
}
static abi_long do_ioctl_kdsigaccept(const IOCTLEntry *ie, uint8_t *buf_temp,
- int fd, abi_long cmd, abi_long arg)
+ int fd, int cmd, abi_long arg)
{
int sig = target_to_host_signal(arg);
return get_errno(ioctl(fd, ie->host_cmd, sig));
@@ -3849,7 +3905,7 @@ static IOCTLEntry ioctl_entries[] = {
/* ??? Implement proper locking for ioctls. */
/* do_ioctl() Must return target values and target errnos. */
-static abi_long do_ioctl(int fd, abi_long cmd, abi_long arg)
+static abi_long do_ioctl(int fd, int cmd, abi_long arg)
{
const IOCTLEntry *ie;
const argtype *arg_type;
@@ -4879,6 +4935,8 @@ void syscall_init(void)
int size;
int i;
+ thunk_init(STRUCT_MAX);
+
#define STRUCT(name, ...) thunk_register_struct(STRUCT_ ## name, #name, struct_ ## name ## _def);
#define STRUCT_SPECIAL(name) thunk_register_struct_direct(STRUCT_ ## name, #name, &struct_ ## name ## _def);
#include "syscall_types.h"
diff --git a/main-loop.c b/main-loop.c
index 981bcb5f8e..82875a4dfd 100644
--- a/main-loop.c
+++ b/main-loop.c
@@ -100,8 +100,7 @@ static int qemu_signal_init(void)
fcntl_setfl(sigfd, O_NONBLOCK);
- qemu_set_fd_handler2(sigfd, NULL, sigfd_handler, NULL,
- (void *)(intptr_t)sigfd);
+ qemu_set_fd_handler(sigfd, sigfd_handler, NULL, (void *)(intptr_t)sigfd);
return 0;
}
diff --git a/memory.c b/memory.c
index 03c536b857..3ac0bd20d2 100644
--- a/memory.c
+++ b/memory.c
@@ -28,6 +28,8 @@
//#define DEBUG_UNASSIGNED
+#define RAM_ADDR_INVALID (~(ram_addr_t)0)
+
static unsigned memory_region_transaction_depth;
static bool memory_region_update_pending;
static bool ioeventfd_update_pending;
@@ -152,7 +154,7 @@ static bool memory_listener_match(MemoryListener *listener,
} while (0)
/* No need to ref/unref .mr, the FlatRange keeps it alive. */
-#define MEMORY_LISTENER_UPDATE_REGION(fr, as, dir, callback) \
+#define MEMORY_LISTENER_UPDATE_REGION(fr, as, dir, callback, _args...) \
MEMORY_LISTENER_CALL(callback, dir, (&(MemoryRegionSection) { \
.mr = (fr)->mr, \
.address_space = (as), \
@@ -160,7 +162,7 @@ static bool memory_listener_match(MemoryListener *listener,
.size = (fr)->addr.size, \
.offset_within_address_space = int128_get64((fr)->addr.start), \
.readonly = (fr)->readonly, \
- }))
+ }), ##_args)
struct CoalescedMemoryRange {
AddrRange addr;
@@ -588,7 +590,7 @@ static void render_memory_region(FlatView *view,
remain = clip.size;
fr.mr = mr;
- fr.dirty_log_mask = mr->dirty_log_mask;
+ fr.dirty_log_mask = memory_region_get_dirty_log_mask(mr);
fr.romd_mode = mr->romd_mode;
fr.readonly = readonly;
@@ -774,10 +776,15 @@ static void address_space_update_topology_pass(AddressSpace *as,
if (adding) {
MEMORY_LISTENER_UPDATE_REGION(frnew, as, Forward, region_nop);
- if (frold->dirty_log_mask && !frnew->dirty_log_mask) {
- MEMORY_LISTENER_UPDATE_REGION(frnew, as, Reverse, log_stop);
- } else if (frnew->dirty_log_mask && !frold->dirty_log_mask) {
- MEMORY_LISTENER_UPDATE_REGION(frnew, as, Forward, log_start);
+ if (frnew->dirty_log_mask & ~frold->dirty_log_mask) {
+ MEMORY_LISTENER_UPDATE_REGION(frnew, as, Forward, log_start,
+ frold->dirty_log_mask,
+ frnew->dirty_log_mask);
+ }
+ if (frold->dirty_log_mask & ~frnew->dirty_log_mask) {
+ MEMORY_LISTENER_UPDATE_REGION(frnew, as, Reverse, log_stop,
+ frold->dirty_log_mask,
+ frnew->dirty_log_mask);
}
}
@@ -1002,6 +1009,7 @@ static void memory_region_initfn(Object *obj)
ObjectProperty *op;
mr->ops = &unassigned_mem_ops;
+ mr->ram_addr = RAM_ADDR_INVALID;
mr->enabled = true;
mr->romd_mode = true;
mr->destructor = memory_region_destructor_none;
@@ -1193,7 +1201,6 @@ void memory_region_init_io(MemoryRegion *mr,
mr->ops = ops;
mr->opaque = opaque;
mr->terminates = true;
- mr->ram_addr = ~(ram_addr_t)0;
}
void memory_region_init_ram(MemoryRegion *mr,
@@ -1207,6 +1214,7 @@ void memory_region_init_ram(MemoryRegion *mr,
mr->terminates = true;
mr->destructor = memory_region_destructor_ram;
mr->ram_addr = qemu_ram_alloc(size, mr, errp);
+ mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
}
void memory_region_init_resizeable_ram(MemoryRegion *mr,
@@ -1224,6 +1232,7 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr,
mr->terminates = true;
mr->destructor = memory_region_destructor_ram;
mr->ram_addr = qemu_ram_alloc_resizeable(size, max_size, resized, mr, errp);
+ mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
}
#ifdef __linux__
@@ -1240,6 +1249,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr,
mr->terminates = true;
mr->destructor = memory_region_destructor_ram;
mr->ram_addr = qemu_ram_alloc_from_file(size, mr, share, path, errp);
+ mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
}
#endif
@@ -1253,6 +1263,7 @@ void memory_region_init_ram_ptr(MemoryRegion *mr,
mr->ram = true;
mr->terminates = true;
mr->destructor = memory_region_destructor_ram_from_ptr;
+ mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
/* qemu_ram_alloc_from_ptr cannot fail with ptr != NULL. */
assert(ptr != NULL);
@@ -1389,9 +1400,18 @@ bool memory_region_is_skip_dump(MemoryRegion *mr)
return mr->skip_dump;
}
-bool memory_region_is_logging(MemoryRegion *mr)
+uint8_t memory_region_get_dirty_log_mask(MemoryRegion *mr)
+{
+ uint8_t mask = mr->dirty_log_mask;
+ if (global_dirty_log) {
+ mask |= (1 << DIRTY_MEMORY_MIGRATION);
+ }
+ return mask;
+}
+
+bool memory_region_is_logging(MemoryRegion *mr, uint8_t client)
{
- return mr->dirty_log_mask;
+ return memory_region_get_dirty_log_mask(mr) & (1 << client);
}
bool memory_region_is_rom(MemoryRegion *mr)
@@ -1425,6 +1445,7 @@ void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client)
{
uint8_t mask = 1 << client;
+ assert(client == DIRTY_MEMORY_VGA);
memory_region_transaction_begin();
mr->dirty_log_mask = (mr->dirty_log_mask & ~mask) | (log * mask);
memory_region_update_pending |= mr->enabled;
@@ -1434,27 +1455,24 @@ void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client)
bool memory_region_get_dirty(MemoryRegion *mr, hwaddr addr,
hwaddr size, unsigned client)
{
- assert(mr->terminates);
+ assert(mr->ram_addr != RAM_ADDR_INVALID);
return cpu_physical_memory_get_dirty(mr->ram_addr + addr, size, client);
}
void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr,
hwaddr size)
{
- assert(mr->terminates);
- cpu_physical_memory_set_dirty_range(mr->ram_addr + addr, size);
+ assert(mr->ram_addr != RAM_ADDR_INVALID);
+ cpu_physical_memory_set_dirty_range(mr->ram_addr + addr, size,
+ memory_region_get_dirty_log_mask(mr));
}
bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr,
hwaddr size, unsigned client)
{
- bool ret;
- assert(mr->terminates);
- ret = cpu_physical_memory_get_dirty(mr->ram_addr + addr, size, client);
- if (ret) {
- cpu_physical_memory_reset_dirty(mr->ram_addr + addr, size, client);
- }
- return ret;
+ assert(mr->ram_addr != RAM_ADDR_INVALID);
+ return cpu_physical_memory_test_and_clear_dirty(mr->ram_addr + addr,
+ size, client);
}
@@ -1497,8 +1515,9 @@ void memory_region_rom_device_set_romd(MemoryRegion *mr, bool romd_mode)
void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr,
hwaddr size, unsigned client)
{
- assert(mr->terminates);
- cpu_physical_memory_reset_dirty(mr->ram_addr + addr, size, client);
+ assert(mr->ram_addr != RAM_ADDR_INVALID);
+ cpu_physical_memory_test_and_clear_dirty(mr->ram_addr + addr, size,
+ client);
}
int memory_region_get_fd(MemoryRegion *mr)
@@ -1507,7 +1526,7 @@ int memory_region_get_fd(MemoryRegion *mr)
return memory_region_get_fd(mr->alias);
}
- assert(mr->terminates);
+ assert(mr->ram_addr != RAM_ADDR_INVALID);
return qemu_get_ram_fd(mr->ram_addr & TARGET_PAGE_MASK);
}
@@ -1518,14 +1537,14 @@ void *memory_region_get_ram_ptr(MemoryRegion *mr)
return memory_region_get_ram_ptr(mr->alias) + mr->alias_offset;
}
- assert(mr->terminates);
+ assert(mr->ram_addr != RAM_ADDR_INVALID);
return qemu_get_ram_ptr(mr->ram_addr & TARGET_PAGE_MASK);
}
void memory_region_ram_resize(MemoryRegion *mr, ram_addr_t newsize, Error **errp)
{
- assert(mr->terminates);
+ assert(mr->ram_addr != RAM_ADDR_INVALID);
qemu_ram_resize(mr->ram_addr, newsize, errp);
}
@@ -1947,12 +1966,24 @@ void address_space_sync_dirty_bitmap(AddressSpace *as)
void memory_global_dirty_log_start(void)
{
global_dirty_log = true;
+
MEMORY_LISTENER_CALL_GLOBAL(log_global_start, Forward);
+
+ /* Refresh DIRTY_LOG_MIGRATION bit. */
+ memory_region_transaction_begin();
+ memory_region_update_pending = true;
+ memory_region_transaction_commit();
}
void memory_global_dirty_log_stop(void)
{
global_dirty_log = false;
+
+ /* Refresh DIRTY_LOG_MIGRATION bit. */
+ memory_region_transaction_begin();
+ memory_region_update_pending = true;
+ memory_region_transaction_commit();
+
MEMORY_LISTENER_CALL_GLOBAL(log_global_stop, Reverse);
}
diff --git a/migration/exec.c b/migration/exec.c
index 479024752f..8406d2bbde 100644
--- a/migration/exec.c
+++ b/migration/exec.c
@@ -49,7 +49,7 @@ static void exec_accept_incoming_migration(void *opaque)
{
QEMUFile *f = opaque;
- qemu_set_fd_handler2(qemu_get_fd(f), NULL, NULL, NULL, NULL);
+ qemu_set_fd_handler(qemu_get_fd(f), NULL, NULL, NULL);
process_incoming_migration(f);
}
@@ -64,6 +64,6 @@ void exec_start_incoming_migration(const char *command, Error **errp)
return;
}
- qemu_set_fd_handler2(qemu_get_fd(f), NULL,
- exec_accept_incoming_migration, NULL, f);
+ qemu_set_fd_handler(qemu_get_fd(f), exec_accept_incoming_migration, NULL,
+ f);
}
diff --git a/migration/fd.c b/migration/fd.c
index 129da9910b..3e4bed0e06 100644
--- a/migration/fd.c
+++ b/migration/fd.c
@@ -62,7 +62,7 @@ static void fd_accept_incoming_migration(void *opaque)
{
QEMUFile *f = opaque;
- qemu_set_fd_handler2(qemu_get_fd(f), NULL, NULL, NULL, NULL);
+ qemu_set_fd_handler(qemu_get_fd(f), NULL, NULL, NULL);
process_incoming_migration(f);
}
@@ -84,5 +84,5 @@ void fd_start_incoming_migration(const char *infd, Error **errp)
return;
}
- qemu_set_fd_handler2(fd, NULL, fd_accept_incoming_migration, NULL, f);
+ qemu_set_fd_handler(fd, fd_accept_incoming_migration, NULL, f);
}
diff --git a/migration/migration.c b/migration/migration.c
index 732d229708..b04b4571a8 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -53,6 +53,7 @@ static bool deferred_incoming;
migrations at once. For now we don't need to add
dynamic creation of migration */
+/* For outgoing */
MigrationState *migrate_get_current(void)
{
static MigrationState current_migration = {
@@ -71,6 +72,30 @@ MigrationState *migrate_get_current(void)
return &current_migration;
}
+/* For incoming */
+static MigrationIncomingState *mis_current;
+
+MigrationIncomingState *migration_incoming_get_current(void)
+{
+ return mis_current;
+}
+
+MigrationIncomingState *migration_incoming_state_new(QEMUFile* f)
+{
+ mis_current = g_malloc0(sizeof(MigrationIncomingState));
+ mis_current->file = f;
+ QLIST_INIT(&mis_current->loadvm_handlers);
+
+ return mis_current;
+}
+
+void migration_incoming_state_destroy(void)
+{
+ loadvm_free_handlers(mis_current);
+ g_free(mis_current);
+ mis_current = NULL;
+}
+
/*
* Called on -incoming with a defer: uri.
* The migration can be started later after any parameters have been
@@ -115,9 +140,14 @@ static void process_incoming_migration_co(void *opaque)
Error *local_err = NULL;
int ret;
+ migration_incoming_state_new(f);
+
ret = qemu_loadvm_state(f);
+
qemu_fclose(f);
free_xbzrle_decoded_buf();
+ migration_incoming_state_destroy();
+
if (ret < 0) {
error_report("load of migration failed: %s", strerror(-ret));
migrate_decompress_threads_join();
@@ -738,6 +768,7 @@ static void *migration_thread(void *opaque)
int64_t start_time = initial_time;
bool old_vm_running = false;
+ qemu_savevm_state_header(s->file);
qemu_savevm_state_begin(s->file, &s->params);
s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start;
@@ -838,9 +869,6 @@ static void *migration_thread(void *opaque)
void migrate_fd_connect(MigrationState *s)
{
- s->state = MIGRATION_STATUS_SETUP;
- trace_migrate_set_state(MIGRATION_STATUS_SETUP);
-
/* This is a best 1st approximation. ns to ms */
s->expected_downtime = max_downtime/1000000;
s->cleanup_bh = qemu_bh_new(migrate_fd_cleanup, s);
diff --git a/migration/qemu-file.c b/migration/qemu-file.c
index 2750365a7e..965a757772 100644
--- a/migration/qemu-file.c
+++ b/migration/qemu-file.c
@@ -349,14 +349,14 @@ void qemu_file_skip(QEMUFile *f, int size)
}
/*
- * Read 'size' bytes from file (at 'offset') into buf without moving the
- * pointer.
+ * Read 'size' bytes from file (at 'offset') without moving the
+ * pointer and set 'buf' to point to that data.
*
* It will return size bytes unless there was an error, in which case it will
* return as many as it managed to read (assuming blocking fd's which
* all current QEMUFile are)
*/
-int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset)
+int qemu_peek_buffer(QEMUFile *f, uint8_t **buf, int size, size_t offset)
{
int pending;
int index;
@@ -392,7 +392,7 @@ int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset)
size = pending;
}
- memcpy(buf, f->buf + index, size);
+ *buf = f->buf + index;
return size;
}
@@ -411,11 +411,13 @@ int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size)
while (pending > 0) {
int res;
+ uint8_t *src;
- res = qemu_peek_buffer(f, buf, MIN(pending, IO_BUF_SIZE), 0);
+ res = qemu_peek_buffer(f, &src, MIN(pending, IO_BUF_SIZE), 0);
if (res == 0) {
return done;
}
+ memcpy(buf, src, res);
qemu_file_skip(f, res);
buf += res;
pending -= res;
@@ -585,3 +587,20 @@ int qemu_put_qemu_file(QEMUFile *f_des, QEMUFile *f_src)
}
return len;
}
+
+/*
+ * Get a string whose length is determined by a single preceding byte
+ * A preallocated 256 byte buffer must be passed in.
+ * Returns: len on success and a 0 terminated string in the buffer
+ * else 0
+ * (Note a 0 length string will return 0 either way)
+ */
+size_t qemu_get_counted_string(QEMUFile *f, char buf[256])
+{
+ size_t len = qemu_get_byte(f);
+ size_t res = qemu_get_buffer(f, (uint8_t *)buf, len);
+
+ buf[res] = 0;
+
+ return res == len ? res : 0;
+}
diff --git a/migration/ram.c b/migration/ram.c
new file mode 100644
index 0000000000..57368e1575
--- /dev/null
+++ b/migration/ram.c
@@ -0,0 +1,1628 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ * Copyright (c) 2011-2015 Red Hat Inc
+ *
+ * Authors:
+ * Juan Quintela <quintela@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdint.h>
+#include <zlib.h>
+#include "qemu/bitops.h"
+#include "qemu/bitmap.h"
+#include "qemu/timer.h"
+#include "qemu/main-loop.h"
+#include "migration/migration.h"
+#include "exec/address-spaces.h"
+#include "migration/page_cache.h"
+#include "qemu/error-report.h"
+#include "trace.h"
+#include "exec/ram_addr.h"
+#include "qemu/rcu_queue.h"
+
+#ifdef DEBUG_MIGRATION_RAM
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stdout, "migration_ram: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+ do { } while (0)
+#endif
+
+static bool mig_throttle_on;
+static int dirty_rate_high_cnt;
+static void check_guest_throttling(void);
+
+static uint64_t bitmap_sync_count;
+
+/***********************************************************/
+/* ram save/restore */
+
+#define RAM_SAVE_FLAG_FULL 0x01 /* Obsolete, not used anymore */
+#define RAM_SAVE_FLAG_COMPRESS 0x02
+#define RAM_SAVE_FLAG_MEM_SIZE 0x04
+#define RAM_SAVE_FLAG_PAGE 0x08
+#define RAM_SAVE_FLAG_EOS 0x10
+#define RAM_SAVE_FLAG_CONTINUE 0x20
+#define RAM_SAVE_FLAG_XBZRLE 0x40
+/* 0x80 is reserved in migration.h start with 0x100 next */
+#define RAM_SAVE_FLAG_COMPRESS_PAGE 0x100
+
+static const uint8_t ZERO_TARGET_PAGE[TARGET_PAGE_SIZE];
+
+static inline bool is_zero_range(uint8_t *p, uint64_t size)
+{
+ return buffer_find_nonzero_offset(p, size) == size;
+}
+
+/* struct contains XBZRLE cache and a static page
+ used by the compression */
+static struct {
+ /* buffer used for XBZRLE encoding */
+ uint8_t *encoded_buf;
+ /* buffer for storing page content */
+ uint8_t *current_buf;
+ /* Cache for XBZRLE, Protected by lock. */
+ PageCache *cache;
+ QemuMutex lock;
+} XBZRLE;
+
+/* buffer used for XBZRLE decoding */
+static uint8_t *xbzrle_decoded_buf;
+
+static void XBZRLE_cache_lock(void)
+{
+ if (migrate_use_xbzrle())
+ qemu_mutex_lock(&XBZRLE.lock);
+}
+
+static void XBZRLE_cache_unlock(void)
+{
+ if (migrate_use_xbzrle())
+ qemu_mutex_unlock(&XBZRLE.lock);
+}
+
+/*
+ * called from qmp_migrate_set_cache_size in main thread, possibly while
+ * a migration is in progress.
+ * A running migration maybe using the cache and might finish during this
+ * call, hence changes to the cache are protected by XBZRLE.lock().
+ */
+int64_t xbzrle_cache_resize(int64_t new_size)
+{
+ PageCache *new_cache;
+ int64_t ret;
+
+ if (new_size < TARGET_PAGE_SIZE) {
+ return -1;
+ }
+
+ XBZRLE_cache_lock();
+
+ if (XBZRLE.cache != NULL) {
+ if (pow2floor(new_size) == migrate_xbzrle_cache_size()) {
+ goto out_new_size;
+ }
+ new_cache = cache_init(new_size / TARGET_PAGE_SIZE,
+ TARGET_PAGE_SIZE);
+ if (!new_cache) {
+ error_report("Error creating cache");
+ ret = -1;
+ goto out;
+ }
+
+ cache_fini(XBZRLE.cache);
+ XBZRLE.cache = new_cache;
+ }
+
+out_new_size:
+ ret = pow2floor(new_size);
+out:
+ XBZRLE_cache_unlock();
+ return ret;
+}
+
+/* accounting for migration statistics */
+typedef struct AccountingInfo {
+ uint64_t dup_pages;
+ uint64_t skipped_pages;
+ uint64_t norm_pages;
+ uint64_t iterations;
+ uint64_t xbzrle_bytes;
+ uint64_t xbzrle_pages;
+ uint64_t xbzrle_cache_miss;
+ double xbzrle_cache_miss_rate;
+ uint64_t xbzrle_overflows;
+} AccountingInfo;
+
+static AccountingInfo acct_info;
+
+static void acct_clear(void)
+{
+ memset(&acct_info, 0, sizeof(acct_info));
+}
+
+uint64_t dup_mig_bytes_transferred(void)
+{
+ return acct_info.dup_pages * TARGET_PAGE_SIZE;
+}
+
+uint64_t dup_mig_pages_transferred(void)
+{
+ return acct_info.dup_pages;
+}
+
+uint64_t skipped_mig_bytes_transferred(void)
+{
+ return acct_info.skipped_pages * TARGET_PAGE_SIZE;
+}
+
+uint64_t skipped_mig_pages_transferred(void)
+{
+ return acct_info.skipped_pages;
+}
+
+uint64_t norm_mig_bytes_transferred(void)
+{
+ return acct_info.norm_pages * TARGET_PAGE_SIZE;
+}
+
+uint64_t norm_mig_pages_transferred(void)
+{
+ return acct_info.norm_pages;
+}
+
+uint64_t xbzrle_mig_bytes_transferred(void)
+{
+ return acct_info.xbzrle_bytes;
+}
+
+uint64_t xbzrle_mig_pages_transferred(void)
+{
+ return acct_info.xbzrle_pages;
+}
+
+uint64_t xbzrle_mig_pages_cache_miss(void)
+{
+ return acct_info.xbzrle_cache_miss;
+}
+
+double xbzrle_mig_cache_miss_rate(void)
+{
+ return acct_info.xbzrle_cache_miss_rate;
+}
+
+uint64_t xbzrle_mig_pages_overflow(void)
+{
+ return acct_info.xbzrle_overflows;
+}
+
+/* This is the last block that we have visited serching for dirty pages
+ */
+static RAMBlock *last_seen_block;
+/* This is the last block from where we have sent data */
+static RAMBlock *last_sent_block;
+static ram_addr_t last_offset;
+static unsigned long *migration_bitmap;
+static uint64_t migration_dirty_pages;
+static uint32_t last_version;
+static bool ram_bulk_stage;
+
+struct CompressParam {
+ bool start;
+ bool done;
+ QEMUFile *file;
+ QemuMutex mutex;
+ QemuCond cond;
+ RAMBlock *block;
+ ram_addr_t offset;
+};
+typedef struct CompressParam CompressParam;
+
+struct DecompressParam {
+ bool start;
+ QemuMutex mutex;
+ QemuCond cond;
+ void *des;
+ uint8 *compbuf;
+ int len;
+};
+typedef struct DecompressParam DecompressParam;
+
+static CompressParam *comp_param;
+static QemuThread *compress_threads;
+/* comp_done_cond is used to wake up the migration thread when
+ * one of the compression threads has finished the compression.
+ * comp_done_lock is used to co-work with comp_done_cond.
+ */
+static QemuMutex *comp_done_lock;
+static QemuCond *comp_done_cond;
+/* The empty QEMUFileOps will be used by file in CompressParam */
+static const QEMUFileOps empty_ops = { };
+
+static bool compression_switch;
+static bool quit_comp_thread;
+static bool quit_decomp_thread;
+static DecompressParam *decomp_param;
+static QemuThread *decompress_threads;
+static uint8_t *compressed_data_buf;
+
+static int do_compress_ram_page(CompressParam *param);
+
+static void *do_data_compress(void *opaque)
+{
+ CompressParam *param = opaque;
+
+ while (!quit_comp_thread) {
+ qemu_mutex_lock(&param->mutex);
+ /* Re-check the quit_comp_thread in case of
+ * terminate_compression_threads is called just before
+ * qemu_mutex_lock(&param->mutex) and after
+ * while(!quit_comp_thread), re-check it here can make
+ * sure the compression thread terminate as expected.
+ */
+ while (!param->start && !quit_comp_thread) {
+ qemu_cond_wait(&param->cond, &param->mutex);
+ }
+ if (!quit_comp_thread) {
+ do_compress_ram_page(param);
+ }
+ param->start = false;
+ qemu_mutex_unlock(&param->mutex);
+
+ qemu_mutex_lock(comp_done_lock);
+ param->done = true;
+ qemu_cond_signal(comp_done_cond);
+ qemu_mutex_unlock(comp_done_lock);
+ }
+
+ return NULL;
+}
+
+static inline void terminate_compression_threads(void)
+{
+ int idx, thread_count;
+
+ thread_count = migrate_compress_threads();
+ quit_comp_thread = true;
+ for (idx = 0; idx < thread_count; idx++) {
+ qemu_mutex_lock(&comp_param[idx].mutex);
+ qemu_cond_signal(&comp_param[idx].cond);
+ qemu_mutex_unlock(&comp_param[idx].mutex);
+ }
+}
+
+void migrate_compress_threads_join(void)
+{
+ int i, thread_count;
+
+ if (!migrate_use_compression()) {
+ return;
+ }
+ terminate_compression_threads();
+ thread_count = migrate_compress_threads();
+ for (i = 0; i < thread_count; i++) {
+ qemu_thread_join(compress_threads + i);
+ qemu_fclose(comp_param[i].file);
+ qemu_mutex_destroy(&comp_param[i].mutex);
+ qemu_cond_destroy(&comp_param[i].cond);
+ }
+ qemu_mutex_destroy(comp_done_lock);
+ qemu_cond_destroy(comp_done_cond);
+ g_free(compress_threads);
+ g_free(comp_param);
+ g_free(comp_done_cond);
+ g_free(comp_done_lock);
+ compress_threads = NULL;
+ comp_param = NULL;
+ comp_done_cond = NULL;
+ comp_done_lock = NULL;
+}
+
+void migrate_compress_threads_create(void)
+{
+ int i, thread_count;
+
+ if (!migrate_use_compression()) {
+ return;
+ }
+ quit_comp_thread = false;
+ compression_switch = true;
+ thread_count = migrate_compress_threads();
+ compress_threads = g_new0(QemuThread, thread_count);
+ comp_param = g_new0(CompressParam, thread_count);
+ comp_done_cond = g_new0(QemuCond, 1);
+ comp_done_lock = g_new0(QemuMutex, 1);
+ qemu_cond_init(comp_done_cond);
+ qemu_mutex_init(comp_done_lock);
+ for (i = 0; i < thread_count; i++) {
+ /* com_param[i].file is just used as a dummy buffer to save data, set
+ * it's ops to empty.
+ */
+ comp_param[i].file = qemu_fopen_ops(NULL, &empty_ops);
+ comp_param[i].done = true;
+ qemu_mutex_init(&comp_param[i].mutex);
+ qemu_cond_init(&comp_param[i].cond);
+ qemu_thread_create(compress_threads + i, "compress",
+ do_data_compress, comp_param + i,
+ QEMU_THREAD_JOINABLE);
+ }
+}
+
+/**
+ * save_page_header: Write page header to wire
+ *
+ * If this is the 1st block, it also writes the block identification
+ *
+ * Returns: Number of bytes written
+ *
+ * @f: QEMUFile where to send the data
+ * @block: block that contains the page we want to send
+ * @offset: offset inside the block for the page
+ * in the lower bits, it contains flags
+ */
+static size_t save_page_header(QEMUFile *f, RAMBlock *block, ram_addr_t offset)
+{
+ size_t size;
+
+ qemu_put_be64(f, offset);
+ size = 8;
+
+ if (!(offset & RAM_SAVE_FLAG_CONTINUE)) {
+ qemu_put_byte(f, strlen(block->idstr));
+ qemu_put_buffer(f, (uint8_t *)block->idstr,
+ strlen(block->idstr));
+ size += 1 + strlen(block->idstr);
+ }
+ return size;
+}
+
+/* Update the xbzrle cache to reflect a page that's been sent as all 0.
+ * The important thing is that a stale (not-yet-0'd) page be replaced
+ * by the new data.
+ * As a bonus, if the page wasn't in the cache it gets added so that
+ * when a small write is made into the 0'd page it gets XBZRLE sent
+ */
+static void xbzrle_cache_zero_page(ram_addr_t current_addr)
+{
+ if (ram_bulk_stage || !migrate_use_xbzrle()) {
+ return;
+ }
+
+ /* We don't care if this fails to allocate a new cache page
+ * as long as it updated an old one */
+ cache_insert(XBZRLE.cache, current_addr, ZERO_TARGET_PAGE,
+ bitmap_sync_count);
+}
+
+#define ENCODING_FLAG_XBZRLE 0x1
+
+/**
+ * save_xbzrle_page: compress and send current page
+ *
+ * Returns: 1 means that we wrote the page
+ * 0 means that page is identical to the one already sent
+ * -1 means that xbzrle would be longer than normal
+ *
+ * @f: QEMUFile where to send the data
+ * @current_data:
+ * @current_addr:
+ * @block: block that contains the page we want to send
+ * @offset: offset inside the block for the page
+ * @last_stage: if we are at the completion stage
+ * @bytes_transferred: increase it with the number of transferred bytes
+ */
+static int save_xbzrle_page(QEMUFile *f, uint8_t **current_data,
+ ram_addr_t current_addr, RAMBlock *block,
+ ram_addr_t offset, bool last_stage,
+ uint64_t *bytes_transferred)
+{
+ int encoded_len = 0, bytes_xbzrle;
+ uint8_t *prev_cached_page;
+
+ if (!cache_is_cached(XBZRLE.cache, current_addr, bitmap_sync_count)) {
+ acct_info.xbzrle_cache_miss++;
+ if (!last_stage) {
+ if (cache_insert(XBZRLE.cache, current_addr, *current_data,
+ bitmap_sync_count) == -1) {
+ return -1;
+ } else {
+ /* update *current_data when the page has been
+ inserted into cache */
+ *current_data = get_cached_data(XBZRLE.cache, current_addr);
+ }
+ }
+ return -1;
+ }
+
+ prev_cached_page = get_cached_data(XBZRLE.cache, current_addr);
+
+ /* save current buffer into memory */
+ memcpy(XBZRLE.current_buf, *current_data, TARGET_PAGE_SIZE);
+
+ /* XBZRLE encoding (if there is no overflow) */
+ encoded_len = xbzrle_encode_buffer(prev_cached_page, XBZRLE.current_buf,
+ TARGET_PAGE_SIZE, XBZRLE.encoded_buf,
+ TARGET_PAGE_SIZE);
+ if (encoded_len == 0) {
+ DPRINTF("Skipping unmodified page\n");
+ return 0;
+ } else if (encoded_len == -1) {
+ DPRINTF("Overflow\n");
+ acct_info.xbzrle_overflows++;
+ /* update data in the cache */
+ if (!last_stage) {
+ memcpy(prev_cached_page, *current_data, TARGET_PAGE_SIZE);
+ *current_data = prev_cached_page;
+ }
+ return -1;
+ }
+
+ /* we need to update the data in the cache, in order to get the same data */
+ if (!last_stage) {
+ memcpy(prev_cached_page, XBZRLE.current_buf, TARGET_PAGE_SIZE);
+ }
+
+ /* Send XBZRLE based compressed page */
+ bytes_xbzrle = save_page_header(f, block, offset | RAM_SAVE_FLAG_XBZRLE);
+ qemu_put_byte(f, ENCODING_FLAG_XBZRLE);
+ qemu_put_be16(f, encoded_len);
+ qemu_put_buffer(f, XBZRLE.encoded_buf, encoded_len);
+ bytes_xbzrle += encoded_len + 1 + 2;
+ acct_info.xbzrle_pages++;
+ acct_info.xbzrle_bytes += bytes_xbzrle;
+ *bytes_transferred += bytes_xbzrle;
+
+ return 1;
+}
+
+static inline
+ram_addr_t migration_bitmap_find_and_reset_dirty(MemoryRegion *mr,
+ ram_addr_t start)
+{
+ unsigned long base = mr->ram_addr >> TARGET_PAGE_BITS;
+ unsigned long nr = base + (start >> TARGET_PAGE_BITS);
+ uint64_t mr_size = TARGET_PAGE_ALIGN(memory_region_size(mr));
+ unsigned long size = base + (mr_size >> TARGET_PAGE_BITS);
+
+ unsigned long next;
+
+ if (ram_bulk_stage && nr > base) {
+ next = nr + 1;
+ } else {
+ next = find_next_bit(migration_bitmap, size, nr);
+ }
+
+ if (next < size) {
+ clear_bit(next, migration_bitmap);
+ migration_dirty_pages--;
+ }
+ return (next - base) << TARGET_PAGE_BITS;
+}
+
+static void migration_bitmap_sync_range(ram_addr_t start, ram_addr_t length)
+{
+ migration_dirty_pages +=
+ cpu_physical_memory_sync_dirty_bitmap(migration_bitmap, start, length);
+}
+
+
+/* Fix me: there are too many global variables used in migration process. */
+static int64_t start_time;
+static int64_t bytes_xfer_prev;
+static int64_t num_dirty_pages_period;
+static uint64_t xbzrle_cache_miss_prev;
+static uint64_t iterations_prev;
+
+static void migration_bitmap_sync_init(void)
+{
+ start_time = 0;
+ bytes_xfer_prev = 0;
+ num_dirty_pages_period = 0;
+ xbzrle_cache_miss_prev = 0;
+ iterations_prev = 0;
+}
+
+/* Called with iothread lock held, to protect ram_list.dirty_memory[] */
+static void migration_bitmap_sync(void)
+{
+ RAMBlock *block;
+ uint64_t num_dirty_pages_init = migration_dirty_pages;
+ MigrationState *s = migrate_get_current();
+ int64_t end_time;
+ int64_t bytes_xfer_now;
+
+ bitmap_sync_count++;
+
+ if (!bytes_xfer_prev) {
+ bytes_xfer_prev = ram_bytes_transferred();
+ }
+
+ if (!start_time) {
+ start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
+ }
+
+ trace_migration_bitmap_sync_start();
+ address_space_sync_dirty_bitmap(&address_space_memory);
+
+ rcu_read_lock();
+ QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
+ migration_bitmap_sync_range(block->mr->ram_addr, block->used_length);
+ }
+ rcu_read_unlock();
+
+ trace_migration_bitmap_sync_end(migration_dirty_pages
+ - num_dirty_pages_init);
+ num_dirty_pages_period += migration_dirty_pages - num_dirty_pages_init;
+ end_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
+
+ /* more than 1 second = 1000 millisecons */
+ if (end_time > start_time + 1000) {
+ if (migrate_auto_converge()) {
+ /* The following detection logic can be refined later. For now:
+ Check to see if the dirtied bytes is 50% more than the approx.
+ amount of bytes that just got transferred since the last time we
+ were in this routine. If that happens >N times (for now N==4)
+ we turn on the throttle down logic */
+ bytes_xfer_now = ram_bytes_transferred();
+ if (s->dirty_pages_rate &&
+ (num_dirty_pages_period * TARGET_PAGE_SIZE >
+ (bytes_xfer_now - bytes_xfer_prev)/2) &&
+ (dirty_rate_high_cnt++ > 4)) {
+ trace_migration_throttle();
+ mig_throttle_on = true;
+ dirty_rate_high_cnt = 0;
+ }
+ bytes_xfer_prev = bytes_xfer_now;
+ } else {
+ mig_throttle_on = false;
+ }
+ if (migrate_use_xbzrle()) {
+ if (iterations_prev != acct_info.iterations) {
+ acct_info.xbzrle_cache_miss_rate =
+ (double)(acct_info.xbzrle_cache_miss -
+ xbzrle_cache_miss_prev) /
+ (acct_info.iterations - iterations_prev);
+ }
+ iterations_prev = acct_info.iterations;
+ xbzrle_cache_miss_prev = acct_info.xbzrle_cache_miss;
+ }
+ s->dirty_pages_rate = num_dirty_pages_period * 1000
+ / (end_time - start_time);
+ s->dirty_bytes_rate = s->dirty_pages_rate * TARGET_PAGE_SIZE;
+ start_time = end_time;
+ num_dirty_pages_period = 0;
+ }
+ s->dirty_sync_count = bitmap_sync_count;
+}
+
+/**
+ * save_zero_page: Send the zero page to the stream
+ *
+ * Returns: Number of pages written.
+ *
+ * @f: QEMUFile where to send the data
+ * @block: block that contains the page we want to send
+ * @offset: offset inside the block for the page
+ * @p: pointer to the page
+ * @bytes_transferred: increase it with the number of transferred bytes
+ */
+static int save_zero_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset,
+ uint8_t *p, uint64_t *bytes_transferred)
+{
+ int pages = -1;
+
+ if (is_zero_range(p, TARGET_PAGE_SIZE)) {
+ acct_info.dup_pages++;
+ *bytes_transferred += save_page_header(f, block,
+ offset | RAM_SAVE_FLAG_COMPRESS);
+ qemu_put_byte(f, 0);
+ *bytes_transferred += 1;
+ pages = 1;
+ }
+
+ return pages;
+}
+
+/**
+ * ram_save_page: Send the given page to the stream
+ *
+ * Returns: Number of pages written.
+ *
+ * @f: QEMUFile where to send the data
+ * @block: block that contains the page we want to send
+ * @offset: offset inside the block for the page
+ * @last_stage: if we are at the completion stage
+ * @bytes_transferred: increase it with the number of transferred bytes
+ */
+static int ram_save_page(QEMUFile *f, RAMBlock* block, ram_addr_t offset,
+ bool last_stage, uint64_t *bytes_transferred)
+{
+ int pages = -1;
+ uint64_t bytes_xmit;
+ ram_addr_t current_addr;
+ MemoryRegion *mr = block->mr;
+ uint8_t *p;
+ int ret;
+ bool send_async = true;
+
+ p = memory_region_get_ram_ptr(mr) + offset;
+
+ /* In doubt sent page as normal */
+ bytes_xmit = 0;
+ ret = ram_control_save_page(f, block->offset,
+ offset, TARGET_PAGE_SIZE, &bytes_xmit);
+ if (bytes_xmit) {
+ *bytes_transferred += bytes_xmit;
+ pages = 1;
+ }
+
+ XBZRLE_cache_lock();
+
+ current_addr = block->offset + offset;
+
+ if (block == last_sent_block) {
+ offset |= RAM_SAVE_FLAG_CONTINUE;
+ }
+ if (ret != RAM_SAVE_CONTROL_NOT_SUPP) {
+ if (ret != RAM_SAVE_CONTROL_DELAYED) {
+ if (bytes_xmit > 0) {
+ acct_info.norm_pages++;
+ } else if (bytes_xmit == 0) {
+ acct_info.dup_pages++;
+ }
+ }
+ } else {
+ pages = save_zero_page(f, block, offset, p, bytes_transferred);
+ if (pages > 0) {
+ /* Must let xbzrle know, otherwise a previous (now 0'd) cached
+ * page would be stale
+ */
+ xbzrle_cache_zero_page(current_addr);
+ } else if (!ram_bulk_stage && migrate_use_xbzrle()) {
+ pages = save_xbzrle_page(f, &p, current_addr, block,
+ offset, last_stage, bytes_transferred);
+ if (!last_stage) {
+ /* Can't send this cached data async, since the cache page
+ * might get updated before it gets to the wire
+ */
+ send_async = false;
+ }
+ }
+ }
+
+ /* XBZRLE overflow or normal page */
+ if (pages == -1) {
+ *bytes_transferred += save_page_header(f, block,
+ offset | RAM_SAVE_FLAG_PAGE);
+ if (send_async) {
+ qemu_put_buffer_async(f, p, TARGET_PAGE_SIZE);
+ } else {
+ qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
+ }
+ *bytes_transferred += TARGET_PAGE_SIZE;
+ pages = 1;
+ acct_info.norm_pages++;
+ }
+
+ XBZRLE_cache_unlock();
+
+ return pages;
+}
+
+static int do_compress_ram_page(CompressParam *param)
+{
+ int bytes_sent, blen;
+ uint8_t *p;
+ RAMBlock *block = param->block;
+ ram_addr_t offset = param->offset;
+
+ p = memory_region_get_ram_ptr(block->mr) + (offset & TARGET_PAGE_MASK);
+
+ bytes_sent = save_page_header(param->file, block, offset |
+ RAM_SAVE_FLAG_COMPRESS_PAGE);
+ blen = qemu_put_compression_data(param->file, p, TARGET_PAGE_SIZE,
+ migrate_compress_level());
+ bytes_sent += blen;
+
+ return bytes_sent;
+}
+
+static inline void start_compression(CompressParam *param)
+{
+ param->done = false;
+ qemu_mutex_lock(&param->mutex);
+ param->start = true;
+ qemu_cond_signal(&param->cond);
+ qemu_mutex_unlock(&param->mutex);
+}
+
+static inline void start_decompression(DecompressParam *param)
+{
+ qemu_mutex_lock(&param->mutex);
+ param->start = true;
+ qemu_cond_signal(&param->cond);
+ qemu_mutex_unlock(&param->mutex);
+}
+
+static uint64_t bytes_transferred;
+
+static void flush_compressed_data(QEMUFile *f)
+{
+ int idx, len, thread_count;
+
+ if (!migrate_use_compression()) {
+ return;
+ }
+ thread_count = migrate_compress_threads();
+ for (idx = 0; idx < thread_count; idx++) {
+ if (!comp_param[idx].done) {
+ qemu_mutex_lock(comp_done_lock);
+ while (!comp_param[idx].done && !quit_comp_thread) {
+ qemu_cond_wait(comp_done_cond, comp_done_lock);
+ }
+ qemu_mutex_unlock(comp_done_lock);
+ }
+ if (!quit_comp_thread) {
+ len = qemu_put_qemu_file(f, comp_param[idx].file);
+ bytes_transferred += len;
+ }
+ }
+}
+
+static inline void set_compress_params(CompressParam *param, RAMBlock *block,
+ ram_addr_t offset)
+{
+ param->block = block;
+ param->offset = offset;
+}
+
+static int compress_page_with_multi_thread(QEMUFile *f, RAMBlock *block,
+ ram_addr_t offset,
+ uint64_t *bytes_transferred)
+{
+ int idx, thread_count, bytes_xmit = -1, pages = -1;
+
+ thread_count = migrate_compress_threads();
+ qemu_mutex_lock(comp_done_lock);
+ while (true) {
+ for (idx = 0; idx < thread_count; idx++) {
+ if (comp_param[idx].done) {
+ bytes_xmit = qemu_put_qemu_file(f, comp_param[idx].file);
+ set_compress_params(&comp_param[idx], block, offset);
+ start_compression(&comp_param[idx]);
+ pages = 1;
+ acct_info.norm_pages++;
+ *bytes_transferred += bytes_xmit;
+ break;
+ }
+ }
+ if (pages > 0) {
+ break;
+ } else {
+ qemu_cond_wait(comp_done_cond, comp_done_lock);
+ }
+ }
+ qemu_mutex_unlock(comp_done_lock);
+
+ return pages;
+}
+
+/**
+ * ram_save_compressed_page: compress the given page and send it to the stream
+ *
+ * Returns: Number of pages written.
+ *
+ * @f: QEMUFile where to send the data
+ * @block: block that contains the page we want to send
+ * @offset: offset inside the block for the page
+ * @last_stage: if we are at the completion stage
+ * @bytes_transferred: increase it with the number of transferred bytes
+ */
+static int ram_save_compressed_page(QEMUFile *f, RAMBlock *block,
+ ram_addr_t offset, bool last_stage,
+ uint64_t *bytes_transferred)
+{
+ int pages = -1;
+ uint64_t bytes_xmit;
+ MemoryRegion *mr = block->mr;
+ uint8_t *p;
+ int ret;
+
+ p = memory_region_get_ram_ptr(mr) + offset;
+
+ bytes_xmit = 0;
+ ret = ram_control_save_page(f, block->offset,
+ offset, TARGET_PAGE_SIZE, &bytes_xmit);
+ if (bytes_xmit) {
+ *bytes_transferred += bytes_xmit;
+ pages = 1;
+ }
+ if (block == last_sent_block) {
+ offset |= RAM_SAVE_FLAG_CONTINUE;
+ }
+ if (ret != RAM_SAVE_CONTROL_NOT_SUPP) {
+ if (ret != RAM_SAVE_CONTROL_DELAYED) {
+ if (bytes_xmit > 0) {
+ acct_info.norm_pages++;
+ } else if (bytes_xmit == 0) {
+ acct_info.dup_pages++;
+ }
+ }
+ } else {
+ /* When starting the process of a new block, the first page of
+ * the block should be sent out before other pages in the same
+ * block, and all the pages in last block should have been sent
+ * out, keeping this order is important, because the 'cont' flag
+ * is used to avoid resending the block name.
+ */
+ if (block != last_sent_block) {
+ flush_compressed_data(f);
+ pages = save_zero_page(f, block, offset, p, bytes_transferred);
+ if (pages == -1) {
+ set_compress_params(&comp_param[0], block, offset);
+ /* Use the qemu thread to compress the data to make sure the
+ * first page is sent out before other pages
+ */
+ bytes_xmit = do_compress_ram_page(&comp_param[0]);
+ acct_info.norm_pages++;
+ qemu_put_qemu_file(f, comp_param[0].file);
+ *bytes_transferred += bytes_xmit;
+ pages = 1;
+ }
+ } else {
+ pages = save_zero_page(f, block, offset, p, bytes_transferred);
+ if (pages == -1) {
+ pages = compress_page_with_multi_thread(f, block, offset,
+ bytes_transferred);
+ }
+ }
+ }
+
+ return pages;
+}
+
+/**
+ * ram_find_and_save_block: Finds a dirty page and sends it to f
+ *
+ * Called within an RCU critical section.
+ *
+ * Returns: The number of pages written
+ * 0 means no dirty pages
+ *
+ * @f: QEMUFile where to send the data
+ * @last_stage: if we are at the completion stage
+ * @bytes_transferred: increase it with the number of transferred bytes
+ */
+
+static int ram_find_and_save_block(QEMUFile *f, bool last_stage,
+ uint64_t *bytes_transferred)
+{
+ RAMBlock *block = last_seen_block;
+ ram_addr_t offset = last_offset;
+ bool complete_round = false;
+ int pages = 0;
+ MemoryRegion *mr;
+
+ if (!block)
+ block = QLIST_FIRST_RCU(&ram_list.blocks);
+
+ while (true) {
+ mr = block->mr;
+ offset = migration_bitmap_find_and_reset_dirty(mr, offset);
+ if (complete_round && block == last_seen_block &&
+ offset >= last_offset) {
+ break;
+ }
+ if (offset >= block->used_length) {
+ offset = 0;
+ block = QLIST_NEXT_RCU(block, next);
+ if (!block) {
+ block = QLIST_FIRST_RCU(&ram_list.blocks);
+ complete_round = true;
+ ram_bulk_stage = false;
+ if (migrate_use_xbzrle()) {
+ /* If xbzrle is on, stop using the data compression at this
+ * point. In theory, xbzrle can do better than compression.
+ */
+ flush_compressed_data(f);
+ compression_switch = false;
+ }
+ }
+ } else {
+ if (compression_switch && migrate_use_compression()) {
+ pages = ram_save_compressed_page(f, block, offset, last_stage,
+ bytes_transferred);
+ } else {
+ pages = ram_save_page(f, block, offset, last_stage,
+ bytes_transferred);
+ }
+
+ /* if page is unmodified, continue to the next */
+ if (pages > 0) {
+ last_sent_block = block;
+ break;
+ }
+ }
+ }
+
+ last_seen_block = block;
+ last_offset = offset;
+
+ return pages;
+}
+
+void acct_update_position(QEMUFile *f, size_t size, bool zero)
+{
+ uint64_t pages = size / TARGET_PAGE_SIZE;
+ if (zero) {
+ acct_info.dup_pages += pages;
+ } else {
+ acct_info.norm_pages += pages;
+ bytes_transferred += size;
+ qemu_update_position(f, size);
+ }
+}
+
+static ram_addr_t ram_save_remaining(void)
+{
+ return migration_dirty_pages;
+}
+
+uint64_t ram_bytes_remaining(void)
+{
+ return ram_save_remaining() * TARGET_PAGE_SIZE;
+}
+
+uint64_t ram_bytes_transferred(void)
+{
+ return bytes_transferred;
+}
+
+uint64_t ram_bytes_total(void)
+{
+ RAMBlock *block;
+ uint64_t total = 0;
+
+ rcu_read_lock();
+ QLIST_FOREACH_RCU(block, &ram_list.blocks, next)
+ total += block->used_length;
+ rcu_read_unlock();
+ return total;
+}
+
+void free_xbzrle_decoded_buf(void)
+{
+ g_free(xbzrle_decoded_buf);
+ xbzrle_decoded_buf = NULL;
+}
+
+static void migration_end(void)
+{
+ if (migration_bitmap) {
+ memory_global_dirty_log_stop();
+ g_free(migration_bitmap);
+ migration_bitmap = NULL;
+ }
+
+ XBZRLE_cache_lock();
+ if (XBZRLE.cache) {
+ cache_fini(XBZRLE.cache);
+ g_free(XBZRLE.encoded_buf);
+ g_free(XBZRLE.current_buf);
+ XBZRLE.cache = NULL;
+ XBZRLE.encoded_buf = NULL;
+ XBZRLE.current_buf = NULL;
+ }
+ XBZRLE_cache_unlock();
+}
+
+static void ram_migration_cancel(void *opaque)
+{
+ migration_end();
+}
+
+static void reset_ram_globals(void)
+{
+ last_seen_block = NULL;
+ last_sent_block = NULL;
+ last_offset = 0;
+ last_version = ram_list.version;
+ ram_bulk_stage = true;
+}
+
+#define MAX_WAIT 50 /* ms, half buffered_file limit */
+
+
+/* Each of ram_save_setup, ram_save_iterate and ram_save_complete has
+ * long-running RCU critical section. When rcu-reclaims in the code
+ * start to become numerous it will be necessary to reduce the
+ * granularity of these critical sections.
+ */
+
+static int ram_save_setup(QEMUFile *f, void *opaque)
+{
+ RAMBlock *block;
+ int64_t ram_bitmap_pages; /* Size of bitmap in pages, including gaps */
+
+ mig_throttle_on = false;
+ dirty_rate_high_cnt = 0;
+ bitmap_sync_count = 0;
+ migration_bitmap_sync_init();
+
+ if (migrate_use_xbzrle()) {
+ XBZRLE_cache_lock();
+ XBZRLE.cache = cache_init(migrate_xbzrle_cache_size() /
+ TARGET_PAGE_SIZE,
+ TARGET_PAGE_SIZE);
+ if (!XBZRLE.cache) {
+ XBZRLE_cache_unlock();
+ error_report("Error creating cache");
+ return -1;
+ }
+ XBZRLE_cache_unlock();
+
+ /* We prefer not to abort if there is no memory */
+ XBZRLE.encoded_buf = g_try_malloc0(TARGET_PAGE_SIZE);
+ if (!XBZRLE.encoded_buf) {
+ error_report("Error allocating encoded_buf");
+ return -1;
+ }
+
+ XBZRLE.current_buf = g_try_malloc(TARGET_PAGE_SIZE);
+ if (!XBZRLE.current_buf) {
+ error_report("Error allocating current_buf");
+ g_free(XBZRLE.encoded_buf);
+ XBZRLE.encoded_buf = NULL;
+ return -1;
+ }
+
+ acct_clear();
+ }
+
+ /* iothread lock needed for ram_list.dirty_memory[] */
+ qemu_mutex_lock_iothread();
+ qemu_mutex_lock_ramlist();
+ rcu_read_lock();
+ bytes_transferred = 0;
+ reset_ram_globals();
+
+ ram_bitmap_pages = last_ram_offset() >> TARGET_PAGE_BITS;
+ migration_bitmap = bitmap_new(ram_bitmap_pages);
+ bitmap_set(migration_bitmap, 0, ram_bitmap_pages);
+
+ /*
+ * Count the total number of pages used by ram blocks not including any
+ * gaps due to alignment or unplugs.
+ */
+ migration_dirty_pages = ram_bytes_total() >> TARGET_PAGE_BITS;
+
+ memory_global_dirty_log_start();
+ migration_bitmap_sync();
+ qemu_mutex_unlock_ramlist();
+ qemu_mutex_unlock_iothread();
+
+ qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE);
+
+ QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
+ qemu_put_byte(f, strlen(block->idstr));
+ qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr));
+ qemu_put_be64(f, block->used_length);
+ }
+
+ rcu_read_unlock();
+
+ ram_control_before_iterate(f, RAM_CONTROL_SETUP);
+ ram_control_after_iterate(f, RAM_CONTROL_SETUP);
+
+ qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
+
+ return 0;
+}
+
+static int ram_save_iterate(QEMUFile *f, void *opaque)
+{
+ int ret;
+ int i;
+ int64_t t0;
+ int pages_sent = 0;
+
+ rcu_read_lock();
+ if (ram_list.version != last_version) {
+ reset_ram_globals();
+ }
+
+ /* Read version before ram_list.blocks */
+ smp_rmb();
+
+ ram_control_before_iterate(f, RAM_CONTROL_ROUND);
+
+ t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
+ i = 0;
+ while ((ret = qemu_file_rate_limit(f)) == 0) {
+ int pages;
+
+ pages = ram_find_and_save_block(f, false, &bytes_transferred);
+ /* no more pages to sent */
+ if (pages == 0) {
+ break;
+ }
+ pages_sent += pages;
+ acct_info.iterations++;
+ check_guest_throttling();
+ /* we want to check in the 1st loop, just in case it was the 1st time
+ and we had to sync the dirty bitmap.
+ qemu_get_clock_ns() is a bit expensive, so we only check each some
+ iterations
+ */
+ if ((i & 63) == 0) {
+ uint64_t t1 = (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - t0) / 1000000;
+ if (t1 > MAX_WAIT) {
+ DPRINTF("big wait: %" PRIu64 " milliseconds, %d iterations\n",
+ t1, i);
+ break;
+ }
+ }
+ i++;
+ }
+ flush_compressed_data(f);
+ rcu_read_unlock();
+
+ /*
+ * Must occur before EOS (or any QEMUFile operation)
+ * because of RDMA protocol.
+ */
+ ram_control_after_iterate(f, RAM_CONTROL_ROUND);
+
+ qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
+ bytes_transferred += 8;
+
+ ret = qemu_file_get_error(f);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return pages_sent;
+}
+
+/* Called with iothread lock */
+static int ram_save_complete(QEMUFile *f, void *opaque)
+{
+ rcu_read_lock();
+
+ migration_bitmap_sync();
+
+ ram_control_before_iterate(f, RAM_CONTROL_FINISH);
+
+ /* try transferring iterative blocks of memory */
+
+ /* flush all remaining blocks regardless of rate limiting */
+ while (true) {
+ int pages;
+
+ pages = ram_find_and_save_block(f, true, &bytes_transferred);
+ /* no more blocks to sent */
+ if (pages == 0) {
+ break;
+ }
+ }
+
+ flush_compressed_data(f);
+ ram_control_after_iterate(f, RAM_CONTROL_FINISH);
+ migration_end();
+
+ rcu_read_unlock();
+ qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
+
+ return 0;
+}
+
+static uint64_t ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size)
+{
+ uint64_t remaining_size;
+
+ remaining_size = ram_save_remaining() * TARGET_PAGE_SIZE;
+
+ if (remaining_size < max_size) {
+ qemu_mutex_lock_iothread();
+ rcu_read_lock();
+ migration_bitmap_sync();
+ rcu_read_unlock();
+ qemu_mutex_unlock_iothread();
+ remaining_size = ram_save_remaining() * TARGET_PAGE_SIZE;
+ }
+ return remaining_size;
+}
+
+static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host)
+{
+ unsigned int xh_len;
+ int xh_flags;
+
+ if (!xbzrle_decoded_buf) {
+ xbzrle_decoded_buf = g_malloc(TARGET_PAGE_SIZE);
+ }
+
+ /* extract RLE header */
+ xh_flags = qemu_get_byte(f);
+ xh_len = qemu_get_be16(f);
+
+ if (xh_flags != ENCODING_FLAG_XBZRLE) {
+ error_report("Failed to load XBZRLE page - wrong compression!");
+ return -1;
+ }
+
+ if (xh_len > TARGET_PAGE_SIZE) {
+ error_report("Failed to load XBZRLE page - len overflow!");
+ return -1;
+ }
+ /* load data and decode */
+ qemu_get_buffer(f, xbzrle_decoded_buf, xh_len);
+
+ /* decode RLE */
+ if (xbzrle_decode_buffer(xbzrle_decoded_buf, xh_len, host,
+ TARGET_PAGE_SIZE) == -1) {
+ error_report("Failed to load XBZRLE page - decode error!");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Must be called from within a rcu critical section.
+ * Returns a pointer from within the RCU-protected ram_list.
+ */
+static inline void *host_from_stream_offset(QEMUFile *f,
+ ram_addr_t offset,
+ int flags)
+{
+ static RAMBlock *block = NULL;
+ char id[256];
+ uint8_t len;
+
+ if (flags & RAM_SAVE_FLAG_CONTINUE) {
+ if (!block || block->max_length <= offset) {
+ error_report("Ack, bad migration stream!");
+ return NULL;
+ }
+
+ return memory_region_get_ram_ptr(block->mr) + offset;
+ }
+
+ len = qemu_get_byte(f);
+ qemu_get_buffer(f, (uint8_t *)id, len);
+ id[len] = 0;
+
+ QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
+ if (!strncmp(id, block->idstr, sizeof(id)) &&
+ block->max_length > offset) {
+ return memory_region_get_ram_ptr(block->mr) + offset;
+ }
+ }
+
+ error_report("Can't find block %s!", id);
+ return NULL;
+}
+
+/*
+ * If a page (or a whole RDMA chunk) has been
+ * determined to be zero, then zap it.
+ */
+void ram_handle_compressed(void *host, uint8_t ch, uint64_t size)
+{
+ if (ch != 0 || !is_zero_range(host, size)) {
+ memset(host, ch, size);
+ }
+}
+
+static void *do_data_decompress(void *opaque)
+{
+ DecompressParam *param = opaque;
+ unsigned long pagesize;
+
+ while (!quit_decomp_thread) {
+ qemu_mutex_lock(&param->mutex);
+ while (!param->start && !quit_decomp_thread) {
+ qemu_cond_wait(&param->cond, &param->mutex);
+ pagesize = TARGET_PAGE_SIZE;
+ if (!quit_decomp_thread) {
+ /* uncompress() will return failed in some case, especially
+ * when the page is dirted when doing the compression, it's
+ * not a problem because the dirty page will be retransferred
+ * and uncompress() won't break the data in other pages.
+ */
+ uncompress((Bytef *)param->des, &pagesize,
+ (const Bytef *)param->compbuf, param->len);
+ }
+ param->start = false;
+ }
+ qemu_mutex_unlock(&param->mutex);
+ }
+
+ return NULL;
+}
+
+void migrate_decompress_threads_create(void)
+{
+ int i, thread_count;
+
+ thread_count = migrate_decompress_threads();
+ decompress_threads = g_new0(QemuThread, thread_count);
+ decomp_param = g_new0(DecompressParam, thread_count);
+ compressed_data_buf = g_malloc0(compressBound(TARGET_PAGE_SIZE));
+ quit_decomp_thread = false;
+ for (i = 0; i < thread_count; i++) {
+ qemu_mutex_init(&decomp_param[i].mutex);
+ qemu_cond_init(&decomp_param[i].cond);
+ decomp_param[i].compbuf = g_malloc0(compressBound(TARGET_PAGE_SIZE));
+ qemu_thread_create(decompress_threads + i, "decompress",
+ do_data_decompress, decomp_param + i,
+ QEMU_THREAD_JOINABLE);
+ }
+}
+
+void migrate_decompress_threads_join(void)
+{
+ int i, thread_count;
+
+ quit_decomp_thread = true;
+ thread_count = migrate_decompress_threads();
+ for (i = 0; i < thread_count; i++) {
+ qemu_mutex_lock(&decomp_param[i].mutex);
+ qemu_cond_signal(&decomp_param[i].cond);
+ qemu_mutex_unlock(&decomp_param[i].mutex);
+ }
+ for (i = 0; i < thread_count; i++) {
+ qemu_thread_join(decompress_threads + i);
+ qemu_mutex_destroy(&decomp_param[i].mutex);
+ qemu_cond_destroy(&decomp_param[i].cond);
+ g_free(decomp_param[i].compbuf);
+ }
+ g_free(decompress_threads);
+ g_free(decomp_param);
+ g_free(compressed_data_buf);
+ decompress_threads = NULL;
+ decomp_param = NULL;
+ compressed_data_buf = NULL;
+}
+
+static void decompress_data_with_multi_threads(uint8_t *compbuf,
+ void *host, int len)
+{
+ int idx, thread_count;
+
+ thread_count = migrate_decompress_threads();
+ while (true) {
+ for (idx = 0; idx < thread_count; idx++) {
+ if (!decomp_param[idx].start) {
+ memcpy(decomp_param[idx].compbuf, compbuf, len);
+ decomp_param[idx].des = host;
+ decomp_param[idx].len = len;
+ start_decompression(&decomp_param[idx]);
+ break;
+ }
+ }
+ if (idx < thread_count) {
+ break;
+ }
+ }
+}
+
+static int ram_load(QEMUFile *f, void *opaque, int version_id)
+{
+ int flags = 0, ret = 0;
+ static uint64_t seq_iter;
+ int len = 0;
+
+ seq_iter++;
+
+ if (version_id != 4) {
+ ret = -EINVAL;
+ }
+
+ /* This RCU critical section can be very long running.
+ * When RCU reclaims in the code start to become numerous,
+ * it will be necessary to reduce the granularity of this
+ * critical section.
+ */
+ rcu_read_lock();
+ while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) {
+ ram_addr_t addr, total_ram_bytes;
+ void *host;
+ uint8_t ch;
+
+ addr = qemu_get_be64(f);
+ flags = addr & ~TARGET_PAGE_MASK;
+ addr &= TARGET_PAGE_MASK;
+
+ switch (flags & ~RAM_SAVE_FLAG_CONTINUE) {
+ case RAM_SAVE_FLAG_MEM_SIZE:
+ /* Synchronize RAM block list */
+ total_ram_bytes = addr;
+ while (!ret && total_ram_bytes) {
+ RAMBlock *block;
+ char id[256];
+ ram_addr_t length;
+
+ len = qemu_get_byte(f);
+ qemu_get_buffer(f, (uint8_t *)id, len);
+ id[len] = 0;
+ length = qemu_get_be64(f);
+
+ QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
+ if (!strncmp(id, block->idstr, sizeof(id))) {
+ if (length != block->used_length) {
+ Error *local_err = NULL;
+
+ ret = qemu_ram_resize(block->offset, length, &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ }
+ }
+ break;
+ }
+ }
+
+ if (!block) {
+ error_report("Unknown ramblock \"%s\", cannot "
+ "accept migration", id);
+ ret = -EINVAL;
+ }
+
+ total_ram_bytes -= length;
+ }
+ break;
+ case RAM_SAVE_FLAG_COMPRESS:
+ host = host_from_stream_offset(f, addr, flags);
+ if (!host) {
+ error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
+ ret = -EINVAL;
+ break;
+ }
+ ch = qemu_get_byte(f);
+ ram_handle_compressed(host, ch, TARGET_PAGE_SIZE);
+ break;
+ case RAM_SAVE_FLAG_PAGE:
+ host = host_from_stream_offset(f, addr, flags);
+ if (!host) {
+ error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
+ ret = -EINVAL;
+ break;
+ }
+ qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
+ break;
+ case RAM_SAVE_FLAG_COMPRESS_PAGE:
+ host = host_from_stream_offset(f, addr, flags);
+ if (!host) {
+ error_report("Invalid RAM offset " RAM_ADDR_FMT, addr);
+ ret = -EINVAL;
+ break;
+ }
+
+ len = qemu_get_be32(f);
+ if (len < 0 || len > compressBound(TARGET_PAGE_SIZE)) {
+ error_report("Invalid compressed data length: %d", len);
+ ret = -EINVAL;
+ break;
+ }
+ qemu_get_buffer(f, compressed_data_buf, len);
+ decompress_data_with_multi_threads(compressed_data_buf, host, len);
+ break;
+ case RAM_SAVE_FLAG_XBZRLE:
+ host = host_from_stream_offset(f, addr, flags);
+ if (!host) {
+ error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
+ ret = -EINVAL;
+ break;
+ }
+ if (load_xbzrle(f, addr, host) < 0) {
+ error_report("Failed to decompress XBZRLE page at "
+ RAM_ADDR_FMT, addr);
+ ret = -EINVAL;
+ break;
+ }
+ break;
+ case RAM_SAVE_FLAG_EOS:
+ /* normal exit */
+ break;
+ default:
+ if (flags & RAM_SAVE_FLAG_HOOK) {
+ ram_control_load_hook(f, flags);
+ } else {
+ error_report("Unknown combination of migration flags: %#x",
+ flags);
+ ret = -EINVAL;
+ }
+ }
+ if (!ret) {
+ ret = qemu_file_get_error(f);
+ }
+ }
+
+ rcu_read_unlock();
+ DPRINTF("Completed load of VM with exit code %d seq iteration "
+ "%" PRIu64 "\n", ret, seq_iter);
+ return ret;
+}
+
+static SaveVMHandlers savevm_ram_handlers = {
+ .save_live_setup = ram_save_setup,
+ .save_live_iterate = ram_save_iterate,
+ .save_live_complete = ram_save_complete,
+ .save_live_pending = ram_save_pending,
+ .load_state = ram_load,
+ .cancel = ram_migration_cancel,
+};
+
+void ram_mig_init(void)
+{
+ qemu_mutex_init(&XBZRLE.lock);
+ register_savevm_live(NULL, "ram", 0, 4, &savevm_ram_handlers, NULL);
+}
+/* Stub function that's gets run on the vcpu when its brought out of the
+ VM to run inside qemu via async_run_on_cpu()*/
+
+static void mig_sleep_cpu(void *opq)
+{
+ qemu_mutex_unlock_iothread();
+ g_usleep(30*1000);
+ qemu_mutex_lock_iothread();
+}
+
+/* To reduce the dirty rate explicitly disallow the VCPUs from spending
+ much time in the VM. The migration thread will try to catchup.
+ Workload will experience a performance drop.
+*/
+static void mig_throttle_guest_down(void)
+{
+ CPUState *cpu;
+
+ qemu_mutex_lock_iothread();
+ CPU_FOREACH(cpu) {
+ async_run_on_cpu(cpu, mig_sleep_cpu, NULL);
+ }
+ qemu_mutex_unlock_iothread();
+}
+
+static void check_guest_throttling(void)
+{
+ static int64_t t0;
+ int64_t t1;
+
+ if (!mig_throttle_on) {
+ return;
+ }
+
+ if (!t0) {
+ t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
+ return;
+ }
+
+ t1 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
+
+ /* If it has been more than 40 ms since the last time the guest
+ * was throttled then do it again.
+ */
+ if (40 < (t1-t0)/1000000) {
+ mig_throttle_guest_down();
+ t0 = t1;
+ }
+}
diff --git a/migration/rdma.c b/migration/rdma.c
index 77e34441dc..cf5de7e2ae 100644
--- a/migration/rdma.c
+++ b/migration/rdma.c
@@ -236,13 +236,13 @@ typedef struct RDMALocalBlock {
* corresponding RDMALocalBlock with
* the information needed to perform the actual RDMA.
*/
-typedef struct QEMU_PACKED RDMARemoteBlock {
+typedef struct QEMU_PACKED RDMADestBlock {
uint64_t remote_host_addr;
uint64_t offset;
uint64_t length;
uint32_t remote_rkey;
uint32_t padding;
-} RDMARemoteBlock;
+} RDMADestBlock;
static uint64_t htonll(uint64_t v)
{
@@ -258,20 +258,20 @@ static uint64_t ntohll(uint64_t v) {
return ((uint64_t)ntohl(u.lv[0]) << 32) | (uint64_t) ntohl(u.lv[1]);
}
-static void remote_block_to_network(RDMARemoteBlock *rb)
+static void dest_block_to_network(RDMADestBlock *db)
{
- rb->remote_host_addr = htonll(rb->remote_host_addr);
- rb->offset = htonll(rb->offset);
- rb->length = htonll(rb->length);
- rb->remote_rkey = htonl(rb->remote_rkey);
+ db->remote_host_addr = htonll(db->remote_host_addr);
+ db->offset = htonll(db->offset);
+ db->length = htonll(db->length);
+ db->remote_rkey = htonl(db->remote_rkey);
}
-static void network_to_remote_block(RDMARemoteBlock *rb)
+static void network_to_dest_block(RDMADestBlock *db)
{
- rb->remote_host_addr = ntohll(rb->remote_host_addr);
- rb->offset = ntohll(rb->offset);
- rb->length = ntohll(rb->length);
- rb->remote_rkey = ntohl(rb->remote_rkey);
+ db->remote_host_addr = ntohll(db->remote_host_addr);
+ db->offset = ntohll(db->offset);
+ db->length = ntohll(db->length);
+ db->remote_rkey = ntohl(db->remote_rkey);
}
/*
@@ -350,7 +350,7 @@ typedef struct RDMAContext {
* Description of ram blocks used throughout the code.
*/
RDMALocalBlocks local_ram_blocks;
- RDMARemoteBlock *block;
+ RDMADestBlock *dest_blocks;
/*
* Migration on *destination* started.
@@ -570,10 +570,10 @@ static int rdma_add_block(RDMAContext *rdma, void *host_addr,
* in advanced before the migration starts. This tells us where the RAM blocks
* are so that we can register them individually.
*/
-static void qemu_rdma_init_one_block(void *host_addr,
+static int qemu_rdma_init_one_block(const char *block_name, void *host_addr,
ram_addr_t block_offset, ram_addr_t length, void *opaque)
{
- rdma_add_block(opaque, host_addr, block_offset, length);
+ return rdma_add_block(opaque, host_addr, block_offset, length);
}
/*
@@ -590,7 +590,7 @@ static int qemu_rdma_init_ram_blocks(RDMAContext *rdma)
memset(local, 0, sizeof *local);
qemu_ram_foreach_block(qemu_rdma_init_one_block, rdma);
trace_qemu_rdma_init_ram_blocks(local->nb_blocks);
- rdma->block = (RDMARemoteBlock *) g_malloc0(sizeof(RDMARemoteBlock) *
+ rdma->dest_blocks = (RDMADestBlock *) g_malloc0(sizeof(RDMADestBlock) *
rdma->local_ram_blocks.nb_blocks);
local->init = true;
return 0;
@@ -790,6 +790,13 @@ static int qemu_rdma_broken_ipv6_kernel(Error **errp, struct ibv_context *verbs)
for (x = 0; x < num_devices; x++) {
verbs = ibv_open_device(dev_list[x]);
+ if (!verbs) {
+ if (errno == EPERM) {
+ continue;
+ } else {
+ return -EINVAL;
+ }
+ }
if (ibv_query_port(verbs, 1, &port_attr)) {
ibv_close_device(verbs);
@@ -2177,8 +2184,8 @@ static void qemu_rdma_cleanup(RDMAContext *rdma)
rdma->connected = false;
}
- g_free(rdma->block);
- rdma->block = NULL;
+ g_free(rdma->dest_blocks);
+ rdma->dest_blocks = NULL;
for (idx = 0; idx < RDMA_WRID_MAX; idx++) {
if (rdma->wr_data[idx].control_mr) {
@@ -2445,7 +2452,6 @@ static void *qemu_rdma_data_init(const char *host_port, Error **errp)
if (host_port) {
rdma = g_malloc0(sizeof(RDMAContext));
- memset(rdma, 0, sizeof(RDMAContext));
rdma->current_index = -1;
rdma->current_chunk = -1;
@@ -2834,7 +2840,7 @@ static int qemu_rdma_accept(RDMAContext *rdma)
}
}
- qemu_set_fd_handler2(rdma->channel->fd, NULL, NULL, NULL, NULL);
+ qemu_set_fd_handler(rdma->channel->fd, NULL, NULL, NULL);
ret = rdma_accept(rdma->cm_id, &conn_param);
if (ret) {
@@ -2967,25 +2973,25 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque,
* their "local" descriptions with what was sent.
*/
for (i = 0; i < local->nb_blocks; i++) {
- rdma->block[i].remote_host_addr =
+ rdma->dest_blocks[i].remote_host_addr =
(uintptr_t)(local->block[i].local_host_addr);
if (rdma->pin_all) {
- rdma->block[i].remote_rkey = local->block[i].mr->rkey;
+ rdma->dest_blocks[i].remote_rkey = local->block[i].mr->rkey;
}
- rdma->block[i].offset = local->block[i].offset;
- rdma->block[i].length = local->block[i].length;
+ rdma->dest_blocks[i].offset = local->block[i].offset;
+ rdma->dest_blocks[i].length = local->block[i].length;
- remote_block_to_network(&rdma->block[i]);
+ dest_block_to_network(&rdma->dest_blocks[i]);
}
blocks.len = rdma->local_ram_blocks.nb_blocks
- * sizeof(RDMARemoteBlock);
+ * sizeof(RDMADestBlock);
ret = qemu_rdma_post_send_control(rdma,
- (uint8_t *) rdma->block, &blocks);
+ (uint8_t *) rdma->dest_blocks, &blocks);
if (ret < 0) {
error_report("rdma migration: error sending remote info");
@@ -3141,7 +3147,7 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque,
if (flags == RAM_CONTROL_SETUP) {
RDMAControlHeader resp = {.type = RDMA_CONTROL_RAM_BLOCKS_RESULT };
RDMALocalBlocks *local = &rdma->local_ram_blocks;
- int reg_result_idx, i, j, nb_remote_blocks;
+ int reg_result_idx, i, j, nb_dest_blocks;
head.type = RDMA_CONTROL_RAM_BLOCKS_REQUEST;
trace_qemu_rdma_registration_stop_ram();
@@ -3162,7 +3168,7 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque,
return ret;
}
- nb_remote_blocks = resp.len / sizeof(RDMARemoteBlock);
+ nb_dest_blocks = resp.len / sizeof(RDMADestBlock);
/*
* The protocol uses two different sets of rkeys (mutually exclusive):
@@ -3176,7 +3182,7 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque,
* and then propagates the remote ram block descriptions to his local copy.
*/
- if (local->nb_blocks != nb_remote_blocks) {
+ if (local->nb_blocks != nb_dest_blocks) {
ERROR(errp, "ram blocks mismatch #1! "
"Your QEMU command line parameters are probably "
"not identical on both the source and destination.");
@@ -3184,26 +3190,26 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque,
}
qemu_rdma_move_header(rdma, reg_result_idx, &resp);
- memcpy(rdma->block,
+ memcpy(rdma->dest_blocks,
rdma->wr_data[reg_result_idx].control_curr, resp.len);
- for (i = 0; i < nb_remote_blocks; i++) {
- network_to_remote_block(&rdma->block[i]);
+ for (i = 0; i < nb_dest_blocks; i++) {
+ network_to_dest_block(&rdma->dest_blocks[i]);
/* search local ram blocks */
for (j = 0; j < local->nb_blocks; j++) {
- if (rdma->block[i].offset != local->block[j].offset) {
+ if (rdma->dest_blocks[i].offset != local->block[j].offset) {
continue;
}
- if (rdma->block[i].length != local->block[j].length) {
+ if (rdma->dest_blocks[i].length != local->block[j].length) {
ERROR(errp, "ram blocks mismatch #2! "
"Your QEMU command line parameters are probably "
"not identical on both the source and destination.");
return -EINVAL;
}
local->block[j].remote_host_addr =
- rdma->block[i].remote_host_addr;
- local->block[j].remote_rkey = rdma->block[i].remote_rkey;
+ rdma->dest_blocks[i].remote_host_addr;
+ local->block[j].remote_rkey = rdma->dest_blocks[i].remote_rkey;
break;
}
@@ -3331,9 +3337,8 @@ void rdma_start_incoming_migration(const char *host_port, Error **errp)
trace_rdma_start_incoming_migration_after_rdma_listen();
- qemu_set_fd_handler2(rdma->channel->fd, NULL,
- rdma_accept_incoming_migration, NULL,
- (void *)(intptr_t) rdma);
+ qemu_set_fd_handler(rdma->channel->fd, rdma_accept_incoming_migration,
+ NULL, (void *)(intptr_t)rdma);
return;
err:
error_propagate(errp, local_err);
diff --git a/savevm.c b/migration/savevm.c
index 3b0e222cb3..2091882196 100644
--- a/savevm.c
+++ b/migration/savevm.c
@@ -2,6 +2,10 @@
* QEMU System Emulator
*
* Copyright (c) 2003-2008 Fabrice Bellard
+ * Copyright (c) 2009-2015 Red Hat Inc
+ *
+ * Authors:
+ * Juan Quintela <quintela@redhat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -51,6 +55,8 @@
#define ARP_PTYPE_IP 0x0800
#define ARP_OP_REQUEST_REV 0x3
+static bool skip_section_footers;
+
static int announce_self_create(uint8_t *buf,
uint8_t *mac_addr)
{
@@ -235,10 +241,15 @@ typedef struct SaveStateEntry {
int is_ram;
} SaveStateEntry;
+typedef struct SaveState {
+ QTAILQ_HEAD(, SaveStateEntry) handlers;
+ int global_section_id;
+} SaveState;
-static QTAILQ_HEAD(savevm_handlers, SaveStateEntry) savevm_handlers =
- QTAILQ_HEAD_INITIALIZER(savevm_handlers);
-static int global_section_id;
+static SaveState savevm_state = {
+ .handlers = QTAILQ_HEAD_INITIALIZER(savevm_state.handlers),
+ .global_section_id = 0,
+};
static void dump_vmstate_vmsd(FILE *out_file,
const VMStateDescription *vmsd, int indent,
@@ -263,11 +274,11 @@ static void dump_vmstate_vmsf(FILE *out_file, const VMStateField *field,
}
static void dump_vmstate_vmss(FILE *out_file,
- const VMStateSubsection *subsection,
+ const VMStateDescription **subsection,
int indent)
{
- if (subsection->vmsd != NULL) {
- dump_vmstate_vmsd(out_file, subsection->vmsd, indent, true);
+ if (*subsection != NULL) {
+ dump_vmstate_vmsd(out_file, *subsection, indent, true);
}
}
@@ -308,12 +319,12 @@ static void dump_vmstate_vmsd(FILE *out_file,
fprintf(out_file, "\n%*s]", indent, "");
}
if (vmsd->subsections != NULL) {
- const VMStateSubsection *subsection = vmsd->subsections;
+ const VMStateDescription **subsection = vmsd->subsections;
bool first;
fprintf(out_file, ",\n%*s\"Subsections\": [\n", indent, "");
first = true;
- while (subsection->vmsd != NULL) {
+ while (*subsection != NULL) {
if (!first) {
fprintf(out_file, ",\n");
}
@@ -383,7 +394,7 @@ static int calculate_new_instance_id(const char *idstr)
SaveStateEntry *se;
int instance_id = 0;
- QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
if (strcmp(idstr, se->idstr) == 0
&& instance_id <= se->instance_id) {
instance_id = se->instance_id + 1;
@@ -397,7 +408,7 @@ static int calculate_compat_instance_id(const char *idstr)
SaveStateEntry *se;
int instance_id = 0;
- QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
if (!se->compat) {
continue;
}
@@ -425,7 +436,7 @@ int register_savevm_live(DeviceState *dev,
se = g_malloc0(sizeof(SaveStateEntry));
se->version_id = version_id;
- se->section_id = global_section_id++;
+ se->section_id = savevm_state.global_section_id++;
se->ops = ops;
se->opaque = opaque;
se->vmsd = NULL;
@@ -457,7 +468,7 @@ int register_savevm_live(DeviceState *dev,
}
assert(!se->compat || se->instance_id == 0);
/* add at the end of list */
- QTAILQ_INSERT_TAIL(&savevm_handlers, se, entry);
+ QTAILQ_INSERT_TAIL(&savevm_state.handlers, se, entry);
return 0;
}
@@ -491,9 +502,9 @@ void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque)
}
pstrcat(id, sizeof(id), idstr);
- QTAILQ_FOREACH_SAFE(se, &savevm_handlers, entry, new_se) {
+ QTAILQ_FOREACH_SAFE(se, &savevm_state.handlers, entry, new_se) {
if (strcmp(se->idstr, id) == 0 && se->opaque == opaque) {
- QTAILQ_REMOVE(&savevm_handlers, se, entry);
+ QTAILQ_REMOVE(&savevm_state.handlers, se, entry);
if (se->compat) {
g_free(se->compat);
}
@@ -515,7 +526,7 @@ int vmstate_register_with_alias_id(DeviceState *dev, int instance_id,
se = g_malloc0(sizeof(SaveStateEntry));
se->version_id = vmsd->version_id;
- se->section_id = global_section_id++;
+ se->section_id = savevm_state.global_section_id++;
se->opaque = opaque;
se->vmsd = vmsd;
se->alias_id = alias_id;
@@ -543,7 +554,7 @@ int vmstate_register_with_alias_id(DeviceState *dev, int instance_id,
}
assert(!se->compat || se->instance_id == 0);
/* add at the end of list */
- QTAILQ_INSERT_TAIL(&savevm_handlers, se, entry);
+ QTAILQ_INSERT_TAIL(&savevm_state.handlers, se, entry);
return 0;
}
@@ -552,9 +563,9 @@ void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd,
{
SaveStateEntry *se, *new_se;
- QTAILQ_FOREACH_SAFE(se, &savevm_handlers, entry, new_se) {
+ QTAILQ_FOREACH_SAFE(se, &savevm_state.handlers, entry, new_se) {
if (se->vmsd == vmsd && se->opaque == opaque) {
- QTAILQ_REMOVE(&savevm_handlers, se, entry);
+ QTAILQ_REMOVE(&savevm_state.handlers, se, entry);
if (se->compat) {
g_free(se->compat);
}
@@ -602,11 +613,84 @@ static void vmstate_save(QEMUFile *f, SaveStateEntry *se, QJSON *vmdesc)
vmstate_save_state(f, se->vmsd, se->opaque, vmdesc);
}
+void savevm_skip_section_footers(void)
+{
+ skip_section_footers = true;
+}
+
+/*
+ * Write the header for device section (QEMU_VM_SECTION START/END/PART/FULL)
+ */
+static void save_section_header(QEMUFile *f, SaveStateEntry *se,
+ uint8_t section_type)
+{
+ qemu_put_byte(f, section_type);
+ qemu_put_be32(f, se->section_id);
+
+ if (section_type == QEMU_VM_SECTION_FULL ||
+ section_type == QEMU_VM_SECTION_START) {
+ /* ID string */
+ size_t len = strlen(se->idstr);
+ qemu_put_byte(f, len);
+ qemu_put_buffer(f, (uint8_t *)se->idstr, len);
+
+ qemu_put_be32(f, se->instance_id);
+ qemu_put_be32(f, se->version_id);
+ }
+}
+
+/*
+ * Write a footer onto device sections that catches cases misformatted device
+ * sections.
+ */
+static void save_section_footer(QEMUFile *f, SaveStateEntry *se)
+{
+ if (!skip_section_footers) {
+ qemu_put_byte(f, QEMU_VM_SECTION_FOOTER);
+ qemu_put_be32(f, se->section_id);
+ }
+}
+
+/*
+ * Read a footer off the wire and check that it matches the expected section
+ *
+ * Returns: true if the footer was good
+ * false if there is a problem (and calls error_report to say why)
+ */
+static bool check_section_footer(QEMUFile *f, SaveStateEntry *se)
+{
+ uint8_t read_mark;
+ uint32_t read_section_id;
+
+ if (skip_section_footers) {
+ /* No footer to check */
+ return true;
+ }
+
+ read_mark = qemu_get_byte(f);
+
+ if (read_mark != QEMU_VM_SECTION_FOOTER) {
+ error_report("Missing section footer for %s", se->idstr);
+ return false;
+ }
+
+ read_section_id = qemu_get_be32(f);
+ if (read_section_id != se->section_id) {
+ error_report("Mismatched section id in footer for %s -"
+ " read 0x%x expected 0x%x",
+ se->idstr, read_section_id, se->section_id);
+ return false;
+ }
+
+ /* All good */
+ return true;
+}
+
bool qemu_savevm_state_blocked(Error **errp)
{
SaveStateEntry *se;
- QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
if (se->vmsd && se->vmsd->unmigratable) {
error_setg(errp, "State blocked by non-migratable device '%s'",
se->idstr);
@@ -616,6 +700,13 @@ bool qemu_savevm_state_blocked(Error **errp)
return false;
}
+void qemu_savevm_state_header(QEMUFile *f)
+{
+ trace_savevm_state_header();
+ qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
+ qemu_put_be32(f, QEMU_VM_FILE_VERSION);
+}
+
void qemu_savevm_state_begin(QEMUFile *f,
const MigrationParams *params)
{
@@ -623,19 +714,14 @@ void qemu_savevm_state_begin(QEMUFile *f,
int ret;
trace_savevm_state_begin();
- QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
if (!se->ops || !se->ops->set_params) {
continue;
}
se->ops->set_params(params, se->opaque);
}
- qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
- qemu_put_be32(f, QEMU_VM_FILE_VERSION);
-
- QTAILQ_FOREACH(se, &savevm_handlers, entry) {
- int len;
-
+ QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
if (!se->ops || !se->ops->save_live_setup) {
continue;
}
@@ -644,19 +730,10 @@ void qemu_savevm_state_begin(QEMUFile *f,
continue;
}
}
- /* Section type */
- qemu_put_byte(f, QEMU_VM_SECTION_START);
- qemu_put_be32(f, se->section_id);
-
- /* ID string */
- len = strlen(se->idstr);
- qemu_put_byte(f, len);
- qemu_put_buffer(f, (uint8_t *)se->idstr, len);
-
- qemu_put_be32(f, se->instance_id);
- qemu_put_be32(f, se->version_id);
+ save_section_header(f, se, QEMU_VM_SECTION_START);
ret = se->ops->save_live_setup(f, se->opaque);
+ save_section_footer(f, se);
if (ret < 0) {
qemu_file_set_error(f, ret);
break;
@@ -676,7 +753,7 @@ int qemu_savevm_state_iterate(QEMUFile *f)
int ret = 1;
trace_savevm_state_iterate();
- QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
if (!se->ops || !se->ops->save_live_iterate) {
continue;
}
@@ -689,12 +766,12 @@ int qemu_savevm_state_iterate(QEMUFile *f)
return 0;
}
trace_savevm_section_start(se->idstr, se->section_id);
- /* Section type */
- qemu_put_byte(f, QEMU_VM_SECTION_PART);
- qemu_put_be32(f, se->section_id);
+
+ save_section_header(f, se, QEMU_VM_SECTION_PART);
ret = se->ops->save_live_iterate(f, se->opaque);
trace_savevm_section_end(se->idstr, se->section_id, ret);
+ save_section_footer(f, se);
if (ret < 0) {
qemu_file_set_error(f, ret);
@@ -727,7 +804,7 @@ void qemu_savevm_state_complete(QEMUFile *f)
cpu_synchronize_all_states();
- QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
if (!se->ops || !se->ops->save_live_complete) {
continue;
}
@@ -737,12 +814,12 @@ void qemu_savevm_state_complete(QEMUFile *f)
}
}
trace_savevm_section_start(se->idstr, se->section_id);
- /* Section type */
- qemu_put_byte(f, QEMU_VM_SECTION_END);
- qemu_put_be32(f, se->section_id);
+
+ save_section_header(f, se, QEMU_VM_SECTION_END);
ret = se->ops->save_live_complete(f, se->opaque);
trace_savevm_section_end(se->idstr, se->section_id, ret);
+ save_section_footer(f, se);
if (ret < 0) {
qemu_file_set_error(f, ret);
return;
@@ -752,8 +829,7 @@ void qemu_savevm_state_complete(QEMUFile *f)
vmdesc = qjson_new();
json_prop_int(vmdesc, "page_size", TARGET_PAGE_SIZE);
json_start_array(vmdesc, "devices");
- QTAILQ_FOREACH(se, &savevm_handlers, entry) {
- int len;
+ QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
if ((!se->ops || !se->ops->save_state) && !se->vmsd) {
continue;
@@ -764,22 +840,13 @@ void qemu_savevm_state_complete(QEMUFile *f)
json_prop_str(vmdesc, "name", se->idstr);
json_prop_int(vmdesc, "instance_id", se->instance_id);
- /* Section type */
- qemu_put_byte(f, QEMU_VM_SECTION_FULL);
- qemu_put_be32(f, se->section_id);
-
- /* ID string */
- len = strlen(se->idstr);
- qemu_put_byte(f, len);
- qemu_put_buffer(f, (uint8_t *)se->idstr, len);
-
- qemu_put_be32(f, se->instance_id);
- qemu_put_be32(f, se->version_id);
+ save_section_header(f, se, QEMU_VM_SECTION_FULL);
vmstate_save(f, se, vmdesc);
json_end_object(vmdesc);
trace_savevm_section_end(se->idstr, se->section_id, 0);
+ save_section_footer(f, se);
}
qemu_put_byte(f, QEMU_VM_EOF);
@@ -803,7 +870,7 @@ uint64_t qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size)
SaveStateEntry *se;
uint64_t ret = 0;
- QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
if (!se->ops || !se->ops->save_live_pending) {
continue;
}
@@ -822,7 +889,7 @@ void qemu_savevm_state_cancel(void)
SaveStateEntry *se;
trace_savevm_state_cancel();
- QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
if (se->ops && se->ops->cancel) {
se->ops->cancel(se->opaque);
}
@@ -842,6 +909,7 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp)
}
qemu_mutex_unlock_iothread();
+ qemu_savevm_state_header(f);
qemu_savevm_state_begin(f, &params);
qemu_mutex_lock_iothread();
@@ -872,9 +940,7 @@ static int qemu_save_device_state(QEMUFile *f)
cpu_synchronize_all_states();
- QTAILQ_FOREACH(se, &savevm_handlers, entry) {
- int len;
-
+ QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
if (se->is_ram) {
continue;
}
@@ -882,19 +948,11 @@ static int qemu_save_device_state(QEMUFile *f)
continue;
}
- /* Section type */
- qemu_put_byte(f, QEMU_VM_SECTION_FULL);
- qemu_put_be32(f, se->section_id);
-
- /* ID string */
- len = strlen(se->idstr);
- qemu_put_byte(f, len);
- qemu_put_buffer(f, (uint8_t *)se->idstr, len);
-
- qemu_put_be32(f, se->instance_id);
- qemu_put_be32(f, se->version_id);
+ save_section_header(f, se, QEMU_VM_SECTION_FULL);
vmstate_save(f, se, NULL);
+
+ save_section_footer(f, se);
}
qemu_put_byte(f, QEMU_VM_EOF);
@@ -906,7 +964,7 @@ static SaveStateEntry *find_se(const char *idstr, int instance_id)
{
SaveStateEntry *se;
- QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
if (!strcmp(se->idstr, idstr) &&
(instance_id == se->instance_id ||
instance_id == se->alias_id))
@@ -922,18 +980,26 @@ static SaveStateEntry *find_se(const char *idstr, int instance_id)
return NULL;
}
-typedef struct LoadStateEntry {
+struct LoadStateEntry {
QLIST_ENTRY(LoadStateEntry) entry;
SaveStateEntry *se;
int section_id;
int version_id;
-} LoadStateEntry;
+};
-int qemu_loadvm_state(QEMUFile *f)
+void loadvm_free_handlers(MigrationIncomingState *mis)
{
- QLIST_HEAD(, LoadStateEntry) loadvm_handlers =
- QLIST_HEAD_INITIALIZER(loadvm_handlers);
LoadStateEntry *le, *new_le;
+
+ QLIST_FOREACH_SAFE(le, &mis->loadvm_handlers, entry, new_le) {
+ QLIST_REMOVE(le, entry);
+ g_free(le);
+ }
+}
+
+int qemu_loadvm_state(QEMUFile *f)
+{
+ MigrationIncomingState *mis = migration_incoming_get_current();
Error *local_err = NULL;
uint8_t section_type;
unsigned int v;
@@ -964,8 +1030,8 @@ int qemu_loadvm_state(QEMUFile *f)
while ((section_type = qemu_get_byte(f)) != QEMU_VM_EOF) {
uint32_t instance_id, version_id, section_id;
SaveStateEntry *se;
- char idstr[257];
- int len;
+ LoadStateEntry *le;
+ char idstr[256];
trace_qemu_loadvm_state_section(section_type);
switch (section_type) {
@@ -973,9 +1039,11 @@ int qemu_loadvm_state(QEMUFile *f)
case QEMU_VM_SECTION_FULL:
/* Read section start */
section_id = qemu_get_be32(f);
- len = qemu_get_byte(f);
- qemu_get_buffer(f, (uint8_t *)idstr, len);
- idstr[len] = 0;
+ if (!qemu_get_counted_string(f, idstr)) {
+ error_report("Unable to read ID string for section %u",
+ section_id);
+ return -EINVAL;
+ }
instance_id = qemu_get_be32(f);
version_id = qemu_get_be32(f);
@@ -1004,7 +1072,7 @@ int qemu_loadvm_state(QEMUFile *f)
le->se = se;
le->section_id = section_id;
le->version_id = version_id;
- QLIST_INSERT_HEAD(&loadvm_handlers, le, entry);
+ QLIST_INSERT_HEAD(&mis->loadvm_handlers, le, entry);
ret = vmstate_load(f, le->se, le->version_id);
if (ret < 0) {
@@ -1012,13 +1080,17 @@ int qemu_loadvm_state(QEMUFile *f)
" device '%s'", instance_id, idstr);
goto out;
}
+ if (!check_section_footer(f, le->se)) {
+ ret = -EINVAL;
+ goto out;
+ }
break;
case QEMU_VM_SECTION_PART:
case QEMU_VM_SECTION_END:
section_id = qemu_get_be32(f);
trace_qemu_loadvm_state_section_partend(section_id);
- QLIST_FOREACH(le, &loadvm_handlers, entry) {
+ QLIST_FOREACH(le, &mis->loadvm_handlers, entry) {
if (le->section_id == section_id) {
break;
}
@@ -1035,6 +1107,10 @@ int qemu_loadvm_state(QEMUFile *f)
section_id, le->se->idstr);
goto out;
}
+ if (!check_section_footer(f, le->se)) {
+ ret = -EINVAL;
+ goto out;
+ }
break;
default:
error_report("Unknown savevm section type %d", section_type);
@@ -1066,11 +1142,6 @@ int qemu_loadvm_state(QEMUFile *f)
ret = 0;
out:
- QLIST_FOREACH_SAFE(le, &loadvm_handlers, entry, new_le) {
- QLIST_REMOVE(le, entry);
- g_free(le);
- }
-
if (ret == 0) {
/* We may not have a VMDESC section, so ignore relative errors */
ret = file_error_after_eof;
@@ -1314,9 +1385,11 @@ int load_vmstate(const char *name)
}
qemu_system_reset(VMRESET_SILENT);
+ migration_incoming_state_new(f);
ret = qemu_loadvm_state(f);
qemu_fclose(f);
+ migration_incoming_state_destroy();
if (ret < 0) {
error_report("Error %d while loading VM state", ret);
return ret;
diff --git a/migration/tcp.c b/migration/tcp.c
index 91c9cf381e..ae891728ef 100644
--- a/migration/tcp.c
+++ b/migration/tcp.c
@@ -65,7 +65,7 @@ static void tcp_accept_incoming_migration(void *opaque)
c = qemu_accept(s, (struct sockaddr *)&addr, &addrlen);
err = socket_error();
} while (c < 0 && err == EINTR);
- qemu_set_fd_handler2(s, NULL, NULL, NULL, NULL);
+ qemu_set_fd_handler(s, NULL, NULL, NULL);
closesocket(s);
DPRINTF("accepted migration\n");
@@ -98,6 +98,6 @@ void tcp_start_incoming_migration(const char *host_port, Error **errp)
return;
}
- qemu_set_fd_handler2(s, NULL, tcp_accept_incoming_migration, NULL,
- (void *)(intptr_t)s);
+ qemu_set_fd_handler(s, tcp_accept_incoming_migration, NULL,
+ (void *)(intptr_t)s);
}
diff --git a/migration/unix.c b/migration/unix.c
index 1cdadfbc83..b591813eb9 100644
--- a/migration/unix.c
+++ b/migration/unix.c
@@ -65,7 +65,7 @@ static void unix_accept_incoming_migration(void *opaque)
c = qemu_accept(s, (struct sockaddr *)&addr, &addrlen);
err = errno;
} while (c < 0 && err == EINTR);
- qemu_set_fd_handler2(s, NULL, NULL, NULL, NULL);
+ qemu_set_fd_handler(s, NULL, NULL, NULL);
close(s);
DPRINTF("accepted migration\n");
@@ -98,6 +98,6 @@ void unix_start_incoming_migration(const char *path, Error **errp)
return;
}
- qemu_set_fd_handler2(s, NULL, unix_accept_incoming_migration, NULL,
- (void *)(intptr_t)s);
+ qemu_set_fd_handler(s, unix_accept_incoming_migration, NULL,
+ (void *)(intptr_t)s);
}
diff --git a/migration/vmstate.c b/migration/vmstate.c
index e5388f0596..6138d1acb7 100644
--- a/migration/vmstate.c
+++ b/migration/vmstate.c
@@ -341,11 +341,11 @@ void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
}
static const VMStateDescription *
- vmstate_get_subsection(const VMStateSubsection *sub, char *idstr)
+vmstate_get_subsection(const VMStateDescription **sub, char *idstr)
{
- while (sub && sub->needed) {
- if (strcmp(idstr, sub->vmsd->name) == 0) {
- return sub->vmsd;
+ while (sub && *sub && (*sub)->needed) {
+ if (strcmp(idstr, (*sub)->name) == 0) {
+ return *sub;
}
sub++;
}
@@ -358,7 +358,7 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
trace_vmstate_subsection_load(vmsd->name);
while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) {
- char idstr[256];
+ char idstr[256], *idstr_ret;
int ret;
uint8_t version_id, len, size;
const VMStateDescription *sub_vmsd;
@@ -369,11 +369,12 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
trace_vmstate_subsection_load_bad(vmsd->name, "(short)");
return 0;
}
- size = qemu_peek_buffer(f, (uint8_t *)idstr, len, 2);
+ size = qemu_peek_buffer(f, (uint8_t **)&idstr_ret, len, 2);
if (size != len) {
trace_vmstate_subsection_load_bad(vmsd->name, "(peek fail)");
return 0;
}
+ memcpy(idstr, idstr_ret, size);
idstr[size] = 0;
if (strncmp(vmsd->name, idstr, strlen(vmsd->name)) != 0) {
@@ -405,12 +406,12 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
void *opaque, QJSON *vmdesc)
{
- const VMStateSubsection *sub = vmsd->subsections;
+ const VMStateDescription **sub = vmsd->subsections;
bool subsection_found = false;
- while (sub && sub->needed) {
- if (sub->needed(opaque)) {
- const VMStateDescription *vmsd = sub->vmsd;
+ while (sub && *sub && (*sub)->needed) {
+ if ((*sub)->needed(opaque)) {
+ const VMStateDescription *vmsd = *sub;
uint8_t len;
if (vmdesc) {
diff --git a/monitor.c b/monitor.c
index b2561e1e2d..8e1a2e85b8 100644
--- a/monitor.c
+++ b/monitor.c
@@ -118,25 +118,15 @@
*
*/
-typedef struct MonitorCompletionData MonitorCompletionData;
-struct MonitorCompletionData {
- Monitor *mon;
- void (*user_print)(Monitor *mon, const QObject *data);
-};
-
typedef struct mon_cmd_t {
const char *name;
const char *args_type;
const char *params;
const char *help;
- void (*user_print)(Monitor *mon, const QObject *data);
union {
void (*cmd)(Monitor *mon, const QDict *qdict);
int (*cmd_new)(Monitor *mon, const QDict *params, QObject **ret_data);
- int (*cmd_async)(Monitor *mon, const QDict *params,
- MonitorCompletion *cb, void *opaque);
} mhandler;
- int flags;
/* @sub_table is a list of 2nd level of commands. If it do not exist,
* mhandler should be used. If it exist, sub_table[?].mhandler should be
* used, and mhandler of 1st level plays the role of help function.
@@ -171,11 +161,16 @@ struct MonFdset {
QLIST_ENTRY(MonFdset) next;
};
-typedef struct MonitorControl {
+typedef struct {
QObject *id;
JSONMessageParser parser;
- int command_mode;
-} MonitorControl;
+ /*
+ * When a client connects, we're in capabilities negotiation mode.
+ * When command qmp_capabilities succeeds, we go into command
+ * mode.
+ */
+ bool in_command_mode; /* are we in command mode? */
+} MonitorQMP;
/*
* To prevent flooding clients, events can be throttled. The
@@ -205,7 +200,7 @@ struct Monitor {
int mux_out;
ReadLineState *rs;
- MonitorControl *mc;
+ MonitorQMP qmp;
CPUState *mon_cpu;
BlockCompletionFunc *password_completion_cb;
void *password_opaque;
@@ -231,26 +226,24 @@ static mon_cmd_t info_cmds[];
static const mon_cmd_t qmp_cmds[];
Monitor *cur_mon;
-Monitor *default_mon;
static void monitor_command_cb(void *opaque, const char *cmdline,
void *readline_opaque);
-static inline int qmp_cmd_mode(const Monitor *mon)
-{
- return (mon->mc ? mon->mc->command_mode : 0);
-}
-
-/* Return true if in control mode, false otherwise */
-static inline int monitor_ctrl_mode(const Monitor *mon)
+/**
+ * Is @mon a QMP monitor?
+ */
+static inline bool monitor_is_qmp(const Monitor *mon)
{
return (mon->flags & MONITOR_USE_CONTROL);
}
-/* Return non-zero iff we have a current monitor, and it is in QMP mode. */
-int monitor_cur_is_qmp(void)
+/**
+ * Is the current monitor, if any, a QMP monitor?
+ */
+bool monitor_cur_is_qmp(void)
{
- return cur_mon && monitor_ctrl_mode(cur_mon);
+ return cur_mon && monitor_is_qmp(cur_mon);
}
void monitor_read_command(Monitor *mon, int show_prompt)
@@ -360,7 +353,7 @@ void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
if (!mon)
return;
- if (monitor_ctrl_mode(mon)) {
+ if (monitor_is_qmp(mon)) {
return;
}
@@ -387,23 +380,6 @@ static int GCC_FMT_ATTR(2, 3) monitor_fprintf(FILE *stream,
return 0;
}
-static void monitor_user_noop(Monitor *mon, const QObject *data) { }
-
-static inline int handler_is_qobject(const mon_cmd_t *cmd)
-{
- return cmd->user_print != NULL;
-}
-
-static inline bool handler_is_async(const mon_cmd_t *cmd)
-{
- return cmd->flags & MONITOR_CMD_ASYNC;
-}
-
-static inline int monitor_has_error(const Monitor *mon)
-{
- return mon->error != NULL;
-}
-
static void monitor_json_emitter(Monitor *mon, const QObject *data)
{
QString *json;
@@ -418,24 +394,25 @@ static void monitor_json_emitter(Monitor *mon, const QObject *data)
QDECREF(json);
}
-static QDict *build_qmp_error_dict(const QError *err)
+static QDict *build_qmp_error_dict(Error *err)
{
QObject *obj;
- obj = qobject_from_jsonf("{ 'error': { 'class': %s, 'desc': %p } }",
- ErrorClass_lookup[err->err_class],
- qerror_human(err));
+ obj = qobject_from_jsonf("{ 'error': { 'class': %s, 'desc': %s } }",
+ ErrorClass_lookup[error_get_class(err)],
+ error_get_pretty(err));
return qobject_to_qdict(obj);
}
-static void monitor_protocol_emitter(Monitor *mon, QObject *data)
+static void monitor_protocol_emitter(Monitor *mon, QObject *data,
+ Error *err)
{
QDict *qmp;
trace_monitor_protocol_emitter(mon);
- if (!monitor_has_error(mon)) {
+ if (!err) {
/* success response */
qmp = qdict_new();
if (data) {
@@ -447,14 +424,12 @@ static void monitor_protocol_emitter(Monitor *mon, QObject *data)
}
} else {
/* error response */
- qmp = build_qmp_error_dict(mon->error);
- QDECREF(mon->error);
- mon->error = NULL;
+ qmp = build_qmp_error_dict(err);
}
- if (mon->mc->id) {
- qdict_put_obj(qmp, "id", mon->mc->id);
- mon->mc->id = NULL;
+ if (mon->qmp.id) {
+ qdict_put_obj(qmp, "id", mon->qmp.id);
+ mon->qmp.id = NULL;
}
monitor_json_emitter(mon, QOBJECT(qmp));
@@ -474,7 +449,7 @@ static void monitor_qapi_event_emit(QAPIEvent event, QObject *data)
trace_monitor_protocol_event_emit(event, data);
QLIST_FOREACH(mon, &mon_list, entry) {
- if (monitor_ctrl_mode(mon) && qmp_cmd_mode(mon)) {
+ if (monitor_is_qmp(mon) && mon->qmp.in_command_mode) {
monitor_json_emitter(mon, data);
}
}
@@ -594,15 +569,11 @@ static void monitor_qapi_event_init(void)
static int do_qmp_capabilities(Monitor *mon, const QDict *params,
QObject **ret_data)
{
- /* Will setup QMP capabilities in the future */
- if (monitor_ctrl_mode(mon)) {
- mon->mc->command_mode = 1;
- }
-
+ mon->qmp.in_command_mode = true;
return 0;
}
-static void handle_user_command(Monitor *mon, const char *cmdline);
+static void handle_hmp_command(Monitor *mon, const char *cmdline);
static void monitor_data_init(Monitor *mon)
{
@@ -641,7 +612,7 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
}
}
- handle_user_command(&hmp, command_line);
+ handle_hmp_command(&hmp, command_line);
cur_mon = old_mon;
qemu_mutex_lock(&hmp.out_lock);
@@ -917,45 +888,6 @@ static void hmp_trace_file(Monitor *mon, const QDict *qdict)
}
#endif
-static void user_monitor_complete(void *opaque, QObject *ret_data)
-{
- MonitorCompletionData *data = (MonitorCompletionData *)opaque;
-
- if (ret_data) {
- data->user_print(data->mon, ret_data);
- }
- monitor_resume(data->mon);
- g_free(data);
-}
-
-static void qmp_monitor_complete(void *opaque, QObject *ret_data)
-{
- monitor_protocol_emitter(opaque, ret_data);
-}
-
-static int qmp_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd,
- const QDict *params)
-{
- return cmd->mhandler.cmd_async(mon, params, qmp_monitor_complete, mon);
-}
-
-static void user_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd,
- const QDict *params)
-{
- int ret;
-
- MonitorCompletionData *cb_data = g_malloc(sizeof(*cb_data));
- cb_data->mon = mon;
- cb_data->user_print = cmd->user_print;
- monitor_suspend(mon);
- ret = cmd->mhandler.cmd_async(mon, params,
- user_monitor_complete, cb_data);
- if (ret < 0) {
- monitor_resume(mon);
- g_free(cb_data);
- }
-}
-
static void hmp_info_help(Monitor *mon, const QDict *qdict)
{
help_cmd(mon, "info");
@@ -1085,39 +1017,33 @@ static void hmp_info_trace_events(Monitor *mon, const QDict *qdict)
qapi_free_TraceEventInfoList(events);
}
-static int client_migrate_info(Monitor *mon, const QDict *qdict,
- QObject **ret_data)
+void qmp_client_migrate_info(const char *protocol, const char *hostname,
+ bool has_port, int64_t port,
+ bool has_tls_port, int64_t tls_port,
+ bool has_cert_subject, const char *cert_subject,
+ Error **errp)
{
- const char *protocol = qdict_get_str(qdict, "protocol");
- const char *hostname = qdict_get_str(qdict, "hostname");
- const char *subject = qdict_get_try_str(qdict, "cert-subject");
- int port = qdict_get_try_int(qdict, "port", -1);
- int tls_port = qdict_get_try_int(qdict, "tls-port", -1);
- Error *err = NULL;
- int ret;
-
if (strcmp(protocol, "spice") == 0) {
- if (!qemu_using_spice(&err)) {
- qerror_report_err(err);
- error_free(err);
- return -1;
+ if (!qemu_using_spice(errp)) {
+ return;
}
- if (port == -1 && tls_port == -1) {
- qerror_report(QERR_MISSING_PARAMETER, "port/tls-port");
- return -1;
+ if (!has_port && !has_tls_port) {
+ error_set(errp, QERR_MISSING_PARAMETER, "port/tls-port");
+ return;
}
- ret = qemu_spice_migrate_info(hostname, port, tls_port, subject);
- if (ret != 0) {
- qerror_report(QERR_UNDEFINED_ERROR);
- return -1;
+ if (qemu_spice_migrate_info(hostname,
+ has_port ? port : -1,
+ has_tls_port ? tls_port : -1,
+ cert_subject)) {
+ error_set(errp, QERR_UNDEFINED_ERROR);
+ return;
}
- return 0;
+ return;
}
- qerror_report(QERR_INVALID_PARAMETER, "protocol");
- return -1;
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, "protocol", "spice");
}
static void hmp_logfile(Monitor *mon, const QDict *qdict)
@@ -2936,6 +2862,34 @@ static mon_cmd_t info_cmds[] = {
.mhandler.cmd = hmp_info_memory_devices,
},
{
+ .name = "rocker",
+ .args_type = "name:s",
+ .params = "name",
+ .help = "Show rocker switch",
+ .mhandler.cmd = hmp_rocker,
+ },
+ {
+ .name = "rocker-ports",
+ .args_type = "name:s",
+ .params = "name",
+ .help = "Show rocker ports",
+ .mhandler.cmd = hmp_rocker_ports,
+ },
+ {
+ .name = "rocker-of-dpa-flows",
+ .args_type = "name:s,tbl_id:i?",
+ .params = "name [tbl_id]",
+ .help = "Show rocker OF-DPA flow tables",
+ .mhandler.cmd = hmp_rocker_of_dpa_flows,
+ },
+ {
+ .name = "rocker-of-dpa-groups",
+ .args_type = "name:s,type:i?",
+ .params = "name [type]",
+ .help = "Show rocker OF-DPA groups",
+ .mhandler.cmd = hmp_rocker_of_dpa_groups,
+ },
+ {
.name = NULL,
},
};
@@ -4098,19 +4052,7 @@ void monitor_set_error(Monitor *mon, QError *qerror)
}
}
-static void handler_audit(Monitor *mon, const mon_cmd_t *cmd, int ret)
-{
- if (ret && !monitor_has_error(mon)) {
- /*
- * If it returns failure, it must have passed on error.
- *
- * Action: Report an internal error to the client if in QMP.
- */
- qerror_report(QERR_UNDEFINED_ERROR);
- }
-}
-
-static void handle_user_command(Monitor *mon, const char *cmdline)
+static void handle_hmp_command(Monitor *mon, const char *cmdline)
{
QDict *qdict;
const mon_cmd_t *cmd;
@@ -4118,26 +4060,10 @@ static void handle_user_command(Monitor *mon, const char *cmdline)
qdict = qdict_new();
cmd = monitor_parse_command(mon, cmdline, 0, mon->cmd_table, qdict);
- if (!cmd)
- goto out;
-
- if (handler_is_async(cmd)) {
- user_async_cmd_handler(mon, cmd, qdict);
- } else if (handler_is_qobject(cmd)) {
- QObject *data = NULL;
-
- /* XXX: ignores the error code */
- cmd->mhandler.cmd_new(mon, qdict, &data);
- assert(!monitor_has_error(mon));
- if (data) {
- cmd->user_print(mon, data);
- qobject_decref(data);
- }
- } else {
+ if (cmd) {
cmd->mhandler.cmd(mon, qdict);
}
-out:
QDECREF(qdict);
}
@@ -4803,19 +4729,21 @@ static int monitor_can_read(void *opaque)
return (mon->suspend_cnt == 0) ? 1 : 0;
}
-static bool invalid_qmp_mode(const Monitor *mon, const mon_cmd_t *cmd)
+static bool invalid_qmp_mode(const Monitor *mon, const mon_cmd_t *cmd,
+ Error **errp)
{
bool is_cap = cmd->mhandler.cmd_new == do_qmp_capabilities;
- if (is_cap && qmp_cmd_mode(mon)) {
- qerror_report(ERROR_CLASS_COMMAND_NOT_FOUND,
- "Capabilities negotiation is already complete, command "
- "'%s' ignored", cmd->name);
+
+ if (is_cap && mon->qmp.in_command_mode) {
+ error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
+ "Capabilities negotiation is already complete, command "
+ "'%s' ignored", cmd->name);
return true;
}
- if (!is_cap && !qmp_cmd_mode(mon)) {
- qerror_report(ERROR_CLASS_COMMAND_NOT_FOUND,
- "Expecting capabilities negotiation with "
- "'qmp_capabilities' before command '%s'", cmd->name);
+ if (!is_cap && !mon->qmp.in_command_mode) {
+ error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
+ "Expecting capabilities negotiation with "
+ "'qmp_capabilities' before command '%s'", cmd->name);
return true;
}
return false;
@@ -4831,8 +4759,9 @@ static bool invalid_qmp_mode(const Monitor *mon, const mon_cmd_t *cmd)
* the QMP_ACCEPT_UNKNOWNS flag is set, then the
* checking is skipped for it.
*/
-static int check_client_args_type(const QDict *client_args,
- const QDict *cmd_args, int flags)
+static void check_client_args_type(const QDict *client_args,
+ const QDict *cmd_args, int flags,
+ Error **errp)
{
const QDictEntry *ent;
@@ -4849,8 +4778,8 @@ static int check_client_args_type(const QDict *client_args,
continue;
}
/* client arg doesn't exist */
- qerror_report(QERR_INVALID_PARAMETER, client_arg_name);
- return -1;
+ error_set(errp, QERR_INVALID_PARAMETER, client_arg_name);
+ return;
}
arg_type = qobject_to_qstring(obj);
@@ -4862,9 +4791,9 @@ static int check_client_args_type(const QDict *client_args,
case 'B':
case 's':
if (qobject_type(client_arg) != QTYPE_QSTRING) {
- qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name,
- "string");
- return -1;
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+ client_arg_name, "string");
+ return;
}
break;
case 'i':
@@ -4872,25 +4801,25 @@ static int check_client_args_type(const QDict *client_args,
case 'M':
case 'o':
if (qobject_type(client_arg) != QTYPE_QINT) {
- qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name,
- "int");
- return -1;
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+ client_arg_name, "int");
+ return;
}
break;
case 'T':
if (qobject_type(client_arg) != QTYPE_QINT &&
qobject_type(client_arg) != QTYPE_QFLOAT) {
- qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name,
- "number");
- return -1;
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+ client_arg_name, "number");
+ return;
}
break;
case 'b':
case '-':
if (qobject_type(client_arg) != QTYPE_QBOOL) {
- qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name,
- "bool");
- return -1;
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+ client_arg_name, "bool");
+ return;
}
break;
case 'O':
@@ -4909,16 +4838,15 @@ static int check_client_args_type(const QDict *client_args,
abort();
}
}
-
- return 0;
}
/*
* - Check if the client has passed all mandatory args
* - Set special flags for argument validation
*/
-static int check_mandatory_args(const QDict *cmd_args,
- const QDict *client_args, int *flags)
+static void check_mandatory_args(const QDict *cmd_args,
+ const QDict *client_args, int *flags,
+ Error **errp)
{
const QDictEntry *ent;
@@ -4933,12 +4861,10 @@ static int check_mandatory_args(const QDict *cmd_args,
} else if (qstring_get_str(type)[0] != '-' &&
qstring_get_str(type)[1] != '?' &&
!qdict_haskey(client_args, cmd_arg_name)) {
- qerror_report(QERR_MISSING_PARAMETER, cmd_arg_name);
- return -1;
+ error_set(errp, QERR_MISSING_PARAMETER, cmd_arg_name);
+ return;
}
}
-
- return 0;
}
static QDict *qdict_from_args_type(const char *args_type)
@@ -4994,24 +4920,26 @@ out:
* 3. Each argument provided by the client must have the type expected
* by the command
*/
-static int qmp_check_client_args(const mon_cmd_t *cmd, QDict *client_args)
+static void qmp_check_client_args(const mon_cmd_t *cmd, QDict *client_args,
+ Error **errp)
{
- int flags, err;
+ Error *err = NULL;
+ int flags;
QDict *cmd_args;
cmd_args = qdict_from_args_type(cmd->args_type);
flags = 0;
- err = check_mandatory_args(cmd_args, client_args, &flags);
+ check_mandatory_args(cmd_args, client_args, &flags, &err);
if (err) {
goto out;
}
- err = check_client_args_type(client_args, cmd_args, flags);
+ check_client_args_type(client_args, cmd_args, flags, &err);
out:
+ error_propagate(errp, err);
QDECREF(cmd_args);
- return err;
}
/*
@@ -5024,14 +4952,14 @@ out:
* 5. If the "id" key exists, it can be anything (ie. json-value)
* 6. Any argument not listed above is considered invalid
*/
-static QDict *qmp_check_input_obj(QObject *input_obj)
+static QDict *qmp_check_input_obj(QObject *input_obj, Error **errp)
{
const QDictEntry *ent;
int has_exec_key = 0;
QDict *input_dict;
if (qobject_type(input_obj) != QTYPE_QDICT) {
- qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "object");
+ error_set(errp, QERR_QMP_BAD_INPUT_OBJECT, "object");
return NULL;
}
@@ -5043,81 +4971,70 @@ static QDict *qmp_check_input_obj(QObject *input_obj)
if (!strcmp(arg_name, "execute")) {
if (qobject_type(arg_obj) != QTYPE_QSTRING) {
- qerror_report(QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "execute",
- "string");
+ error_set(errp, QERR_QMP_BAD_INPUT_OBJECT_MEMBER,
+ "execute", "string");
return NULL;
}
has_exec_key = 1;
} else if (!strcmp(arg_name, "arguments")) {
if (qobject_type(arg_obj) != QTYPE_QDICT) {
- qerror_report(QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "arguments",
- "object");
+ error_set(errp, QERR_QMP_BAD_INPUT_OBJECT_MEMBER,
+ "arguments", "object");
return NULL;
}
} else if (!strcmp(arg_name, "id")) {
- /* FIXME: check duplicated IDs for async commands */
+ /* Any string is acceptable as "id", so nothing to check */
} else {
- qerror_report(QERR_QMP_EXTRA_MEMBER, arg_name);
+ error_set(errp, QERR_QMP_EXTRA_MEMBER, arg_name);
return NULL;
}
}
if (!has_exec_key) {
- qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "execute");
+ error_set(errp, QERR_QMP_BAD_INPUT_OBJECT, "execute");
return NULL;
}
return input_dict;
}
-static void qmp_call_cmd(Monitor *mon, const mon_cmd_t *cmd,
- const QDict *params)
-{
- int ret;
- QObject *data = NULL;
-
- ret = cmd->mhandler.cmd_new(mon, params, &data);
- handler_audit(mon, cmd, ret);
- monitor_protocol_emitter(mon, data);
- qobject_decref(data);
-}
-
static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
{
- int err;
- QObject *obj;
+ Error *local_err = NULL;
+ QObject *obj, *data;
QDict *input, *args;
const mon_cmd_t *cmd;
const char *cmd_name;
Monitor *mon = cur_mon;
args = input = NULL;
+ data = NULL;
obj = json_parser_parse(tokens, NULL);
if (!obj) {
// FIXME: should be triggered in json_parser_parse()
- qerror_report(QERR_JSON_PARSING);
+ error_set(&local_err, QERR_JSON_PARSING);
goto err_out;
}
- input = qmp_check_input_obj(obj);
+ input = qmp_check_input_obj(obj, &local_err);
if (!input) {
qobject_decref(obj);
goto err_out;
}
- mon->mc->id = qdict_get(input, "id");
- qobject_incref(mon->mc->id);
+ mon->qmp.id = qdict_get(input, "id");
+ qobject_incref(mon->qmp.id);
cmd_name = qdict_get_str(input, "execute");
trace_handle_qmp_command(mon, cmd_name);
cmd = qmp_find_cmd(cmd_name);
if (!cmd) {
- qerror_report(ERROR_CLASS_COMMAND_NOT_FOUND,
- "The command %s has not been found", cmd_name);
+ error_set(&local_err, ERROR_CLASS_COMMAND_NOT_FOUND,
+ "The command %s has not been found", cmd_name);
goto err_out;
}
- if (invalid_qmp_mode(mon, cmd)) {
+ if (invalid_qmp_mode(mon, cmd, &local_err)) {
goto err_out;
}
@@ -5129,40 +5046,39 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
QINCREF(args);
}
- err = qmp_check_client_args(cmd, args);
- if (err < 0) {
+ qmp_check_client_args(cmd, args, &local_err);
+ if (local_err) {
goto err_out;
}
- if (handler_is_async(cmd)) {
- err = qmp_async_cmd_handler(mon, cmd, args);
- if (err) {
- /* emit the error response */
- goto err_out;
+ if (cmd->mhandler.cmd_new(mon, args, &data)) {
+ /* Command failed... */
+ if (!mon->error) {
+ /* ... without setting an error, so make one up */
+ error_set(&local_err, QERR_UNDEFINED_ERROR);
}
- } else {
- qmp_call_cmd(mon, cmd, args);
}
-
- goto out;
+ if (mon->error) {
+ error_set(&local_err, mon->error->err_class, "%s",
+ mon->error->err_msg);
+ }
err_out:
- monitor_protocol_emitter(mon, NULL);
-out:
+ monitor_protocol_emitter(mon, data, local_err);
+ qobject_decref(data);
+ QDECREF(mon->error);
+ mon->error = NULL;
QDECREF(input);
QDECREF(args);
}
-/**
- * monitor_control_read(): Read and handle QMP input
- */
-static void monitor_control_read(void *opaque, const uint8_t *buf, int size)
+static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size)
{
Monitor *old_mon = cur_mon;
cur_mon = opaque;
- json_message_parser_feed(&cur_mon->mc->parser, (const char *) buf, size);
+ json_message_parser_feed(&cur_mon->qmp.parser, (const char *) buf, size);
cur_mon = old_mon;
}
@@ -5181,7 +5097,7 @@ static void monitor_read(void *opaque, const uint8_t *buf, int size)
if (size == 0 || buf[size - 1] != 0)
monitor_printf(cur_mon, "corrupted command\n");
else
- handle_user_command(cur_mon, (char *)buf);
+ handle_hmp_command(cur_mon, (char *)buf);
}
cur_mon = old_mon;
@@ -5193,7 +5109,7 @@ static void monitor_command_cb(void *opaque, const char *cmdline,
Monitor *mon = opaque;
monitor_suspend(mon);
- handle_user_command(mon, cmdline);
+ handle_hmp_command(mon, cmdline);
monitor_resume(mon);
}
@@ -5221,25 +5137,22 @@ static QObject *get_qmp_greeting(void)
return qobject_from_jsonf("{'QMP':{'version': %p,'capabilities': []}}",ver);
}
-/**
- * monitor_control_event(): Print QMP gretting
- */
-static void monitor_control_event(void *opaque, int event)
+static void monitor_qmp_event(void *opaque, int event)
{
QObject *data;
Monitor *mon = opaque;
switch (event) {
case CHR_EVENT_OPENED:
- mon->mc->command_mode = 0;
+ mon->qmp.in_command_mode = false;
data = get_qmp_greeting();
monitor_json_emitter(mon, data);
qobject_decref(data);
mon_refcount++;
break;
case CHR_EVENT_CLOSED:
- json_message_parser_destroy(&mon->mc->parser);
- json_message_parser_init(&mon->mc->parser, handle_qmp_command);
+ json_message_parser_destroy(&mon->qmp.parser);
+ json_message_parser_init(&mon->qmp.parser, handle_qmp_command);
mon_refcount--;
monitor_fdsets_cleanup();
break;
@@ -5371,14 +5284,11 @@ void monitor_init(CharDriverState *chr, int flags)
monitor_read_command(mon, 0);
}
- if (monitor_ctrl_mode(mon)) {
- mon->mc = g_malloc0(sizeof(MonitorControl));
- /* Control mode requires special handlers */
- qemu_chr_add_handlers(chr, monitor_can_read, monitor_control_read,
- monitor_control_event, mon);
+ if (monitor_is_qmp(mon)) {
+ qemu_chr_add_handlers(chr, monitor_can_read, monitor_qmp_read,
+ monitor_qmp_event, mon);
qemu_chr_fe_set_echo(chr, true);
-
- json_message_parser_init(&mon->mc->parser, handle_qmp_command);
+ json_message_parser_init(&mon->qmp.parser, handle_qmp_command);
} else {
qemu_chr_add_handlers(chr, monitor_can_read, monitor_read,
monitor_event, mon);
@@ -5387,9 +5297,6 @@ void monitor_init(CharDriverState *chr, int flags)
qemu_mutex_lock(&monitor_lock);
QLIST_INSERT_HEAD(&mon_list, mon, entry);
qemu_mutex_unlock(&monitor_lock);
-
- if (!default_mon || (flags & MONITOR_IS_DEFAULT))
- default_mon = mon;
}
static void bdrv_password_cb(void *opaque, const char *password,
diff --git a/net/l2tpv3.c b/net/l2tpv3.c
index ed395dc126..356dae2b72 100644
--- a/net/l2tpv3.c
+++ b/net/l2tpv3.c
@@ -133,17 +133,15 @@ typedef struct NetL2TPV3State {
} NetL2TPV3State;
-static int l2tpv3_can_send(void *opaque);
static void net_l2tpv3_send(void *opaque);
static void l2tpv3_writable(void *opaque);
static void l2tpv3_update_fd_handler(NetL2TPV3State *s)
{
- qemu_set_fd_handler2(s->fd,
- s->read_poll ? l2tpv3_can_send : NULL,
- s->read_poll ? net_l2tpv3_send : NULL,
- s->write_poll ? l2tpv3_writable : NULL,
- s);
+ qemu_set_fd_handler(s->fd,
+ s->read_poll ? net_l2tpv3_send : NULL,
+ s->write_poll ? l2tpv3_writable : NULL,
+ s);
}
static void l2tpv3_read_poll(NetL2TPV3State *s, bool enable)
@@ -169,13 +167,6 @@ static void l2tpv3_writable(void *opaque)
qemu_flush_queued_packets(&s->nc);
}
-static int l2tpv3_can_send(void *opaque)
-{
- NetL2TPV3State *s = opaque;
-
- return qemu_can_send_packet(&s->nc);
-}
-
static void l2tpv3_send_completed(NetClientState *nc, ssize_t len)
{
NetL2TPV3State *s = DO_UPCAST(NetL2TPV3State, nc, nc);
diff --git a/net/net.c b/net/net.c
index 5148aacf81..6dbd61a8f2 100644
--- a/net/net.c
+++ b/net/net.c
@@ -1347,7 +1347,7 @@ void net_check_clients(void)
}
}
-static int net_init_client(QemuOpts *opts, void *dummy)
+static int net_init_client(void *dummy, QemuOpts *opts, Error **errp)
{
Error *local_err = NULL;
@@ -1360,7 +1360,7 @@ static int net_init_client(QemuOpts *opts, void *dummy)
return 0;
}
-static int net_init_netdev(QemuOpts *opts, void *dummy)
+static int net_init_netdev(void *dummy, QemuOpts *opts, Error **errp)
{
Error *local_err = NULL;
int ret;
@@ -1391,10 +1391,12 @@ int net_init_clients(void)
QTAILQ_INIT(&net_clients);
- if (qemu_opts_foreach(qemu_find_opts("netdev"), net_init_netdev, NULL, 1) == -1)
+ if (qemu_opts_foreach(qemu_find_opts("netdev"),
+ net_init_netdev, NULL, NULL)) {
return -1;
+ }
- if (qemu_opts_foreach(net, net_init_client, NULL, 1) == -1) {
+ if (qemu_opts_foreach(net, net_init_client, NULL, NULL)) {
return -1;
}
diff --git a/net/netmap.c b/net/netmap.c
index 69300eb1ae..508b82947d 100644
--- a/net/netmap.c
+++ b/net/netmap.c
@@ -132,26 +132,16 @@ error:
return -1;
}
-/* Tell the event-loop if the netmap backend can send packets
- to the frontend. */
-static int netmap_can_send(void *opaque)
-{
- NetmapState *s = opaque;
-
- return qemu_can_send_packet(&s->nc);
-}
-
static void netmap_send(void *opaque);
static void netmap_writable(void *opaque);
/* Set the event-loop handlers for the netmap backend. */
static void netmap_update_fd_handler(NetmapState *s)
{
- qemu_set_fd_handler2(s->me.fd,
- s->read_poll ? netmap_can_send : NULL,
- s->read_poll ? netmap_send : NULL,
- s->write_poll ? netmap_writable : NULL,
- s);
+ qemu_set_fd_handler(s->me.fd,
+ s->read_poll ? netmap_send : NULL,
+ s->write_poll ? netmap_writable : NULL,
+ s);
}
/* Update the read handler. */
@@ -317,7 +307,7 @@ static void netmap_send(void *opaque)
/* Keep sending while there are available packets into the netmap
RX ring and the forwarding path towards the peer is open. */
- while (!nm_ring_empty(ring) && qemu_can_send_packet(&s->nc)) {
+ while (!nm_ring_empty(ring)) {
uint32_t i;
uint32_t idx;
bool morefrag;
diff --git a/net/slirp.c b/net/slirp.c
index 0e15cf6750..35338376f7 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -481,7 +481,6 @@ static void slirp_smb_cleanup(SlirpState *s)
static int slirp_smb(SlirpState* s, const char *exported_dir,
struct in_addr vserver_addr)
{
- static int instance;
char smb_conf[128];
char smb_cmdline[128];
struct passwd *passwd;
@@ -505,10 +504,10 @@ static int slirp_smb(SlirpState* s, const char *exported_dir,
return -1;
}
- snprintf(s->smb_dir, sizeof(s->smb_dir), "/tmp/qemu-smb.%ld-%d",
- (long)getpid(), instance++);
- if (mkdir(s->smb_dir, 0700) < 0) {
+ snprintf(s->smb_dir, sizeof(s->smb_dir), "/tmp/qemu-smb.XXXXXX");
+ if (!mkdtemp(s->smb_dir)) {
error_report("could not create samba server dir '%s'", s->smb_dir);
+ s->smb_dir[0] = 0;
return -1;
}
snprintf(smb_conf, sizeof(smb_conf), "%s/%s", s->smb_dir, "smb.conf");
diff --git a/net/socket.c b/net/socket.c
index 5a19aa1881..c752696cbb 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -51,21 +51,12 @@ typedef struct NetSocketState {
static void net_socket_accept(void *opaque);
static void net_socket_writable(void *opaque);
-/* Only read packets from socket when peer can receive them */
-static int net_socket_can_send(void *opaque)
-{
- NetSocketState *s = opaque;
-
- return qemu_can_send_packet(&s->nc);
-}
-
static void net_socket_update_fd_handler(NetSocketState *s)
{
- qemu_set_fd_handler2(s->fd,
- s->read_poll ? net_socket_can_send : NULL,
- s->read_poll ? s->send_fn : NULL,
- s->write_poll ? net_socket_writable : NULL,
- s);
+ qemu_set_fd_handler(s->fd,
+ s->read_poll ? s->send_fn : NULL,
+ s->write_poll ? net_socket_writable : NULL,
+ s);
}
static void net_socket_read_poll(NetSocketState *s, bool enable)
@@ -142,6 +133,15 @@ static ssize_t net_socket_receive_dgram(NetClientState *nc, const uint8_t *buf,
return ret;
}
+static void net_socket_send_completed(NetClientState *nc, ssize_t len)
+{
+ NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
+
+ if (!s->read_poll) {
+ net_socket_read_poll(s, true);
+ }
+}
+
static void net_socket_send(void *opaque)
{
NetSocketState *s = opaque;
@@ -211,9 +211,13 @@ static void net_socket_send(void *opaque)
buf += l;
size -= l;
if (s->index >= s->packet_len) {
- qemu_send_packet(&s->nc, s->buf, s->packet_len);
s->index = 0;
s->state = 0;
+ if (qemu_send_packet_async(&s->nc, s->buf, size,
+ net_socket_send_completed) == 0) {
+ net_socket_read_poll(s, false);
+ break;
+ }
}
break;
}
@@ -234,7 +238,10 @@ static void net_socket_send_dgram(void *opaque)
net_socket_write_poll(s, false);
return;
}
- qemu_send_packet(&s->nc, s->buf, size);
+ if (qemu_send_packet_async(&s->nc, s->buf, size,
+ net_socket_send_completed) == 0) {
+ net_socket_read_poll(s, false);
+ }
}
static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr *localaddr)
diff --git a/net/tap.c b/net/tap.c
index 3a3f522eac..bd01590e8e 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -62,17 +62,15 @@ typedef struct TAPState {
static void launch_script(const char *setup_script, const char *ifname,
int fd, Error **errp);
-static int tap_can_send(void *opaque);
static void tap_send(void *opaque);
static void tap_writable(void *opaque);
static void tap_update_fd_handler(TAPState *s)
{
- qemu_set_fd_handler2(s->fd,
- s->read_poll && s->enabled ? tap_can_send : NULL,
- s->read_poll && s->enabled ? tap_send : NULL,
- s->write_poll && s->enabled ? tap_writable : NULL,
- s);
+ qemu_set_fd_handler(s->fd,
+ s->read_poll && s->enabled ? tap_send : NULL,
+ s->write_poll && s->enabled ? tap_writable : NULL,
+ s);
}
static void tap_read_poll(TAPState *s, bool enable)
@@ -166,13 +164,6 @@ static ssize_t tap_receive(NetClientState *nc, const uint8_t *buf, size_t size)
return tap_write_packet(s, iov, 1);
}
-static int tap_can_send(void *opaque)
-{
- TAPState *s = opaque;
-
- return qemu_can_send_packet(&s->nc);
-}
-
#ifndef __sun__
ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen)
{
@@ -192,7 +183,7 @@ static void tap_send(void *opaque)
int size;
int packets = 0;
- while (qemu_can_send_packet(&s->nc)) {
+ while (true) {
uint8_t *buf = s->buf;
size = tap_read_packet(s->fd, s->buf, sizeof(s->buf));
diff --git a/net/vhost-user.c b/net/vhost-user.c
index f1df26df95..b51bc044b5 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -156,8 +156,9 @@ static int net_vhost_user_init(NetClientState *peer, const char *device,
return 0;
}
-static int net_vhost_chardev_opts(const char *name, const char *value,
- void *opaque)
+static int net_vhost_chardev_opts(void *opaque,
+ const char *name, const char *value,
+ Error **errp)
{
VhostUserChardevProps *props = opaque;
@@ -168,33 +169,34 @@ static int net_vhost_chardev_opts(const char *name, const char *value,
} else if (strcmp(name, "server") == 0) {
props->is_server = true;
} else {
- error_report("vhost-user does not support a chardev"
- " with the following option:\n %s = %s",
- name, value);
+ error_setg(errp,
+ "vhost-user does not support a chardev with option %s=%s",
+ name, value);
return -1;
}
return 0;
}
-static CharDriverState *net_vhost_parse_chardev(const NetdevVhostUserOptions *opts)
+static CharDriverState *net_vhost_parse_chardev(
+ const NetdevVhostUserOptions *opts, Error **errp)
{
CharDriverState *chr = qemu_chr_find(opts->chardev);
VhostUserChardevProps props;
if (chr == NULL) {
- error_report("chardev \"%s\" not found", opts->chardev);
+ error_setg(errp, "chardev \"%s\" not found", opts->chardev);
return NULL;
}
/* inspect chardev opts */
memset(&props, 0, sizeof(props));
- if (qemu_opt_foreach(chr->opts, net_vhost_chardev_opts, &props, true) != 0) {
+ if (qemu_opt_foreach(chr->opts, net_vhost_chardev_opts, &props, errp)) {
return NULL;
}
if (!props.is_socket || !props.is_unix) {
- error_report("chardev \"%s\" is not a unix socket",
- opts->chardev);
+ error_setg(errp, "chardev \"%s\" is not a unix socket",
+ opts->chardev);
return NULL;
}
@@ -203,7 +205,7 @@ static CharDriverState *net_vhost_parse_chardev(const NetdevVhostUserOptions *op
return chr;
}
-static int net_vhost_check_net(QemuOpts *opts, void *opaque)
+static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp)
{
const char *name = opaque;
const char *driver, *netdev;
@@ -218,7 +220,7 @@ static int net_vhost_check_net(QemuOpts *opts, void *opaque)
if (strcmp(netdev, name) == 0 &&
strncmp(driver, virtio_name, strlen(virtio_name)) != 0) {
- error_report("vhost-user requires frontend driver virtio-net-*");
+ error_setg(errp, "vhost-user requires frontend driver virtio-net-*");
return -1;
}
@@ -228,7 +230,6 @@ static int net_vhost_check_net(QemuOpts *opts, void *opaque)
int net_init_vhost_user(const NetClientOptions *opts, const char *name,
NetClientState *peer, Error **errp)
{
- /* FIXME error_setg(errp, ...) on failure */
uint32_t queues;
const NetdevVhostUserOptions *vhost_user_opts;
CharDriverState *chr;
@@ -236,15 +237,14 @@ int net_init_vhost_user(const NetClientOptions *opts, const char *name,
assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
vhost_user_opts = opts->vhost_user;
- chr = net_vhost_parse_chardev(vhost_user_opts);
+ chr = net_vhost_parse_chardev(vhost_user_opts, errp);
if (!chr) {
- error_report("No suitable chardev found");
return -1;
}
/* verify net frontend */
if (qemu_opts_foreach(qemu_find_opts("device"), net_vhost_check_net,
- (char *)name, true) == -1) {
+ (char *)name, errp)) {
return -1;
}
diff --git a/numa.c b/numa.c
index c975fb2682..d227ccc23b 100644
--- a/numa.c
+++ b/numa.c
@@ -125,7 +125,7 @@ static void numa_node_parse(NumaNodeOptions *node, QemuOpts *opts, Error **errp)
max_numa_nodeid = MAX(max_numa_nodeid, nodenr + 1);
}
-static int parse_numa(QemuOpts *opts, void *opaque)
+static int parse_numa(void *opaque, QemuOpts *opts, Error **errp)
{
NumaOptions *object = NULL;
Error *err = NULL;
@@ -216,8 +216,7 @@ void parse_numa_opts(MachineClass *mc)
{
int i;
- if (qemu_opts_foreach(qemu_find_opts("numa"), parse_numa,
- NULL, 1) != 0) {
+ if (qemu_opts_foreach(qemu_find_opts("numa"), parse_numa, NULL, NULL)) {
exit(1);
}
diff --git a/pc-bios/bios-256k.bin b/pc-bios/bios-256k.bin
index c6e25acc90..f86adff128 100644
--- a/pc-bios/bios-256k.bin
+++ b/pc-bios/bios-256k.bin
Binary files differ
diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin
index 46ca37b94c..db835fb5f2 100644
--- a/pc-bios/bios.bin
+++ b/pc-bios/bios.bin
Binary files differ
diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc
index d83347a9ec..540e45a74c 100644
--- a/pc-bios/openbios-ppc
+++ b/pc-bios/openbios-ppc
Binary files differ
diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32
index e2bc9aabd0..0da11883bb 100644
--- a/pc-bios/openbios-sparc32
+++ b/pc-bios/openbios-sparc32
Binary files differ
diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64
index 7a0cdbe6d7..9bf3ce5450 100644
--- a/pc-bios/openbios-sparc64
+++ b/pc-bios/openbios-sparc64
Binary files differ
diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
index 009bb8de1c..746603a315 100644
--- a/pc-bios/s390-ccw/Makefile
+++ b/pc-bios/s390-ccw/Makefile
@@ -10,7 +10,7 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw)
.PHONY : all clean build-all
OBJECTS = start.o main.o bootmap.o sclp-ascii.o virtio.o
-CFLAGS += -fPIE -fno-stack-protector -ffreestanding
+CFLAGS += -fPIE -fno-stack-protector -ffreestanding -fno-delete-null-pointer-checks
LDFLAGS += -Wl,-pie -nostdlib
build-all: s390-ccw.img
diff --git a/pc-bios/vgabios-cirrus.bin b/pc-bios/vgabios-cirrus.bin
index 02227d374f..dde8502909 100644
--- a/pc-bios/vgabios-cirrus.bin
+++ b/pc-bios/vgabios-cirrus.bin
Binary files differ
diff --git a/pc-bios/vgabios-qxl.bin b/pc-bios/vgabios-qxl.bin
index 8a87c2388a..5c43bd222e 100644
--- a/pc-bios/vgabios-qxl.bin
+++ b/pc-bios/vgabios-qxl.bin
Binary files differ
diff --git a/pc-bios/vgabios-stdvga.bin b/pc-bios/vgabios-stdvga.bin
index 00cb73cb8c..b2dd8f91ef 100644
--- a/pc-bios/vgabios-stdvga.bin
+++ b/pc-bios/vgabios-stdvga.bin
Binary files differ
diff --git a/pc-bios/vgabios-virtio.bin b/pc-bios/vgabios-virtio.bin
new file mode 100644
index 0000000000..03ac8a7d4a
--- /dev/null
+++ b/pc-bios/vgabios-virtio.bin
Binary files differ
diff --git a/pc-bios/vgabios-vmware.bin b/pc-bios/vgabios-vmware.bin
index c9a94f9eae..15e21c2a28 100644
--- a/pc-bios/vgabios-vmware.bin
+++ b/pc-bios/vgabios-vmware.bin
Binary files differ
diff --git a/pc-bios/vgabios.bin b/pc-bios/vgabios.bin
index 3e3335df6a..84f1561d92 100644
--- a/pc-bios/vgabios.bin
+++ b/pc-bios/vgabios.bin
Binary files differ
diff --git a/qapi-schema.json b/qapi-schema.json
index 0662a9b445..106008cdeb 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -638,6 +638,25 @@
'returns': 'MigrationParameters' }
##
+# @client_migrate_info
+#
+# Set migration information for remote display. This makes the server
+# ask the client to automatically reconnect using the new parameters
+# once migration finished successfully. Only implemented for SPICE.
+#
+# @protocol: must be "spice"
+# @hostname: migration target hostname
+# @port: #optional spice tcp port for plaintext channels
+# @tls-port: #optional spice tcp port for tls-secured channels
+# @cert-subject: #optional server certificate subject
+#
+# Since: 0.14.0
+##
+{ 'command': 'client_migrate_info',
+ 'data': { 'protocol': 'str', 'hostname': 'str', '*port': 'int',
+ '*tls-port': 'int', '*cert-subject': 'str' } }
+
+##
# @MouseInfo:
#
# Information about a mouse device.
@@ -3727,10 +3746,14 @@
#
# @none: nothing is done
#
+# @inject-nmi: a non-maskable interrupt is injected into the first VCPU (all
+# VCPUS on x86) (since 2.4)
+#
# Since: 2.1
##
{ 'enum': 'WatchdogExpirationAction',
- 'data': [ 'reset', 'shutdown', 'poweroff', 'pause', 'debug', 'none' ] }
+ 'data': [ 'reset', 'shutdown', 'poweroff', 'pause', 'debug', 'none',
+ 'inject-nmi' ] }
##
# @IoOperationType
@@ -3769,3 +3792,6 @@
# Since: 2.1
##
{ 'command': 'rtc-reset-reinjection' }
+
+# Rocker ethernet network switch
+{ 'include': 'qapi/rocker.json' }
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 8411d4f83a..afa9d3d1f3 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -259,6 +259,8 @@
#
# @iops_size: #optional an I/O size in bytes (Since 1.7)
#
+# @group: #optional throttle group name (Since 2.4)
+#
# @cache: the cache mode used for the block device (since: 2.3)
#
# @write_threshold: configured write threshold for the device.
@@ -278,7 +280,7 @@
'*bps_max': 'int', '*bps_rd_max': 'int',
'*bps_wr_max': 'int', '*iops_max': 'int',
'*iops_rd_max': 'int', '*iops_wr_max': 'int',
- '*iops_size': 'int', 'cache': 'BlockdevCacheInfo',
+ '*iops_size': 'int', '*group': 'str', 'cache': 'BlockdevCacheInfo',
'write_threshold': 'int' } }
##
@@ -1062,6 +1064,27 @@
#
# Change I/O throttle limits for a block drive.
#
+# Since QEMU 2.4, each device with I/O limits is member of a throttle
+# group.
+#
+# If two or more devices are members of the same group, the limits
+# will apply to the combined I/O of the whole group in a round-robin
+# fashion. Therefore, setting new I/O limits to a device will affect
+# the whole group.
+#
+# The name of the group can be specified using the 'group' parameter.
+# If the parameter is unset, it is assumed to be the current group of
+# that device. If it's not in any group yet, the name of the device
+# will be used as the name for its group.
+#
+# The 'group' parameter can also be used to move a device to a
+# different group. In this case the limits specified in the parameters
+# will be applied to the new group only.
+#
+# I/O limits can be disabled by setting all of them to 0. In this case
+# the device will be removed from its group and the rest of its
+# members will no be affected. The 'group' parameter is ignored.
+#
# @device: The name of the device
#
# @bps: total throughput limit in bytes per second
@@ -1090,6 +1113,8 @@
#
# @iops_size: #optional an I/O size in bytes (Since 1.7)
#
+# @group: #optional throttle group name (Since 2.4)
+#
# Returns: Nothing on success
# If @device is not a valid block device, DeviceNotFound
#
@@ -1101,7 +1126,7 @@
'*bps_max': 'int', '*bps_rd_max': 'int',
'*bps_wr_max': 'int', '*iops_max': 'int',
'*iops_rd_max': 'int', '*iops_wr_max': 'int',
- '*iops_size': 'int' } }
+ '*iops_size': 'int', '*group': 'str' } }
##
# @block-stream:
diff --git a/qapi/rocker.json b/qapi/rocker.json
new file mode 100644
index 0000000000..2fe7fdfa66
--- /dev/null
+++ b/qapi/rocker.json
@@ -0,0 +1,286 @@
+##
+# @Rocker:
+#
+# Rocker switch information.
+#
+# @name: switch name
+#
+# @id: switch ID
+#
+# @ports: number of front-panel ports
+#
+# Since: 2.4
+##
+{ 'struct': 'RockerSwitch',
+ 'data': { 'name': 'str', 'id': 'uint64', 'ports': 'uint32' } }
+
+##
+# @query-rocker:
+#
+# Return rocker switch information.
+#
+# Returns: @Rocker information
+#
+# Since: 2.4
+##
+{ 'command': 'query-rocker',
+ 'data': { 'name': 'str' },
+ 'returns': 'RockerSwitch' }
+
+##
+# @RockerPortDuplex:
+#
+# An eumeration of port duplex states.
+#
+# @half: half duplex
+#
+# @full: full duplex
+#
+# Since: 2.4
+##
+{ 'enum': 'RockerPortDuplex', 'data': [ 'half', 'full' ] }
+
+##
+# @RockerPortAutoneg:
+#
+# An eumeration of port autoneg states.
+#
+# @off: autoneg is off
+#
+# @on: autoneg is on
+#
+# Since: 2.4
+##
+{ 'enum': 'RockerPortAutoneg', 'data': [ 'off', 'on' ] }
+
+##
+# @RockerPort:
+#
+# Rocker switch port information.
+#
+# @name: port name
+#
+# @enabled: port is enabled for I/O
+#
+# @link-up: physical link is UP on port
+#
+# @speed: port link speed in Mbps
+#
+# @duplex: port link duplex
+#
+# @autoneg: port link autoneg
+#
+# Since: 2.4
+##
+{ 'struct': 'RockerPort',
+ 'data': { 'name': 'str', 'enabled': 'bool', 'link-up': 'bool',
+ 'speed': 'uint32', 'duplex': 'RockerPortDuplex',
+ 'autoneg': 'RockerPortAutoneg' } }
+
+##
+# @query-rocker-ports:
+#
+# Return rocker switch information.
+#
+# Returns: @Rocker information
+#
+# Since: 2.4
+##
+{ 'command': 'query-rocker-ports',
+ 'data': { 'name': 'str' },
+ 'returns': ['RockerPort'] }
+
+##
+# @RockerOfDpaFlowKey:
+#
+# Rocker switch OF-DPA flow key
+#
+# @priority: key priority, 0 being lowest priority
+#
+# @tbl-id: flow table ID
+#
+# @in-pport: #optional physical input port
+#
+# @tunnel-id: #optional tunnel ID
+#
+# @vlan-id: #optional VLAN ID
+#
+# @eth-type: #optional Ethernet header type
+#
+# @eth-src: #optional Ethernet header source MAC address
+#
+# @eth-dst: #optional Ethernet header destination MAC address
+#
+# @ip-proto: #optional IP Header protocol field
+#
+# @ip-tos: #optional IP header TOS field
+#
+# @ip-dst: #optional IP header destination address
+#
+# Note: fields are marked #optional to indicate that they may or may not
+# appear in the flow key depending if they're relevant to the flow key.
+#
+# Since: 2.4
+##
+{ 'struct': 'RockerOfDpaFlowKey',
+ 'data' : { 'priority': 'uint32', 'tbl-id': 'uint32', '*in-pport': 'uint32',
+ '*tunnel-id': 'uint32', '*vlan-id': 'uint16',
+ '*eth-type': 'uint16', '*eth-src': 'str', '*eth-dst': 'str',
+ '*ip-proto': 'uint8', '*ip-tos': 'uint8', '*ip-dst': 'str' } }
+
+##
+# @RockerOfDpaFlowMask:
+#
+# Rocker switch OF-DPA flow mask
+#
+# @in-pport: #optional physical input port
+#
+# @tunnel-id: #optional tunnel ID
+#
+# @vlan-id: #optional VLAN ID
+#
+# @eth-src: #optional Ethernet header source MAC address
+#
+# @eth-dst: #optional Ethernet header destination MAC address
+#
+# @ip-proto: #optional IP Header protocol field
+#
+# @ip-tos: #optional IP header TOS field
+#
+# Note: fields are marked #optional to indicate that they may or may not
+# appear in the flow mask depending if they're relevant to the flow mask.
+#
+# Since: 2.4
+##
+{ 'struct': 'RockerOfDpaFlowMask',
+ 'data' : { '*in-pport': 'uint32', '*tunnel-id': 'uint32',
+ '*vlan-id': 'uint16', '*eth-src': 'str', '*eth-dst': 'str',
+ '*ip-proto': 'uint8', '*ip-tos': 'uint8' } }
+
+##
+# @RockerOfDpaFlowAction:
+#
+# Rocker switch OF-DPA flow action
+#
+# @goto-tbl: #optional next table ID
+#
+# @group-id: #optional group ID
+#
+# @tunnel-lport: #optional tunnel logical port ID
+#
+# @vlan-id: #optional VLAN ID
+#
+# @new-vlan-id: #optional new VLAN ID
+#
+# @out-pport: #optional physical output port
+#
+# Note: fields are marked #optional to indicate that they may or may not
+# appear in the flow action depending if they're relevant to the flow action.
+#
+# Since: 2.4
+##
+{ 'struct': 'RockerOfDpaFlowAction',
+ 'data' : { '*goto-tbl': 'uint32', '*group-id': 'uint32',
+ '*tunnel-lport': 'uint32', '*vlan-id': 'uint16',
+ '*new-vlan-id': 'uint16', '*out-pport': 'uint32' } }
+
+##
+# @RockerOfDpaFlow:
+#
+# Rocker switch OF-DPA flow
+#
+# @cookie: flow unique cookie ID
+#
+# @hits: count of matches (hits) on flow
+#
+# @key: flow key
+#
+# @mask: flow mask
+#
+# @action: flow action
+#
+# Since: 2.4
+##
+{ 'struct': 'RockerOfDpaFlow',
+ 'data': { 'cookie': 'uint64', 'hits': 'uint64', 'key': 'RockerOfDpaFlowKey',
+ 'mask': 'RockerOfDpaFlowMask', 'action': 'RockerOfDpaFlowAction' } }
+
+##
+# @query-rocker-of-dpa-flows:
+#
+# Return rocker OF-DPA flow information.
+#
+# @name: switch name
+#
+# @tbl-id: #optional flow table ID. If tbl-id is not specified, returns
+# flow information for all tables.
+#
+# Returns: @Rocker OF-DPA flow information
+#
+# Since: 2.4
+##
+{ 'command': 'query-rocker-of-dpa-flows',
+ 'data': { 'name': 'str', '*tbl-id': 'uint32' },
+ 'returns': ['RockerOfDpaFlow'] }
+
+##
+# @RockerOfDpaGroup:
+#
+# Rocker switch OF-DPA group
+#
+# @id: group unique ID
+#
+# @type: group type
+#
+# @vlan-id: #optional VLAN ID
+#
+# @pport: #optional physical port number
+#
+# @index: #optional group index, unique with group type
+#
+# @out-pport: #optional output physical port number
+#
+# @group-id: #optional next group ID
+#
+# @set-vlan-id: #optional VLAN ID to set
+#
+# @pop-vlan: #optional pop VLAN headr from packet
+#
+# @group-ids: #optional list of next group IDs
+#
+# @set-eth-src: #optional set source MAC address in Ethernet header
+#
+# @set-eth-dst: #optional set destination MAC address in Ethernet header
+#
+# @ttl-check: #optional perform TTL check
+#
+# Note: fields are marked #optional to indicate that they may or may not
+# appear in the group depending if they're relevant to the group type.
+#
+# Since: 2.4
+##
+{ 'struct': 'RockerOfDpaGroup',
+ 'data': { 'id': 'uint32', 'type': 'uint8', '*vlan-id': 'uint16',
+ '*pport': 'uint32', '*index': 'uint32', '*out-pport': 'uint32',
+ '*group-id': 'uint32', '*set-vlan-id': 'uint16',
+ '*pop-vlan': 'uint8', '*group-ids': ['uint32'],
+ '*set-eth-src': 'str', '*set-eth-dst': 'str',
+ '*ttl-check': 'uint8' } }
+
+##
+# @query-rocker-of-dpa-groups:
+#
+# Return rocker OF-DPA group information.
+#
+# @name: switch name
+#
+# @type: #optional group type. If type is not specified, returns
+# group information for all group types.
+#
+# Returns: @Rocker OF-DPA group information
+#
+# Since: 2.4
+##
+{ 'command': 'query-rocker-of-dpa-groups',
+ 'data': { 'name': 'str', '*type': 'uint8' },
+ 'returns': ['RockerOfDpaGroup'] }
diff --git a/qdev-monitor.c b/qdev-monitor.c
index 1d87f573e8..d71d1ee520 100644
--- a/qdev-monitor.c
+++ b/qdev-monitor.c
@@ -42,9 +42,9 @@ static const QDevAlias qdev_alias_table[] = {
{ "virtio-serial-pci", "virtio-serial", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
{ "virtio-balloon-pci", "virtio-balloon",
QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-blk-s390", "virtio-blk", QEMU_ARCH_S390X },
- { "virtio-net-s390", "virtio-net", QEMU_ARCH_S390X },
- { "virtio-serial-s390", "virtio-serial", QEMU_ARCH_S390X },
+ { "virtio-blk-ccw", "virtio-blk", QEMU_ARCH_S390X },
+ { "virtio-net-ccw", "virtio-net", QEMU_ARCH_S390X },
+ { "virtio-serial-ccw", "virtio-serial", QEMU_ARCH_S390X },
{ "lsi53c895a", "lsi" },
{ "ich9-ahci", "ahci" },
{ "kvm-pci-assign", "pci-assign" },
@@ -143,7 +143,8 @@ static void qdev_print_devinfos(bool show_no_user)
g_slist_free(list);
}
-static int set_property(const char *name, const char *value, void *opaque)
+static int set_property(void *opaque, const char *name, const char *value,
+ Error **errp)
{
Object *obj = opaque;
Error *err = NULL;
@@ -564,7 +565,7 @@ DeviceState *qdev_device_add(QemuOpts *opts)
}
/* set properties */
- if (qemu_opt_foreach(opts, set_property, dev, 1) != 0) {
+ if (qemu_opt_foreach(opts, set_property, dev, NULL)) {
object_unparent(OBJECT(dev));
object_unref(OBJECT(dev));
return NULL;
@@ -822,15 +823,19 @@ int qemu_global_option(const char *str)
QemuOpts *opts;
int rc, offset;
- rc = sscanf(str, "%63[^.].%63[^=]%n", driver, property, &offset);
- if (rc < 2 || str[offset] != '=') {
- error_report("can't parse: \"%s\"", str);
+ rc = sscanf(str, "%63[^.=].%63[^=]%n", driver, property, &offset);
+ if (rc == 2 && str[offset] == '=') {
+ opts = qemu_opts_create(&qemu_global_opts, NULL, 0, &error_abort);
+ qemu_opt_set(opts, "driver", driver, &error_abort);
+ qemu_opt_set(opts, "property", property, &error_abort);
+ qemu_opt_set(opts, "value", str + offset + 1, &error_abort);
+ return 0;
+ }
+
+ opts = qemu_opts_parse(&qemu_global_opts, str, false);
+ if (!opts) {
return -1;
}
- opts = qemu_opts_create(&qemu_global_opts, NULL, 0, &error_abort);
- qemu_opt_set(opts, "driver", driver, &error_abort);
- qemu_opt_set(opts, "property", property, &error_abort);
- qemu_opt_set(opts, "value", str + offset + 1, &error_abort);
return 0;
}
diff --git a/qemu-nbd.c b/qemu-nbd.c
index 7e690fff7e..5af6d11e33 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -53,6 +53,7 @@ static int persistent = 0;
static enum { RUNNING, TERMINATE, TERMINATING, TERMINATED } state;
static int shared = 1;
static int nb_fds;
+static int server_fd;
static void usage(const char *name)
{
@@ -340,7 +341,7 @@ out:
return (void *) EXIT_FAILURE;
}
-static int nbd_can_accept(void *opaque)
+static int nbd_can_accept(void)
{
return nb_fds < shared;
}
@@ -351,19 +352,21 @@ static void nbd_export_closed(NBDExport *exp)
state = TERMINATED;
}
+static void nbd_update_server_fd_handler(int fd);
+
static void nbd_client_closed(NBDClient *client)
{
nb_fds--;
if (nb_fds == 0 && !persistent && state == RUNNING) {
state = TERMINATE;
}
+ nbd_update_server_fd_handler(server_fd);
qemu_notify_event();
nbd_client_put(client);
}
static void nbd_accept(void *opaque)
{
- int server_fd = (uintptr_t) opaque;
struct sockaddr_in addr;
socklen_t addr_len = sizeof(addr);
@@ -380,12 +383,22 @@ static void nbd_accept(void *opaque)
if (nbd_client_new(exp, fd, nbd_client_closed)) {
nb_fds++;
+ nbd_update_server_fd_handler(server_fd);
} else {
shutdown(fd, 2);
close(fd);
}
}
+static void nbd_update_server_fd_handler(int fd)
+{
+ if (nbd_can_accept()) {
+ qemu_set_fd_handler(fd, nbd_accept, NULL, (void *)(uintptr_t)fd);
+ } else {
+ qemu_set_fd_handler(fd, NULL, NULL, NULL);
+ }
+}
+
int main(int argc, char **argv)
{
BlockBackend *blk;
@@ -761,8 +774,8 @@ int main(int argc, char **argv)
memset(&client_thread, 0, sizeof(client_thread));
}
- qemu_set_fd_handler2(fd, nbd_can_accept, nbd_accept, NULL,
- (void *)(uintptr_t)fd);
+ server_fd = fd;
+ nbd_update_server_fd_handler(fd);
/* now when the initialization is (almost) complete, chdir("/")
* to free any busy filesystems */
diff --git a/qemu-options.hx b/qemu-options.hx
index 7edd1f18ce..5438f9862c 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -171,11 +171,13 @@ Set parameter @var{arg} for item @var{id} of type @var{group}\n"
ETEXI
DEF("global", HAS_ARG, QEMU_OPTION_global,
- "-global driver.prop=value\n"
+ "-global driver.property=value\n"
+ "-global driver=driver,property=property,value=value\n"
" set a global default for a driver property\n",
QEMU_ARCH_ALL)
STEXI
@item -global @var{driver}.@var{prop}=@var{value}
+@itemx -global driver=@var{driver},property=@var{property},value=@var{value}
@findex -global
Set default value of @var{driver}'s property @var{prop} to @var{value}, e.g.:
@@ -186,6 +188,9 @@ qemu-system-i386 -global ide-drive.physical_block_size=4096 -drive file=file,if=
In particular, you can use this to set driver properties for devices which are
created automatically by the machine model. To create a device which is not
created automatically and set properties on it, use -@option{device}.
+
+The two syntaxes are equivalent. The longer one works for drivers whose name
+contains a dot.
ETEXI
DEF("boot", HAS_ARG, QEMU_OPTION_boot,
@@ -241,8 +246,7 @@ ETEXI
DEF("m", HAS_ARG, QEMU_OPTION_m,
"-m[emory] [size=]megs[,slots=n,maxmem=size]\n"
" configure guest RAM\n"
- " size: initial amount of guest memory (default: "
- stringify(DEFAULT_RAM_SIZE) "MiB)\n"
+ " size: initial amount of guest memory\n"
" slots: number of hotplug slots (default: none)\n"
" maxmem: maximum amount of guest memory (default: none)\n"
"NOTE: Some architectures might enforce a specific granularity\n",
@@ -464,6 +468,7 @@ DEF("drive", HAS_ARG, QEMU_OPTION_drive,
" [[,bps_max=bm]|[[,bps_rd_max=rm][,bps_wr_max=wm]]]\n"
" [[,iops_max=im]|[[,iops_rd_max=irm][,iops_wr_max=iwm]]]\n"
" [[,iops_size=is]]\n"
+ " [[,group=g]]\n"
" use 'file' as a drive image\n", QEMU_ARCH_ALL)
STEXI
@item -drive @var{option}[,@var{option}[,@var{option}[,...]]]
@@ -1099,7 +1104,7 @@ Rotate graphical output some deg left (only PXA LCD).
ETEXI
DEF("vga", HAS_ARG, QEMU_OPTION_vga,
- "-vga [std|cirrus|vmware|qxl|xenfb|tcx|cg3|none]\n"
+ "-vga [std|cirrus|vmware|qxl|xenfb|tcx|cg3|virtio|none]\n"
" select video card type\n", QEMU_ARCH_ALL)
STEXI
@item -vga @var{type}
@@ -1132,6 +1137,8 @@ fixed resolution of 1024x768.
(sun4m only) Sun cgthree framebuffer. This is a simple 8-bit framebuffer
for sun4m machines available in both 1024x768 (OpenBIOS) and 1152x900 (OBP)
resolutions aimed at people wishing to run older Solaris versions.
+@item virtio
+Virtio VGA card.
@item none
Disable VGA card.
@end table
@@ -2682,6 +2689,17 @@ STEXI
@table @option
ETEXI
+DEF("fw_cfg", HAS_ARG, QEMU_OPTION_fwcfg,
+ "-fw_cfg [name=]<name>,file=<file>\n"
+ " add named fw_cfg entry from file\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -fw_cfg [name=]@var{name},file=@var{file}
+@findex -fw_cfg
+Add named fw_cfg entry from file. @var{name} determines the name of
+the entry in the fw_cfg file directory exposed to the guest.
+ETEXI
+
DEF("serial", HAS_ARG, QEMU_OPTION_serial, \
"-serial dev redirect the serial port to char device 'dev'\n",
QEMU_ARCH_ALL)
@@ -3100,9 +3118,10 @@ re-inject them.
ETEXI
DEF("icount", HAS_ARG, QEMU_OPTION_icount, \
- "-icount [shift=N|auto][,align=on|off]\n" \
+ "-icount [shift=N|auto][,align=on|off][,sleep=no]\n" \
" enable virtual instruction counter with 2^N clock ticks per\n" \
- " instruction and enable aligning the host and virtual clocks\n", QEMU_ARCH_ALL)
+ " instruction, enable aligning the host and virtual clocks\n" \
+ " or disable real time cpu sleeping\n", QEMU_ARCH_ALL)
STEXI
@item -icount [shift=@var{N}|auto]
@findex -icount
@@ -3111,6 +3130,13 @@ instruction every 2^@var{N} ns of virtual time. If @code{auto} is specified
then the virtual cpu speed will be automatically adjusted to keep virtual
time within a few seconds of real time.
+When the virtual cpu is sleeping, the virtual time will advance at default
+speed unless @option{sleep=no} is specified.
+With @option{sleep=no}, the virtual time will jump to the next timer deadline
+instantly whenever the virtual cpu goes to sleep mode and will not advance
+if no timer is enabled. This behavior give deterministic execution times from
+the guest point of view.
+
Note that while this option can give deterministic behavior, it does not
provide cycle accurate emulation. Modern CPUs contain superscalar out of
order cores with complex cache hierarchies. The number of instructions
@@ -3129,7 +3155,7 @@ when the shift value is high (how high depends on the host machine).
ETEXI
DEF("watchdog", HAS_ARG, QEMU_OPTION_watchdog, \
- "-watchdog i6300esb|ib700\n" \
+ "-watchdog model\n" \
" enable virtual hardware watchdog [default=none]\n",
QEMU_ARCH_ALL)
STEXI
@@ -3137,16 +3163,24 @@ STEXI
@findex -watchdog
Create a virtual hardware watchdog device. Once enabled (by a guest
action), the watchdog must be periodically polled by an agent inside
-the guest or else the guest will be restarted.
+the guest or else the guest will be restarted. Choose a model for
+which your guest has drivers.
-The @var{model} is the model of hardware watchdog to emulate. Choices
-for model are: @code{ib700} (iBASE 700) which is a very simple ISA
-watchdog with a single timer, or @code{i6300esb} (Intel 6300ESB I/O
-controller hub) which is a much more featureful PCI-based dual-timer
-watchdog. Choose a model for which your guest has drivers.
-
-Use @code{-watchdog help} to list available hardware models. Only one
+The @var{model} is the model of hardware watchdog to emulate. Use
+@code{-watchdog help} to list available hardware models. Only one
watchdog can be enabled for a guest.
+
+The following models may be available:
+@table @option
+@item ib700
+iBASE 700 is a very simple ISA watchdog with a single timer.
+@item i6300esb
+Intel 6300ESB I/O controller hub is a much more featureful PCI-based
+dual-timer watchdog.
+@item diag288
+A virtual watchdog for s390x backed by the diagnose 288 hypercall
+(currently KVM only).
+@end table
ETEXI
DEF("watchdog-action", HAS_ARG, QEMU_OPTION_watchdog_action, \
@@ -3239,7 +3273,9 @@ DEF("incoming", HAS_ARG, QEMU_OPTION_incoming, \
"-incoming fd:fd\n" \
"-incoming exec:cmdline\n" \
" accept incoming migration on given file descriptor\n" \
- " or from given external command\n",
+ " or from given external command\n" \
+ "-incoming defer\n" \
+ " wait for the URI to be specified via migrate_incoming\n",
QEMU_ARCH_ALL)
STEXI
@item -incoming tcp:[@var{host}]:@var{port}[,to=@var{maxport}][,ipv4][,ipv6]
@@ -3255,6 +3291,11 @@ Accept incoming migration from a given filedescriptor.
@item -incoming exec:@var{cmdline}
Accept incoming migration as an output from specified external command.
+
+@item -incoming defer
+Wait for the URI to be specified via migrate_incoming. The monitor can
+be used to change settings (such as migration parameters) prior to issuing
+the migrate_incoming to allow the migration to begin.
ETEXI
DEF("nodefaults", 0, QEMU_OPTION_nodefaults, \
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 14e109eb5c..1db6524e97 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -784,23 +784,23 @@ EQMP
.name = "client_migrate_info",
.args_type = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?",
.params = "protocol hostname port tls-port cert-subject",
- .help = "send migration info to spice/vnc client",
- .mhandler.cmd_new = client_migrate_info,
+ .help = "set migration information for remote display",
+ .mhandler.cmd_new = qmp_marshal_input_client_migrate_info,
},
SQMP
client_migrate_info
-------------------
+-------------------
-Set the spice/vnc connection info for the migration target. The spice/vnc
-server will ask the spice/vnc client to automatically reconnect using the
-new parameters (if specified) once the vm migration finished successfully.
+Set migration information for remote display. This makes the server
+ask the client to automatically reconnect using the new parameters
+once migration finished successfully. Only implemented for SPICE.
Arguments:
-- "protocol": protocol: "spice" or "vnc" (json-string)
+- "protocol": must be "spice" (json-string)
- "hostname": migration target hostname (json-string)
-- "port": spice/vnc tcp port for plaintext channels (json-int, optional)
+- "port": spice tcp port for plaintext channels (json-int, optional)
- "tls-port": spice tcp port for tls-secured channels (json-int, optional)
- "cert-subject": server certificate subject (json-string, optional)
@@ -1853,7 +1853,7 @@ EQMP
{
.name = "block_set_io_throttle",
- .args_type = "device:B,bps:l,bps_rd:l,bps_wr:l,iops:l,iops_rd:l,iops_wr:l,bps_max:l?,bps_rd_max:l?,bps_wr_max:l?,iops_max:l?,iops_rd_max:l?,iops_wr_max:l?,iops_size:l?",
+ .args_type = "device:B,bps:l,bps_rd:l,bps_wr:l,iops:l,iops_rd:l,iops_wr:l,bps_max:l?,bps_rd_max:l?,bps_wr_max:l?,iops_max:l?,iops_rd_max:l?,iops_wr_max:l?,iops_size:l?,group:s?",
.mhandler.cmd_new = qmp_marshal_input_block_set_io_throttle,
},
@@ -1879,6 +1879,7 @@ Arguments:
- "iops_rd_max": read I/O operations max (json-int)
- "iops_wr_max": write I/O operations max (json-int)
- "iops_size": I/O size in bytes when limiting (json-int)
+- "group": throttle group name (json-string)
Example:
@@ -4165,3 +4166,106 @@ Example:
<- { "return": {} }
EQMP
+
+ {
+ .name = "query-rocker",
+ .args_type = "name:s",
+ .mhandler.cmd_new = qmp_marshal_input_query_rocker,
+ },
+
+SQMP
+Show rocker switch
+------------------
+
+Arguments:
+
+- "name": switch name
+
+Example:
+
+-> { "execute": "query-rocker", "arguments": { "name": "sw1" } }
+<- { "return": {"name": "sw1", "ports": 2, "id": 1327446905938}}
+
+EQMP
+
+ {
+ .name = "query-rocker-ports",
+ .args_type = "name:s",
+ .mhandler.cmd_new = qmp_marshal_input_query_rocker_ports,
+ },
+
+SQMP
+Show rocker switch ports
+------------------------
+
+Arguments:
+
+- "name": switch name
+
+Example:
+
+-> { "execute": "query-rocker-ports", "arguments": { "name": "sw1" } }
+<- { "return": [ {"duplex": "full", "enabled": true, "name": "sw1.1",
+ "autoneg": "off", "link-up": true, "speed": 10000},
+ {"duplex": "full", "enabled": true, "name": "sw1.2",
+ "autoneg": "off", "link-up": true, "speed": 10000}
+ ]}
+
+EQMP
+
+ {
+ .name = "query-rocker-of-dpa-flows",
+ .args_type = "name:s,tbl-id:i?",
+ .mhandler.cmd_new = qmp_marshal_input_query_rocker_of_dpa_flows,
+ },
+
+SQMP
+Show rocker switch OF-DPA flow tables
+-------------------------------------
+
+Arguments:
+
+- "name": switch name
+- "tbl-id": (optional) flow table ID
+
+Example:
+
+-> { "execute": "query-rocker-of-dpa-flows", "arguments": { "name": "sw1" } }
+<- { "return": [ {"key": {"in-pport": 0, "priority": 1, "tbl-id": 0},
+ "hits": 138,
+ "cookie": 0,
+ "action": {"goto-tbl": 10},
+ "mask": {"in-pport": 4294901760}
+ },
+ {...more...},
+ ]}
+
+EQMP
+
+ {
+ .name = "query-rocker-of-dpa-groups",
+ .args_type = "name:s,type:i?",
+ .mhandler.cmd_new = qmp_marshal_input_query_rocker_of_dpa_groups,
+ },
+
+SQMP
+Show rocker OF-DPA group tables
+-------------------------------
+
+Arguments:
+
+- "name": switch name
+- "type": (optional) group type
+
+Example:
+
+-> { "execute": "query-rocker-of-dpa-groups", "arguments": { "name": "sw1" } }
+<- { "return": [ {"type": 0, "out-pport": 2, "pport": 2, "vlan-id": 3841,
+ "pop-vlan": 1, "id": 251723778},
+ {"type": 0, "out-pport": 0, "pport": 0, "vlan-id": 3841,
+ "pop-vlan": 1, "id": 251723776},
+ {"type": 0, "out-pport": 1, "pport": 1, "vlan-id": 3840,
+ "pop-vlan": 1, "id": 251658241},
+ {"type": 0, "out-pport": 0, "pport": 0, "vlan-id": 3840,
+ "pop-vlan": 1, "id": 251658240}
+ ]}
diff --git a/qobject/qdict.c b/qobject/qdict.c
index ea239f082e..190791becf 100644
--- a/qobject/qdict.c
+++ b/qobject/qdict.c
@@ -477,6 +477,39 @@ static void qdict_destroy_obj(QObject *obj)
g_free(qdict);
}
+/**
+ * qdict_copy_default(): If no entry mapped by 'key' exists in 'dst' yet, the
+ * value of 'key' in 'src' is copied there (and the refcount increased
+ * accordingly).
+ */
+void qdict_copy_default(QDict *dst, QDict *src, const char *key)
+{
+ QObject *val;
+
+ if (qdict_haskey(dst, key)) {
+ return;
+ }
+
+ val = qdict_get(src, key);
+ if (val) {
+ qobject_incref(val);
+ qdict_put_obj(dst, key, val);
+ }
+}
+
+/**
+ * qdict_set_default_str(): If no entry mapped by 'key' exists in 'dst' yet, a
+ * new QString initialised by 'val' is put there.
+ */
+void qdict_set_default_str(QDict *dst, const char *key, const char *val)
+{
+ if (qdict_haskey(dst, key)) {
+ return;
+ }
+
+ qdict_put(dst, key, qstring_from_str(val));
+}
+
static void qdict_flatten_qdict(QDict *qdict, QDict *target,
const char *prefix);
@@ -597,17 +630,21 @@ void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start)
}
}
-static bool qdict_has_prefixed_entries(const QDict *src, const char *start)
+static int qdict_count_prefixed_entries(const QDict *src, const char *start)
{
const QDictEntry *entry;
+ int count = 0;
for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) {
if (strstart(entry->key, start, NULL)) {
- return true;
+ if (count == INT_MAX) {
+ return -ERANGE;
+ }
+ count++;
}
}
- return false;
+ return count;
}
/**
@@ -646,7 +683,8 @@ void qdict_array_split(QDict *src, QList **dst)
snprintf_ret = snprintf(prefix, 32, "%u.", i);
assert(snprintf_ret < 32);
- is_subqdict = qdict_has_prefixed_entries(src, prefix);
+ /* Overflow is the same as positive non-zero results */
+ is_subqdict = qdict_count_prefixed_entries(src, prefix);
// There may be either a single subordinate object (named "%u") or
// multiple objects (each with a key prefixed "%u."), but not both.
@@ -667,6 +705,71 @@ void qdict_array_split(QDict *src, QList **dst)
}
/**
+ * qdict_array_entries(): Returns the number of direct array entries if the
+ * sub-QDict of src specified by the prefix in subqdict (or src itself for
+ * prefix == "") is valid as an array, i.e. the length of the created list if
+ * the sub-QDict would become empty after calling qdict_array_split() on it. If
+ * the array is not valid, -EINVAL is returned.
+ */
+int qdict_array_entries(QDict *src, const char *subqdict)
+{
+ const QDictEntry *entry;
+ unsigned i;
+ unsigned entries = 0;
+ size_t subqdict_len = strlen(subqdict);
+
+ assert(!subqdict_len || subqdict[subqdict_len - 1] == '.');
+
+ /* qdict_array_split() loops until UINT_MAX, but as we want to return
+ * negative errors, we only have a signed return value here. Any additional
+ * entries will lead to -EINVAL. */
+ for (i = 0; i < INT_MAX; i++) {
+ QObject *subqobj;
+ int subqdict_entries;
+ size_t slen = 32 + subqdict_len;
+ char indexstr[slen], prefix[slen];
+ size_t snprintf_ret;
+
+ snprintf_ret = snprintf(indexstr, slen, "%s%u", subqdict, i);
+ assert(snprintf_ret < slen);
+
+ subqobj = qdict_get(src, indexstr);
+
+ snprintf_ret = snprintf(prefix, slen, "%s%u.", subqdict, i);
+ assert(snprintf_ret < slen);
+
+ subqdict_entries = qdict_count_prefixed_entries(src, prefix);
+ if (subqdict_entries < 0) {
+ return subqdict_entries;
+ }
+
+ /* There may be either a single subordinate object (named "%u") or
+ * multiple objects (each with a key prefixed "%u."), but not both. */
+ if (subqobj && subqdict_entries) {
+ return -EINVAL;
+ } else if (!subqobj && !subqdict_entries) {
+ break;
+ }
+
+ entries += subqdict_entries ? subqdict_entries : 1;
+ }
+
+ /* Consider everything handled that isn't part of the given sub-QDict */
+ for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) {
+ if (!strstart(qdict_entry_key(entry), subqdict, NULL)) {
+ entries++;
+ }
+ }
+
+ /* Anything left in the sub-QDict that wasn't handled? */
+ if (qdict_size(src) != entries) {
+ return -EINVAL;
+ }
+
+ return i;
+}
+
+/**
* qdict_join(): Absorb the src QDict into the dest QDict, that is, move all
* elements from src to dest.
*
diff --git a/qom/object.c b/qom/object.c
index b8dff43297..96abd347b1 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -1266,6 +1266,22 @@ out:
g_free(full_type);
}
+void object_property_add_const_link(Object *obj, const char *name,
+ Object *target, Error **errp)
+{
+ char *link_type;
+ ObjectProperty *op;
+
+ link_type = g_strdup_printf("link<%s>", object_get_typename(target));
+ op = object_property_add(obj, name, link_type,
+ object_get_child_property, NULL,
+ NULL, target, errp);
+ if (op != NULL) {
+ op->resolve = object_resolve_child_property;
+ }
+ g_free(link_type);
+}
+
gchar *object_get_canonical_path_component(Object *obj)
{
ObjectProperty *prop = NULL;
diff --git a/roms/Makefile b/roms/Makefile
index 610b534628..7b3f156321 100644
--- a/roms/Makefile
+++ b/roms/Makefile
@@ -1,5 +1,5 @@
-vgabios_variants := stdvga cirrus vmware qxl isavga
+vgabios_variants := stdvga cirrus vmware qxl isavga virtio
vgabios_targets := $(subst -isavga,,$(patsubst %,vgabios-%.bin,$(vgabios_variants)))
pxerom_variants := e1000 eepro100 ne2k_pci pcnet rtl8139 virtio
pxerom_targets := 8086100e 80861209 10500940 10222000 10ec8139 1af41000
@@ -34,6 +34,9 @@ powerpc64_cross_prefix := $(call find-cross-prefix,powerpc64)
powerpc_cross_prefix := $(call find-cross-prefix,powerpc)
x86_64_cross_prefix := $(call find-cross-prefix,x86_64)
+# tag our seabios builds
+SEABIOS_VERSION="$(shell cd seabios; git describe --tags --long) by qemu-project.org"
+
#
# EfiRom utility is shipped with edk2 / tianocore, in BaseTools/
#
@@ -75,10 +78,12 @@ build-seabios-config-%: config.%
mkdir -p seabios/builds/$*
cp $< seabios/builds/$*/.config
$(MAKE) -C seabios \
+ VERSION=$(SEABIOS_VERSION) \
CROSS_COMPILE=$(x86_64_cross_prefix) \
KCONFIG_CONFIG=$(CURDIR)/seabios/builds/$*/.config \
OUT=$(CURDIR)/seabios/builds/$*/ oldnoconfig
$(MAKE) -C seabios \
+ VERSION=$(SEABIOS_VERSION) \
CROSS_COMPILE=$(x86_64_cross_prefix) \
KCONFIG_CONFIG=$(CURDIR)/seabios/builds/$*/.config \
OUT=$(CURDIR)/seabios/builds/$*/ all
diff --git a/roms/config.vga-virtio b/roms/config.vga-virtio
new file mode 100644
index 0000000000..aa7a15ba11
--- /dev/null
+++ b/roms/config.vga-virtio
@@ -0,0 +1,6 @@
+CONFIG_BUILD_VGABIOS=y
+CONFIG_VGA_BOCHS=y
+CONFIG_VGA_PCI=y
+CONFIG_OVERRIDE_PCI_ID=y
+CONFIG_VGA_VID=0x1af4
+CONFIG_VGA_DID=0x1050
diff --git a/roms/openbios b/roms/openbios
-Subproject 5d3db901435ef5a114c9d89461dd0f6d1ef1d44
+Subproject 18f02b14de795c1aab4fe23c1810bfd0944da6a
diff --git a/roms/seabios b/roms/seabios
-Subproject 4adadbde6904807de2e990c0af839ad0cc97780
+Subproject 33fbe13a3e2a01e0ba1087a8feed801a0451db2
diff --git a/scripts/analyze-migration.py b/scripts/analyze-migration.py
index 0c8b22f2aa..f6894bece9 100755
--- a/scripts/analyze-migration.py
+++ b/scripts/analyze-migration.py
@@ -474,6 +474,7 @@ class MigrationDump(object):
QEMU_VM_SECTION_FULL = 0x04
QEMU_VM_SUBSECTION = 0x05
QEMU_VM_VMDESCRIPTION = 0x06
+ QEMU_VM_SECTION_FOOTER= 0x7e
def __init__(self, filename):
self.section_classes = { ( 'ram', 0 ) : [ RamSection, None ],
@@ -526,6 +527,10 @@ class MigrationDump(object):
elif section_type == self.QEMU_VM_SECTION_PART or section_type == self.QEMU_VM_SECTION_END:
section_id = file.read32()
self.sections[section_id].read()
+ elif section_type == self.QEMU_VM_SECTION_FOOTER:
+ read_section_id = file.read32()
+ if read_section_id != section_id:
+ raise Exception("Mismatched section footer: %x vs %x" % (read_section_id, section_id))
else:
raise Exception("Unknown section type: %d" % section_type)
file.close()
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 6bd0b13759..d28a6b07be 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -12,9 +12,8 @@
from ordereddict import OrderedDict
from qapi import *
-def generate_fwd_struct(name, members, builtin_type=False):
- if builtin_type:
- return mcgen('''
+def generate_fwd_builtin(name):
+ return mcgen('''
typedef struct %(name)sList
{
@@ -25,9 +24,10 @@ typedef struct %(name)sList
struct %(name)sList *next;
} %(name)sList;
''',
- type=c_type(name),
- name=name)
+ type=c_type(name),
+ name=name)
+def generate_fwd_struct(name):
return mcgen('''
typedef struct %(name)s %(name)s;
@@ -43,7 +43,7 @@ typedef struct %(name)sList
''',
name=c_name(name))
-def generate_fwd_enum_struct(name, members):
+def generate_fwd_enum_struct(name):
return mcgen('''
typedef struct %(name)sList
{
@@ -75,7 +75,6 @@ def generate_struct_fields(members):
def generate_struct(expr):
structname = expr.get('struct', "")
- fieldname = expr.get('field', "")
members = expr['data']
base = expr.get('base')
@@ -98,12 +97,9 @@ struct %(name)s
char qapi_dummy_field_for_empty_struct;
''')
- if len(fieldname):
- fieldname = " " + fieldname
ret += mcgen('''
-}%(field)s;
-''',
- field=fieldname)
+};
+''')
return ret
@@ -329,30 +325,29 @@ fdecl.write(mcgen('''
'''))
exprs = parse_schema(input_file)
-exprs = filter(lambda expr: not expr.has_key('gen'), exprs)
fdecl.write(guardstart("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
for typename in builtin_types.keys():
- fdecl.write(generate_fwd_struct(typename, None, builtin_type=True))
+ fdecl.write(generate_fwd_builtin(typename))
fdecl.write(guardend("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
for expr in exprs:
ret = "\n"
if expr.has_key('struct'):
- ret += generate_fwd_struct(expr['struct'], expr['data'])
+ ret += generate_fwd_struct(expr['struct'])
elif expr.has_key('enum'):
ret += generate_enum(expr['enum'], expr['data']) + "\n"
- ret += generate_fwd_enum_struct(expr['enum'], expr['data'])
+ ret += generate_fwd_enum_struct(expr['enum'])
fdef.write(generate_enum_lookup(expr['enum'], expr['data']))
elif expr.has_key('union'):
- ret += generate_fwd_struct(expr['union'], expr['data']) + "\n"
+ ret += generate_fwd_struct(expr['union']) + "\n"
enum_define = discriminator_find_enum_define(expr)
if not enum_define:
ret += generate_enum('%sKind' % expr['union'], expr['data'].keys())
fdef.write(generate_enum_lookup('%sKind' % expr['union'],
expr['data'].keys()))
elif expr.has_key('alternate'):
- ret += generate_fwd_struct(expr['alternate'], expr['data']) + "\n"
+ ret += generate_fwd_struct(expr['alternate']) + "\n"
ret += generate_enum('%sKind' % expr['alternate'], expr['data'].keys())
fdef.write(generate_enum_lookup('%sKind' % expr['alternate'],
expr['data'].keys()))
diff --git a/scripts/qapi.py b/scripts/qapi.py
index f96a7772e5..06d7fc2848 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -65,6 +65,10 @@ union_types = []
events = []
all_names = {}
+#
+# Parsing the schema into expressions
+#
+
def error_path(parent):
res = ""
while parent:
@@ -75,7 +79,7 @@ def error_path(parent):
class QAPISchemaError(Exception):
def __init__(self, schema, msg):
- self.input_file = schema.input_file
+ self.fname = schema.fname
self.msg = msg
self.col = 1
self.line = schema.line
@@ -84,11 +88,11 @@ class QAPISchemaError(Exception):
self.col = (self.col + 7) % 8 + 1
else:
self.col += 1
- self.info = schema.parent_info
+ self.info = schema.incl_info
def __str__(self):
return error_path(self.info) + \
- "%s:%d:%d: %s" % (self.input_file, self.line, self.col, self.msg)
+ "%s:%d:%d: %s" % (self.fname, self.line, self.col, self.msg)
class QAPIExprError(Exception):
def __init__(self, expr_info, msg):
@@ -101,19 +105,12 @@ class QAPIExprError(Exception):
class QAPISchema:
- def __init__(self, fp, input_relname=None, include_hist=[],
- previously_included=[], parent_info=None):
- """ include_hist is a stack used to detect inclusion cycles
- previously_included is a global state used to avoid multiple
- inclusions of the same file"""
- input_fname = os.path.abspath(fp.name)
- if input_relname is None:
- input_relname = fp.name
- self.input_dir = os.path.dirname(input_fname)
- self.input_file = input_relname
- self.include_hist = include_hist + [(input_relname, input_fname)]
- previously_included.append(input_fname)
- self.parent_info = parent_info
+ def __init__(self, fp, previously_included = [], incl_info = None):
+ abs_fname = os.path.abspath(fp.name)
+ fname = fp.name
+ self.fname = fname
+ previously_included.append(abs_fname)
+ self.incl_info = incl_info
self.src = fp.read()
if self.src == '' or self.src[-1] != '\n':
self.src += '\n'
@@ -124,7 +121,8 @@ class QAPISchema:
self.accept()
while self.tok != None:
- expr_info = {'file': input_relname, 'line': self.line, 'parent': self.parent_info}
+ expr_info = {'file': fname, 'line': self.line,
+ 'parent': self.incl_info}
expr = self.get_expr(False)
if isinstance(expr, dict) and "include" in expr:
if len(expr) != 1:
@@ -134,21 +132,25 @@ class QAPISchema:
raise QAPIExprError(expr_info,
'Expected a file name (string), got: %s'
% include)
- include_path = os.path.join(self.input_dir, include)
- for elem in self.include_hist:
- if include_path == elem[1]:
+ incl_abs_fname = os.path.join(os.path.dirname(abs_fname),
+ include)
+ # catch inclusion cycle
+ inf = expr_info
+ while inf:
+ if incl_abs_fname == os.path.abspath(inf['file']):
raise QAPIExprError(expr_info, "Inclusion loop for %s"
% include)
+ inf = inf['parent']
# skip multiple include of the same file
- if include_path in previously_included:
+ if incl_abs_fname in previously_included:
continue
try:
- fobj = open(include_path, 'r')
+ fobj = open(incl_abs_fname, 'r')
except IOError, e:
raise QAPIExprError(expr_info,
'%s: %s' % (e.strerror, include))
- exprs_include = QAPISchema(fobj, include, self.include_hist,
- previously_included, expr_info)
+ exprs_include = QAPISchema(fobj, previously_included,
+ expr_info)
self.exprs.extend(exprs_include.exprs)
else:
expr_elem = {'expr': expr,
@@ -219,20 +221,18 @@ class QAPISchema:
return
else:
string += ch
- elif self.tok in "tfn":
- val = self.src[self.cursor - 1:]
- if val.startswith("true"):
- self.val = True
- self.cursor += 3
- return
- elif val.startswith("false"):
- self.val = False
- self.cursor += 4
- return
- elif val.startswith("null"):
- self.val = None
- self.cursor += 3
- return
+ elif self.src.startswith("true", self.pos):
+ self.val = True
+ self.cursor += 3
+ return
+ elif self.src.startswith("false", self.pos):
+ self.val = False
+ self.cursor += 4
+ return
+ elif self.src.startswith("null", self.pos):
+ self.val = None
+ self.cursor += 3
+ return
elif self.tok == '\n':
if self.cursor == len(self.src):
self.tok = None
@@ -300,6 +300,10 @@ class QAPISchema:
raise QAPISchemaError(self, 'Expected "{", "[" or string')
return expr
+#
+# Semantic analysis of schema expressions
+#
+
def find_base_fields(base):
base_struct_define = find_struct(base)
if not base_struct_define:
@@ -360,6 +364,60 @@ def check_name(expr_info, source, name, allow_optional = False,
raise QAPIExprError(expr_info,
"%s uses invalid name '%s'" % (source, name))
+def add_name(name, info, meta, implicit = False):
+ global all_names
+ check_name(info, "'%s'" % meta, name)
+ if name in all_names:
+ raise QAPIExprError(info,
+ "%s '%s' is already defined"
+ % (all_names[name], name))
+ if not implicit and name[-4:] == 'Kind':
+ raise QAPIExprError(info,
+ "%s '%s' should not end in 'Kind'"
+ % (meta, name))
+ all_names[name] = meta
+
+def add_struct(definition, info):
+ global struct_types
+ name = definition['struct']
+ add_name(name, info, 'struct')
+ struct_types.append(definition)
+
+def find_struct(name):
+ global struct_types
+ for struct in struct_types:
+ if struct['struct'] == name:
+ return struct
+ return None
+
+def add_union(definition, info):
+ global union_types
+ name = definition['union']
+ add_name(name, info, 'union')
+ union_types.append(definition)
+
+def find_union(name):
+ global union_types
+ for union in union_types:
+ if union['union'] == name:
+ return union
+ return None
+
+def add_enum(name, info, enum_values = None, implicit = False):
+ global enum_types
+ add_name(name, info, 'enum', implicit)
+ enum_types.append({"enum_name": name, "enum_values": enum_values})
+
+def find_enum(name):
+ global enum_types
+ for enum in enum_types:
+ if enum['enum_name'] == name:
+ return enum
+ return None
+
+def is_enum(name):
+ return find_enum(name) != None
+
def check_type(expr_info, source, value, allow_array = False,
allow_dict = False, allow_optional = False,
allow_star = False, allow_metas = []):
@@ -522,7 +580,7 @@ def check_union(expr, expr_info):
# Each value must name a known type; furthermore, in flat unions,
# branches must be a struct with no overlapping member names
check_type(expr_info, "Member '%s' of union '%s'" % (key, name),
- value, allow_array=True, allow_metas=allow_metas)
+ value, allow_array=not base, allow_metas=allow_metas)
if base:
branch_struct = find_struct(value)
assert branch_struct
@@ -607,26 +665,6 @@ def check_struct(expr, expr_info):
if expr.get('base'):
check_member_clash(expr_info, expr['base'], expr['data'])
-def check_exprs(schema):
- for expr_elem in schema.exprs:
- expr = expr_elem['expr']
- info = expr_elem['info']
-
- if expr.has_key('enum'):
- check_enum(expr, info)
- elif expr.has_key('union'):
- check_union(expr, info)
- elif expr.has_key('alternate'):
- check_alternate(expr, info)
- elif expr.has_key('struct'):
- check_struct(expr, info)
- elif expr.has_key('command'):
- check_command(expr, info)
- elif expr.has_key('event'):
- check_event(expr, info)
- else:
- assert False, 'unexpected meta type'
-
def check_keys(expr_elem, meta, required, optional=[]):
expr = expr_elem['expr']
info = expr_elem['info']
@@ -650,69 +688,83 @@ def check_keys(expr_elem, meta, required, optional=[]):
"Key '%s' is missing from %s '%s'"
% (key, meta, name))
-
-def parse_schema(input_file):
+def check_exprs(exprs):
global all_names
- exprs = []
- # First pass: read entire file into memory
- try:
- schema = QAPISchema(open(input_file, "r"))
- except (QAPISchemaError, QAPIExprError), e:
- print >>sys.stderr, e
- exit(1)
+ # Learn the types and check for valid expression keys
+ for builtin in builtin_types.keys():
+ all_names[builtin] = 'built-in'
+ for expr_elem in exprs:
+ expr = expr_elem['expr']
+ info = expr_elem['info']
+ if expr.has_key('enum'):
+ check_keys(expr_elem, 'enum', ['data'])
+ add_enum(expr['enum'], info, expr['data'])
+ elif expr.has_key('union'):
+ check_keys(expr_elem, 'union', ['data'],
+ ['base', 'discriminator'])
+ add_union(expr, info)
+ elif expr.has_key('alternate'):
+ check_keys(expr_elem, 'alternate', ['data'])
+ add_name(expr['alternate'], info, 'alternate')
+ elif expr.has_key('struct'):
+ check_keys(expr_elem, 'struct', ['data'], ['base'])
+ add_struct(expr, info)
+ elif expr.has_key('command'):
+ check_keys(expr_elem, 'command', [],
+ ['data', 'returns', 'gen', 'success-response'])
+ add_name(expr['command'], info, 'command')
+ elif expr.has_key('event'):
+ check_keys(expr_elem, 'event', [], ['data'])
+ add_name(expr['event'], info, 'event')
+ else:
+ raise QAPIExprError(expr_elem['info'],
+ "Expression is missing metatype")
- try:
- # Next pass: learn the types and check for valid expression keys. At
- # this point, top-level 'include' has already been flattened.
- for builtin in builtin_types.keys():
- all_names[builtin] = 'built-in'
- for expr_elem in schema.exprs:
- expr = expr_elem['expr']
- info = expr_elem['info']
- if expr.has_key('enum'):
- check_keys(expr_elem, 'enum', ['data'])
- add_enum(expr['enum'], info, expr['data'])
- elif expr.has_key('union'):
- check_keys(expr_elem, 'union', ['data'],
- ['base', 'discriminator'])
- add_union(expr, info)
- elif expr.has_key('alternate'):
- check_keys(expr_elem, 'alternate', ['data'])
- add_name(expr['alternate'], info, 'alternate')
- elif expr.has_key('struct'):
- check_keys(expr_elem, 'struct', ['data'], ['base'])
- add_struct(expr, info)
- elif expr.has_key('command'):
- check_keys(expr_elem, 'command', [],
- ['data', 'returns', 'gen', 'success-response'])
- add_name(expr['command'], info, 'command')
- elif expr.has_key('event'):
- check_keys(expr_elem, 'event', [], ['data'])
- add_name(expr['event'], info, 'event')
- else:
- raise QAPIExprError(expr_elem['info'],
- "Expression is missing metatype")
- exprs.append(expr)
-
- # Try again for hidden UnionKind enum
- for expr_elem in schema.exprs:
- expr = expr_elem['expr']
- if expr.has_key('union'):
- if not discriminator_find_enum_define(expr):
- add_enum('%sKind' % expr['union'], expr_elem['info'],
- implicit=True)
- elif expr.has_key('alternate'):
- add_enum('%sKind' % expr['alternate'], expr_elem['info'],
+ # Try again for hidden UnionKind enum
+ for expr_elem in exprs:
+ expr = expr_elem['expr']
+ if expr.has_key('union'):
+ if not discriminator_find_enum_define(expr):
+ add_enum('%sKind' % expr['union'], expr_elem['info'],
implicit=True)
+ elif expr.has_key('alternate'):
+ add_enum('%sKind' % expr['alternate'], expr_elem['info'],
+ implicit=True)
+
+ # Validate that exprs make sense
+ for expr_elem in exprs:
+ expr = expr_elem['expr']
+ info = expr_elem['info']
+
+ if expr.has_key('enum'):
+ check_enum(expr, info)
+ elif expr.has_key('union'):
+ check_union(expr, info)
+ elif expr.has_key('alternate'):
+ check_alternate(expr, info)
+ elif expr.has_key('struct'):
+ check_struct(expr, info)
+ elif expr.has_key('command'):
+ check_command(expr, info)
+ elif expr.has_key('event'):
+ check_event(expr, info)
+ else:
+ assert False, 'unexpected meta type'
+
+ return map(lambda expr_elem: expr_elem['expr'], exprs)
- # Final pass - validate that exprs make sense
- check_exprs(schema)
- except QAPIExprError, e:
+def parse_schema(fname):
+ try:
+ schema = QAPISchema(open(fname, "r"))
+ return check_exprs(schema.exprs)
+ except (QAPISchemaError, QAPIExprError), e:
print >>sys.stderr, e
exit(1)
- return exprs
+#
+# Code generation helpers
+#
def parse_args(typeinfo):
if isinstance(typeinfo, str):
@@ -831,60 +883,6 @@ def type_name(value):
return value
return c_name(value)
-def add_name(name, info, meta, implicit = False):
- global all_names
- check_name(info, "'%s'" % meta, name)
- if name in all_names:
- raise QAPIExprError(info,
- "%s '%s' is already defined"
- % (all_names[name], name))
- if not implicit and name[-4:] == 'Kind':
- raise QAPIExprError(info,
- "%s '%s' should not end in 'Kind'"
- % (meta, name))
- all_names[name] = meta
-
-def add_struct(definition, info):
- global struct_types
- name = definition['struct']
- add_name(name, info, 'struct')
- struct_types.append(definition)
-
-def find_struct(name):
- global struct_types
- for struct in struct_types:
- if struct['struct'] == name:
- return struct
- return None
-
-def add_union(definition, info):
- global union_types
- name = definition['union']
- add_name(name, info, 'union')
- union_types.append(definition)
-
-def find_union(name):
- global union_types
- for union in union_types:
- if union['union'] == name:
- return union
- return None
-
-def add_enum(name, info, enum_values = None, implicit = False):
- global enum_types
- add_name(name, info, 'enum', implicit)
- enum_types.append({"enum_name": name, "enum_values": enum_values})
-
-def find_enum(name):
- global enum_types
- for enum in enum_types:
- if enum['enum_name'] == name:
- return enum
- return None
-
-def is_enum(name):
- return find_enum(name) != None
-
eatspace = '\033EATSPACE.'
pointer_suffix = ' *' + eatspace
@@ -981,6 +979,10 @@ def guardend(name):
''',
name=guardname(name))
+#
+# Common command line parsing
+#
+
def parse_command_line(extra_options = "", extra_long_options = []):
try:
@@ -1018,9 +1020,13 @@ def parse_command_line(extra_options = "", extra_long_options = []):
if len(args) != 1:
print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
sys.exit(1)
- input_file = args[0]
+ fname = args[0]
+
+ return (fname, output_dir, do_c, do_h, prefix, extra_opts)
- return (input_file, output_dir, do_c, do_h, prefix, extra_opts)
+#
+# Generate output files with boilerplate
+#
def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
c_comment, h_comment):
diff --git a/softmmu_template.h b/softmmu_template.h
index 39f571b0ca..d42d89d541 100644
--- a/softmmu_template.h
+++ b/softmmu_template.h
@@ -548,6 +548,28 @@ glue(glue(helper_st, SUFFIX), MMUSUFFIX)(CPUArchState *env, target_ulong addr,
helper_te_st_name(env, addr, val, oi, GETRA());
}
+#if DATA_SIZE == 1
+/* Probe for whether the specified guest write access is permitted.
+ * If it is not permitted then an exception will be taken in the same
+ * way as if this were a real write access (and we will not return).
+ * Otherwise the function will return, and there will be a valid
+ * entry in the TLB for this access.
+ */
+void probe_write(CPUArchState *env, target_ulong addr, int mmu_idx,
+ uintptr_t retaddr)
+{
+ int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
+
+ if ((addr & TARGET_PAGE_MASK)
+ != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+ /* TLB entry is for a different page */
+ if (!VICTIM_TLB_HIT(addr_write)) {
+ tlb_fill(ENV_GET_CPU(env), addr, MMU_DATA_STORE, mmu_idx, retaddr);
+ }
+ }
+}
+#endif
#endif /* !defined(SOFTMMU_CODE_ACCESS) */
#undef READ_ACCESS_TYPE
diff --git a/stubs/mon-is-qmp.c b/stubs/mon-is-qmp.c
index 1f0a8fd98a..1ef136ab1d 100644
--- a/stubs/mon-is-qmp.c
+++ b/stubs/mon-is-qmp.c
@@ -1,7 +1,7 @@
#include "qemu-common.h"
#include "monitor/monitor.h"
-int monitor_cur_is_qmp(void)
+bool monitor_cur_is_qmp(void)
{
- return 0;
+ return false;
}
diff --git a/stubs/set-fd-handler.c b/stubs/set-fd-handler.c
index fc874d33fe..a8481bc3c1 100644
--- a/stubs/set-fd-handler.c
+++ b/stubs/set-fd-handler.c
@@ -1,8 +1,7 @@
#include "qemu-common.h"
#include "qemu/main-loop.h"
-int qemu_set_fd_handler2(int fd,
- IOCanReadHandler *fd_read_poll,
+void qemu_set_fd_handler(int fd,
IOHandler *fd_read,
IOHandler *fd_write,
void *opaque)
diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h
index ed5a6441bb..072aa9baa7 100644
--- a/target-arm/cpu-qom.h
+++ b/target-arm/cpu-qom.h
@@ -103,6 +103,9 @@ typedef struct ARMCPU {
/* CPU has security extension */
bool has_el3;
+ /* CPU has memory protection unit */
+ bool has_mpu;
+
/* PSCI conduit used to invoke PSCI methods
* 0 - disabled, 1 - smc, 2 - hvc
*/
@@ -116,6 +119,9 @@ typedef struct ARMCPU {
/* KVM init features for this CPU */
uint32_t kvm_init_features[7];
+ /* Uniprocessor system with MP extensions */
+ bool mp_is_up;
+
/* The instance init functions for implementation-specific subclasses
* set these fields to specify the implementation-dependent values of
* various constant registers and reset values of non-constant
@@ -127,6 +133,7 @@ typedef struct ARMCPU {
* prefix means a constant register.
*/
uint32_t midr;
+ uint32_t revidr;
uint32_t reset_fpsid;
uint32_t mvfr0;
uint32_t mvfr1;
@@ -159,6 +166,7 @@ typedef struct ARMCPU {
uint64_t id_aa64mmfr1;
uint32_t dbgdidr;
uint32_t clidr;
+ uint64_t mp_affinity; /* MP ID without feature bits */
/* The elements of this array are the CCSIDR values for each cache,
* in the order L1DCache, L1ICache, L2DCache, L2ICache, etc.
*/
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index 4a888ab47a..7496983b42 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -383,17 +383,29 @@ static inline void unset_feature(CPUARMState *env, int feature)
env->features &= ~(1ULL << feature);
}
+#define ARM_CPUS_PER_CLUSTER 8
+
static void arm_cpu_initfn(Object *obj)
{
CPUState *cs = CPU(obj);
ARMCPU *cpu = ARM_CPU(obj);
static bool inited;
+ uint32_t Aff1, Aff0;
cs->env_ptr = &cpu->env;
cpu_exec_init(&cpu->env);
cpu->cp_regs = g_hash_table_new_full(g_int_hash, g_int_equal,
g_free, g_free);
+ /* This cpu-id-to-MPIDR affinity is used only for TCG; KVM will override it.
+ * We don't support setting cluster ID ([16..23]) (known as Aff2
+ * in later ARM ARM versions), or any of the higher affinity level fields,
+ * so these bits always RAZ.
+ */
+ Aff1 = cs->cpu_index / ARM_CPUS_PER_CLUSTER;
+ Aff0 = cs->cpu_index % ARM_CPUS_PER_CLUSTER;
+ cpu->mp_affinity = (Aff1 << 8) | Aff0;
+
#ifndef CONFIG_USER_ONLY
/* Our inbound IRQ and FIQ lines */
if (kvm_enabled()) {
@@ -442,6 +454,9 @@ static Property arm_cpu_rvbar_property =
static Property arm_cpu_has_el3_property =
DEFINE_PROP_BOOL("has_el3", ARMCPU, has_el3, true);
+static Property arm_cpu_has_mpu_property =
+ DEFINE_PROP_BOOL("has-mpu", ARMCPU, has_mpu, true);
+
static void arm_cpu_post_init(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
@@ -469,6 +484,12 @@ static void arm_cpu_post_init(Object *obj)
qdev_property_add_static(DEVICE(obj), &arm_cpu_has_el3_property,
&error_abort);
}
+
+ if (arm_feature(&cpu->env, ARM_FEATURE_MPU)) {
+ qdev_property_add_static(DEVICE(obj), &arm_cpu_has_mpu_property,
+ &error_abort);
+ }
+
}
static void arm_cpu_finalizefn(Object *obj)
@@ -533,6 +554,10 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
if (arm_feature(env, ARM_FEATURE_CBAR_RO)) {
set_feature(env, ARM_FEATURE_CBAR);
}
+ if (arm_feature(env, ARM_FEATURE_THUMB2) &&
+ !arm_feature(env, ARM_FEATURE_M)) {
+ set_feature(env, ARM_FEATURE_THUMB_DSP);
+ }
if (cpu->reset_hivecs) {
cpu->reset_sctlr |= (1 << 13);
@@ -551,6 +576,10 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
cpu->id_aa64pfr0 &= ~0xf000;
}
+ if (!cpu->has_mpu) {
+ unset_feature(env, ARM_FEATURE_MPU);
+ }
+
register_cp_regs_for_features(cpu);
arm_cpu_register_gdb_regs_for_features(cpu);
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 21b5b8e538..c9d2330014 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -384,7 +384,6 @@ typedef struct CPUARMState {
uint32_t control;
int current_sp;
int exception;
- int pending_exception;
} v7m;
/* Information associated with an exception about to be taken:
@@ -890,6 +889,7 @@ enum arm_features {
ARM_FEATURE_V8_SHA1, /* implements SHA1 part of v8 Crypto Extensions */
ARM_FEATURE_V8_SHA256, /* implements SHA256 part of v8 Crypto Extensions */
ARM_FEATURE_V8_PMULL, /* implements PMULL part of v8 Crypto Extensions */
+ ARM_FEATURE_THUMB_DSP, /* DSP insns supported in the Thumb encodings */
};
static inline int arm_feature(CPUARMState *env, int feature)
diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c
index bf7dd685f8..63c8b1cfa9 100644
--- a/target-arm/cpu64.c
+++ b/target-arm/cpu64.c
@@ -110,6 +110,7 @@ static void aarch64_a57_initfn(Object *obj)
set_feature(&cpu->env, ARM_FEATURE_CRC);
cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A57;
cpu->midr = 0x411fd070;
+ cpu->revidr = 0x00000000;
cpu->reset_fpsid = 0x41034070;
cpu->mvfr0 = 0x10110222;
cpu->mvfr1 = 0x12111111;
@@ -159,7 +160,9 @@ static void aarch64_a53_initfn(Object *obj)
set_feature(&cpu->env, ARM_FEATURE_V8_SHA256);
set_feature(&cpu->env, ARM_FEATURE_V8_PMULL);
set_feature(&cpu->env, ARM_FEATURE_CRC);
+ cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A53;
cpu->midr = 0x410fd034;
+ cpu->revidr = 0x00000000;
cpu->reset_fpsid = 0x41034070;
cpu->mvfr0 = 0x10110222;
cpu->mvfr1 = 0x12111111;
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 1cc4993ca1..00509b1382 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -12,10 +12,10 @@
#include <zlib.h> /* For crc32 */
#ifndef CONFIG_USER_ONLY
-static inline int get_phys_addr(CPUARMState *env, target_ulong address,
- int access_type, ARMMMUIdx mmu_idx,
- hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot,
- target_ulong *page_size);
+static inline bool get_phys_addr(CPUARMState *env, target_ulong address,
+ int access_type, ARMMMUIdx mmu_idx,
+ hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot,
+ target_ulong *page_size, uint32_t *fsr);
/* Definitions for the PMCCNTR and PMCR registers */
#define PMCRD 0x8
@@ -294,23 +294,15 @@ static gint cpreg_key_compare(gconstpointer a, gconstpointer b)
return 0;
}
-static void cpreg_make_keylist(gpointer key, gpointer value, gpointer udata)
-{
- GList **plist = udata;
-
- *plist = g_list_prepend(*plist, key);
-}
-
void init_cpreg_list(ARMCPU *cpu)
{
/* Initialise the cpreg_tuples[] array based on the cp_regs hash.
* Note that we require cpreg_tuples[] to be sorted by key ID.
*/
- GList *keys = NULL;
+ GList *keys;
int arraylen;
- g_hash_table_foreach(cpu->cp_regs, cpreg_make_keylist, &keys);
-
+ keys = g_hash_table_get_keys(cpu->cp_regs);
keys = g_list_sort(keys, cpreg_key_compare);
cpu->cpreg_array_len = 0;
@@ -492,10 +484,16 @@ static const ARMCPRegInfo not_v8_cp_reginfo[] = {
.writefn = dacr_write, .raw_writefn = raw_write,
.bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.dacr_s),
offsetoflow32(CPUARMState, cp15.dacr_ns) } },
- /* ??? This covers not just the impdef TLB lockdown registers but also
- * some v7VMSA registers relating to TEX remap, so it is overly broad.
+ /* ARMv7 allocates a range of implementation defined TLB LOCKDOWN regs.
+ * For v6 and v5, these mappings are overly broad.
*/
- { .name = "TLB_LOCKDOWN", .cp = 15, .crn = 10, .crm = CP_ANY,
+ { .name = "TLB_LOCKDOWN", .cp = 15, .crn = 10, .crm = 0,
+ .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_NOP },
+ { .name = "TLB_LOCKDOWN", .cp = 15, .crn = 10, .crm = 1,
+ .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_NOP },
+ { .name = "TLB_LOCKDOWN", .cp = 15, .crn = 10, .crm = 4,
+ .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_NOP },
+ { .name = "TLB_LOCKDOWN", .cp = 15, .crn = 10, .crm = 8,
.opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_NOP },
/* Cache maintenance ops; some of this space may be overridden later. */
{ .name = "CACHEMAINT", .cp = 15, .crn = 7, .crm = CP_ANY,
@@ -555,6 +553,10 @@ static const ARMCPRegInfo not_v7_cp_reginfo[] = {
{ .name = "TLBIMVAA", .cp = 15, .crn = 8, .crm = CP_ANY,
.opc1 = CP_ANY, .opc2 = 3, .access = PL1_W, .writefn = tlbimvaa_write,
.type = ARM_CP_NO_RAW },
+ { .name = "PRRR", .cp = 15, .crn = 10, .crm = 2,
+ .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_NOP },
+ { .name = "NMRR", .cp = 15, .crn = 10, .crm = 2,
+ .opc1 = 0, .opc2 = 1, .access = PL1_RW, .type = ARM_CP_NOP },
REGINFO_SENTINEL
};
@@ -1021,19 +1023,17 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
.resetvalue = 0 },
/* For non-long-descriptor page tables these are PRRR and NMRR;
* regardless they still act as reads-as-written for QEMU.
- * The override is necessary because of the overly-broad TLB_LOCKDOWN
- * definition.
*/
/* MAIR0/1 are defined separately from their 64-bit counterpart which
* allows them to assign the correct fieldoffset based on the endianness
* handled in the field definitions.
*/
- { .name = "MAIR0", .state = ARM_CP_STATE_AA32, .type = ARM_CP_OVERRIDE,
+ { .name = "MAIR0", .state = ARM_CP_STATE_AA32,
.cp = 15, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 0, .access = PL1_RW,
.bank_fieldoffsets = { offsetof(CPUARMState, cp15.mair0_s),
offsetof(CPUARMState, cp15.mair0_ns) },
.resetfn = arm_cp_reset_ignore },
- { .name = "MAIR1", .state = ARM_CP_STATE_AA32, .type = ARM_CP_OVERRIDE,
+ { .name = "MAIR1", .state = ARM_CP_STATE_AA32,
.cp = 15, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 1, .access = PL1_RW,
.bank_fieldoffsets = { offsetof(CPUARMState, cp15.mair1_s),
offsetof(CPUARMState, cp15.mair1_ns) },
@@ -1495,19 +1495,20 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value,
hwaddr phys_addr;
target_ulong page_size;
int prot;
- int ret;
+ uint32_t fsr;
+ bool ret;
uint64_t par64;
MemTxAttrs attrs = {};
ret = get_phys_addr(env, value, access_type, mmu_idx,
- &phys_addr, &attrs, &prot, &page_size);
+ &phys_addr, &attrs, &prot, &page_size, &fsr);
if (extended_addresses_enabled(env)) {
- /* ret is a DFSR/IFSR value for the long descriptor
+ /* fsr is a DFSR/IFSR value for the long descriptor
* translation table format, but with WnR always clear.
* Convert it to a 64-bit PAR.
*/
par64 = (1 << 11); /* LPAE bit always set */
- if (ret == 0) {
+ if (!ret) {
par64 |= phys_addr & ~0xfffULL;
if (!attrs.secure) {
par64 |= (1 << 9); /* NS */
@@ -1515,18 +1516,18 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value,
/* We don't set the ATTR or SH fields in the PAR. */
} else {
par64 |= 1; /* F */
- par64 |= (ret & 0x3f) << 1; /* FS */
+ par64 |= (fsr & 0x3f) << 1; /* FS */
/* Note that S2WLK and FSTAGE are always zero, because we don't
* implement virtualization and therefore there can't be a stage 2
* fault.
*/
}
} else {
- /* ret is a DFSR/IFSR value for the short descriptor
+ /* fsr is a DFSR/IFSR value for the short descriptor
* translation table format (with WnR always clear).
* Convert it to a 32-bit PAR.
*/
- if (ret == 0) {
+ if (!ret) {
/* We do not set any attribute bits in the PAR */
if (page_size == (1 << 24)
&& arm_feature(env, ARM_FEATURE_V7)) {
@@ -1538,8 +1539,8 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value,
par64 |= (1 << 9); /* NS */
}
} else {
- par64 = ((ret & (1 << 10)) >> 5) | ((ret & (1 << 12)) >> 6) |
- ((ret & 0xf) << 1) | 1;
+ par64 = ((fsr & (1 << 10)) >> 5) | ((fsr & (1 << 12)) >> 6) |
+ ((fsr & 0xf) << 1) | 1;
}
}
return par64;
@@ -1846,7 +1847,7 @@ static void vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri,
raw_write(env, ri, value);
}
-static const ARMCPRegInfo vmsa_cp_reginfo[] = {
+static const ARMCPRegInfo vmsa_pmsa_cp_reginfo[] = {
{ .name = "DFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0,
.access = PL1_RW, .type = ARM_CP_ALIAS,
.bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.dfsr_s),
@@ -1856,6 +1857,18 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = {
.access = PL1_RW, .resetvalue = 0,
.bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.ifsr_s),
offsetoflow32(CPUARMState, cp15.ifsr_ns) } },
+ { .name = "DFAR", .cp = 15, .opc1 = 0, .crn = 6, .crm = 0, .opc2 = 0,
+ .access = PL1_RW, .resetvalue = 0,
+ .bank_fieldoffsets = { offsetof(CPUARMState, cp15.dfar_s),
+ offsetof(CPUARMState, cp15.dfar_ns) } },
+ { .name = "FAR_EL1", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0,
+ .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[1]),
+ .resetvalue = 0, },
+ REGINFO_SENTINEL
+};
+
+static const ARMCPRegInfo vmsa_cp_reginfo[] = {
{ .name = "ESR_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .crn = 5, .crm = 2, .opc1 = 0, .opc2 = 0,
.access = PL1_RW,
@@ -1880,14 +1893,6 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = {
.resetfn = arm_cp_reset_ignore, .raw_writefn = vmsa_ttbcr_raw_write,
.bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.tcr_el[3]),
offsetoflow32(CPUARMState, cp15.tcr_el[1])} },
- { .name = "FAR_EL1", .state = ARM_CP_STATE_AA64,
- .opc0 = 3, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0,
- .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[1]),
- .resetvalue = 0, },
- { .name = "DFAR", .cp = 15, .opc1 = 0, .crn = 6, .crm = 0, .opc2 = 0,
- .access = PL1_RW, .resetvalue = 0,
- .bank_fieldoffsets = { offsetof(CPUARMState, cp15.dfar_s),
- offsetof(CPUARMState, cp15.dfar_ns) } },
REGINFO_SENTINEL
};
@@ -2063,19 +2068,18 @@ static const ARMCPRegInfo strongarm_cp_reginfo[] = {
static uint64_t mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri)
{
- CPUState *cs = CPU(arm_env_get_cpu(env));
- uint32_t mpidr = cs->cpu_index;
- /* We don't support setting cluster ID ([8..11]) (known as Aff1
- * in later ARM ARM versions), or any of the higher affinity level fields,
- * so these bits always RAZ.
- */
+ ARMCPU *cpu = ARM_CPU(arm_env_get_cpu(env));
+ uint64_t mpidr = cpu->mp_affinity;
+
if (arm_feature(env, ARM_FEATURE_V7MP)) {
mpidr |= (1U << 31);
/* Cores which are uniprocessor (non-coherent)
* but still implement the MP extensions set
- * bit 30. (For instance, A9UP.) However we do
- * not currently model any of those cores.
+ * bit 30. (For instance, Cortex-R5).
*/
+ if (cpu->mp_is_up) {
+ mpidr |= (1u << 30);
+ }
}
return mpidr;
}
@@ -2088,16 +2092,14 @@ static const ARMCPRegInfo mpidr_cp_reginfo[] = {
};
static const ARMCPRegInfo lpae_cp_reginfo[] = {
- /* NOP AMAIR0/1: the override is because these clash with the rather
- * broadly specified TLB_LOCKDOWN entry in the generic cp_reginfo.
- */
+ /* NOP AMAIR0/1 */
{ .name = "AMAIR0", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 0,
- .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE,
+ .access = PL1_RW, .type = ARM_CP_CONST,
.resetvalue = 0 },
/* AMAIR1 is mapped to AMAIR_EL1[63:32] */
{ .name = "AMAIR1", .cp = 15, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 1,
- .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE,
+ .access = PL1_RW, .type = ARM_CP_CONST,
.resetvalue = 0 },
{ .name = "PAR", .cp = 15, .crm = 7, .opc1 = 0,
.access = PL1_RW, .type = ARM_CP_64BIT, .resetvalue = 0,
@@ -2362,6 +2364,14 @@ static const ARMCPRegInfo v8_cp_reginfo[] = {
.opc0 = 1, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 2,
.access = PL1_W, .type = ARM_CP_NOP },
/* TLBI operations */
+ { .name = "TLBI_ALLE1", .state = ARM_CP_STATE_AA64,
+ .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 4,
+ .access = PL2_W, .type = ARM_CP_NO_RAW,
+ .writefn = tlbiall_write },
+ { .name = "TLBI_ALLE1IS", .state = ARM_CP_STATE_AA64,
+ .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 4,
+ .access = PL2_W, .type = ARM_CP_NO_RAW,
+ .writefn = tlbiall_write },
{ .name = "TLBI_VMALLE1IS", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0,
.access = PL1_W, .type = ARM_CP_NO_RAW,
@@ -2498,7 +2508,7 @@ static const ARMCPRegInfo v8_cp_reginfo[] = {
};
/* Used to describe the behaviour of EL2 regs when EL2 does not exist. */
-static const ARMCPRegInfo v8_el3_no_el2_cp_reginfo[] = {
+static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = {
{ .name = "VBAR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 0,
.access = PL2_RW,
@@ -2511,6 +2521,28 @@ static const ARMCPRegInfo v8_el3_no_el2_cp_reginfo[] = {
{ .name = "CPTR_EL2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 2,
.access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+ { .name = "MAIR_EL2", .state = ARM_CP_STATE_BOTH,
+ .opc0 = 3, .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 0,
+ .access = PL2_RW, .type = ARM_CP_CONST,
+ .resetvalue = 0 },
+ { .name = "HMAIR1", .state = ARM_CP_STATE_AA32,
+ .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 1,
+ .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+ { .name = "TCR_EL2", .state = ARM_CP_STATE_BOTH,
+ .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 2,
+ .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+ { .name = "SCTLR_EL2", .state = ARM_CP_STATE_BOTH,
+ .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 0,
+ .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+ { .name = "TPIDR_EL2", .state = ARM_CP_STATE_BOTH,
+ .opc0 = 3, .opc1 = 4, .crn = 13, .crm = 0, .opc2 = 2,
+ .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+ { .name = "TTBR0_EL2", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 0,
+ .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+ { .name = "HTTBR", .cp = 15, .opc1 = 4, .crm = 2,
+ .access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_CONST,
+ .resetvalue = 0 },
REGINFO_SENTINEL
};
@@ -2539,7 +2571,7 @@ static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
raw_write(env, ri, value);
}
-static const ARMCPRegInfo v8_el2_cp_reginfo[] = {
+static const ARMCPRegInfo el2_cp_reginfo[] = {
{ .name = "HCR_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0,
.access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.hcr_el2),
@@ -2582,6 +2614,47 @@ static const ARMCPRegInfo v8_el2_cp_reginfo[] = {
.opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 2,
.access = PL2_RW, .accessfn = cptr_access, .resetvalue = 0,
.fieldoffset = offsetof(CPUARMState, cp15.cptr_el[2]) },
+ { .name = "MAIR_EL2", .state = ARM_CP_STATE_BOTH,
+ .opc0 = 3, .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 0,
+ .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.mair_el[2]),
+ .resetvalue = 0 },
+ { .name = "HMAIR1", .state = ARM_CP_STATE_AA32,
+ .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 1,
+ .access = PL2_RW, .type = ARM_CP_ALIAS,
+ .fieldoffset = offsetofhigh32(CPUARMState, cp15.mair_el[2]) },
+ { .name = "TCR_EL2", .state = ARM_CP_STATE_BOTH,
+ .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 2,
+ .access = PL2_RW, .writefn = vmsa_tcr_el1_write,
+ .resetfn = vmsa_ttbcr_reset, .raw_writefn = raw_write,
+ .fieldoffset = offsetof(CPUARMState, cp15.tcr_el[2]) },
+ { .name = "SCTLR_EL2", .state = ARM_CP_STATE_BOTH,
+ .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 0,
+ .access = PL2_RW, .raw_writefn = raw_write, .writefn = sctlr_write,
+ .fieldoffset = offsetof(CPUARMState, cp15.sctlr_el[2]) },
+ { .name = "TPIDR_EL2", .state = ARM_CP_STATE_BOTH,
+ .opc0 = 3, .opc1 = 4, .crn = 13, .crm = 0, .opc2 = 2,
+ .access = PL2_RW, .resetvalue = 0,
+ .fieldoffset = offsetof(CPUARMState, cp15.tpidr_el[2]) },
+ { .name = "TTBR0_EL2", .state = ARM_CP_STATE_AA64,
+ .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 0,
+ .access = PL2_RW, .resetvalue = 0,
+ .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el[2]) },
+ { .name = "HTTBR", .cp = 15, .opc1 = 4, .crm = 2,
+ .access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS,
+ .resetvalue = 0,
+ .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el[2]) },
+ { .name = "TLBI_ALLE2", .state = ARM_CP_STATE_AA64,
+ .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 0,
+ .type = ARM_CP_NO_RAW, .access = PL2_W,
+ .writefn = tlbiall_write },
+ { .name = "TLBI_VAE2", .state = ARM_CP_STATE_AA64,
+ .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 7, .opc2 = 1,
+ .type = ARM_CP_NO_RAW, .access = PL2_W,
+ .writefn = tlbi_aa64_vaa_write },
+ { .name = "TLBI_VAE2IS", .state = ARM_CP_STATE_AA64,
+ .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 1,
+ .type = ARM_CP_NO_RAW, .access = PL2_W,
+ .writefn = tlbi_aa64_vaa_write },
REGINFO_SENTINEL
};
@@ -3127,7 +3200,8 @@ void register_cp_regs_for_features(ARMCPU *cpu)
if (arm_feature(env, ARM_FEATURE_V6K)) {
define_arm_cp_regs(cpu, v6k_cp_reginfo);
}
- if (arm_feature(env, ARM_FEATURE_V7MP)) {
+ if (arm_feature(env, ARM_FEATURE_V7MP) &&
+ !arm_feature(env, ARM_FEATURE_MPU)) {
define_arm_cp_regs(cpu, v7mp_cp_reginfo);
}
if (arm_feature(env, ARM_FEATURE_V7)) {
@@ -3243,7 +3317,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
define_arm_cp_regs(cpu, v8_cp_reginfo);
}
if (arm_feature(env, ARM_FEATURE_EL2)) {
- define_arm_cp_regs(cpu, v8_el2_cp_reginfo);
+ define_arm_cp_regs(cpu, el2_cp_reginfo);
/* RVBAR_EL2 is only implemented if EL2 is the highest EL */
if (!arm_feature(env, ARM_FEATURE_EL3)) {
ARMCPRegInfo rvbar = {
@@ -3258,7 +3332,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
* register the no_el2 reginfos.
*/
if (arm_feature(env, ARM_FEATURE_EL3)) {
- define_arm_cp_regs(cpu, v8_el3_no_el2_cp_reginfo);
+ define_arm_cp_regs(cpu, el3_no_el2_cp_reginfo);
}
}
if (arm_feature(env, ARM_FEATURE_EL3)) {
@@ -3279,6 +3353,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
assert(!arm_feature(env, ARM_FEATURE_V6));
define_arm_cp_regs(cpu, pmsav5_cp_reginfo);
} else {
+ define_arm_cp_regs(cpu, vmsa_pmsa_cp_reginfo);
define_arm_cp_regs(cpu, vmsa_cp_reginfo);
}
if (arm_feature(env, ARM_FEATURE_THUMB2EE)) {
@@ -3354,16 +3429,19 @@ void register_cp_regs_for_features(ARMCPU *cpu)
REGINFO_SENTINEL
};
ARMCPRegInfo id_v8_midr_cp_reginfo[] = {
- /* v8 MIDR -- the wildcard isn't necessary, and nor is the
- * variable-MIDR TI925 behaviour. Instead we have a single
- * (strictly speaking IMPDEF) alias of the MIDR, REVIDR.
- */
{ .name = "MIDR_EL1", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 0, .opc2 = 0,
.access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->midr },
+ /* crn = 0 op1 = 0 crm = 0 op2 = 4,7 : AArch32 aliases of MIDR */
+ { .name = "MIDR", .type = ARM_CP_ALIAS | ARM_CP_CONST,
+ .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 4,
+ .access = PL1_R, .resetvalue = cpu->midr },
+ { .name = "MIDR", .type = ARM_CP_ALIAS | ARM_CP_CONST,
+ .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 7,
+ .access = PL1_R, .resetvalue = cpu->midr },
{ .name = "REVIDR_EL1", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 0, .opc2 = 6,
- .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->midr },
+ .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->revidr },
REGINFO_SENTINEL
};
ARMCPRegInfo id_cp_reginfo[] = {
@@ -3379,11 +3457,14 @@ void register_cp_regs_for_features(ARMCPU *cpu)
{ .name = "TCMTR",
.cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 2,
.access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 },
- { .name = "TLBTR",
- .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 3,
- .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 },
REGINFO_SENTINEL
};
+ /* TLBTR is specific to VMSA */
+ ARMCPRegInfo id_tlbtr_reginfo = {
+ .name = "TLBTR",
+ .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 3,
+ .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0,
+ };
ARMCPRegInfo crn0_wi_reginfo = {
.name = "CRN0_WI", .cp = 15, .crn = 0, .crm = CP_ANY,
.opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_W,
@@ -3405,6 +3486,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
for (r = id_cp_reginfo; r->type != ARM_CP_SENTINEL; r++) {
r->access = PL1_RW;
}
+ id_tlbtr_reginfo.access = PL1_RW;
}
if (arm_feature(env, ARM_FEATURE_V8)) {
define_arm_cp_regs(cpu, id_v8_midr_cp_reginfo);
@@ -3412,6 +3494,9 @@ void register_cp_regs_for_features(ARMCPU *cpu)
define_arm_cp_regs(cpu, id_pre_v8_midr_cp_reginfo);
}
define_arm_cp_regs(cpu, id_cp_reginfo);
+ if (!arm_feature(env, ARM_FEATURE_MPU)) {
+ define_one_arm_cp_reg(cpu, &id_tlbtr_reginfo);
+ }
}
if (arm_feature(env, ARM_FEATURE_MPIDR)) {
@@ -5160,9 +5245,10 @@ static uint64_t arm_ldq_ptw(CPUState *cs, hwaddr addr, bool is_secure)
return address_space_ldq(cs->as, addr, attrs, NULL);
}
-static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type,
- ARMMMUIdx mmu_idx, hwaddr *phys_ptr,
- int *prot, target_ulong *page_size)
+static bool get_phys_addr_v5(CPUARMState *env, uint32_t address,
+ int access_type, ARMMMUIdx mmu_idx,
+ hwaddr *phys_ptr, int *prot,
+ target_ulong *page_size, uint32_t *fsr)
{
CPUState *cs = CPU(arm_env_get_cpu(env));
int code;
@@ -5233,20 +5319,25 @@ static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type,
ap = (desc >> (4 + ((address >> 9) & 6))) & 3;
*page_size = 0x1000;
break;
- case 3: /* 1k page. */
+ case 3: /* 1k page, or ARMv6/XScale "extended small (4k) page" */
if (type == 1) {
- if (arm_feature(env, ARM_FEATURE_XSCALE)) {
+ /* ARMv6/XScale extended small page format */
+ if (arm_feature(env, ARM_FEATURE_XSCALE)
+ || arm_feature(env, ARM_FEATURE_V6)) {
phys_addr = (desc & 0xfffff000) | (address & 0xfff);
+ *page_size = 0x1000;
} else {
- /* Page translation fault. */
+ /* UNPREDICTABLE in ARMv5; we choose to take a
+ * page translation fault.
+ */
code = 7;
goto do_fault;
}
} else {
phys_addr = (desc & 0xfffffc00) | (address & 0x3ff);
+ *page_size = 0x400;
}
ap = (desc >> 4) & 3;
- *page_size = 0x400;
break;
default:
/* Never happens, but compiler isn't smart enough to tell. */
@@ -5261,15 +5352,16 @@ static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type,
goto do_fault;
}
*phys_ptr = phys_addr;
- return 0;
+ return false;
do_fault:
- return code | (domain << 4);
+ *fsr = code | (domain << 4);
+ return true;
}
-static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type,
- ARMMMUIdx mmu_idx, hwaddr *phys_ptr,
- MemTxAttrs *attrs,
- int *prot, target_ulong *page_size)
+static bool get_phys_addr_v6(CPUARMState *env, uint32_t address,
+ int access_type, ARMMMUIdx mmu_idx,
+ hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot,
+ target_ulong *page_size, uint32_t *fsr)
{
CPUState *cs = CPU(arm_env_get_cpu(env));
int code;
@@ -5323,6 +5415,8 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type,
if (desc & (1 << 18)) {
/* Supersection. */
phys_addr = (desc & 0xff000000) | (address & 0x00ffffff);
+ phys_addr |= (uint64_t)extract32(desc, 20, 4) << 32;
+ phys_addr |= (uint64_t)extract32(desc, 5, 4) << 36;
*page_size = 0x1000000;
} else {
/* Section. */
@@ -5400,9 +5494,10 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type,
attrs->secure = false;
}
*phys_ptr = phys_addr;
- return 0;
+ return false;
do_fault:
- return code | (domain << 4);
+ *fsr = code | (domain << 4);
+ return true;
}
/* Fault type for long-descriptor MMU fault reporting; this corresponds
@@ -5414,10 +5509,10 @@ typedef enum {
permission_fault = 3,
} MMUFaultType;
-static int get_phys_addr_lpae(CPUARMState *env, target_ulong address,
- int access_type, ARMMMUIdx mmu_idx,
- hwaddr *phys_ptr, MemTxAttrs *txattrs, int *prot,
- target_ulong *page_size_ptr)
+static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
+ int access_type, ARMMMUIdx mmu_idx,
+ hwaddr *phys_ptr, MemTxAttrs *txattrs, int *prot,
+ target_ulong *page_size_ptr, uint32_t *fsr)
{
CPUState *cs = CPU(arm_env_get_cpu(env));
/* Read an LPAE long-descriptor translation table. */
@@ -5656,16 +5751,17 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address,
}
*phys_ptr = descaddr;
*page_size_ptr = page_size;
- return 0;
+ return false;
do_fault:
/* Long-descriptor format IFSR/DFSR value */
- return (1 << 9) | (fault_type << 2) | level;
+ *fsr = (1 << 9) | (fault_type << 2) | level;
+ return true;
}
-static int get_phys_addr_mpu(CPUARMState *env, uint32_t address,
- int access_type, ARMMMUIdx mmu_idx,
- hwaddr *phys_ptr, int *prot)
+static bool get_phys_addr_pmsav5(CPUARMState *env, uint32_t address,
+ int access_type, ARMMMUIdx mmu_idx,
+ hwaddr *phys_ptr, int *prot, uint32_t *fsr)
{
int n;
uint32_t mask;
@@ -5687,7 +5783,8 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address,
}
}
if (n < 0) {
- return 2;
+ *fsr = 2;
+ return true;
}
if (access_type == 2) {
@@ -5698,10 +5795,12 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address,
mask = (mask >> (n * 4)) & 0xf;
switch (mask) {
case 0:
- return 1;
+ *fsr = 1;
+ return true;
case 1:
if (is_user) {
- return 1;
+ *fsr = 1;
+ return true;
}
*prot = PAGE_READ | PAGE_WRITE;
break;
@@ -5716,7 +5815,8 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address,
break;
case 5:
if (is_user) {
- return 1;
+ *fsr = 1;
+ return true;
}
*prot = PAGE_READ;
break;
@@ -5725,10 +5825,11 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address,
break;
default:
/* Bad permission. */
- return 1;
+ *fsr = 1;
+ return true;
}
*prot |= PAGE_EXEC;
- return 0;
+ return false;
}
/* get_phys_addr - get the physical address for this virtual address
@@ -5737,8 +5838,8 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address,
* by doing a translation table walk on MMU based systems or using the
* MPU state on MPU based systems.
*
- * Returns 0 if the translation was successful. Otherwise, phys_ptr, attrs,
- * prot and page_size may not be filled in, and the return value provides
+ * Returns false if the translation was successful. Otherwise, phys_ptr, attrs,
+ * prot and page_size may not be filled in, and the populated fsr value provides
* information on why the translation aborted, in the format of a
* DFSR/IFSR fault register, with the following caveats:
* * we honour the short vs long DFSR format differences.
@@ -5754,11 +5855,12 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address,
* @attrs: set to the memory transaction attributes to use
* @prot: set to the permissions for the page containing phys_ptr
* @page_size: set to the size of the page containing phys_ptr
+ * @fsr: set to the DFSR/IFSR value on failure
*/
-static inline int get_phys_addr(CPUARMState *env, target_ulong address,
- int access_type, ARMMMUIdx mmu_idx,
- hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot,
- target_ulong *page_size)
+static inline bool get_phys_addr(CPUARMState *env, target_ulong address,
+ int access_type, ARMMMUIdx mmu_idx,
+ hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot,
+ target_ulong *page_size, uint32_t *fsr)
{
if (mmu_idx == ARMMMUIdx_S12NSE0 || mmu_idx == ARMMMUIdx_S12NSE1) {
/* TODO: when we support EL2 we should here call ourselves recursively
@@ -5800,28 +5902,28 @@ static inline int get_phys_addr(CPUARMState *env, target_ulong address,
if (arm_feature(env, ARM_FEATURE_MPU)) {
*page_size = TARGET_PAGE_SIZE;
- return get_phys_addr_mpu(env, address, access_type, mmu_idx, phys_ptr,
- prot);
+ return get_phys_addr_pmsav5(env, address, access_type, mmu_idx,
+ phys_ptr, prot, fsr);
}
if (regime_using_lpae_format(env, mmu_idx)) {
return get_phys_addr_lpae(env, address, access_type, mmu_idx, phys_ptr,
- attrs, prot, page_size);
+ attrs, prot, page_size, fsr);
} else if (regime_sctlr(env, mmu_idx) & SCTLR_XP) {
return get_phys_addr_v6(env, address, access_type, mmu_idx, phys_ptr,
- attrs, prot, page_size);
+ attrs, prot, page_size, fsr);
} else {
return get_phys_addr_v5(env, address, access_type, mmu_idx, phys_ptr,
- prot, page_size);
+ prot, page_size, fsr);
}
}
/* Walk the page table and (if the mapping exists) add the page
- * to the TLB. Return 0 on success, or an ARM DFSR/IFSR fault
- * register format value on failure.
+ * to the TLB. Return false on success, or true on failure. Populate
+ * fsr with ARM DFSR/IFSR fault register format value on failure.
*/
-int arm_tlb_fill(CPUState *cs, vaddr address,
- int access_type, int mmu_idx)
+bool arm_tlb_fill(CPUState *cs, vaddr address,
+ int access_type, int mmu_idx, uint32_t *fsr)
{
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
@@ -5832,8 +5934,8 @@ int arm_tlb_fill(CPUState *cs, vaddr address,
MemTxAttrs attrs = {};
ret = get_phys_addr(env, address, access_type, mmu_idx, &phys_addr,
- &attrs, &prot, &page_size);
- if (ret == 0) {
+ &attrs, &prot, &page_size, fsr);
+ if (!ret) {
/* Map a single [sub]page. */
phys_addr &= TARGET_PAGE_MASK;
address &= TARGET_PAGE_MASK;
@@ -5852,13 +5954,14 @@ hwaddr arm_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
hwaddr phys_addr;
target_ulong page_size;
int prot;
- int ret;
+ bool ret;
+ uint32_t fsr;
MemTxAttrs attrs = {};
ret = get_phys_addr(env, addr, 0, cpu_mmu_index(env), &phys_addr,
- &attrs, &prot, &page_size);
+ &attrs, &prot, &page_size, &fsr);
- if (ret != 0) {
+ if (ret) {
return -1;
}
diff --git a/target-arm/internals.h b/target-arm/internals.h
index 1e5071ea72..924aff9d04 100644
--- a/target-arm/internals.h
+++ b/target-arm/internals.h
@@ -388,6 +388,7 @@ void arm_handle_psci_call(ARMCPU *cpu);
#endif
/* Do a page table walk and add page to TLB if possible */
-int arm_tlb_fill(CPUState *cpu, vaddr address, int rw, int mmu_idx);
+bool arm_tlb_fill(CPUState *cpu, vaddr address, int rw, int mmu_idx,
+ uint32_t *fsr);
#endif
diff --git a/target-arm/kvm-consts.h b/target-arm/kvm-consts.h
index aea12f1bc4..943bf8980a 100644
--- a/target-arm/kvm-consts.h
+++ b/target-arm/kvm-consts.h
@@ -127,6 +127,8 @@ MISMATCH_CHECK(QEMU_PSCI_RET_DISABLED, PSCI_RET_DISABLED)
#define QEMU_KVM_ARM_TARGET_AEM_V8 0
#define QEMU_KVM_ARM_TARGET_FOUNDATION_V8 1
#define QEMU_KVM_ARM_TARGET_CORTEX_A57 2
+#define QEMU_KVM_ARM_TARGET_XGENE_POTENZA 3
+#define QEMU_KVM_ARM_TARGET_CORTEX_A53 4
/* There's no kernel define for this: sentinel value which
* matches no KVM target value for either 64 or 32 bit
@@ -137,6 +139,8 @@ MISMATCH_CHECK(QEMU_PSCI_RET_DISABLED, PSCI_RET_DISABLED)
MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_AEM_V8, KVM_ARM_TARGET_AEM_V8)
MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_FOUNDATION_V8, KVM_ARM_TARGET_FOUNDATION_V8)
MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A57, KVM_ARM_TARGET_CORTEX_A57)
+MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_XGENE_POTENZA, KVM_ARM_TARGET_XGENE_POTENZA)
+MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A53, KVM_ARM_TARGET_CORTEX_A53)
#else
MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A15, KVM_ARM_TARGET_CORTEX_A15)
MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A7, KVM_ARM_TARGET_CORTEX_A7)
diff --git a/target-arm/kvm.c b/target-arm/kvm.c
index 16abbf198c..548bfd768d 100644
--- a/target-arm/kvm.c
+++ b/target-arm/kvm.c
@@ -600,3 +600,8 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
{
return 0;
}
+
+int kvm_arch_msi_data_to_gsi(uint32_t data)
+{
+ return (data - 32) & 0xffff;
+}
diff --git a/target-arm/kvm32.c b/target-arm/kvm32.c
index 49b6babc05..d7e7d6877f 100644
--- a/target-arm/kvm32.c
+++ b/target-arm/kvm32.c
@@ -153,10 +153,14 @@ bool kvm_arm_reg_syncs_via_cpreg_list(uint64_t regidx)
}
}
+#define ARM_MPIDR_HWID_BITMASK 0xFFFFFF
+#define ARM_CPU_ID_MPIDR 0, 0, 0, 5
+
int kvm_arch_init_vcpu(CPUState *cs)
{
int ret;
uint64_t v;
+ uint32_t mpidr;
struct kvm_one_reg r;
ARMCPU *cpu = ARM_CPU(cs);
@@ -193,6 +197,17 @@ int kvm_arch_init_vcpu(CPUState *cs)
return -EINVAL;
}
+ /*
+ * When KVM is in use, PSCI is emulated in-kernel and not by qemu.
+ * Currently KVM has its own idea about MPIDR assignment, so we
+ * override our defaults with what we get from KVM.
+ */
+ ret = kvm_get_one_reg(cs, ARM_CP15_REG32(ARM_CPU_ID_MPIDR), &mpidr);
+ if (ret) {
+ return ret;
+ }
+ cpu->mp_affinity = mpidr & ARM_MPIDR_HWID_BITMASK;
+
return kvm_arm_init_cpreg_list(cpu);
}
diff --git a/target-arm/kvm64.c b/target-arm/kvm64.c
index 93c1ca8b21..ac34f51498 100644
--- a/target-arm/kvm64.c
+++ b/target-arm/kvm64.c
@@ -77,9 +77,13 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc)
return true;
}
+#define ARM_MPIDR_HWID_BITMASK 0xFF00FFFFFFULL
+#define ARM_CPU_ID_MPIDR 3, 0, 0, 0, 5
+
int kvm_arch_init_vcpu(CPUState *cs)
{
int ret;
+ uint64_t mpidr;
ARMCPU *cpu = ARM_CPU(cs);
if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE ||
@@ -107,6 +111,17 @@ int kvm_arch_init_vcpu(CPUState *cs)
return ret;
}
+ /*
+ * When KVM is in use, PSCI is emulated in-kernel and not by qemu.
+ * Currently KVM has its own idea about MPIDR assignment, so we
+ * override our defaults with what we get from KVM.
+ */
+ ret = kvm_get_one_reg(cs, ARM64_SYS_REG(ARM_CPU_ID_MPIDR), &mpidr);
+ if (ret) {
+ return ret;
+ }
+ cpu->mp_affinity = mpidr & ARM_MPIDR_HWID_BITMASK;
+
return kvm_arm_init_cpreg_list(cpu);
}
diff --git a/target-arm/machine.c b/target-arm/machine.c
index 9446e5a8ab..36365a57c7 100644
--- a/target-arm/machine.c
+++ b/target-arm/machine.c
@@ -40,6 +40,7 @@ static const VMStateDescription vmstate_vfp = {
.name = "cpu/vfp",
.version_id = 3,
.minimum_version_id = 3,
+ .needed = vfp_needed,
.fields = (VMStateField[]) {
VMSTATE_FLOAT64_ARRAY(env.vfp.regs, ARMCPU, 64),
/* The xregs array is a little awkward because element 1 (FPSCR)
@@ -72,6 +73,7 @@ static const VMStateDescription vmstate_iwmmxt = {
.name = "cpu/iwmmxt",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = iwmmxt_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64_ARRAY(env.iwmmxt.regs, ARMCPU, 16),
VMSTATE_UINT32_ARRAY(env.iwmmxt.cregs, ARMCPU, 16),
@@ -91,6 +93,7 @@ static const VMStateDescription vmstate_m = {
.name = "cpu/m",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = m_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT32(env.v7m.other_sp, ARMCPU),
VMSTATE_UINT32(env.v7m.vecbase, ARMCPU),
@@ -114,6 +117,7 @@ static const VMStateDescription vmstate_thumb2ee = {
.name = "cpu/thumb2ee",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = thumb2ee_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT32(env.teecr, ARMCPU),
VMSTATE_UINT32(env.teehbr, ARMCPU),
@@ -282,21 +286,11 @@ const VMStateDescription vmstate_arm_cpu = {
VMSTATE_BOOL(powered_off, ARMCPU),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_vfp,
- .needed = vfp_needed,
- } , {
- .vmsd = &vmstate_iwmmxt,
- .needed = iwmmxt_needed,
- } , {
- .vmsd = &vmstate_m,
- .needed = m_needed,
- } , {
- .vmsd = &vmstate_thumb2ee,
- .needed = thumb2ee_needed,
- } , {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_vfp,
+ &vmstate_iwmmxt,
+ &vmstate_m,
+ &vmstate_thumb2ee,
+ NULL
}
};
diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c
index 3f5b9ab596..7fa32c4707 100644
--- a/target-arm/op_helper.c
+++ b/target-arm/op_helper.c
@@ -81,9 +81,10 @@ uint32_t HELPER(neon_tbl)(CPUARMState *env, uint32_t ireg, uint32_t def,
void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx,
uintptr_t retaddr)
{
- int ret;
+ bool ret;
+ uint32_t fsr = 0;
- ret = arm_tlb_fill(cs, addr, is_write, mmu_idx);
+ ret = arm_tlb_fill(cs, addr, is_write, mmu_idx, &fsr);
if (unlikely(ret)) {
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
@@ -96,7 +97,7 @@ void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx,
}
/* AArch64 syndrome does not have an LPAE bit */
- syn = ret & ~(1 << 9);
+ syn = fsr & ~(1 << 9);
/* For insn and data aborts we assume there is no instruction syndrome
* information; this is always true for exceptions reported to EL1.
@@ -107,13 +108,13 @@ void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx,
} else {
syn = syn_data_abort(same_el, 0, 0, 0, is_write == 1, syn);
if (is_write == 1 && arm_feature(env, ARM_FEATURE_V6)) {
- ret |= (1 << 11);
+ fsr |= (1 << 11);
}
exc = EXCP_DATA_ABORT;
}
env->exception.vaddress = addr;
- env->exception.fsr = ret;
+ env->exception.fsr = fsr;
raise_exception(env, exc, syn, exception_target_el(env));
}
}
@@ -421,7 +422,7 @@ void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome)
/* Requesting a trap to EL2 when we're in EL3 or S-EL0/1 is
* a bug in the access function.
*/
- assert(!arm_is_secure(env) && !arm_current_el(env) == 3);
+ assert(!arm_is_secure(env) && arm_current_el(env) != 3);
target_el = 2;
break;
case CP_ACCESS_TRAP_EL3:
diff --git a/target-arm/psci.c b/target-arm/psci.c
index d8fafab2fe..20e4cb6f9c 100644
--- a/target-arm/psci.c
+++ b/target-arm/psci.c
@@ -72,6 +72,21 @@ bool arm_is_psci_call(ARMCPU *cpu, int excp_type)
}
}
+static CPUState *get_cpu_by_id(uint64_t id)
+{
+ CPUState *cpu;
+
+ CPU_FOREACH(cpu) {
+ ARMCPU *armcpu = ARM_CPU(cpu);
+
+ if (armcpu->mp_affinity == id) {
+ return cpu;
+ }
+ }
+
+ return NULL;
+}
+
void arm_handle_psci_call(ARMCPU *cpu)
{
/*
@@ -121,7 +136,7 @@ void arm_handle_psci_call(ARMCPU *cpu)
switch (param[2]) {
case 0:
- target_cpu_state = qemu_get_cpu(mpidr & 0xff);
+ target_cpu_state = get_cpu_by_id(mpidr);
if (!target_cpu_state) {
ret = QEMU_PSCI_RET_INVALID_PARAMS;
break;
@@ -153,7 +168,7 @@ void arm_handle_psci_call(ARMCPU *cpu)
context_id = param[3];
/* change to the cpu we are powering up */
- target_cpu_state = qemu_get_cpu(mpidr & 0xff);
+ target_cpu_state = get_cpu_by_id(mpidr);
if (!target_cpu_state) {
ret = QEMU_PSCI_RET_INVALID_PARAMS;
break;
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 39692d7a8e..ead08f4820 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -7175,7 +7175,7 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn)
break;
}
- gen_set_pc_im(s, s->pc);
+ gen_set_pc_im(s, s->pc - 4);
tmpptr = tcg_const_ptr(ri);
tcg_syn = tcg_const_i32(syndrome);
gen_helper_access_check_cp_reg(cpu_env, tmpptr, tcg_syn);
@@ -9444,6 +9444,9 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
op = (insn >> 21) & 0xf;
if (op == 6) {
+ if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+ goto illegal_op;
+ }
/* Halfword pack. */
tmp = load_reg(s, rn);
tmp2 = load_reg(s, rm);
@@ -9508,6 +9511,27 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
store_reg_bx(s, rd, tmp);
break;
case 1: /* Sign/zero extend. */
+ op = (insn >> 20) & 7;
+ switch (op) {
+ case 0: /* SXTAH, SXTH */
+ case 1: /* UXTAH, UXTH */
+ case 4: /* SXTAB, SXTB */
+ case 5: /* UXTAB, UXTB */
+ break;
+ case 2: /* SXTAB16, SXTB16 */
+ case 3: /* UXTAB16, UXTB16 */
+ if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+ goto illegal_op;
+ }
+ break;
+ default:
+ goto illegal_op;
+ }
+ if (rn != 15) {
+ if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+ goto illegal_op;
+ }
+ }
tmp = load_reg(s, rm);
shift = (insn >> 4) & 3;
/* ??? In many cases it's not necessary to do a
@@ -9522,7 +9546,8 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
case 3: gen_uxtb16(tmp); break;
case 4: gen_sxtb(tmp); break;
case 5: gen_uxtb(tmp); break;
- default: goto illegal_op;
+ default:
+ g_assert_not_reached();
}
if (rn != 15) {
tmp2 = load_reg(s, rn);
@@ -9536,6 +9561,9 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
store_reg(s, rd, tmp);
break;
case 2: /* SIMD add/subtract. */
+ if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+ goto illegal_op;
+ }
op = (insn >> 20) & 7;
shift = (insn >> 4) & 7;
if ((op & 3) == 3 || (shift & 3) == 3)
@@ -9550,6 +9578,9 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
op = ((insn >> 17) & 0x38) | ((insn >> 4) & 7);
if (op < 4) {
/* Saturating add/subtract. */
+ if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+ goto illegal_op;
+ }
tmp = load_reg(s, rn);
tmp2 = load_reg(s, rm);
if (op & 1)
@@ -9560,6 +9591,31 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
gen_helper_add_saturate(tmp, cpu_env, tmp, tmp2);
tcg_temp_free_i32(tmp2);
} else {
+ switch (op) {
+ case 0x0a: /* rbit */
+ case 0x08: /* rev */
+ case 0x09: /* rev16 */
+ case 0x0b: /* revsh */
+ case 0x18: /* clz */
+ break;
+ case 0x10: /* sel */
+ if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+ goto illegal_op;
+ }
+ break;
+ case 0x20: /* crc32/crc32c */
+ case 0x21:
+ case 0x22:
+ case 0x28:
+ case 0x29:
+ case 0x2a:
+ if (!arm_dc_feature(s, ARM_FEATURE_CRC)) {
+ goto illegal_op;
+ }
+ break;
+ default:
+ goto illegal_op;
+ }
tmp = load_reg(s, rn);
switch (op) {
case 0x0a: /* rbit */
@@ -9596,10 +9652,6 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
uint32_t sz = op & 0x3;
uint32_t c = op & 0x8;
- if (!arm_dc_feature(s, ARM_FEATURE_CRC)) {
- goto illegal_op;
- }
-
tmp2 = load_reg(s, rm);
if (sz == 0) {
tcg_gen_andi_i32(tmp2, tmp2, 0xff);
@@ -9617,12 +9669,26 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
break;
}
default:
- goto illegal_op;
+ g_assert_not_reached();
}
}
store_reg(s, rd, tmp);
break;
case 4: case 5: /* 32-bit multiply. Sum of absolute differences. */
+ switch ((insn >> 20) & 7) {
+ case 0: /* 32 x 32 -> 32 */
+ case 7: /* Unsigned sum of absolute differences. */
+ break;
+ case 1: /* 16 x 16 -> 32 */
+ case 2: /* Dual multiply add. */
+ case 3: /* 32 * 16 -> 32msb */
+ case 4: /* Dual multiply subtract. */
+ case 5: case 6: /* 32 * 32 -> 32msb (SMMUL, SMMLA, SMMLS) */
+ if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+ goto illegal_op;
+ }
+ break;
+ }
op = (insn >> 4) & 0xf;
tmp = load_reg(s, rn);
tmp2 = load_reg(s, rm);
@@ -9735,6 +9801,11 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
store_reg(s, rd, tmp);
} else if ((op & 0xe) == 0xc) {
/* Dual multiply accumulate long. */
+ if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+ tcg_temp_free_i32(tmp);
+ tcg_temp_free_i32(tmp2);
+ goto illegal_op;
+ }
if (op & 1)
gen_swap_half(tmp2);
gen_smul_dual(tmp, tmp2);
@@ -9758,6 +9829,11 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
} else {
if (op & 8) {
/* smlalxy */
+ if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+ tcg_temp_free_i32(tmp2);
+ tcg_temp_free_i32(tmp);
+ goto illegal_op;
+ }
gen_mulxy(tmp, tmp2, op & 2, op & 1);
tcg_temp_free_i32(tmp2);
tmp64 = tcg_temp_new_i64();
@@ -9770,6 +9846,10 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
}
if (op & 4) {
/* umaal */
+ if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+ tcg_temp_free_i64(tmp64);
+ goto illegal_op;
+ }
gen_addq_lo(s, tmp64, rs);
gen_addq_lo(s, tmp64, rd);
} else if (op & 0x40) {
@@ -10034,16 +10114,28 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
tmp2 = tcg_const_i32(imm);
if (op & 4) {
/* Unsigned. */
- if ((op & 1) && shift == 0)
+ if ((op & 1) && shift == 0) {
+ if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+ tcg_temp_free_i32(tmp);
+ tcg_temp_free_i32(tmp2);
+ goto illegal_op;
+ }
gen_helper_usat16(tmp, cpu_env, tmp, tmp2);
- else
+ } else {
gen_helper_usat(tmp, cpu_env, tmp, tmp2);
+ }
} else {
/* Signed. */
- if ((op & 1) && shift == 0)
+ if ((op & 1) && shift == 0) {
+ if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) {
+ tcg_temp_free_i32(tmp);
+ tcg_temp_free_i32(tmp2);
+ goto illegal_op;
+ }
gen_helper_ssat16(tmp, cpu_env, tmp, tmp2);
- else
+ } else {
gen_helper_ssat(tmp, cpu_env, tmp, tmp2);
+ }
}
tcg_temp_free_i32(tmp2);
break;
diff --git a/target-i386/Makefile.objs b/target-i386/Makefile.objs
index 027b94e1d3..7a1df2c983 100644
--- a/target-i386/Makefile.objs
+++ b/target-i386/Makefile.objs
@@ -5,5 +5,3 @@ obj-y += gdbstub.o
obj-$(CONFIG_SOFTMMU) += machine.o arch_memory_mapping.o arch_dump.o
obj-$(CONFIG_KVM) += kvm.o
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
-obj-$(CONFIG_LINUX_USER) += ioport-user.o
-obj-$(CONFIG_BSD_USER) += ioport-user.o
diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h
index 31a0c1e776..7a4fddd85f 100644
--- a/target-i386/cpu-qom.h
+++ b/target-i386/cpu-qom.h
@@ -23,6 +23,7 @@
#include "qom/cpu.h"
#include "cpu.h"
#include "qapi/error.h"
+#include "qemu/notify.h"
#ifdef TARGET_X86_64
#define TYPE_X86_CPU "x86_64-cpu"
@@ -111,6 +112,8 @@ typedef struct X86CPU {
/* in order to simplify APIC support, we leave this pointer to the
user */
struct DeviceState *apic_state;
+ struct MemoryRegion *cpu_as_root, *cpu_as_mem, *smram;
+ Notifier machine_done;
} X86CPU;
static inline X86CPU *x86_env_get_cpu(CPUX86State *env)
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 3305e09413..4e7cdaaaa5 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -44,6 +44,7 @@
#include "hw/qdev-properties.h"
#include "hw/cpu/icc_bus.h"
#ifndef CONFIG_USER_ONLY
+#include "exec/address-spaces.h"
#include "hw/xen/xen.h"
#include "hw/i386/apic_internal.h"
#endif
@@ -2750,6 +2751,21 @@ static void x86_cpu_apic_realize(X86CPU *cpu, Error **errp)
object_property_set_bool(OBJECT(cpu->apic_state), true, "realized",
errp);
}
+
+static void x86_cpu_machine_done(Notifier *n, void *unused)
+{
+ X86CPU *cpu = container_of(n, X86CPU, machine_done);
+ MemoryRegion *smram =
+ (MemoryRegion *) object_resolve_path("/machine/smram", NULL);
+
+ if (smram) {
+ cpu->smram = g_new(MemoryRegion, 1);
+ memory_region_init_alias(cpu->smram, OBJECT(cpu), "smram",
+ smram, 0, 1ull << 32);
+ memory_region_set_enabled(cpu->smram, false);
+ memory_region_add_subregion_overlap(cpu->cpu_as_root, 0, cpu->smram, 1);
+ }
+}
#else
static void x86_cpu_apic_realize(X86CPU *cpu, Error **errp)
{
@@ -2811,6 +2827,32 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
#endif
mce_init(cpu);
+
+#ifndef CONFIG_USER_ONLY
+ if (tcg_enabled()) {
+ cpu->cpu_as_mem = g_new(MemoryRegion, 1);
+ cpu->cpu_as_root = g_new(MemoryRegion, 1);
+ cs->as = g_new(AddressSpace, 1);
+
+ /* Outer container... */
+ memory_region_init(cpu->cpu_as_root, OBJECT(cpu), "memory", ~0ull);
+ memory_region_set_enabled(cpu->cpu_as_root, true);
+
+ /* ... with two regions inside: normal system memory with low
+ * priority, and...
+ */
+ memory_region_init_alias(cpu->cpu_as_mem, OBJECT(cpu), "memory",
+ get_system_memory(), 0, ~0ull);
+ memory_region_add_subregion_overlap(cpu->cpu_as_root, 0, cpu->cpu_as_mem, 0);
+ memory_region_set_enabled(cpu->cpu_as_mem, true);
+ address_space_init(cs->as, cpu->cpu_as_root, "CPU");
+
+ /* ... SMRAM with higher priority, linked from /machine/smram. */
+ cpu->machine_done.notify = x86_cpu_machine_done;
+ qemu_add_machine_init_done_notifier(&cpu->machine_done);
+ }
+#endif
+
qemu_init_vcpu(cs);
/* Only Intel CPUs support hyperthreading. Even though QEMU fixes this
@@ -2834,6 +2876,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
cpu_reset(cs);
xcc->parent_realize(dev, &local_err);
+
out:
if (local_err != NULL) {
error_propagate(errp, local_err);
@@ -2841,12 +2884,126 @@ out:
}
}
+typedef struct BitProperty {
+ uint32_t *ptr;
+ uint32_t mask;
+} BitProperty;
+
+static void x86_cpu_get_bit_prop(Object *obj,
+ struct Visitor *v,
+ void *opaque,
+ const char *name,
+ Error **errp)
+{
+ BitProperty *fp = opaque;
+ bool value = (*fp->ptr & fp->mask) == fp->mask;
+ visit_type_bool(v, &value, name, errp);
+}
+
+static void x86_cpu_set_bit_prop(Object *obj,
+ struct Visitor *v,
+ void *opaque,
+ const char *name,
+ Error **errp)
+{
+ DeviceState *dev = DEVICE(obj);
+ BitProperty *fp = opaque;
+ Error *local_err = NULL;
+ bool value;
+
+ if (dev->realized) {
+ qdev_prop_set_after_realize(dev, name, errp);
+ return;
+ }
+
+ visit_type_bool(v, &value, name, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (value) {
+ *fp->ptr |= fp->mask;
+ } else {
+ *fp->ptr &= ~fp->mask;
+ }
+}
+
+static void x86_cpu_release_bit_prop(Object *obj, const char *name,
+ void *opaque)
+{
+ BitProperty *prop = opaque;
+ g_free(prop);
+}
+
+/* Register a boolean property to get/set a single bit in a uint32_t field.
+ *
+ * The same property name can be registered multiple times to make it affect
+ * multiple bits in the same FeatureWord. In that case, the getter will return
+ * true only if all bits are set.
+ */
+static void x86_cpu_register_bit_prop(X86CPU *cpu,
+ const char *prop_name,
+ uint32_t *field,
+ int bitnr)
+{
+ BitProperty *fp;
+ ObjectProperty *op;
+ uint32_t mask = (1UL << bitnr);
+
+ op = object_property_find(OBJECT(cpu), prop_name, NULL);
+ if (op) {
+ fp = op->opaque;
+ assert(fp->ptr == field);
+ fp->mask |= mask;
+ } else {
+ fp = g_new0(BitProperty, 1);
+ fp->ptr = field;
+ fp->mask = mask;
+ object_property_add(OBJECT(cpu), prop_name, "bool",
+ x86_cpu_get_bit_prop,
+ x86_cpu_set_bit_prop,
+ x86_cpu_release_bit_prop, fp, &error_abort);
+ }
+}
+
+static void x86_cpu_register_feature_bit_props(X86CPU *cpu,
+ FeatureWord w,
+ int bitnr)
+{
+ Object *obj = OBJECT(cpu);
+ int i;
+ char **names;
+ FeatureWordInfo *fi = &feature_word_info[w];
+
+ if (!fi->feat_names) {
+ return;
+ }
+ if (!fi->feat_names[bitnr]) {
+ return;
+ }
+
+ names = g_strsplit(fi->feat_names[bitnr], "|", 0);
+
+ feat2prop(names[0]);
+ x86_cpu_register_bit_prop(cpu, names[0], &cpu->env.features[w], bitnr);
+
+ for (i = 1; names[i]; i++) {
+ feat2prop(names[i]);
+ object_property_add_alias(obj, names[i], obj, g_strdup(names[0]),
+ &error_abort);
+ }
+
+ g_strfreev(names);
+}
+
static void x86_cpu_initfn(Object *obj)
{
CPUState *cs = CPU(obj);
X86CPU *cpu = X86_CPU(obj);
X86CPUClass *xcc = X86_CPU_GET_CLASS(obj);
CPUX86State *env = &cpu->env;
+ FeatureWord w;
static int inited;
cs->env_ptr = env;
@@ -2887,6 +3044,14 @@ static void x86_cpu_initfn(Object *obj)
cpu->apic_id = -1;
#endif
+ for (w = 0; w < FEATURE_WORDS; w++) {
+ int bitnr;
+
+ for (bitnr = 0; bitnr < 32; bitnr++) {
+ x86_cpu_register_feature_bit_props(cpu, w, bitnr);
+ }
+ }
+
x86_cpu_load_def(cpu, xcc->cpu_def, &error_abort);
/* init various static tables used in TCG mode */
@@ -2941,7 +3106,9 @@ static bool x86_cpu_has_work(CPUState *cs)
(cs->interrupt_request & (CPU_INTERRUPT_NMI |
CPU_INTERRUPT_INIT |
CPU_INTERRUPT_SIPI |
- CPU_INTERRUPT_MCE));
+ CPU_INTERRUPT_MCE)) ||
+ ((cs->interrupt_request & CPU_INTERRUPT_SMI) &&
+ !(env->hflags & HF_SMM_MASK));
}
static Property x86_cpu_properties[] = {
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index 4ee12ca2e9..603aaf0924 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -180,15 +180,17 @@
/* hflags2 */
-#define HF2_GIF_SHIFT 0 /* if set CPU takes interrupts */
-#define HF2_HIF_SHIFT 1 /* value of IF_MASK when entering SVM */
-#define HF2_NMI_SHIFT 2 /* CPU serving NMI */
-#define HF2_VINTR_SHIFT 3 /* value of V_INTR_MASKING bit */
-
-#define HF2_GIF_MASK (1 << HF2_GIF_SHIFT)
-#define HF2_HIF_MASK (1 << HF2_HIF_SHIFT)
-#define HF2_NMI_MASK (1 << HF2_NMI_SHIFT)
-#define HF2_VINTR_MASK (1 << HF2_VINTR_SHIFT)
+#define HF2_GIF_SHIFT 0 /* if set CPU takes interrupts */
+#define HF2_HIF_SHIFT 1 /* value of IF_MASK when entering SVM */
+#define HF2_NMI_SHIFT 2 /* CPU serving NMI */
+#define HF2_VINTR_SHIFT 3 /* value of V_INTR_MASKING bit */
+#define HF2_SMM_INSIDE_NMI_SHIFT 4 /* CPU serving SMI nested inside NMI */
+
+#define HF2_GIF_MASK (1 << HF2_GIF_SHIFT)
+#define HF2_HIF_MASK (1 << HF2_HIF_SHIFT)
+#define HF2_NMI_MASK (1 << HF2_NMI_SHIFT)
+#define HF2_VINTR_MASK (1 << HF2_VINTR_SHIFT)
+#define HF2_SMM_INSIDE_NMI_MASK (1 << HF2_SMM_INSIDE_NMI_SHIFT)
#define CR0_PE_SHIFT 0
#define CR0_MP_SHIFT 1
@@ -305,7 +307,7 @@
#define MSR_IA32_APICBASE 0x1b
#define MSR_IA32_APICBASE_BSP (1<<8)
#define MSR_IA32_APICBASE_ENABLE (1<<11)
-#define MSR_IA32_APICBASE_BASE (0xfffff<<12)
+#define MSR_IA32_APICBASE_BASE (0xfffffU<<12)
#define MSR_IA32_FEATURE_CONTROL 0x0000003a
#define MSR_TSC_ADJUST 0x0000003b
#define MSR_IA32_TSCDEADLINE 0x6e0
@@ -1105,6 +1107,18 @@ int x86_cpu_handle_mmu_fault(CPUState *cpu, vaddr addr,
int is_write, int mmu_idx);
void x86_cpu_set_a20(X86CPU *cpu, int a20_state);
+#ifndef CONFIG_USER_ONLY
+uint8_t x86_ldub_phys(CPUState *cs, hwaddr addr);
+uint32_t x86_lduw_phys(CPUState *cs, hwaddr addr);
+uint32_t x86_ldl_phys(CPUState *cs, hwaddr addr);
+uint64_t x86_ldq_phys(CPUState *cs, hwaddr addr);
+void x86_stb_phys(CPUState *cs, hwaddr addr, uint8_t val);
+void x86_stl_phys_notdirty(CPUState *cs, hwaddr addr, uint32_t val);
+void x86_stw_phys(CPUState *cs, hwaddr addr, uint32_t val);
+void x86_stl_phys(CPUState *cs, hwaddr addr, uint32_t val);
+void x86_stq_phys(CPUState *cs, hwaddr addr, uint64_t val);
+#endif
+
static inline bool hw_local_breakpoint_enabled(unsigned long dr7, int index)
{
return (dr7 >> (index * 2)) & 1;
@@ -1143,7 +1157,6 @@ void cpu_x86_update_cr3(CPUX86State *env, target_ulong new_cr3);
void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4);
/* hw/pc.c */
-void cpu_smm_update(CPUX86State *env);
uint64_t cpu_get_tsc(CPUX86State *env);
#define TARGET_PAGE_BITS 12
@@ -1292,6 +1305,11 @@ static inline void cpu_load_efer(CPUX86State *env, uint64_t val)
}
}
+static inline MemTxAttrs cpu_get_mem_attrs(CPUX86State *env)
+{
+ return ((MemTxAttrs) { .secure = (env->hflags & HF_SMM_MASK) != 0 });
+}
+
/* fpu_helper.c */
void cpu_set_mxcsr(CPUX86State *env, uint32_t val);
void cpu_set_fpuc(CPUX86State *env, uint16_t val);
@@ -1304,7 +1322,9 @@ void cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, uint64_t exit_info_1);
/* seg_helper.c */
void do_interrupt_x86_hardirq(CPUX86State *env, int intno, int is_hw);
+/* smm_helper.c */
void do_smm_enter(X86CPU *cpu);
+void cpu_smm_update(X86CPU *cpu);
void cpu_report_tpr_access(CPUX86State *env, TPRAccess access);
diff --git a/target-i386/helper.c b/target-i386/helper.c
index 4f1ddf701e..5480a96a0f 100644
--- a/target-i386/helper.c
+++ b/target-i386/helper.c
@@ -565,7 +565,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr,
pml4e_addr = ((env->cr[3] & ~0xfff) + (((addr >> 39) & 0x1ff) << 3)) &
env->a20_mask;
- pml4e = ldq_phys(cs->as, pml4e_addr);
+ pml4e = x86_ldq_phys(cs, pml4e_addr);
if (!(pml4e & PG_PRESENT_MASK)) {
goto do_fault;
}
@@ -574,12 +574,12 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr,
}
if (!(pml4e & PG_ACCESSED_MASK)) {
pml4e |= PG_ACCESSED_MASK;
- stl_phys_notdirty(cs->as, pml4e_addr, pml4e);
+ x86_stl_phys_notdirty(cs, pml4e_addr, pml4e);
}
ptep = pml4e ^ PG_NX_MASK;
pdpe_addr = ((pml4e & PG_ADDRESS_MASK) + (((addr >> 30) & 0x1ff) << 3)) &
env->a20_mask;
- pdpe = ldq_phys(cs->as, pdpe_addr);
+ pdpe = x86_ldq_phys(cs, pdpe_addr);
if (!(pdpe & PG_PRESENT_MASK)) {
goto do_fault;
}
@@ -589,7 +589,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr,
ptep &= pdpe ^ PG_NX_MASK;
if (!(pdpe & PG_ACCESSED_MASK)) {
pdpe |= PG_ACCESSED_MASK;
- stl_phys_notdirty(cs->as, pdpe_addr, pdpe);
+ x86_stl_phys_notdirty(cs, pdpe_addr, pdpe);
}
if (pdpe & PG_PSE_MASK) {
/* 1 GB page */
@@ -604,7 +604,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr,
/* XXX: load them when cr3 is loaded ? */
pdpe_addr = ((env->cr[3] & ~0x1f) + ((addr >> 27) & 0x18)) &
env->a20_mask;
- pdpe = ldq_phys(cs->as, pdpe_addr);
+ pdpe = x86_ldq_phys(cs, pdpe_addr);
if (!(pdpe & PG_PRESENT_MASK)) {
goto do_fault;
}
@@ -617,7 +617,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr,
pde_addr = ((pdpe & PG_ADDRESS_MASK) + (((addr >> 21) & 0x1ff) << 3)) &
env->a20_mask;
- pde = ldq_phys(cs->as, pde_addr);
+ pde = x86_ldq_phys(cs, pde_addr);
if (!(pde & PG_PRESENT_MASK)) {
goto do_fault;
}
@@ -635,11 +635,11 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr,
/* 4 KB page */
if (!(pde & PG_ACCESSED_MASK)) {
pde |= PG_ACCESSED_MASK;
- stl_phys_notdirty(cs->as, pde_addr, pde);
+ x86_stl_phys_notdirty(cs, pde_addr, pde);
}
pte_addr = ((pde & PG_ADDRESS_MASK) + (((addr >> 12) & 0x1ff) << 3)) &
env->a20_mask;
- pte = ldq_phys(cs->as, pte_addr);
+ pte = x86_ldq_phys(cs, pte_addr);
if (!(pte & PG_PRESENT_MASK)) {
goto do_fault;
}
@@ -655,7 +655,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr,
/* page directory entry */
pde_addr = ((env->cr[3] & ~0xfff) + ((addr >> 20) & 0xffc)) &
env->a20_mask;
- pde = ldl_phys(cs->as, pde_addr);
+ pde = x86_ldl_phys(cs, pde_addr);
if (!(pde & PG_PRESENT_MASK)) {
goto do_fault;
}
@@ -676,13 +676,13 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr,
if (!(pde & PG_ACCESSED_MASK)) {
pde |= PG_ACCESSED_MASK;
- stl_phys_notdirty(cs->as, pde_addr, pde);
+ x86_stl_phys_notdirty(cs, pde_addr, pde);
}
/* page directory entry */
pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) &
env->a20_mask;
- pte = ldl_phys(cs->as, pte_addr);
+ pte = x86_ldl_phys(cs, pte_addr);
if (!(pte & PG_PRESENT_MASK)) {
goto do_fault;
}
@@ -737,7 +737,7 @@ do_check_protect_pse36:
if (is_dirty) {
pte |= PG_DIRTY_MASK;
}
- stl_phys_notdirty(cs->as, pte_addr, pte);
+ x86_stl_phys_notdirty(cs, pte_addr, pte);
}
/* the page can be put in the TLB */
@@ -771,7 +771,8 @@ do_check_protect_pse36:
page_offset = vaddr & (page_size - 1);
paddr = pte + page_offset;
- tlb_set_page(cs, vaddr, paddr, prot, mmu_idx, page_size);
+ tlb_set_page_with_attrs(cs, vaddr, paddr, cpu_get_mem_attrs(env),
+ prot, mmu_idx, page_size);
return 0;
do_fault_rsvd:
error_code |= PG_ERROR_RSVD_MASK;
@@ -788,7 +789,7 @@ do_check_protect_pse36:
error_code |= PG_ERROR_I_D_MASK;
if (env->intercept_exceptions & (1 << EXCP0E_PAGE)) {
/* cr2 is not modified in case of exceptions */
- stq_phys(cs->as,
+ x86_stq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2),
addr);
} else {
@@ -827,13 +828,13 @@ hwaddr x86_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
}
pml4e_addr = ((env->cr[3] & ~0xfff) + (((addr >> 39) & 0x1ff) << 3)) &
env->a20_mask;
- pml4e = ldq_phys(cs->as, pml4e_addr);
+ pml4e = x86_ldq_phys(cs, pml4e_addr);
if (!(pml4e & PG_PRESENT_MASK)) {
return -1;
}
pdpe_addr = ((pml4e & PG_ADDRESS_MASK) +
(((addr >> 30) & 0x1ff) << 3)) & env->a20_mask;
- pdpe = ldq_phys(cs->as, pdpe_addr);
+ pdpe = x86_ldq_phys(cs, pdpe_addr);
if (!(pdpe & PG_PRESENT_MASK)) {
return -1;
}
@@ -848,14 +849,14 @@ hwaddr x86_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
{
pdpe_addr = ((env->cr[3] & ~0x1f) + ((addr >> 27) & 0x18)) &
env->a20_mask;
- pdpe = ldq_phys(cs->as, pdpe_addr);
+ pdpe = x86_ldq_phys(cs, pdpe_addr);
if (!(pdpe & PG_PRESENT_MASK))
return -1;
}
pde_addr = ((pdpe & PG_ADDRESS_MASK) +
(((addr >> 21) & 0x1ff) << 3)) & env->a20_mask;
- pde = ldq_phys(cs->as, pde_addr);
+ pde = x86_ldq_phys(cs, pde_addr);
if (!(pde & PG_PRESENT_MASK)) {
return -1;
}
@@ -868,7 +869,7 @@ hwaddr x86_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
pte_addr = ((pde & PG_ADDRESS_MASK) +
(((addr >> 12) & 0x1ff) << 3)) & env->a20_mask;
page_size = 4096;
- pte = ldq_phys(cs->as, pte_addr);
+ pte = x86_ldq_phys(cs, pte_addr);
}
if (!(pte & PG_PRESENT_MASK)) {
return -1;
@@ -878,7 +879,7 @@ hwaddr x86_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
/* page directory entry */
pde_addr = ((env->cr[3] & ~0xfff) + ((addr >> 20) & 0xffc)) & env->a20_mask;
- pde = ldl_phys(cs->as, pde_addr);
+ pde = x86_ldl_phys(cs, pde_addr);
if (!(pde & PG_PRESENT_MASK))
return -1;
if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
@@ -887,7 +888,7 @@ hwaddr x86_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
} else {
/* page directory entry */
pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & env->a20_mask;
- pte = ldl_phys(cs->as, pte_addr);
+ pte = x86_ldl_phys(cs, pte_addr);
if (!(pte & PG_PRESENT_MASK)) {
return -1;
}
@@ -1276,3 +1277,95 @@ void x86_cpu_exec_exit(CPUState *cs)
env->eflags = cpu_compute_eflags(env);
}
+
+#ifndef CONFIG_USER_ONLY
+uint8_t x86_ldub_phys(CPUState *cs, hwaddr addr)
+{
+ X86CPU *cpu = X86_CPU(cs);
+ CPUX86State *env = &cpu->env;
+
+ return address_space_ldub(cs->as, addr,
+ cpu_get_mem_attrs(env),
+ NULL);
+}
+
+uint32_t x86_lduw_phys(CPUState *cs, hwaddr addr)
+{
+ X86CPU *cpu = X86_CPU(cs);
+ CPUX86State *env = &cpu->env;
+
+ return address_space_lduw(cs->as, addr,
+ cpu_get_mem_attrs(env),
+ NULL);
+}
+
+uint32_t x86_ldl_phys(CPUState *cs, hwaddr addr)
+{
+ X86CPU *cpu = X86_CPU(cs);
+ CPUX86State *env = &cpu->env;
+
+ return address_space_ldl(cs->as, addr,
+ cpu_get_mem_attrs(env),
+ NULL);
+}
+
+uint64_t x86_ldq_phys(CPUState *cs, hwaddr addr)
+{
+ X86CPU *cpu = X86_CPU(cs);
+ CPUX86State *env = &cpu->env;
+
+ return address_space_ldq(cs->as, addr,
+ cpu_get_mem_attrs(env),
+ NULL);
+}
+
+void x86_stb_phys(CPUState *cs, hwaddr addr, uint8_t val)
+{
+ X86CPU *cpu = X86_CPU(cs);
+ CPUX86State *env = &cpu->env;
+
+ address_space_stb(cs->as, addr, val,
+ cpu_get_mem_attrs(env),
+ NULL);
+}
+
+void x86_stl_phys_notdirty(CPUState *cs, hwaddr addr, uint32_t val)
+{
+ X86CPU *cpu = X86_CPU(cs);
+ CPUX86State *env = &cpu->env;
+
+ address_space_stl_notdirty(cs->as, addr, val,
+ cpu_get_mem_attrs(env),
+ NULL);
+}
+
+void x86_stw_phys(CPUState *cs, hwaddr addr, uint32_t val)
+{
+ X86CPU *cpu = X86_CPU(cs);
+ CPUX86State *env = &cpu->env;
+
+ address_space_stw(cs->as, addr, val,
+ cpu_get_mem_attrs(env),
+ NULL);
+}
+
+void x86_stl_phys(CPUState *cs, hwaddr addr, uint32_t val)
+{
+ X86CPU *cpu = X86_CPU(cs);
+ CPUX86State *env = &cpu->env;
+
+ address_space_stl(cs->as, addr, val,
+ cpu_get_mem_attrs(env),
+ NULL);
+}
+
+void x86_stq_phys(CPUState *cs, hwaddr addr, uint64_t val)
+{
+ X86CPU *cpu = X86_CPU(cs);
+ CPUX86State *env = &cpu->env;
+
+ address_space_stq(cs->as, addr, val,
+ cpu_get_mem_attrs(env),
+ NULL);
+}
+#endif
diff --git a/target-i386/helper.h b/target-i386/helper.h
index 8eb0145039..74308f442e 100644
--- a/target-i386/helper.h
+++ b/target-i386/helper.h
@@ -86,12 +86,12 @@ DEF_HELPER_1(wrmsr, void, env)
DEF_HELPER_2(check_iob, void, env, i32)
DEF_HELPER_2(check_iow, void, env, i32)
DEF_HELPER_2(check_iol, void, env, i32)
-DEF_HELPER_2(outb, void, i32, i32)
-DEF_HELPER_1(inb, tl, i32)
-DEF_HELPER_2(outw, void, i32, i32)
-DEF_HELPER_1(inw, tl, i32)
-DEF_HELPER_2(outl, void, i32, i32)
-DEF_HELPER_1(inl, tl, i32)
+DEF_HELPER_3(outb, void, env, i32, i32)
+DEF_HELPER_2(inb, tl, env, i32)
+DEF_HELPER_3(outw, void, env, i32, i32)
+DEF_HELPER_2(inw, tl, env, i32)
+DEF_HELPER_3(outl, void, env, i32, i32)
+DEF_HELPER_2(inl, tl, env, i32)
DEF_HELPER_3(svm_check_intercept_param, void, env, i32, i64)
DEF_HELPER_3(vmexit, void, env, i32, i64)
diff --git a/target-i386/ioport-user.c b/target-i386/ioport-user.c
deleted file mode 100644
index f7636e0a87..0000000000
--- a/target-i386/ioport-user.c
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * qemu user ioport functions
- *
- * Copyright (c) 2003-2008 Fabrice Bellard
- *
- * 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 <stdio.h>
-
-#include "qemu.h"
-#include "qemu-common.h"
-#include "exec/ioport.h"
-
-void cpu_outb(pio_addr_t addr, uint8_t val)
-{
- fprintf(stderr, "outb: port=0x%04"FMT_pioaddr", data=%02"PRIx8"\n",
- addr, val);
-}
-
-void cpu_outw(pio_addr_t addr, uint16_t val)
-{
- fprintf(stderr, "outw: port=0x%04"FMT_pioaddr", data=%04"PRIx16"\n",
- addr, val);
-}
-
-void cpu_outl(pio_addr_t addr, uint32_t val)
-{
- fprintf(stderr, "outl: port=0x%04"FMT_pioaddr", data=%08"PRIx32"\n",
- addr, val);
-}
-
-uint8_t cpu_inb(pio_addr_t addr)
-{
- fprintf(stderr, "inb: port=0x%04"FMT_pioaddr"\n", addr);
- return 0;
-}
-
-uint16_t cpu_inw(pio_addr_t addr)
-{
- fprintf(stderr, "inw: port=0x%04"FMT_pioaddr"\n", addr);
- return 0;
-}
-
-uint32_t cpu_inl(pio_addr_t addr)
-{
- fprintf(stderr, "inl: port=0x%04"FMT_pioaddr"\n", addr);
- return 0;
-}
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index a26d25a81f..5a236e3103 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -2259,7 +2259,7 @@ MemTxAttrs kvm_arch_post_run(CPUState *cpu, struct kvm_run *run)
}
cpu_set_apic_tpr(x86_cpu->apic_state, run->cr8);
cpu_set_apic_base(x86_cpu->apic_state, run->apic_base);
- return MEMTXATTRS_UNSPECIFIED;
+ return cpu_get_mem_attrs(env);
}
int kvm_arch_process_async_events(CPUState *cs)
@@ -2766,3 +2766,8 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
{
return 0;
}
+
+int kvm_arch_msi_data_to_gsi(uint32_t data)
+{
+ abort();
+}
diff --git a/target-i386/machine.c b/target-i386/machine.c
index cd1ddd29e9..a0df64b577 100644
--- a/target-i386/machine.c
+++ b/target-i386/machine.c
@@ -372,6 +372,9 @@ static int cpu_post_load(void *opaque, int version_id)
}
tlb_flush(cs, 1);
+ if (tcg_enabled()) {
+ cpu_smm_update(cpu);
+ }
return 0;
}
@@ -400,6 +403,7 @@ static const VMStateDescription vmstate_steal_time_msr = {
.name = "cpu/steal_time_msr",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = steal_time_msr_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64(env.steal_time_msr, X86CPU),
VMSTATE_END_OF_LIST()
@@ -410,6 +414,7 @@ static const VMStateDescription vmstate_async_pf_msr = {
.name = "cpu/async_pf_msr",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = async_pf_msr_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64(env.async_pf_en_msr, X86CPU),
VMSTATE_END_OF_LIST()
@@ -420,6 +425,7 @@ static const VMStateDescription vmstate_pv_eoi_msr = {
.name = "cpu/async_pv_eoi_msr",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = pv_eoi_msr_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64(env.pv_eoi_en_msr, X86CPU),
VMSTATE_END_OF_LIST()
@@ -438,6 +444,7 @@ static const VMStateDescription vmstate_fpop_ip_dp = {
.name = "cpu/fpop_ip_dp",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = fpop_ip_dp_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT16(env.fpop, X86CPU),
VMSTATE_UINT64(env.fpip, X86CPU),
@@ -458,6 +465,7 @@ static const VMStateDescription vmstate_msr_tsc_adjust = {
.name = "cpu/msr_tsc_adjust",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = tsc_adjust_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64(env.tsc_adjust, X86CPU),
VMSTATE_END_OF_LIST()
@@ -476,6 +484,7 @@ static const VMStateDescription vmstate_msr_tscdeadline = {
.name = "cpu/msr_tscdeadline",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = tscdeadline_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64(env.tsc_deadline, X86CPU),
VMSTATE_END_OF_LIST()
@@ -502,6 +511,7 @@ static const VMStateDescription vmstate_msr_ia32_misc_enable = {
.name = "cpu/msr_ia32_misc_enable",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = misc_enable_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64(env.msr_ia32_misc_enable, X86CPU),
VMSTATE_END_OF_LIST()
@@ -512,6 +522,7 @@ static const VMStateDescription vmstate_msr_ia32_feature_control = {
.name = "cpu/msr_ia32_feature_control",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = feature_control_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64(env.msr_ia32_feature_control, X86CPU),
VMSTATE_END_OF_LIST()
@@ -546,6 +557,7 @@ static const VMStateDescription vmstate_msr_architectural_pmu = {
.name = "cpu/msr_architectural_pmu",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = pmu_enable_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64(env.msr_fixed_ctr_ctrl, X86CPU),
VMSTATE_UINT64(env.msr_global_ctrl, X86CPU),
@@ -581,6 +593,7 @@ static const VMStateDescription vmstate_mpx = {
.name = "cpu/mpx",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = mpx_needed,
.fields = (VMStateField[]) {
VMSTATE_BND_REGS(env.bnd_regs, X86CPU, 4),
VMSTATE_UINT64(env.bndcs_regs.cfgu, X86CPU),
@@ -602,6 +615,7 @@ static const VMStateDescription vmstate_msr_hypercall_hypercall = {
.name = "cpu/msr_hyperv_hypercall",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = hyperv_hypercall_enable_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64(env.msr_hv_guest_os_id, X86CPU),
VMSTATE_UINT64(env.msr_hv_hypercall, X86CPU),
@@ -621,6 +635,7 @@ static const VMStateDescription vmstate_msr_hyperv_vapic = {
.name = "cpu/msr_hyperv_vapic",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = hyperv_vapic_enable_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64(env.msr_hv_vapic, X86CPU),
VMSTATE_END_OF_LIST()
@@ -639,6 +654,7 @@ static const VMStateDescription vmstate_msr_hyperv_time = {
.name = "cpu/msr_hyperv_time",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = hyperv_time_enable_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64(env.msr_hv_tsc, X86CPU),
VMSTATE_END_OF_LIST()
@@ -680,6 +696,7 @@ static const VMStateDescription vmstate_avx512 = {
.name = "cpu/avx512",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = avx512_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64_ARRAY(env.opmask_regs, X86CPU, NB_OPMASK_REGS),
VMSTATE_ZMMH_REGS_VARS(env.xmm_regs, X86CPU, 0),
@@ -702,6 +719,7 @@ static const VMStateDescription vmstate_xss = {
.name = "cpu/xss",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = xss_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64(env.xss, X86CPU),
VMSTATE_END_OF_LIST()
@@ -810,54 +828,22 @@ VMStateDescription vmstate_x86_cpu = {
VMSTATE_END_OF_LIST()
/* The above list is not sorted /wrt version numbers, watch out! */
},
- .subsections = (VMStateSubsection []) {
- {
- .vmsd = &vmstate_async_pf_msr,
- .needed = async_pf_msr_needed,
- } , {
- .vmsd = &vmstate_pv_eoi_msr,
- .needed = pv_eoi_msr_needed,
- } , {
- .vmsd = &vmstate_steal_time_msr,
- .needed = steal_time_msr_needed,
- } , {
- .vmsd = &vmstate_fpop_ip_dp,
- .needed = fpop_ip_dp_needed,
- }, {
- .vmsd = &vmstate_msr_tsc_adjust,
- .needed = tsc_adjust_needed,
- }, {
- .vmsd = &vmstate_msr_tscdeadline,
- .needed = tscdeadline_needed,
- }, {
- .vmsd = &vmstate_msr_ia32_misc_enable,
- .needed = misc_enable_needed,
- }, {
- .vmsd = &vmstate_msr_ia32_feature_control,
- .needed = feature_control_needed,
- }, {
- .vmsd = &vmstate_msr_architectural_pmu,
- .needed = pmu_enable_needed,
- } , {
- .vmsd = &vmstate_mpx,
- .needed = mpx_needed,
- }, {
- .vmsd = &vmstate_msr_hypercall_hypercall,
- .needed = hyperv_hypercall_enable_needed,
- }, {
- .vmsd = &vmstate_msr_hyperv_vapic,
- .needed = hyperv_vapic_enable_needed,
- }, {
- .vmsd = &vmstate_msr_hyperv_time,
- .needed = hyperv_time_enable_needed,
- }, {
- .vmsd = &vmstate_avx512,
- .needed = avx512_needed,
- }, {
- .vmsd = &vmstate_xss,
- .needed = xss_needed,
- } , {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_async_pf_msr,
+ &vmstate_pv_eoi_msr,
+ &vmstate_steal_time_msr,
+ &vmstate_fpop_ip_dp,
+ &vmstate_msr_tsc_adjust,
+ &vmstate_msr_tscdeadline,
+ &vmstate_msr_ia32_misc_enable,
+ &vmstate_msr_ia32_feature_control,
+ &vmstate_msr_architectural_pmu,
+ &vmstate_mpx,
+ &vmstate_msr_hypercall_hypercall,
+ &vmstate_msr_hyperv_vapic,
+ &vmstate_msr_hyperv_time,
+ &vmstate_avx512,
+ &vmstate_xss,
+ NULL
}
};
diff --git a/target-i386/misc_helper.c b/target-i386/misc_helper.c
index 4aaf1e4d95..52c5d65e91 100644
--- a/target-i386/misc_helper.c
+++ b/target-i386/misc_helper.c
@@ -18,38 +18,71 @@
*/
#include "cpu.h"
-#include "exec/ioport.h"
#include "exec/helper-proto.h"
#include "exec/cpu_ldst.h"
+#include "exec/address-spaces.h"
-void helper_outb(uint32_t port, uint32_t data)
+void helper_outb(CPUX86State *env, uint32_t port, uint32_t data)
{
- cpu_outb(port, data & 0xff);
+#ifdef CONFIG_USER_ONLY
+ fprintf(stderr, "outb: port=0x%04x, data=%02x\n", port, data);
+#else
+ address_space_stb(&address_space_io, port, data,
+ cpu_get_mem_attrs(env), NULL);
+#endif
}
-target_ulong helper_inb(uint32_t port)
+target_ulong helper_inb(CPUX86State *env, uint32_t port)
{
- return cpu_inb(port);
+#ifdef CONFIG_USER_ONLY
+ fprintf(stderr, "inb: port=0x%04x\n", port);
+ return 0;
+#else
+ return address_space_ldub(&address_space_io, port,
+ cpu_get_mem_attrs(env), NULL);
+#endif
}
-void helper_outw(uint32_t port, uint32_t data)
+void helper_outw(CPUX86State *env, uint32_t port, uint32_t data)
{
- cpu_outw(port, data & 0xffff);
+#ifdef CONFIG_USER_ONLY
+ fprintf(stderr, "outw: port=0x%04x, data=%04x\n", port, data);
+#else
+ address_space_stw(&address_space_io, port, data,
+ cpu_get_mem_attrs(env), NULL);
+#endif
}
-target_ulong helper_inw(uint32_t port)
+target_ulong helper_inw(CPUX86State *env, uint32_t port)
{
- return cpu_inw(port);
+#ifdef CONFIG_USER_ONLY
+ fprintf(stderr, "inw: port=0x%04x\n", port);
+ return 0;
+#else
+ return address_space_lduw(&address_space_io, port,
+ cpu_get_mem_attrs(env), NULL);
+#endif
}
-void helper_outl(uint32_t port, uint32_t data)
+void helper_outl(CPUX86State *env, uint32_t port, uint32_t data)
{
- cpu_outl(port, data);
+#ifdef CONFIG_USER_ONLY
+ fprintf(stderr, "outw: port=0x%04x, data=%08x\n", port, data);
+#else
+ address_space_stl(&address_space_io, port, data,
+ cpu_get_mem_attrs(env), NULL);
+#endif
}
-target_ulong helper_inl(uint32_t port)
+target_ulong helper_inl(CPUX86State *env, uint32_t port)
{
- return cpu_inl(port);
+#ifdef CONFIG_USER_ONLY
+ fprintf(stderr, "inl: port=0x%04x\n", port);
+ return 0;
+#else
+ return address_space_ldl(&address_space_io, port,
+ cpu_get_mem_attrs(env), NULL);
+#endif
}
void helper_into(CPUX86State *env, int next_eip_addend)
diff --git a/target-i386/seg_helper.c b/target-i386/seg_helper.c
index 2bc757af31..8a4271ebe2 100644
--- a/target-i386/seg_helper.c
+++ b/target-i386/seg_helper.c
@@ -1144,7 +1144,7 @@ static void handle_even_inj(CPUX86State *env, int intno, int is_int,
int error_code, int is_hw, int rm)
{
CPUState *cs = CPU(x86_env_get_cpu(env));
- uint32_t event_inj = ldl_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb,
+ uint32_t event_inj = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb,
control.event_inj));
if (!(event_inj & SVM_EVTINJ_VALID)) {
@@ -1158,11 +1158,11 @@ static void handle_even_inj(CPUX86State *env, int intno, int is_int,
event_inj = intno | type | SVM_EVTINJ_VALID;
if (!rm && exception_has_error_code(intno)) {
event_inj |= SVM_EVTINJ_VALID_ERR;
- stl_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb,
+ x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb,
control.event_inj_err),
error_code);
}
- stl_phys(cs->as,
+ x86_stl_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, control.event_inj),
event_inj);
}
@@ -1240,11 +1240,11 @@ static void do_interrupt_all(X86CPU *cpu, int intno, int is_int,
#if !defined(CONFIG_USER_ONLY)
if (env->hflags & HF_SVMI_MASK) {
CPUState *cs = CPU(cpu);
- uint32_t event_inj = ldl_phys(cs->as, env->vm_vmcb +
+ uint32_t event_inj = x86_ldl_phys(cs, env->vm_vmcb +
offsetof(struct vmcb,
control.event_inj));
- stl_phys(cs->as,
+ x86_stl_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, control.event_inj),
event_inj & ~SVM_EVTINJ_VALID);
}
@@ -1339,7 +1339,7 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
int intno;
/* FIXME: this should respect TPR */
cpu_svm_check_intercept_param(env, SVM_EXIT_VINTR, 0);
- intno = ldl_phys(cs->as, env->vm_vmcb
+ intno = x86_ldl_phys(cs, env->vm_vmcb
+ offsetof(struct vmcb, control.int_vector));
qemu_log_mask(CPU_LOG_TB_IN_ASM,
"Servicing virtual hardware INT=0x%02x\n", intno);
diff --git a/target-i386/smm_helper.c b/target-i386/smm_helper.c
index c62f46847c..02e24b9236 100644
--- a/target-i386/smm_helper.c
+++ b/target-i386/smm_helper.c
@@ -40,6 +40,16 @@ void helper_rsm(CPUX86State *env)
#define SMM_REVISION_ID 0x00020000
#endif
+void cpu_smm_update(X86CPU *cpu)
+{
+ CPUX86State *env = &cpu->env;
+ bool smm_enabled = (env->hflags & HF_SMM_MASK);
+
+ if (cpu->smram) {
+ memory_region_set_enabled(cpu->smram, smm_enabled);
+ }
+}
+
void do_smm_enter(X86CPU *cpu)
{
CPUX86State *env = &cpu->env;
@@ -52,7 +62,12 @@ void do_smm_enter(X86CPU *cpu)
log_cpu_state_mask(CPU_LOG_INT, CPU(cpu), CPU_DUMP_CCOP);
env->hflags |= HF_SMM_MASK;
- cpu_smm_update(env);
+ if (env->hflags2 & HF2_NMI_MASK) {
+ env->hflags2 |= HF2_SMM_INSIDE_NMI_MASK;
+ } else {
+ env->hflags2 |= HF2_NMI_MASK;
+ }
+ cpu_smm_update(cpu);
sm_state = env->smbase + 0x8000;
@@ -60,83 +75,83 @@ void do_smm_enter(X86CPU *cpu)
for (i = 0; i < 6; i++) {
dt = &env->segs[i];
offset = 0x7e00 + i * 16;
- stw_phys(cs->as, sm_state + offset, dt->selector);
- stw_phys(cs->as, sm_state + offset + 2, (dt->flags >> 8) & 0xf0ff);
- stl_phys(cs->as, sm_state + offset + 4, dt->limit);
- stq_phys(cs->as, sm_state + offset + 8, dt->base);
+ x86_stw_phys(cs, sm_state + offset, dt->selector);
+ x86_stw_phys(cs, sm_state + offset + 2, (dt->flags >> 8) & 0xf0ff);
+ x86_stl_phys(cs, sm_state + offset + 4, dt->limit);
+ x86_stq_phys(cs, sm_state + offset + 8, dt->base);
}
- stq_phys(cs->as, sm_state + 0x7e68, env->gdt.base);
- stl_phys(cs->as, sm_state + 0x7e64, env->gdt.limit);
+ x86_stq_phys(cs, sm_state + 0x7e68, env->gdt.base);
+ x86_stl_phys(cs, sm_state + 0x7e64, env->gdt.limit);
- stw_phys(cs->as, sm_state + 0x7e70, env->ldt.selector);
- stq_phys(cs->as, sm_state + 0x7e78, env->ldt.base);
- stl_phys(cs->as, sm_state + 0x7e74, env->ldt.limit);
- stw_phys(cs->as, sm_state + 0x7e72, (env->ldt.flags >> 8) & 0xf0ff);
+ x86_stw_phys(cs, sm_state + 0x7e70, env->ldt.selector);
+ x86_stq_phys(cs, sm_state + 0x7e78, env->ldt.base);
+ x86_stl_phys(cs, sm_state + 0x7e74, env->ldt.limit);
+ x86_stw_phys(cs, sm_state + 0x7e72, (env->ldt.flags >> 8) & 0xf0ff);
- stq_phys(cs->as, sm_state + 0x7e88, env->idt.base);
- stl_phys(cs->as, sm_state + 0x7e84, env->idt.limit);
+ x86_stq_phys(cs, sm_state + 0x7e88, env->idt.base);
+ x86_stl_phys(cs, sm_state + 0x7e84, env->idt.limit);
- stw_phys(cs->as, sm_state + 0x7e90, env->tr.selector);
- stq_phys(cs->as, sm_state + 0x7e98, env->tr.base);
- stl_phys(cs->as, sm_state + 0x7e94, env->tr.limit);
- stw_phys(cs->as, sm_state + 0x7e92, (env->tr.flags >> 8) & 0xf0ff);
+ x86_stw_phys(cs, sm_state + 0x7e90, env->tr.selector);
+ x86_stq_phys(cs, sm_state + 0x7e98, env->tr.base);
+ x86_stl_phys(cs, sm_state + 0x7e94, env->tr.limit);
+ x86_stw_phys(cs, sm_state + 0x7e92, (env->tr.flags >> 8) & 0xf0ff);
- stq_phys(cs->as, sm_state + 0x7ed0, env->efer);
+ x86_stq_phys(cs, sm_state + 0x7ed0, env->efer);
- stq_phys(cs->as, sm_state + 0x7ff8, env->regs[R_EAX]);
- stq_phys(cs->as, sm_state + 0x7ff0, env->regs[R_ECX]);
- stq_phys(cs->as, sm_state + 0x7fe8, env->regs[R_EDX]);
- stq_phys(cs->as, sm_state + 0x7fe0, env->regs[R_EBX]);
- stq_phys(cs->as, sm_state + 0x7fd8, env->regs[R_ESP]);
- stq_phys(cs->as, sm_state + 0x7fd0, env->regs[R_EBP]);
- stq_phys(cs->as, sm_state + 0x7fc8, env->regs[R_ESI]);
- stq_phys(cs->as, sm_state + 0x7fc0, env->regs[R_EDI]);
+ x86_stq_phys(cs, sm_state + 0x7ff8, env->regs[R_EAX]);
+ x86_stq_phys(cs, sm_state + 0x7ff0, env->regs[R_ECX]);
+ x86_stq_phys(cs, sm_state + 0x7fe8, env->regs[R_EDX]);
+ x86_stq_phys(cs, sm_state + 0x7fe0, env->regs[R_EBX]);
+ x86_stq_phys(cs, sm_state + 0x7fd8, env->regs[R_ESP]);
+ x86_stq_phys(cs, sm_state + 0x7fd0, env->regs[R_EBP]);
+ x86_stq_phys(cs, sm_state + 0x7fc8, env->regs[R_ESI]);
+ x86_stq_phys(cs, sm_state + 0x7fc0, env->regs[R_EDI]);
for (i = 8; i < 16; i++) {
- stq_phys(cs->as, sm_state + 0x7ff8 - i * 8, env->regs[i]);
+ x86_stq_phys(cs, sm_state + 0x7ff8 - i * 8, env->regs[i]);
}
- stq_phys(cs->as, sm_state + 0x7f78, env->eip);
- stl_phys(cs->as, sm_state + 0x7f70, cpu_compute_eflags(env));
- stl_phys(cs->as, sm_state + 0x7f68, env->dr[6]);
- stl_phys(cs->as, sm_state + 0x7f60, env->dr[7]);
+ x86_stq_phys(cs, sm_state + 0x7f78, env->eip);
+ x86_stl_phys(cs, sm_state + 0x7f70, cpu_compute_eflags(env));
+ x86_stl_phys(cs, sm_state + 0x7f68, env->dr[6]);
+ x86_stl_phys(cs, sm_state + 0x7f60, env->dr[7]);
- stl_phys(cs->as, sm_state + 0x7f48, env->cr[4]);
- stq_phys(cs->as, sm_state + 0x7f50, env->cr[3]);
- stl_phys(cs->as, sm_state + 0x7f58, env->cr[0]);
+ x86_stl_phys(cs, sm_state + 0x7f48, env->cr[4]);
+ x86_stq_phys(cs, sm_state + 0x7f50, env->cr[3]);
+ x86_stl_phys(cs, sm_state + 0x7f58, env->cr[0]);
- stl_phys(cs->as, sm_state + 0x7efc, SMM_REVISION_ID);
- stl_phys(cs->as, sm_state + 0x7f00, env->smbase);
+ x86_stl_phys(cs, sm_state + 0x7efc, SMM_REVISION_ID);
+ x86_stl_phys(cs, sm_state + 0x7f00, env->smbase);
#else
- stl_phys(cs->as, sm_state + 0x7ffc, env->cr[0]);
- stl_phys(cs->as, sm_state + 0x7ff8, env->cr[3]);
- stl_phys(cs->as, sm_state + 0x7ff4, cpu_compute_eflags(env));
- stl_phys(cs->as, sm_state + 0x7ff0, env->eip);
- stl_phys(cs->as, sm_state + 0x7fec, env->regs[R_EDI]);
- stl_phys(cs->as, sm_state + 0x7fe8, env->regs[R_ESI]);
- stl_phys(cs->as, sm_state + 0x7fe4, env->regs[R_EBP]);
- stl_phys(cs->as, sm_state + 0x7fe0, env->regs[R_ESP]);
- stl_phys(cs->as, sm_state + 0x7fdc, env->regs[R_EBX]);
- stl_phys(cs->as, sm_state + 0x7fd8, env->regs[R_EDX]);
- stl_phys(cs->as, sm_state + 0x7fd4, env->regs[R_ECX]);
- stl_phys(cs->as, sm_state + 0x7fd0, env->regs[R_EAX]);
- stl_phys(cs->as, sm_state + 0x7fcc, env->dr[6]);
- stl_phys(cs->as, sm_state + 0x7fc8, env->dr[7]);
-
- stl_phys(cs->as, sm_state + 0x7fc4, env->tr.selector);
- stl_phys(cs->as, sm_state + 0x7f64, env->tr.base);
- stl_phys(cs->as, sm_state + 0x7f60, env->tr.limit);
- stl_phys(cs->as, sm_state + 0x7f5c, (env->tr.flags >> 8) & 0xf0ff);
-
- stl_phys(cs->as, sm_state + 0x7fc0, env->ldt.selector);
- stl_phys(cs->as, sm_state + 0x7f80, env->ldt.base);
- stl_phys(cs->as, sm_state + 0x7f7c, env->ldt.limit);
- stl_phys(cs->as, sm_state + 0x7f78, (env->ldt.flags >> 8) & 0xf0ff);
-
- stl_phys(cs->as, sm_state + 0x7f74, env->gdt.base);
- stl_phys(cs->as, sm_state + 0x7f70, env->gdt.limit);
-
- stl_phys(cs->as, sm_state + 0x7f58, env->idt.base);
- stl_phys(cs->as, sm_state + 0x7f54, env->idt.limit);
+ x86_stl_phys(cs, sm_state + 0x7ffc, env->cr[0]);
+ x86_stl_phys(cs, sm_state + 0x7ff8, env->cr[3]);
+ x86_stl_phys(cs, sm_state + 0x7ff4, cpu_compute_eflags(env));
+ x86_stl_phys(cs, sm_state + 0x7ff0, env->eip);
+ x86_stl_phys(cs, sm_state + 0x7fec, env->regs[R_EDI]);
+ x86_stl_phys(cs, sm_state + 0x7fe8, env->regs[R_ESI]);
+ x86_stl_phys(cs, sm_state + 0x7fe4, env->regs[R_EBP]);
+ x86_stl_phys(cs, sm_state + 0x7fe0, env->regs[R_ESP]);
+ x86_stl_phys(cs, sm_state + 0x7fdc, env->regs[R_EBX]);
+ x86_stl_phys(cs, sm_state + 0x7fd8, env->regs[R_EDX]);
+ x86_stl_phys(cs, sm_state + 0x7fd4, env->regs[R_ECX]);
+ x86_stl_phys(cs, sm_state + 0x7fd0, env->regs[R_EAX]);
+ x86_stl_phys(cs, sm_state + 0x7fcc, env->dr[6]);
+ x86_stl_phys(cs, sm_state + 0x7fc8, env->dr[7]);
+
+ x86_stl_phys(cs, sm_state + 0x7fc4, env->tr.selector);
+ x86_stl_phys(cs, sm_state + 0x7f64, env->tr.base);
+ x86_stl_phys(cs, sm_state + 0x7f60, env->tr.limit);
+ x86_stl_phys(cs, sm_state + 0x7f5c, (env->tr.flags >> 8) & 0xf0ff);
+
+ x86_stl_phys(cs, sm_state + 0x7fc0, env->ldt.selector);
+ x86_stl_phys(cs, sm_state + 0x7f80, env->ldt.base);
+ x86_stl_phys(cs, sm_state + 0x7f7c, env->ldt.limit);
+ x86_stl_phys(cs, sm_state + 0x7f78, (env->ldt.flags >> 8) & 0xf0ff);
+
+ x86_stl_phys(cs, sm_state + 0x7f74, env->gdt.base);
+ x86_stl_phys(cs, sm_state + 0x7f70, env->gdt.limit);
+
+ x86_stl_phys(cs, sm_state + 0x7f58, env->idt.base);
+ x86_stl_phys(cs, sm_state + 0x7f54, env->idt.limit);
for (i = 0; i < 6; i++) {
dt = &env->segs[i];
@@ -145,15 +160,15 @@ void do_smm_enter(X86CPU *cpu)
} else {
offset = 0x7f2c + (i - 3) * 12;
}
- stl_phys(cs->as, sm_state + 0x7fa8 + i * 4, dt->selector);
- stl_phys(cs->as, sm_state + offset + 8, dt->base);
- stl_phys(cs->as, sm_state + offset + 4, dt->limit);
- stl_phys(cs->as, sm_state + offset, (dt->flags >> 8) & 0xf0ff);
+ x86_stl_phys(cs, sm_state + 0x7fa8 + i * 4, dt->selector);
+ x86_stl_phys(cs, sm_state + offset + 8, dt->base);
+ x86_stl_phys(cs, sm_state + offset + 4, dt->limit);
+ x86_stl_phys(cs, sm_state + offset, (dt->flags >> 8) & 0xf0ff);
}
- stl_phys(cs->as, sm_state + 0x7f14, env->cr[4]);
+ x86_stl_phys(cs, sm_state + 0x7f14, env->cr[4]);
- stl_phys(cs->as, sm_state + 0x7efc, SMM_REVISION_ID);
- stl_phys(cs->as, sm_state + 0x7ef8, env->smbase);
+ x86_stl_phys(cs, sm_state + 0x7efc, SMM_REVISION_ID);
+ x86_stl_phys(cs, sm_state + 0x7ef8, env->smbase);
#endif
/* init SMM cpu state */
@@ -172,22 +187,22 @@ void do_smm_enter(X86CPU *cpu)
cpu_x86_load_seg_cache(env, R_CS, (env->smbase >> 4) & 0xffff, env->smbase,
0xffffffff,
DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
- DESC_A_MASK);
+ DESC_G_MASK | DESC_A_MASK);
cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0xffffffff,
DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
- DESC_A_MASK);
+ DESC_G_MASK | DESC_A_MASK);
cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0xffffffff,
DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
- DESC_A_MASK);
+ DESC_G_MASK | DESC_A_MASK);
cpu_x86_load_seg_cache(env, R_SS, 0, 0, 0xffffffff,
DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
- DESC_A_MASK);
+ DESC_G_MASK | DESC_A_MASK);
cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0xffffffff,
DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
- DESC_A_MASK);
+ DESC_G_MASK | DESC_A_MASK);
cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0xffffffff,
DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
- DESC_A_MASK);
+ DESC_G_MASK | DESC_A_MASK);
}
void helper_rsm(CPUX86State *env)
@@ -200,91 +215,91 @@ void helper_rsm(CPUX86State *env)
sm_state = env->smbase + 0x8000;
#ifdef TARGET_X86_64
- cpu_load_efer(env, ldq_phys(cs->as, sm_state + 0x7ed0));
-
- env->gdt.base = ldq_phys(cs->as, sm_state + 0x7e68);
- env->gdt.limit = ldl_phys(cs->as, sm_state + 0x7e64);
-
- env->ldt.selector = lduw_phys(cs->as, sm_state + 0x7e70);
- env->ldt.base = ldq_phys(cs->as, sm_state + 0x7e78);
- env->ldt.limit = ldl_phys(cs->as, sm_state + 0x7e74);
- env->ldt.flags = (lduw_phys(cs->as, sm_state + 0x7e72) & 0xf0ff) << 8;
-
- env->idt.base = ldq_phys(cs->as, sm_state + 0x7e88);
- env->idt.limit = ldl_phys(cs->as, sm_state + 0x7e84);
-
- env->tr.selector = lduw_phys(cs->as, sm_state + 0x7e90);
- env->tr.base = ldq_phys(cs->as, sm_state + 0x7e98);
- env->tr.limit = ldl_phys(cs->as, sm_state + 0x7e94);
- env->tr.flags = (lduw_phys(cs->as, sm_state + 0x7e92) & 0xf0ff) << 8;
-
- env->regs[R_EAX] = ldq_phys(cs->as, sm_state + 0x7ff8);
- env->regs[R_ECX] = ldq_phys(cs->as, sm_state + 0x7ff0);
- env->regs[R_EDX] = ldq_phys(cs->as, sm_state + 0x7fe8);
- env->regs[R_EBX] = ldq_phys(cs->as, sm_state + 0x7fe0);
- env->regs[R_ESP] = ldq_phys(cs->as, sm_state + 0x7fd8);
- env->regs[R_EBP] = ldq_phys(cs->as, sm_state + 0x7fd0);
- env->regs[R_ESI] = ldq_phys(cs->as, sm_state + 0x7fc8);
- env->regs[R_EDI] = ldq_phys(cs->as, sm_state + 0x7fc0);
+ cpu_load_efer(env, x86_ldq_phys(cs, sm_state + 0x7ed0));
+
+ env->gdt.base = x86_ldq_phys(cs, sm_state + 0x7e68);
+ env->gdt.limit = x86_ldl_phys(cs, sm_state + 0x7e64);
+
+ env->ldt.selector = x86_lduw_phys(cs, sm_state + 0x7e70);
+ env->ldt.base = x86_ldq_phys(cs, sm_state + 0x7e78);
+ env->ldt.limit = x86_ldl_phys(cs, sm_state + 0x7e74);
+ env->ldt.flags = (x86_lduw_phys(cs, sm_state + 0x7e72) & 0xf0ff) << 8;
+
+ env->idt.base = x86_ldq_phys(cs, sm_state + 0x7e88);
+ env->idt.limit = x86_ldl_phys(cs, sm_state + 0x7e84);
+
+ env->tr.selector = x86_lduw_phys(cs, sm_state + 0x7e90);
+ env->tr.base = x86_ldq_phys(cs, sm_state + 0x7e98);
+ env->tr.limit = x86_ldl_phys(cs, sm_state + 0x7e94);
+ env->tr.flags = (x86_lduw_phys(cs, sm_state + 0x7e92) & 0xf0ff) << 8;
+
+ env->regs[R_EAX] = x86_ldq_phys(cs, sm_state + 0x7ff8);
+ env->regs[R_ECX] = x86_ldq_phys(cs, sm_state + 0x7ff0);
+ env->regs[R_EDX] = x86_ldq_phys(cs, sm_state + 0x7fe8);
+ env->regs[R_EBX] = x86_ldq_phys(cs, sm_state + 0x7fe0);
+ env->regs[R_ESP] = x86_ldq_phys(cs, sm_state + 0x7fd8);
+ env->regs[R_EBP] = x86_ldq_phys(cs, sm_state + 0x7fd0);
+ env->regs[R_ESI] = x86_ldq_phys(cs, sm_state + 0x7fc8);
+ env->regs[R_EDI] = x86_ldq_phys(cs, sm_state + 0x7fc0);
for (i = 8; i < 16; i++) {
- env->regs[i] = ldq_phys(cs->as, sm_state + 0x7ff8 - i * 8);
+ env->regs[i] = x86_ldq_phys(cs, sm_state + 0x7ff8 - i * 8);
}
- env->eip = ldq_phys(cs->as, sm_state + 0x7f78);
- cpu_load_eflags(env, ldl_phys(cs->as, sm_state + 0x7f70),
+ env->eip = x86_ldq_phys(cs, sm_state + 0x7f78);
+ cpu_load_eflags(env, x86_ldl_phys(cs, sm_state + 0x7f70),
~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
- env->dr[6] = ldl_phys(cs->as, sm_state + 0x7f68);
- env->dr[7] = ldl_phys(cs->as, sm_state + 0x7f60);
+ env->dr[6] = x86_ldl_phys(cs, sm_state + 0x7f68);
+ env->dr[7] = x86_ldl_phys(cs, sm_state + 0x7f60);
- cpu_x86_update_cr4(env, ldl_phys(cs->as, sm_state + 0x7f48));
- cpu_x86_update_cr3(env, ldq_phys(cs->as, sm_state + 0x7f50));
- cpu_x86_update_cr0(env, ldl_phys(cs->as, sm_state + 0x7f58));
+ cpu_x86_update_cr4(env, x86_ldl_phys(cs, sm_state + 0x7f48));
+ cpu_x86_update_cr3(env, x86_ldq_phys(cs, sm_state + 0x7f50));
+ cpu_x86_update_cr0(env, x86_ldl_phys(cs, sm_state + 0x7f58));
for (i = 0; i < 6; i++) {
offset = 0x7e00 + i * 16;
cpu_x86_load_seg_cache(env, i,
- lduw_phys(cs->as, sm_state + offset),
- ldq_phys(cs->as, sm_state + offset + 8),
- ldl_phys(cs->as, sm_state + offset + 4),
- (lduw_phys(cs->as, sm_state + offset + 2) &
+ x86_lduw_phys(cs, sm_state + offset),
+ x86_ldq_phys(cs, sm_state + offset + 8),
+ x86_ldl_phys(cs, sm_state + offset + 4),
+ (x86_lduw_phys(cs, sm_state + offset + 2) &
0xf0ff) << 8);
}
- val = ldl_phys(cs->as, sm_state + 0x7efc); /* revision ID */
+ val = x86_ldl_phys(cs, sm_state + 0x7efc); /* revision ID */
if (val & 0x20000) {
- env->smbase = ldl_phys(cs->as, sm_state + 0x7f00) & ~0x7fff;
+ env->smbase = x86_ldl_phys(cs, sm_state + 0x7f00) & ~0x7fff;
}
#else
- cpu_x86_update_cr0(env, ldl_phys(cs->as, sm_state + 0x7ffc));
- cpu_x86_update_cr3(env, ldl_phys(cs->as, sm_state + 0x7ff8));
- cpu_load_eflags(env, ldl_phys(cs->as, sm_state + 0x7ff4),
+ cpu_x86_update_cr0(env, x86_ldl_phys(cs, sm_state + 0x7ffc));
+ cpu_x86_update_cr3(env, x86_ldl_phys(cs, sm_state + 0x7ff8));
+ cpu_load_eflags(env, x86_ldl_phys(cs, sm_state + 0x7ff4),
~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
- env->eip = ldl_phys(cs->as, sm_state + 0x7ff0);
- env->regs[R_EDI] = ldl_phys(cs->as, sm_state + 0x7fec);
- env->regs[R_ESI] = ldl_phys(cs->as, sm_state + 0x7fe8);
- env->regs[R_EBP] = ldl_phys(cs->as, sm_state + 0x7fe4);
- env->regs[R_ESP] = ldl_phys(cs->as, sm_state + 0x7fe0);
- env->regs[R_EBX] = ldl_phys(cs->as, sm_state + 0x7fdc);
- env->regs[R_EDX] = ldl_phys(cs->as, sm_state + 0x7fd8);
- env->regs[R_ECX] = ldl_phys(cs->as, sm_state + 0x7fd4);
- env->regs[R_EAX] = ldl_phys(cs->as, sm_state + 0x7fd0);
- env->dr[6] = ldl_phys(cs->as, sm_state + 0x7fcc);
- env->dr[7] = ldl_phys(cs->as, sm_state + 0x7fc8);
-
- env->tr.selector = ldl_phys(cs->as, sm_state + 0x7fc4) & 0xffff;
- env->tr.base = ldl_phys(cs->as, sm_state + 0x7f64);
- env->tr.limit = ldl_phys(cs->as, sm_state + 0x7f60);
- env->tr.flags = (ldl_phys(cs->as, sm_state + 0x7f5c) & 0xf0ff) << 8;
-
- env->ldt.selector = ldl_phys(cs->as, sm_state + 0x7fc0) & 0xffff;
- env->ldt.base = ldl_phys(cs->as, sm_state + 0x7f80);
- env->ldt.limit = ldl_phys(cs->as, sm_state + 0x7f7c);
- env->ldt.flags = (ldl_phys(cs->as, sm_state + 0x7f78) & 0xf0ff) << 8;
-
- env->gdt.base = ldl_phys(cs->as, sm_state + 0x7f74);
- env->gdt.limit = ldl_phys(cs->as, sm_state + 0x7f70);
-
- env->idt.base = ldl_phys(cs->as, sm_state + 0x7f58);
- env->idt.limit = ldl_phys(cs->as, sm_state + 0x7f54);
+ env->eip = x86_ldl_phys(cs, sm_state + 0x7ff0);
+ env->regs[R_EDI] = x86_ldl_phys(cs, sm_state + 0x7fec);
+ env->regs[R_ESI] = x86_ldl_phys(cs, sm_state + 0x7fe8);
+ env->regs[R_EBP] = x86_ldl_phys(cs, sm_state + 0x7fe4);
+ env->regs[R_ESP] = x86_ldl_phys(cs, sm_state + 0x7fe0);
+ env->regs[R_EBX] = x86_ldl_phys(cs, sm_state + 0x7fdc);
+ env->regs[R_EDX] = x86_ldl_phys(cs, sm_state + 0x7fd8);
+ env->regs[R_ECX] = x86_ldl_phys(cs, sm_state + 0x7fd4);
+ env->regs[R_EAX] = x86_ldl_phys(cs, sm_state + 0x7fd0);
+ env->dr[6] = x86_ldl_phys(cs, sm_state + 0x7fcc);
+ env->dr[7] = x86_ldl_phys(cs, sm_state + 0x7fc8);
+
+ env->tr.selector = x86_ldl_phys(cs, sm_state + 0x7fc4) & 0xffff;
+ env->tr.base = x86_ldl_phys(cs, sm_state + 0x7f64);
+ env->tr.limit = x86_ldl_phys(cs, sm_state + 0x7f60);
+ env->tr.flags = (x86_ldl_phys(cs, sm_state + 0x7f5c) & 0xf0ff) << 8;
+
+ env->ldt.selector = x86_ldl_phys(cs, sm_state + 0x7fc0) & 0xffff;
+ env->ldt.base = x86_ldl_phys(cs, sm_state + 0x7f80);
+ env->ldt.limit = x86_ldl_phys(cs, sm_state + 0x7f7c);
+ env->ldt.flags = (x86_ldl_phys(cs, sm_state + 0x7f78) & 0xf0ff) << 8;
+
+ env->gdt.base = x86_ldl_phys(cs, sm_state + 0x7f74);
+ env->gdt.limit = x86_ldl_phys(cs, sm_state + 0x7f70);
+
+ env->idt.base = x86_ldl_phys(cs, sm_state + 0x7f58);
+ env->idt.limit = x86_ldl_phys(cs, sm_state + 0x7f54);
for (i = 0; i < 6; i++) {
if (i < 3) {
@@ -293,22 +308,26 @@ void helper_rsm(CPUX86State *env)
offset = 0x7f2c + (i - 3) * 12;
}
cpu_x86_load_seg_cache(env, i,
- ldl_phys(cs->as,
+ x86_ldl_phys(cs,
sm_state + 0x7fa8 + i * 4) & 0xffff,
- ldl_phys(cs->as, sm_state + offset + 8),
- ldl_phys(cs->as, sm_state + offset + 4),
- (ldl_phys(cs->as,
+ x86_ldl_phys(cs, sm_state + offset + 8),
+ x86_ldl_phys(cs, sm_state + offset + 4),
+ (x86_ldl_phys(cs,
sm_state + offset) & 0xf0ff) << 8);
}
- cpu_x86_update_cr4(env, ldl_phys(cs->as, sm_state + 0x7f14));
+ cpu_x86_update_cr4(env, x86_ldl_phys(cs, sm_state + 0x7f14));
- val = ldl_phys(cs->as, sm_state + 0x7efc); /* revision ID */
+ val = x86_ldl_phys(cs, sm_state + 0x7efc); /* revision ID */
if (val & 0x20000) {
- env->smbase = ldl_phys(cs->as, sm_state + 0x7ef8) & ~0x7fff;
+ env->smbase = x86_ldl_phys(cs, sm_state + 0x7ef8) & ~0x7fff;
}
#endif
+ if ((env->hflags2 & HF2_SMM_INSIDE_NMI_MASK) == 0) {
+ env->hflags2 &= ~HF2_NMI_MASK;
+ }
+ env->hflags2 &= ~HF2_SMM_INSIDE_NMI_MASK;
env->hflags &= ~HF_SMM_MASK;
- cpu_smm_update(env);
+ cpu_smm_update(cpu);
qemu_log_mask(CPU_LOG_INT, "SMM: after RSM\n");
log_cpu_state_mask(CPU_LOG_INT, CPU(cpu), CPU_DUMP_CCOP);
diff --git a/target-i386/svm_helper.c b/target-i386/svm_helper.c
index 429d029a3d..f1fabf54e7 100644
--- a/target-i386/svm_helper.c
+++ b/target-i386/svm_helper.c
@@ -87,13 +87,13 @@ static inline void svm_save_seg(CPUX86State *env, hwaddr addr,
{
CPUState *cs = CPU(x86_env_get_cpu(env));
- stw_phys(cs->as, addr + offsetof(struct vmcb_seg, selector),
+ x86_stw_phys(cs, addr + offsetof(struct vmcb_seg, selector),
sc->selector);
- stq_phys(cs->as, addr + offsetof(struct vmcb_seg, base),
+ x86_stq_phys(cs, addr + offsetof(struct vmcb_seg, base),
sc->base);
- stl_phys(cs->as, addr + offsetof(struct vmcb_seg, limit),
+ x86_stl_phys(cs, addr + offsetof(struct vmcb_seg, limit),
sc->limit);
- stw_phys(cs->as, addr + offsetof(struct vmcb_seg, attrib),
+ x86_stw_phys(cs, addr + offsetof(struct vmcb_seg, attrib),
((sc->flags >> 8) & 0xff) | ((sc->flags >> 12) & 0x0f00));
}
@@ -103,11 +103,11 @@ static inline void svm_load_seg(CPUX86State *env, hwaddr addr,
CPUState *cs = CPU(x86_env_get_cpu(env));
unsigned int flags;
- sc->selector = lduw_phys(cs->as,
+ sc->selector = x86_lduw_phys(cs,
addr + offsetof(struct vmcb_seg, selector));
- sc->base = ldq_phys(cs->as, addr + offsetof(struct vmcb_seg, base));
- sc->limit = ldl_phys(cs->as, addr + offsetof(struct vmcb_seg, limit));
- flags = lduw_phys(cs->as, addr + offsetof(struct vmcb_seg, attrib));
+ sc->base = x86_ldq_phys(cs, addr + offsetof(struct vmcb_seg, base));
+ sc->limit = x86_ldl_phys(cs, addr + offsetof(struct vmcb_seg, limit));
+ flags = x86_lduw_phys(cs, addr + offsetof(struct vmcb_seg, attrib));
sc->flags = ((flags & 0xff) << 8) | ((flags & 0x0f00) << 12);
}
@@ -141,32 +141,32 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend)
env->vm_vmcb = addr;
/* save the current CPU state in the hsave page */
- stq_phys(cs->as, env->vm_hsave + offsetof(struct vmcb, save.gdtr.base),
+ x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.gdtr.base),
env->gdt.base);
- stl_phys(cs->as, env->vm_hsave + offsetof(struct vmcb, save.gdtr.limit),
+ x86_stl_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.gdtr.limit),
env->gdt.limit);
- stq_phys(cs->as, env->vm_hsave + offsetof(struct vmcb, save.idtr.base),
+ x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.idtr.base),
env->idt.base);
- stl_phys(cs->as, env->vm_hsave + offsetof(struct vmcb, save.idtr.limit),
+ x86_stl_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.idtr.limit),
env->idt.limit);
- stq_phys(cs->as,
+ x86_stq_phys(cs,
env->vm_hsave + offsetof(struct vmcb, save.cr0), env->cr[0]);
- stq_phys(cs->as,
+ x86_stq_phys(cs,
env->vm_hsave + offsetof(struct vmcb, save.cr2), env->cr[2]);
- stq_phys(cs->as,
+ x86_stq_phys(cs,
env->vm_hsave + offsetof(struct vmcb, save.cr3), env->cr[3]);
- stq_phys(cs->as,
+ x86_stq_phys(cs,
env->vm_hsave + offsetof(struct vmcb, save.cr4), env->cr[4]);
- stq_phys(cs->as,
+ x86_stq_phys(cs,
env->vm_hsave + offsetof(struct vmcb, save.dr6), env->dr[6]);
- stq_phys(cs->as,
+ x86_stq_phys(cs,
env->vm_hsave + offsetof(struct vmcb, save.dr7), env->dr[7]);
- stq_phys(cs->as,
+ x86_stq_phys(cs,
env->vm_hsave + offsetof(struct vmcb, save.efer), env->efer);
- stq_phys(cs->as,
+ x86_stq_phys(cs,
env->vm_hsave + offsetof(struct vmcb, save.rflags),
cpu_compute_eflags(env));
@@ -179,30 +179,30 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend)
svm_save_seg(env, env->vm_hsave + offsetof(struct vmcb, save.ds),
&env->segs[R_DS]);
- stq_phys(cs->as, env->vm_hsave + offsetof(struct vmcb, save.rip),
+ x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.rip),
env->eip + next_eip_addend);
- stq_phys(cs->as,
+ x86_stq_phys(cs,
env->vm_hsave + offsetof(struct vmcb, save.rsp), env->regs[R_ESP]);
- stq_phys(cs->as,
+ x86_stq_phys(cs,
env->vm_hsave + offsetof(struct vmcb, save.rax), env->regs[R_EAX]);
/* load the interception bitmaps so we do not need to access the
vmcb in svm mode */
- env->intercept = ldq_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb,
+ env->intercept = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb,
control.intercept));
- env->intercept_cr_read = lduw_phys(cs->as, env->vm_vmcb +
+ env->intercept_cr_read = x86_lduw_phys(cs, env->vm_vmcb +
offsetof(struct vmcb,
control.intercept_cr_read));
- env->intercept_cr_write = lduw_phys(cs->as, env->vm_vmcb +
+ env->intercept_cr_write = x86_lduw_phys(cs, env->vm_vmcb +
offsetof(struct vmcb,
control.intercept_cr_write));
- env->intercept_dr_read = lduw_phys(cs->as, env->vm_vmcb +
+ env->intercept_dr_read = x86_lduw_phys(cs, env->vm_vmcb +
offsetof(struct vmcb,
control.intercept_dr_read));
- env->intercept_dr_write = lduw_phys(cs->as, env->vm_vmcb +
+ env->intercept_dr_write = x86_lduw_phys(cs, env->vm_vmcb +
offsetof(struct vmcb,
control.intercept_dr_write));
- env->intercept_exceptions = ldl_phys(cs->as, env->vm_vmcb +
+ env->intercept_exceptions = x86_ldl_phys(cs, env->vm_vmcb +
offsetof(struct vmcb,
control.intercept_exceptions
));
@@ -210,35 +210,35 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend)
/* enable intercepts */
env->hflags |= HF_SVMI_MASK;
- env->tsc_offset = ldq_phys(cs->as, env->vm_vmcb +
+ env->tsc_offset = x86_ldq_phys(cs, env->vm_vmcb +
offsetof(struct vmcb, control.tsc_offset));
- env->gdt.base = ldq_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb,
+ env->gdt.base = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb,
save.gdtr.base));
- env->gdt.limit = ldl_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb,
+ env->gdt.limit = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb,
save.gdtr.limit));
- env->idt.base = ldq_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb,
+ env->idt.base = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb,
save.idtr.base));
- env->idt.limit = ldl_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb,
+ env->idt.limit = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb,
save.idtr.limit));
/* clear exit_info_2 so we behave like the real hardware */
- stq_phys(cs->as,
+ x86_stq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), 0);
- cpu_x86_update_cr0(env, ldq_phys(cs->as,
+ cpu_x86_update_cr0(env, x86_ldq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb,
save.cr0)));
- cpu_x86_update_cr4(env, ldq_phys(cs->as,
+ cpu_x86_update_cr4(env, x86_ldq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb,
save.cr4)));
- cpu_x86_update_cr3(env, ldq_phys(cs->as,
+ cpu_x86_update_cr3(env, x86_ldq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb,
save.cr3)));
- env->cr[2] = ldq_phys(cs->as,
+ env->cr[2] = x86_ldq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, save.cr2));
- int_ctl = ldl_phys(cs->as,
+ int_ctl = x86_ldl_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, control.int_ctl));
env->hflags2 &= ~(HF2_HIF_MASK | HF2_VINTR_MASK);
if (int_ctl & V_INTR_MASKING_MASK) {
@@ -250,10 +250,10 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend)
}
cpu_load_efer(env,
- ldq_phys(cs->as,
+ x86_ldq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, save.efer)));
env->eflags = 0;
- cpu_load_eflags(env, ldq_phys(cs->as,
+ cpu_load_eflags(env, x86_ldq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb,
save.rflags)),
~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
@@ -267,21 +267,21 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend)
svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.ds),
R_DS);
- env->eip = ldq_phys(cs->as,
+ env->eip = x86_ldq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, save.rip));
- env->regs[R_ESP] = ldq_phys(cs->as,
+ env->regs[R_ESP] = x86_ldq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, save.rsp));
- env->regs[R_EAX] = ldq_phys(cs->as,
+ env->regs[R_EAX] = x86_ldq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, save.rax));
- env->dr[7] = ldq_phys(cs->as,
+ env->dr[7] = x86_ldq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, save.dr7));
- env->dr[6] = ldq_phys(cs->as,
+ env->dr[6] = x86_ldq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, save.dr6));
/* FIXME: guest state consistency checks */
- switch (ldub_phys(cs->as,
+ switch (x86_ldub_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, control.tlb_ctl))) {
case TLB_CONTROL_DO_NOTHING:
break;
@@ -300,12 +300,12 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend)
}
/* maybe we need to inject an event */
- event_inj = ldl_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb,
+ event_inj = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb,
control.event_inj));
if (event_inj & SVM_EVTINJ_VALID) {
uint8_t vector = event_inj & SVM_EVTINJ_VEC_MASK;
uint16_t valid_err = event_inj & SVM_EVTINJ_VALID_ERR;
- uint32_t event_inj_err = ldl_phys(cs->as, env->vm_vmcb +
+ uint32_t event_inj_err = x86_ldl_phys(cs, env->vm_vmcb +
offsetof(struct vmcb,
control.event_inj_err));
@@ -372,7 +372,7 @@ void helper_vmload(CPUX86State *env, int aflag)
qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmload! " TARGET_FMT_lx
"\nFS: %016" PRIx64 " | " TARGET_FMT_lx "\n",
- addr, ldq_phys(cs->as, addr + offsetof(struct vmcb,
+ addr, x86_ldq_phys(cs, addr + offsetof(struct vmcb,
save.fs.base)),
env->segs[R_FS].base);
@@ -382,18 +382,18 @@ void helper_vmload(CPUX86State *env, int aflag)
svm_load_seg(env, addr + offsetof(struct vmcb, save.ldtr), &env->ldt);
#ifdef TARGET_X86_64
- env->kernelgsbase = ldq_phys(cs->as, addr + offsetof(struct vmcb,
+ env->kernelgsbase = x86_ldq_phys(cs, addr + offsetof(struct vmcb,
save.kernel_gs_base));
- env->lstar = ldq_phys(cs->as, addr + offsetof(struct vmcb, save.lstar));
- env->cstar = ldq_phys(cs->as, addr + offsetof(struct vmcb, save.cstar));
- env->fmask = ldq_phys(cs->as, addr + offsetof(struct vmcb, save.sfmask));
+ env->lstar = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.lstar));
+ env->cstar = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.cstar));
+ env->fmask = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.sfmask));
#endif
- env->star = ldq_phys(cs->as, addr + offsetof(struct vmcb, save.star));
- env->sysenter_cs = ldq_phys(cs->as,
+ env->star = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.star));
+ env->sysenter_cs = x86_ldq_phys(cs,
addr + offsetof(struct vmcb, save.sysenter_cs));
- env->sysenter_esp = ldq_phys(cs->as, addr + offsetof(struct vmcb,
+ env->sysenter_esp = x86_ldq_phys(cs, addr + offsetof(struct vmcb,
save.sysenter_esp));
- env->sysenter_eip = ldq_phys(cs->as, addr + offsetof(struct vmcb,
+ env->sysenter_eip = x86_ldq_phys(cs, addr + offsetof(struct vmcb,
save.sysenter_eip));
}
@@ -412,7 +412,7 @@ void helper_vmsave(CPUX86State *env, int aflag)
qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmsave! " TARGET_FMT_lx
"\nFS: %016" PRIx64 " | " TARGET_FMT_lx "\n",
- addr, ldq_phys(cs->as,
+ addr, x86_ldq_phys(cs,
addr + offsetof(struct vmcb, save.fs.base)),
env->segs[R_FS].base);
@@ -426,18 +426,18 @@ void helper_vmsave(CPUX86State *env, int aflag)
&env->ldt);
#ifdef TARGET_X86_64
- stq_phys(cs->as, addr + offsetof(struct vmcb, save.kernel_gs_base),
+ x86_stq_phys(cs, addr + offsetof(struct vmcb, save.kernel_gs_base),
env->kernelgsbase);
- stq_phys(cs->as, addr + offsetof(struct vmcb, save.lstar), env->lstar);
- stq_phys(cs->as, addr + offsetof(struct vmcb, save.cstar), env->cstar);
- stq_phys(cs->as, addr + offsetof(struct vmcb, save.sfmask), env->fmask);
+ x86_stq_phys(cs, addr + offsetof(struct vmcb, save.lstar), env->lstar);
+ x86_stq_phys(cs, addr + offsetof(struct vmcb, save.cstar), env->cstar);
+ x86_stq_phys(cs, addr + offsetof(struct vmcb, save.sfmask), env->fmask);
#endif
- stq_phys(cs->as, addr + offsetof(struct vmcb, save.star), env->star);
- stq_phys(cs->as,
+ x86_stq_phys(cs, addr + offsetof(struct vmcb, save.star), env->star);
+ x86_stq_phys(cs,
addr + offsetof(struct vmcb, save.sysenter_cs), env->sysenter_cs);
- stq_phys(cs->as, addr + offsetof(struct vmcb, save.sysenter_esp),
+ x86_stq_phys(cs, addr + offsetof(struct vmcb, save.sysenter_esp),
env->sysenter_esp);
- stq_phys(cs->as, addr + offsetof(struct vmcb, save.sysenter_eip),
+ x86_stq_phys(cs, addr + offsetof(struct vmcb, save.sysenter_eip),
env->sysenter_eip);
}
@@ -515,7 +515,7 @@ void helper_svm_check_intercept_param(CPUX86State *env, uint32_t type,
case SVM_EXIT_MSR:
if (env->intercept & (1ULL << (SVM_EXIT_MSR - SVM_EXIT_INTR))) {
/* FIXME: this should be read in at vmrun (faster this way?) */
- uint64_t addr = ldq_phys(cs->as, env->vm_vmcb +
+ uint64_t addr = x86_ldq_phys(cs, env->vm_vmcb +
offsetof(struct vmcb,
control.msrpm_base_pa));
uint32_t t0, t1;
@@ -541,7 +541,7 @@ void helper_svm_check_intercept_param(CPUX86State *env, uint32_t type,
t1 = 0;
break;
}
- if (ldub_phys(cs->as, addr + t1) & ((1 << param) << t0)) {
+ if (x86_ldub_phys(cs, addr + t1) & ((1 << param) << t0)) {
helper_vmexit(env, type, param);
}
}
@@ -567,13 +567,13 @@ void helper_svm_check_io(CPUX86State *env, uint32_t port, uint32_t param,
if (env->intercept & (1ULL << (SVM_EXIT_IOIO - SVM_EXIT_INTR))) {
/* FIXME: this should be read in at vmrun (faster this way?) */
- uint64_t addr = ldq_phys(cs->as, env->vm_vmcb +
+ uint64_t addr = x86_ldq_phys(cs, env->vm_vmcb +
offsetof(struct vmcb, control.iopm_base_pa));
uint16_t mask = (1 << ((param >> 4) & 7)) - 1;
- if (lduw_phys(cs->as, addr + port / 8) & (mask << (port & 7))) {
+ if (x86_lduw_phys(cs, addr + port / 8) & (mask << (port & 7))) {
/* next env->eip */
- stq_phys(cs->as,
+ x86_stq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2),
env->eip + next_eip_addend);
helper_vmexit(env, SVM_EXIT_IOIO, param | (port << 16));
@@ -590,17 +590,17 @@ void helper_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1)
qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmexit(%08x, %016" PRIx64 ", %016"
PRIx64 ", " TARGET_FMT_lx ")!\n",
exit_code, exit_info_1,
- ldq_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb,
+ x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb,
control.exit_info_2)),
env->eip);
if (env->hflags & HF_INHIBIT_IRQ_MASK) {
- stl_phys(cs->as,
+ x86_stl_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, control.int_state),
SVM_INTERRUPT_SHADOW_MASK);
env->hflags &= ~HF_INHIBIT_IRQ_MASK;
} else {
- stl_phys(cs->as,
+ x86_stl_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, control.int_state), 0);
}
@@ -614,50 +614,50 @@ void helper_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1)
svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.ds),
&env->segs[R_DS]);
- stq_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, save.gdtr.base),
+ x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.gdtr.base),
env->gdt.base);
- stl_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, save.gdtr.limit),
+ x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.gdtr.limit),
env->gdt.limit);
- stq_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, save.idtr.base),
+ x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.idtr.base),
env->idt.base);
- stl_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, save.idtr.limit),
+ x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.idtr.limit),
env->idt.limit);
- stq_phys(cs->as,
+ x86_stq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, save.efer), env->efer);
- stq_phys(cs->as,
+ x86_stq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, save.cr0), env->cr[0]);
- stq_phys(cs->as,
+ x86_stq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, save.cr2), env->cr[2]);
- stq_phys(cs->as,
+ x86_stq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, save.cr3), env->cr[3]);
- stq_phys(cs->as,
+ x86_stq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, save.cr4), env->cr[4]);
- int_ctl = ldl_phys(cs->as,
+ int_ctl = x86_ldl_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, control.int_ctl));
int_ctl &= ~(V_TPR_MASK | V_IRQ_MASK);
int_ctl |= env->v_tpr & V_TPR_MASK;
if (cs->interrupt_request & CPU_INTERRUPT_VIRQ) {
int_ctl |= V_IRQ_MASK;
}
- stl_phys(cs->as,
+ x86_stl_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, control.int_ctl), int_ctl);
- stq_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, save.rflags),
+ x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.rflags),
cpu_compute_eflags(env));
- stq_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, save.rip),
+ x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.rip),
env->eip);
- stq_phys(cs->as,
+ x86_stq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, save.rsp), env->regs[R_ESP]);
- stq_phys(cs->as,
+ x86_stq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, save.rax), env->regs[R_EAX]);
- stq_phys(cs->as,
+ x86_stq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, save.dr7), env->dr[7]);
- stq_phys(cs->as,
+ x86_stq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, save.dr6), env->dr[6]);
- stb_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, save.cpl),
+ x86_stb_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.cpl),
env->hflags & HF_CPL_MASK);
/* Reload the host state from vm_hsave */
@@ -668,32 +668,32 @@ void helper_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1)
cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
env->tsc_offset = 0;
- env->gdt.base = ldq_phys(cs->as, env->vm_hsave + offsetof(struct vmcb,
+ env->gdt.base = x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb,
save.gdtr.base));
- env->gdt.limit = ldl_phys(cs->as, env->vm_hsave + offsetof(struct vmcb,
+ env->gdt.limit = x86_ldl_phys(cs, env->vm_hsave + offsetof(struct vmcb,
save.gdtr.limit));
- env->idt.base = ldq_phys(cs->as, env->vm_hsave + offsetof(struct vmcb,
+ env->idt.base = x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb,
save.idtr.base));
- env->idt.limit = ldl_phys(cs->as, env->vm_hsave + offsetof(struct vmcb,
+ env->idt.limit = x86_ldl_phys(cs, env->vm_hsave + offsetof(struct vmcb,
save.idtr.limit));
- cpu_x86_update_cr0(env, ldq_phys(cs->as,
+ cpu_x86_update_cr0(env, x86_ldq_phys(cs,
env->vm_hsave + offsetof(struct vmcb,
save.cr0)) |
CR0_PE_MASK);
- cpu_x86_update_cr4(env, ldq_phys(cs->as,
+ cpu_x86_update_cr4(env, x86_ldq_phys(cs,
env->vm_hsave + offsetof(struct vmcb,
save.cr4)));
- cpu_x86_update_cr3(env, ldq_phys(cs->as,
+ cpu_x86_update_cr3(env, x86_ldq_phys(cs,
env->vm_hsave + offsetof(struct vmcb,
save.cr3)));
/* we need to set the efer after the crs so the hidden flags get
set properly */
- cpu_load_efer(env, ldq_phys(cs->as, env->vm_hsave + offsetof(struct vmcb,
+ cpu_load_efer(env, x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb,
save.efer)));
env->eflags = 0;
- cpu_load_eflags(env, ldq_phys(cs->as,
+ cpu_load_eflags(env, x86_ldq_phys(cs,
env->vm_hsave + offsetof(struct vmcb,
save.rflags)),
~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK |
@@ -708,33 +708,33 @@ void helper_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1)
svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.ds),
R_DS);
- env->eip = ldq_phys(cs->as,
+ env->eip = x86_ldq_phys(cs,
env->vm_hsave + offsetof(struct vmcb, save.rip));
- env->regs[R_ESP] = ldq_phys(cs->as, env->vm_hsave +
+ env->regs[R_ESP] = x86_ldq_phys(cs, env->vm_hsave +
offsetof(struct vmcb, save.rsp));
- env->regs[R_EAX] = ldq_phys(cs->as, env->vm_hsave +
+ env->regs[R_EAX] = x86_ldq_phys(cs, env->vm_hsave +
offsetof(struct vmcb, save.rax));
- env->dr[6] = ldq_phys(cs->as,
+ env->dr[6] = x86_ldq_phys(cs,
env->vm_hsave + offsetof(struct vmcb, save.dr6));
- env->dr[7] = ldq_phys(cs->as,
+ env->dr[7] = x86_ldq_phys(cs,
env->vm_hsave + offsetof(struct vmcb, save.dr7));
/* other setups */
- stq_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, control.exit_code),
+ x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_code),
exit_code);
- stq_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb, control.exit_info_1),
+ x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_info_1),
exit_info_1);
- stl_phys(cs->as,
+ x86_stl_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, control.exit_int_info),
- ldl_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb,
+ x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb,
control.event_inj)));
- stl_phys(cs->as,
+ x86_stl_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, control.exit_int_info_err),
- ldl_phys(cs->as, env->vm_vmcb + offsetof(struct vmcb,
+ x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb,
control.event_inj_err)));
- stl_phys(cs->as,
+ x86_stl_phys(cs,
env->vm_vmcb + offsetof(struct vmcb, control.event_inj), 0);
env->hflags2 &= ~HF2_GIF_MASK;
diff --git a/target-i386/translate.c b/target-i386/translate.c
index 305ce5077c..58b1959154 100644
--- a/target-i386/translate.c
+++ b/target-i386/translate.c
@@ -631,13 +631,13 @@ static void gen_helper_in_func(TCGMemOp ot, TCGv v, TCGv_i32 n)
{
switch (ot) {
case MO_8:
- gen_helper_inb(v, n);
+ gen_helper_inb(v, cpu_env, n);
break;
case MO_16:
- gen_helper_inw(v, n);
+ gen_helper_inw(v, cpu_env, n);
break;
case MO_32:
- gen_helper_inl(v, n);
+ gen_helper_inl(v, cpu_env, n);
break;
default:
tcg_abort();
@@ -648,13 +648,13 @@ static void gen_helper_out_func(TCGMemOp ot, TCGv_i32 v, TCGv_i32 n)
{
switch (ot) {
case MO_8:
- gen_helper_outb(v, n);
+ gen_helper_outb(cpu_env, v, n);
break;
case MO_16:
- gen_helper_outw(v, n);
+ gen_helper_outw(cpu_env, v, n);
break;
case MO_32:
- gen_helper_outl(v, n);
+ gen_helper_outl(cpu_env, v, n);
break;
default:
tcg_abort();
diff --git a/target-microblaze/cpu.h b/target-microblaze/cpu.h
index 4ea04acc4d..d73e1c7286 100644
--- a/target-microblaze/cpu.h
+++ b/target-microblaze/cpu.h
@@ -285,7 +285,6 @@ int cpu_mb_signal_handler(int host_signum, void *pinfo,
/* FIXME: MB uses variable pages down to 1K but linux only uses 4k. */
#define TARGET_PAGE_BITS 12
-#define MMAP_SHIFT TARGET_PAGE_BITS
#define TARGET_PHYS_ADDR_SPACE_BITS 32
#define TARGET_VIRT_ADDR_SPACE_BITS 32
diff --git a/target-mips/cpu.h b/target-mips/cpu.h
index f9d2b4c5af..474a0e327d 100644
--- a/target-mips/cpu.h
+++ b/target-mips/cpu.h
@@ -34,7 +34,7 @@ struct r4k_tlb_t {
uint_fast16_t RI0:1;
uint_fast16_t RI1:1;
uint_fast16_t EHINV:1;
- target_ulong PFN[2];
+ uint64_t PFN[2];
};
#if !defined(CONFIG_USER_ONLY)
@@ -100,6 +100,7 @@ struct CPUMIPSFPUContext {
float_status fp_status;
/* fpu implementation/revision register (fir) */
uint32_t fcr0;
+#define FCR0_FREP 29
#define FCR0_UFRP 28
#define FCR0_F64 22
#define FCR0_L 21
@@ -223,8 +224,14 @@ struct CPUMIPSState {
uint32_t SEGBITS;
uint32_t PABITS;
+#if defined(TARGET_MIPS64)
+# define PABITS_BASE 36
+#else
+# define PABITS_BASE 32
+#endif
target_ulong SEGMask;
- target_ulong PAMask;
+ uint64_t PAMask;
+#define PAMASK_BASE ((1ULL << PABITS_BASE) - 1)
int32_t msair;
#define MSAIR_ProcID 8
@@ -272,8 +279,8 @@ struct CPUMIPSState {
#define CP0VPEOpt_DWX2 2
#define CP0VPEOpt_DWX1 1
#define CP0VPEOpt_DWX0 0
- target_ulong CP0_EntryLo0;
- target_ulong CP0_EntryLo1;
+ uint64_t CP0_EntryLo0;
+ uint64_t CP0_EntryLo1;
#if defined(TARGET_MIPS64)
# define CP0EnLo_RI 63
# define CP0EnLo_XI 62
@@ -288,6 +295,7 @@ struct CPUMIPSState {
int32_t CP0_PageGrain;
#define CP0PG_RIE 31
#define CP0PG_XIE 30
+#define CP0PG_ELPA 29
#define CP0PG_IEC 27
int32_t CP0_Wired;
int32_t CP0_SRSConf0_rw_bitmask;
@@ -462,17 +470,21 @@ struct CPUMIPSState {
#define CP0C5_CV 29
#define CP0C5_EVA 28
#define CP0C5_MSAEn 27
+#define CP0C5_UFE 9
+#define CP0C5_FRE 8
#define CP0C5_SBRI 6
+#define CP0C5_MVH 5
+#define CP0C5_LLB 4
#define CP0C5_UFR 2
#define CP0C5_NFExists 0
int32_t CP0_Config6;
int32_t CP0_Config7;
/* XXX: Maybe make LLAddr per-TC? */
- target_ulong lladdr;
+ uint64_t lladdr;
target_ulong llval;
target_ulong llnewval;
target_ulong llreg;
- target_ulong CP0_LLAddr_rw_bitmask;
+ uint64_t CP0_LLAddr_rw_bitmask;
int CP0_LLAddr_shift;
target_ulong CP0_WatchLo[8];
int32_t CP0_WatchHi[8];
@@ -499,7 +511,7 @@ struct CPUMIPSState {
#define CP0DB_DSS 0
target_ulong CP0_DEPC;
int32_t CP0_Performance0;
- int32_t CP0_TagLo;
+ uint64_t CP0_TagLo;
int32_t CP0_DataLo;
int32_t CP0_TagHi;
int32_t CP0_DataHi;
@@ -514,7 +526,7 @@ struct CPUMIPSState {
#define EXCP_INST_NOTAVAIL 0x2 /* No valid instruction word for BadInstr */
uint32_t hflags; /* CPU State */
/* TMASK defines different execution modes */
-#define MIPS_HFLAG_TMASK 0x15807FF
+#define MIPS_HFLAG_TMASK 0x75807FF
#define MIPS_HFLAG_MODE 0x00007 /* execution modes */
/* The KSU flags must be the lowest bits in hflags. The flag order
must be the same as defined for CP0 Status. This allows to use
@@ -561,6 +573,8 @@ struct CPUMIPSState {
#define MIPS_HFLAG_SBRI 0x400000 /* R6 SDBBP causes RI excpt. in user mode */
#define MIPS_HFLAG_FBNSLOT 0x800000 /* Forbidden slot */
#define MIPS_HFLAG_MSA 0x1000000
+#define MIPS_HFLAG_FRE 0x2000000 /* FRE enabled */
+#define MIPS_HFLAG_ELPA 0x4000000
target_ulong btarget; /* Jump / branch target */
target_ulong bcond; /* Branch condition (if needed) */
@@ -796,6 +810,15 @@ static inline void restore_msa_fp_status(CPUMIPSState *env)
set_flush_inputs_to_zero(flush_to_zero, status);
}
+static inline void restore_pamask(CPUMIPSState *env)
+{
+ if (env->hflags & MIPS_HFLAG_ELPA) {
+ env->PAMask = (1ULL << env->PABITS) - 1;
+ } else {
+ env->PAMask = PAMASK_BASE;
+ }
+}
+
static inline void cpu_get_tb_cpu_state(CPUMIPSState *env, target_ulong *pc,
target_ulong *cs_base, int *flags)
{
@@ -843,7 +866,8 @@ static inline void compute_hflags(CPUMIPSState *env)
env->hflags &= ~(MIPS_HFLAG_COP1X | MIPS_HFLAG_64 | MIPS_HFLAG_CP0 |
MIPS_HFLAG_F64 | MIPS_HFLAG_FPU | MIPS_HFLAG_KSU |
MIPS_HFLAG_AWRAP | MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2 |
- MIPS_HFLAG_SBRI | MIPS_HFLAG_MSA);
+ MIPS_HFLAG_SBRI | MIPS_HFLAG_MSA | MIPS_HFLAG_FRE |
+ MIPS_HFLAG_ELPA);
if (!(env->CP0_Status & (1 << CP0St_EXL)) &&
!(env->CP0_Status & (1 << CP0St_ERL)) &&
!(env->hflags & MIPS_HFLAG_DM)) {
@@ -924,6 +948,16 @@ static inline void compute_hflags(CPUMIPSState *env)
env->hflags |= MIPS_HFLAG_MSA;
}
}
+ if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) {
+ if (env->CP0_Config5 & (1 << CP0C5_FRE)) {
+ env->hflags |= MIPS_HFLAG_FRE;
+ }
+ }
+ if (env->CP0_Config3 & (1 << CP0C3_LPA)) {
+ if (env->CP0_PageGrain & (1 << CP0PG_ELPA)) {
+ env->hflags |= MIPS_HFLAG_ELPA;
+ }
+ }
}
#ifndef CONFIG_USER_ONLY
diff --git a/target-mips/helper.h b/target-mips/helper.h
index 3bd0b029e4..8df98c71b8 100644
--- a/target-mips/helper.h
+++ b/target-mips/helper.h
@@ -348,6 +348,7 @@ DEF_HELPER_1(tlbinvf, void, env)
DEF_HELPER_1(di, tl, env)
DEF_HELPER_1(ei, tl, env)
DEF_HELPER_1(eret, void, env)
+DEF_HELPER_1(eretnc, void, env)
DEF_HELPER_1(deret, void, env)
#endif /* !CONFIG_USER_ONLY */
DEF_HELPER_1(rdhwr_cpunum, tl, env)
@@ -931,5 +932,11 @@ DEF_HELPER_4(msa_ftint_u_df, void, env, i32, i32, i32)
DEF_HELPER_4(msa_ffint_s_df, void, env, i32, i32, i32)
DEF_HELPER_4(msa_ffint_u_df, void, env, i32, i32, i32)
-DEF_HELPER_5(msa_ld_df, void, env, i32, i32, i32, s32)
-DEF_HELPER_5(msa_st_df, void, env, i32, i32, i32, s32)
+#define MSALDST_PROTO(type) \
+DEF_HELPER_3(msa_ld_ ## type, void, env, i32, tl) \
+DEF_HELPER_3(msa_st_ ## type, void, env, i32, tl)
+MSALDST_PROTO(b)
+MSALDST_PROTO(h)
+MSALDST_PROTO(w)
+MSALDST_PROTO(d)
+#undef MSALDST_PROTO
diff --git a/target-mips/kvm.c b/target-mips/kvm.c
index 59eb11105a..948619fbab 100644
--- a/target-mips/kvm.c
+++ b/target-mips/kvm.c
@@ -696,3 +696,8 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
{
return 0;
}
+
+int kvm_arch_msi_data_to_gsi(uint32_t data)
+{
+ abort();
+}
diff --git a/target-mips/machine.c b/target-mips/machine.c
index 7d1fa32e57..8fa755cd39 100644
--- a/target-mips/machine.c
+++ b/target-mips/machine.c
@@ -10,6 +10,7 @@ static int cpu_post_load(void *opaque, int version_id)
restore_fp_status(env);
restore_msa_fp_status(env);
compute_hflags(env);
+ restore_pamask(env);
return 0;
}
@@ -142,8 +143,8 @@ static int get_tlb(QEMUFile *f, void *pv, size_t size)
v->RI0 = (flags >> 13) & 1;
v->XI1 = (flags >> 12) & 1;
v->XI0 = (flags >> 11) & 1;
- qemu_get_betls(f, &v->PFN[0]);
- qemu_get_betls(f, &v->PFN[1]);
+ qemu_get_be64s(f, &v->PFN[0]);
+ qemu_get_be64s(f, &v->PFN[1]);
return 0;
}
@@ -169,8 +170,8 @@ static void put_tlb(QEMUFile *f, void *pv, size_t size)
qemu_put_be32s(f, &v->PageMask);
qemu_put_8s(f, &v->ASID);
qemu_put_be16s(f, &flags);
- qemu_put_betls(f, &v->PFN[0]);
- qemu_put_betls(f, &v->PFN[1]);
+ qemu_put_be64s(f, &v->PFN[0]);
+ qemu_put_be64s(f, &v->PFN[1]);
}
const VMStateInfo vmstate_info_tlb = {
@@ -201,8 +202,8 @@ const VMStateDescription vmstate_tlb = {
const VMStateDescription vmstate_mips_cpu = {
.name = "cpu",
- .version_id = 6,
- .minimum_version_id = 6,
+ .version_id = 7,
+ .minimum_version_id = 7,
.post_load = cpu_post_load,
.fields = (VMStateField[]) {
/* Active TC */
@@ -237,8 +238,8 @@ const VMStateDescription vmstate_mips_cpu = {
VMSTATE_UINTTL(env.CP0_VPESchedule, MIPSCPU),
VMSTATE_UINTTL(env.CP0_VPEScheFBack, MIPSCPU),
VMSTATE_INT32(env.CP0_VPEOpt, MIPSCPU),
- VMSTATE_UINTTL(env.CP0_EntryLo0, MIPSCPU),
- VMSTATE_UINTTL(env.CP0_EntryLo1, MIPSCPU),
+ VMSTATE_UINT64(env.CP0_EntryLo0, MIPSCPU),
+ VMSTATE_UINT64(env.CP0_EntryLo1, MIPSCPU),
VMSTATE_UINTTL(env.CP0_Context, MIPSCPU),
VMSTATE_INT32(env.CP0_PageMask, MIPSCPU),
VMSTATE_INT32(env.CP0_PageGrain, MIPSCPU),
@@ -269,7 +270,7 @@ const VMStateDescription vmstate_mips_cpu = {
VMSTATE_INT32(env.CP0_Config3, MIPSCPU),
VMSTATE_INT32(env.CP0_Config6, MIPSCPU),
VMSTATE_INT32(env.CP0_Config7, MIPSCPU),
- VMSTATE_UINTTL(env.lladdr, MIPSCPU),
+ VMSTATE_UINT64(env.lladdr, MIPSCPU),
VMSTATE_UINTTL_ARRAY(env.CP0_WatchLo, MIPSCPU, 8),
VMSTATE_INT32_ARRAY(env.CP0_WatchHi, MIPSCPU, 8),
VMSTATE_UINTTL(env.CP0_XContext, MIPSCPU),
@@ -277,7 +278,7 @@ const VMStateDescription vmstate_mips_cpu = {
VMSTATE_INT32(env.CP0_Debug, MIPSCPU),
VMSTATE_UINTTL(env.CP0_DEPC, MIPSCPU),
VMSTATE_INT32(env.CP0_Performance0, MIPSCPU),
- VMSTATE_INT32(env.CP0_TagLo, MIPSCPU),
+ VMSTATE_UINT64(env.CP0_TagLo, MIPSCPU),
VMSTATE_INT32(env.CP0_DataLo, MIPSCPU),
VMSTATE_INT32(env.CP0_TagHi, MIPSCPU),
VMSTATE_INT32(env.CP0_DataHi, MIPSCPU),
diff --git a/target-mips/mips-defs.h b/target-mips/mips-defs.h
index 1784227494..20aa87c24c 100644
--- a/target-mips/mips-defs.h
+++ b/target-mips/mips-defs.h
@@ -10,11 +10,11 @@
#if defined(TARGET_MIPS64)
#define TARGET_LONG_BITS 64
-#define TARGET_PHYS_ADDR_SPACE_BITS 36
+#define TARGET_PHYS_ADDR_SPACE_BITS 48
#define TARGET_VIRT_ADDR_SPACE_BITS 42
#else
#define TARGET_LONG_BITS 32
-#define TARGET_PHYS_ADDR_SPACE_BITS 36
+#define TARGET_PHYS_ADDR_SPACE_BITS 40
#define TARGET_VIRT_ADDR_SPACE_BITS 32
#endif
diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c
index 73a8e458fc..2a9ddff70f 100644
--- a/target-mips/op_helper.c
+++ b/target-mips/op_helper.c
@@ -90,10 +90,10 @@ static inline type do_##name(CPUMIPSState *env, target_ulong addr, \
} \
}
#endif
-HELPER_LD(lbu, ldub, uint8_t)
-HELPER_LD(lhu, lduw, uint16_t)
HELPER_LD(lw, ldl, int32_t)
+#if defined(TARGET_MIPS64)
HELPER_LD(ld, ldq, int64_t)
+#endif
#undef HELPER_LD
#if defined(CONFIG_USER_ONLY)
@@ -118,9 +118,10 @@ static inline void do_##name(CPUMIPSState *env, target_ulong addr, \
}
#endif
HELPER_ST(sb, stb, uint8_t)
-HELPER_ST(sh, stw, uint16_t)
HELPER_ST(sw, stl, uint32_t)
+#if defined(TARGET_MIPS64)
HELPER_ST(sd, stq, uint64_t)
+#endif
#undef HELPER_ST
target_ulong helper_clo (target_ulong arg1)
@@ -1067,19 +1068,23 @@ void helper_mtc0_vpeopt(CPUMIPSState *env, target_ulong arg1)
env->CP0_VPEOpt = arg1 & 0x0000ffff;
}
+#define MTC0_ENTRYLO_MASK(env) ((env->PAMask >> 6) & 0x3FFFFFFF)
+
void helper_mtc0_entrylo0(CPUMIPSState *env, target_ulong arg1)
{
- /* Large physaddr (PABITS) not implemented */
/* 1k pages not implemented */
target_ulong rxi = arg1 & (env->CP0_PageGrain & (3u << CP0PG_XIE));
- env->CP0_EntryLo0 = (arg1 & 0x3FFFFFFF) | (rxi << (CP0EnLo_XI - 30));
+ env->CP0_EntryLo0 = (arg1 & MTC0_ENTRYLO_MASK(env))
+ | (rxi << (CP0EnLo_XI - 30));
}
#if defined(TARGET_MIPS64)
+#define DMTC0_ENTRYLO_MASK(env) (env->PAMask >> 6)
+
void helper_dmtc0_entrylo0(CPUMIPSState *env, uint64_t arg1)
{
uint64_t rxi = arg1 & ((env->CP0_PageGrain & (3ull << CP0PG_XIE)) << 32);
- env->CP0_EntryLo0 = (arg1 & 0x3FFFFFFF) | rxi;
+ env->CP0_EntryLo0 = (arg1 & DMTC0_ENTRYLO_MASK(env)) | rxi;
}
#endif
@@ -1245,17 +1250,17 @@ void helper_mttc0_tcschefback(CPUMIPSState *env, target_ulong arg1)
void helper_mtc0_entrylo1(CPUMIPSState *env, target_ulong arg1)
{
- /* Large physaddr (PABITS) not implemented */
/* 1k pages not implemented */
target_ulong rxi = arg1 & (env->CP0_PageGrain & (3u << CP0PG_XIE));
- env->CP0_EntryLo1 = (arg1 & 0x3FFFFFFF) | (rxi << (CP0EnLo_XI - 30));
+ env->CP0_EntryLo1 = (arg1 & MTC0_ENTRYLO_MASK(env))
+ | (rxi << (CP0EnLo_XI - 30));
}
#if defined(TARGET_MIPS64)
void helper_dmtc0_entrylo1(CPUMIPSState *env, uint64_t arg1)
{
uint64_t rxi = arg1 & ((env->CP0_PageGrain & (3ull << CP0PG_XIE)) << 32);
- env->CP0_EntryLo1 = (arg1 & 0x3FFFFFFF) | rxi;
+ env->CP0_EntryLo1 = (arg1 & DMTC0_ENTRYLO_MASK(env)) | rxi;
}
#endif
@@ -1278,10 +1283,11 @@ void helper_mtc0_pagemask(CPUMIPSState *env, target_ulong arg1)
void helper_mtc0_pagegrain(CPUMIPSState *env, target_ulong arg1)
{
/* SmartMIPS not implemented */
- /* Large physaddr (PABITS) not implemented */
/* 1k pages not implemented */
env->CP0_PageGrain = (arg1 & env->CP0_PageGrain_rw_bitmask) |
(env->CP0_PageGrain & ~env->CP0_PageGrain_rw_bitmask);
+ compute_hflags(env);
+ restore_pamask(env);
}
void helper_mtc0_wired(CPUMIPSState *env, target_ulong arg1)
@@ -1825,6 +1831,16 @@ static void r4k_mips_tlb_flush_extra (CPUMIPSState *env, int first)
}
}
+static inline uint64_t get_tlb_pfn_from_entrylo(uint64_t entrylo)
+{
+#if defined(TARGET_MIPS64)
+ return extract64(entrylo, 6, 54);
+#else
+ return extract64(entrylo, 6, 24) | /* PFN */
+ (extract64(entrylo, 32, 32) << 24); /* PFNX */
+#endif
+}
+
static void r4k_fill_tlb(CPUMIPSState *env, int idx)
{
r4k_tlb_t *tlb;
@@ -1848,13 +1864,13 @@ static void r4k_fill_tlb(CPUMIPSState *env, int idx)
tlb->C0 = (env->CP0_EntryLo0 >> 3) & 0x7;
tlb->XI0 = (env->CP0_EntryLo0 >> CP0EnLo_XI) & 1;
tlb->RI0 = (env->CP0_EntryLo0 >> CP0EnLo_RI) & 1;
- tlb->PFN[0] = (env->CP0_EntryLo0 >> 6) << 12;
+ tlb->PFN[0] = get_tlb_pfn_from_entrylo(env->CP0_EntryLo0) << 12;
tlb->V1 = (env->CP0_EntryLo1 & 2) != 0;
tlb->D1 = (env->CP0_EntryLo1 & 4) != 0;
tlb->C1 = (env->CP0_EntryLo1 >> 3) & 0x7;
tlb->XI1 = (env->CP0_EntryLo1 >> CP0EnLo_XI) & 1;
tlb->RI1 = (env->CP0_EntryLo1 >> CP0EnLo_RI) & 1;
- tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12;
+ tlb->PFN[1] = get_tlb_pfn_from_entrylo(env->CP0_EntryLo1) << 12;
}
void r4k_helper_tlbinv(CPUMIPSState *env)
@@ -1971,6 +1987,16 @@ void r4k_helper_tlbp(CPUMIPSState *env)
}
}
+static inline uint64_t get_entrylo_pfn_from_tlb(uint64_t tlb_pfn)
+{
+#if defined(TARGET_MIPS64)
+ return tlb_pfn << 6;
+#else
+ return (extract64(tlb_pfn, 0, 24) << 6) | /* PFN */
+ (extract64(tlb_pfn, 24, 32) << 32); /* PFNX */
+#endif
+}
+
void r4k_helper_tlbr(CPUMIPSState *env)
{
r4k_tlb_t *tlb;
@@ -1996,13 +2022,13 @@ void r4k_helper_tlbr(CPUMIPSState *env)
env->CP0_EntryHi = tlb->VPN | tlb->ASID;
env->CP0_PageMask = tlb->PageMask;
env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2) |
- ((target_ulong)tlb->RI0 << CP0EnLo_RI) |
- ((target_ulong)tlb->XI0 << CP0EnLo_XI) |
- (tlb->C0 << 3) | (tlb->PFN[0] >> 6);
+ ((uint64_t)tlb->RI0 << CP0EnLo_RI) |
+ ((uint64_t)tlb->XI0 << CP0EnLo_XI) | (tlb->C0 << 3) |
+ get_entrylo_pfn_from_tlb(tlb->PFN[0] >> 12);
env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) |
- ((target_ulong)tlb->RI1 << CP0EnLo_RI) |
- ((target_ulong)tlb->XI1 << CP0EnLo_XI) |
- (tlb->C1 << 3) | (tlb->PFN[1] >> 6);
+ ((uint64_t)tlb->RI1 << CP0EnLo_RI) |
+ ((uint64_t)tlb->XI1 << CP0EnLo_XI) | (tlb->C1 << 3) |
+ get_entrylo_pfn_from_tlb(tlb->PFN[1] >> 12);
}
}
@@ -2098,7 +2124,7 @@ static void set_pc(CPUMIPSState *env, target_ulong error_pc)
}
}
-void helper_eret(CPUMIPSState *env)
+static inline void exception_return(CPUMIPSState *env)
{
debug_pre_eret(env);
if (env->CP0_Status & (1 << CP0St_ERL)) {
@@ -2110,9 +2136,19 @@ void helper_eret(CPUMIPSState *env)
}
compute_hflags(env);
debug_post_eret(env);
+}
+
+void helper_eret(CPUMIPSState *env)
+{
+ exception_return(env);
env->lladdr = 1;
}
+void helper_eretnc(CPUMIPSState *env)
+{
+ exception_return(env);
+}
+
void helper_deret(CPUMIPSState *env)
{
debug_pre_eret(env);
@@ -2303,6 +2339,16 @@ target_ulong helper_cfc1(CPUMIPSState *env, uint32_t reg)
}
}
break;
+ case 5:
+ /* FRE Support - read Config5.FRE bit */
+ if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) {
+ if (env->CP0_Config5 & (1 << CP0C5_UFE)) {
+ arg1 = (env->CP0_Config5 >> CP0C5_FRE) & 1;
+ } else {
+ helper_raise_exception(env, EXCP_RI);
+ }
+ }
+ break;
case 25:
arg1 = ((env->active_fpu.fcr31 >> 24) & 0xfe) | ((env->active_fpu.fcr31 >> 23) & 0x1);
break;
@@ -2347,6 +2393,30 @@ void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t fs, uint32_t rt)
helper_raise_exception(env, EXCP_RI);
}
break;
+ case 5:
+ /* FRE Support - clear Config5.FRE bit */
+ if (!((env->active_fpu.fcr0 & (1 << FCR0_FREP)) && (rt == 0))) {
+ return;
+ }
+ if (env->CP0_Config5 & (1 << CP0C5_UFE)) {
+ env->CP0_Config5 &= ~(1 << CP0C5_FRE);
+ compute_hflags(env);
+ } else {
+ helper_raise_exception(env, EXCP_RI);
+ }
+ break;
+ case 6:
+ /* FRE Support - set Config5.FRE bit */
+ if (!((env->active_fpu.fcr0 & (1 << FCR0_FREP)) && (rt == 0))) {
+ return;
+ }
+ if (env->CP0_Config5 & (1 << CP0C5_UFE)) {
+ env->CP0_Config5 |= (1 << CP0C5_FRE);
+ compute_hflags(env);
+ } else {
+ helper_raise_exception(env, EXCP_RI);
+ }
+ break;
case 25:
if ((env->insn_flags & ISA_MIPS32R6) || (arg1 & 0xffffff00)) {
return;
@@ -3558,72 +3628,82 @@ FOP_CONDN_S(sne, (float32_lt(fst1, fst0, &env->active_fpu.fp_status)
/* Element-by-element access macros */
#define DF_ELEMENTS(df) (MSA_WRLEN / DF_BITS(df))
-void helper_msa_ld_df(CPUMIPSState *env, uint32_t df, uint32_t wd, uint32_t rs,
- int32_t s10)
-{
- wr_t *pwd = &(env->active_fpu.fpr[wd].wr);
- target_ulong addr = env->active_tc.gpr[rs] + (s10 << df);
- int i;
+#if !defined(CONFIG_USER_ONLY)
+#define MEMOP_IDX(DF) \
+ TCGMemOpIdx oi = make_memop_idx(MO_TE | DF | MO_UNALN, \
+ cpu_mmu_index(env));
+#else
+#define MEMOP_IDX(DF)
+#endif
- switch (df) {
- case DF_BYTE:
- for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) {
- pwd->b[i] = do_lbu(env, addr + (i << DF_BYTE),
- env->hflags & MIPS_HFLAG_KSU);
- }
- break;
- case DF_HALF:
- for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) {
- pwd->h[i] = do_lhu(env, addr + (i << DF_HALF),
- env->hflags & MIPS_HFLAG_KSU);
- }
- break;
- case DF_WORD:
- for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) {
- pwd->w[i] = do_lw(env, addr + (i << DF_WORD),
- env->hflags & MIPS_HFLAG_KSU);
- }
- break;
- case DF_DOUBLE:
- for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) {
- pwd->d[i] = do_ld(env, addr + (i << DF_DOUBLE),
- env->hflags & MIPS_HFLAG_KSU);
- }
- break;
- }
+#define MSA_LD_DF(DF, TYPE, LD_INSN, ...) \
+void helper_msa_ld_ ## TYPE(CPUMIPSState *env, uint32_t wd, \
+ target_ulong addr) \
+{ \
+ wr_t *pwd = &(env->active_fpu.fpr[wd].wr); \
+ wr_t wx; \
+ int i; \
+ MEMOP_IDX(DF) \
+ for (i = 0; i < DF_ELEMENTS(DF); i++) { \
+ wx.TYPE[i] = LD_INSN(env, addr + (i << DF), ##__VA_ARGS__); \
+ } \
+ memcpy(pwd, &wx, sizeof(wr_t)); \
}
-void helper_msa_st_df(CPUMIPSState *env, uint32_t df, uint32_t wd, uint32_t rs,
- int32_t s10)
-{
- wr_t *pwd = &(env->active_fpu.fpr[wd].wr);
- target_ulong addr = env->active_tc.gpr[rs] + (s10 << df);
- int i;
+#if !defined(CONFIG_USER_ONLY)
+MSA_LD_DF(DF_BYTE, b, helper_ret_ldub_mmu, oi, GETRA())
+MSA_LD_DF(DF_HALF, h, helper_ret_lduw_mmu, oi, GETRA())
+MSA_LD_DF(DF_WORD, w, helper_ret_ldul_mmu, oi, GETRA())
+MSA_LD_DF(DF_DOUBLE, d, helper_ret_ldq_mmu, oi, GETRA())
+#else
+MSA_LD_DF(DF_BYTE, b, cpu_ldub_data)
+MSA_LD_DF(DF_HALF, h, cpu_lduw_data)
+MSA_LD_DF(DF_WORD, w, cpu_ldl_data)
+MSA_LD_DF(DF_DOUBLE, d, cpu_ldq_data)
+#endif
- switch (df) {
- case DF_BYTE:
- for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) {
- do_sb(env, addr + (i << DF_BYTE), pwd->b[i],
- env->hflags & MIPS_HFLAG_KSU);
- }
- break;
- case DF_HALF:
- for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) {
- do_sh(env, addr + (i << DF_HALF), pwd->h[i],
- env->hflags & MIPS_HFLAG_KSU);
- }
- break;
- case DF_WORD:
- for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) {
- do_sw(env, addr + (i << DF_WORD), pwd->w[i],
- env->hflags & MIPS_HFLAG_KSU);
- }
- break;
- case DF_DOUBLE:
- for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) {
- do_sd(env, addr + (i << DF_DOUBLE), pwd->d[i],
- env->hflags & MIPS_HFLAG_KSU);
- }
- break;
+#define MSA_PAGESPAN(x) \
+ ((((x) & ~TARGET_PAGE_MASK) + MSA_WRLEN/8 - 1) >= TARGET_PAGE_SIZE)
+
+static inline void ensure_writable_pages(CPUMIPSState *env,
+ target_ulong addr,
+ int mmu_idx,
+ uintptr_t retaddr)
+{
+#if !defined(CONFIG_USER_ONLY)
+ target_ulong page_addr;
+ if (unlikely(MSA_PAGESPAN(addr))) {
+ /* first page */
+ probe_write(env, addr, mmu_idx, retaddr);
+ /* second page */
+ page_addr = (addr & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
+ probe_write(env, page_addr, mmu_idx, retaddr);
}
+#endif
+}
+
+#define MSA_ST_DF(DF, TYPE, ST_INSN, ...) \
+void helper_msa_st_ ## TYPE(CPUMIPSState *env, uint32_t wd, \
+ target_ulong addr) \
+{ \
+ wr_t *pwd = &(env->active_fpu.fpr[wd].wr); \
+ int mmu_idx = cpu_mmu_index(env); \
+ int i; \
+ MEMOP_IDX(DF) \
+ ensure_writable_pages(env, addr, mmu_idx, GETRA()); \
+ for (i = 0; i < DF_ELEMENTS(DF); i++) { \
+ ST_INSN(env, addr + (i << DF), pwd->TYPE[i], ##__VA_ARGS__); \
+ } \
}
+
+#if !defined(CONFIG_USER_ONLY)
+MSA_ST_DF(DF_BYTE, b, helper_ret_stb_mmu, oi, GETRA())
+MSA_ST_DF(DF_HALF, h, helper_ret_stw_mmu, oi, GETRA())
+MSA_ST_DF(DF_WORD, w, helper_ret_stl_mmu, oi, GETRA())
+MSA_ST_DF(DF_DOUBLE, d, helper_ret_stq_mmu, oi, GETRA())
+#else
+MSA_ST_DF(DF_BYTE, b, cpu_stb_data)
+MSA_ST_DF(DF_HALF, h, cpu_stw_data)
+MSA_ST_DF(DF_WORD, w, cpu_stl_data)
+MSA_ST_DF(DF_DOUBLE, d, cpu_stq_data)
+#endif
diff --git a/target-mips/translate.c b/target-mips/translate.c
index fd063a2aae..1d128eef02 100644
--- a/target-mips/translate.c
+++ b/target-mips/translate.c
@@ -868,8 +868,10 @@ enum {
enum {
OPC_MFC0 = (0x00 << 21) | OPC_CP0,
OPC_DMFC0 = (0x01 << 21) | OPC_CP0,
+ OPC_MFHC0 = (0x02 << 21) | OPC_CP0,
OPC_MTC0 = (0x04 << 21) | OPC_CP0,
OPC_DMTC0 = (0x05 << 21) | OPC_CP0,
+ OPC_MTHC0 = (0x06 << 21) | OPC_CP0,
OPC_MFTR = (0x08 << 21) | OPC_CP0,
OPC_RDPGPR = (0x0A << 21) | OPC_CP0,
OPC_MFMC0 = (0x0B << 21) | OPC_CP0,
@@ -1414,6 +1416,7 @@ typedef struct DisasContext {
int32_t CP0_Config1;
/* Routine used to access memory */
int mem_idx;
+ TCGMemOp default_tcg_memop_mask;
uint32_t hflags, saved_hflags;
int bstate;
target_ulong btarget;
@@ -1423,6 +1426,9 @@ typedef struct DisasContext {
int ie;
bool bi;
bool bp;
+ uint64_t PAMask;
+ bool mvh;
+ int CP0_LLAddr_shift;
} DisasContext;
enum {
@@ -1557,15 +1563,80 @@ static inline void gen_store_srsgpr (int from, int to)
}
}
+/* Tests */
+static inline void gen_save_pc(target_ulong pc)
+{
+ tcg_gen_movi_tl(cpu_PC, pc);
+}
+
+static inline void save_cpu_state(DisasContext *ctx, int do_save_pc)
+{
+ LOG_DISAS("hflags %08x saved %08x\n", ctx->hflags, ctx->saved_hflags);
+ if (do_save_pc && ctx->pc != ctx->saved_pc) {
+ gen_save_pc(ctx->pc);
+ ctx->saved_pc = ctx->pc;
+ }
+ if (ctx->hflags != ctx->saved_hflags) {
+ tcg_gen_movi_i32(hflags, ctx->hflags);
+ ctx->saved_hflags = ctx->hflags;
+ switch (ctx->hflags & MIPS_HFLAG_BMASK_BASE) {
+ case MIPS_HFLAG_BR:
+ break;
+ case MIPS_HFLAG_BC:
+ case MIPS_HFLAG_BL:
+ case MIPS_HFLAG_B:
+ tcg_gen_movi_tl(btarget, ctx->btarget);
+ break;
+ }
+ }
+}
+
+static inline void restore_cpu_state(CPUMIPSState *env, DisasContext *ctx)
+{
+ ctx->saved_hflags = ctx->hflags;
+ switch (ctx->hflags & MIPS_HFLAG_BMASK_BASE) {
+ case MIPS_HFLAG_BR:
+ break;
+ case MIPS_HFLAG_BC:
+ case MIPS_HFLAG_BL:
+ case MIPS_HFLAG_B:
+ ctx->btarget = env->btarget;
+ break;
+ }
+}
+
+static inline void generate_exception_err(DisasContext *ctx, int excp, int err)
+{
+ TCGv_i32 texcp = tcg_const_i32(excp);
+ TCGv_i32 terr = tcg_const_i32(err);
+ save_cpu_state(ctx, 1);
+ gen_helper_raise_exception_err(cpu_env, texcp, terr);
+ tcg_temp_free_i32(terr);
+ tcg_temp_free_i32(texcp);
+}
+
+static inline void generate_exception(DisasContext *ctx, int excp)
+{
+ save_cpu_state(ctx, 1);
+ gen_helper_0e0i(raise_exception, excp);
+}
+
/* Floating point register moves. */
-static void gen_load_fpr32(TCGv_i32 t, int reg)
+static void gen_load_fpr32(DisasContext *ctx, TCGv_i32 t, int reg)
{
+ if (ctx->hflags & MIPS_HFLAG_FRE) {
+ generate_exception(ctx, EXCP_RI);
+ }
tcg_gen_trunc_i64_i32(t, fpu_f64[reg]);
}
-static void gen_store_fpr32(TCGv_i32 t, int reg)
+static void gen_store_fpr32(DisasContext *ctx, TCGv_i32 t, int reg)
{
- TCGv_i64 t64 = tcg_temp_new_i64();
+ TCGv_i64 t64;
+ if (ctx->hflags & MIPS_HFLAG_FRE) {
+ generate_exception(ctx, EXCP_RI);
+ }
+ t64 = tcg_temp_new_i64();
tcg_gen_extu_i32_i64(t64, t);
tcg_gen_deposit_i64(fpu_f64[reg], fpu_f64[reg], t64, 0, 32);
tcg_temp_free_i64(t64);
@@ -1579,7 +1650,7 @@ static void gen_load_fpr32h(DisasContext *ctx, TCGv_i32 t, int reg)
tcg_gen_trunc_i64_i32(t, t64);
tcg_temp_free_i64(t64);
} else {
- gen_load_fpr32(t, reg | 1);
+ gen_load_fpr32(ctx, t, reg | 1);
}
}
@@ -1591,7 +1662,7 @@ static void gen_store_fpr32h(DisasContext *ctx, TCGv_i32 t, int reg)
tcg_gen_deposit_i64(fpu_f64[reg], fpu_f64[reg], t64, 32, 32);
tcg_temp_free_i64(t64);
} else {
- gen_store_fpr32(t, reg | 1);
+ gen_store_fpr32(ctx, t, reg | 1);
}
}
@@ -1626,66 +1697,6 @@ static inline int get_fp_bit (int cc)
return 23;
}
-/* Tests */
-static inline void gen_save_pc(target_ulong pc)
-{
- tcg_gen_movi_tl(cpu_PC, pc);
-}
-
-static inline void save_cpu_state (DisasContext *ctx, int do_save_pc)
-{
- LOG_DISAS("hflags %08x saved %08x\n", ctx->hflags, ctx->saved_hflags);
- if (do_save_pc && ctx->pc != ctx->saved_pc) {
- gen_save_pc(ctx->pc);
- ctx->saved_pc = ctx->pc;
- }
- if (ctx->hflags != ctx->saved_hflags) {
- tcg_gen_movi_i32(hflags, ctx->hflags);
- ctx->saved_hflags = ctx->hflags;
- switch (ctx->hflags & MIPS_HFLAG_BMASK_BASE) {
- case MIPS_HFLAG_BR:
- break;
- case MIPS_HFLAG_BC:
- case MIPS_HFLAG_BL:
- case MIPS_HFLAG_B:
- tcg_gen_movi_tl(btarget, ctx->btarget);
- break;
- }
- }
-}
-
-static inline void restore_cpu_state (CPUMIPSState *env, DisasContext *ctx)
-{
- ctx->saved_hflags = ctx->hflags;
- switch (ctx->hflags & MIPS_HFLAG_BMASK_BASE) {
- case MIPS_HFLAG_BR:
- break;
- case MIPS_HFLAG_BC:
- case MIPS_HFLAG_BL:
- case MIPS_HFLAG_B:
- ctx->btarget = env->btarget;
- break;
- }
-}
-
-static inline void
-generate_exception_err (DisasContext *ctx, int excp, int err)
-{
- TCGv_i32 texcp = tcg_const_i32(excp);
- TCGv_i32 terr = tcg_const_i32(err);
- save_cpu_state(ctx, 1);
- gen_helper_raise_exception_err(cpu_env, texcp, terr);
- tcg_temp_free_i32(terr);
- tcg_temp_free_i32(texcp);
-}
-
-static inline void
-generate_exception (DisasContext *ctx, int excp)
-{
- save_cpu_state(ctx, 1);
- gen_helper_0e0i(raise_exception, excp);
-}
-
/* Addresses computation */
static inline void gen_op_addr_add (DisasContext *ctx, TCGv ret, TCGv arg0, TCGv arg1)
{
@@ -1815,11 +1826,20 @@ static inline void check_mips_64(DisasContext *ctx)
}
#endif
+#ifndef CONFIG_USER_ONLY
+static inline void check_mvh(DisasContext *ctx)
+{
+ if (unlikely(!ctx->mvh)) {
+ generate_exception(ctx, EXCP_RI);
+ }
+}
+#endif
+
/* Define small wrappers for gen_load_fpr* so that we have a uniform
calling interface for 32 and 64-bit FPRs. No sense in changing
all callers for gen_load_fpr32 when we need the CTX parameter for
this one use. */
-#define gen_ldcmp_fpr32(ctx, x, y) gen_load_fpr32(x, y)
+#define gen_ldcmp_fpr32(ctx, x, y) gen_load_fpr32(ctx, x, y)
#define gen_ldcmp_fpr64(ctx, x, y) gen_load_fpr64(ctx, x, y)
#define FOP_CONDS(type, abs, fmt, ifmt, bits) \
static inline void gen_cmp ## type ## _ ## fmt(DisasContext *ctx, int n, \
@@ -1963,7 +1983,7 @@ static inline void gen_r6_cmp_ ## fmt(DisasContext * ctx, int n, \
}
FOP_CONDNS(d, FMT_D, 64, gen_store_fpr64(ctx, fp0, fd))
-FOP_CONDNS(s, FMT_S, 32, gen_store_fpr32(fp0, fd))
+FOP_CONDNS(s, FMT_S, 32, gen_store_fpr32(ctx, fp0, fd))
#undef FOP_CONDNS
#undef gen_ldcmp_fpr32
#undef gen_ldcmp_fpr64
@@ -2081,12 +2101,14 @@ static void gen_ld(DisasContext *ctx, uint32_t opc,
switch (opc) {
#if defined(TARGET_MIPS64)
case OPC_LWU:
- tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUL);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUL |
+ ctx->default_tcg_memop_mask);
gen_store_gpr(t0, rt);
opn = "lwu";
break;
case OPC_LD:
- tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ |
+ ctx->default_tcg_memop_mask);
gen_store_gpr(t0, rt);
opn = "ld";
break;
@@ -2157,17 +2179,20 @@ static void gen_ld(DisasContext *ctx, uint32_t opc,
opn = "lwpc";
break;
case OPC_LW:
- tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL |
+ ctx->default_tcg_memop_mask);
gen_store_gpr(t0, rt);
opn = "lw";
break;
case OPC_LH:
- tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESW);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESW |
+ ctx->default_tcg_memop_mask);
gen_store_gpr(t0, rt);
opn = "lh";
break;
case OPC_LHU:
- tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUW);
+ tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUW |
+ ctx->default_tcg_memop_mask);
gen_store_gpr(t0, rt);
opn = "lhu";
break;
@@ -2251,7 +2276,8 @@ static void gen_st (DisasContext *ctx, uint32_t opc, int rt,
switch (opc) {
#if defined(TARGET_MIPS64)
case OPC_SD:
- tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEQ);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEQ |
+ ctx->default_tcg_memop_mask);
opn = "sd";
break;
case OPC_SDL:
@@ -2266,11 +2292,13 @@ static void gen_st (DisasContext *ctx, uint32_t opc, int rt,
break;
#endif
case OPC_SW:
- tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL |
+ ctx->default_tcg_memop_mask);
opn = "sw";
break;
case OPC_SH:
- tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUW);
+ tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUW |
+ ctx->default_tcg_memop_mask);
opn = "sh";
break;
case OPC_SB:
@@ -2347,8 +2375,9 @@ static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft,
case OPC_LWC1:
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- tcg_gen_qemu_ld_i32(fp0, t0, ctx->mem_idx, MO_TESL);
- gen_store_fpr32(fp0, ft);
+ tcg_gen_qemu_ld_i32(fp0, t0, ctx->mem_idx, MO_TESL |
+ ctx->default_tcg_memop_mask);
+ gen_store_fpr32(ctx, fp0, ft);
tcg_temp_free_i32(fp0);
}
opn = "lwc1";
@@ -2356,8 +2385,9 @@ static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft,
case OPC_SWC1:
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, ft);
- tcg_gen_qemu_st_i32(fp0, t0, ctx->mem_idx, MO_TEUL);
+ gen_load_fpr32(ctx, fp0, ft);
+ tcg_gen_qemu_st_i32(fp0, t0, ctx->mem_idx, MO_TEUL |
+ ctx->default_tcg_memop_mask);
tcg_temp_free_i32(fp0);
}
opn = "swc1";
@@ -2365,7 +2395,8 @@ static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft,
case OPC_LDC1:
{
TCGv_i64 fp0 = tcg_temp_new_i64();
- tcg_gen_qemu_ld_i64(fp0, t0, ctx->mem_idx, MO_TEQ);
+ tcg_gen_qemu_ld_i64(fp0, t0, ctx->mem_idx, MO_TEQ |
+ ctx->default_tcg_memop_mask);
gen_store_fpr64(ctx, fp0, ft);
tcg_temp_free_i64(fp0);
}
@@ -2375,7 +2406,8 @@ static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft,
{
TCGv_i64 fp0 = tcg_temp_new_i64();
gen_load_fpr64(ctx, fp0, ft);
- tcg_gen_qemu_st_i64(fp0, t0, ctx->mem_idx, MO_TEQ);
+ tcg_gen_qemu_st_i64(fp0, t0, ctx->mem_idx, MO_TEQ |
+ ctx->default_tcg_memop_mask);
tcg_temp_free_i64(fp0);
}
opn = "sdc1";
@@ -4815,6 +4847,69 @@ static void gen_bshfl (DisasContext *ctx, uint32_t op2, int rt, int rd)
#ifndef CONFIG_USER_ONLY
/* CP0 (MMU and control) */
+static inline void gen_move_low32(TCGv ret, TCGv_i64 arg)
+{
+#if defined(TARGET_MIPS64)
+ tcg_gen_ext32s_tl(ret, arg);
+#else
+ tcg_gen_trunc_i64_tl(ret, arg);
+#endif
+}
+
+static inline void gen_mthc0_entrylo(TCGv arg, target_ulong off)
+{
+ TCGv_i64 t0 = tcg_temp_new_i64();
+ TCGv_i64 t1 = tcg_temp_new_i64();
+
+ tcg_gen_ext_tl_i64(t0, arg);
+ tcg_gen_ld_i64(t1, cpu_env, off);
+#if defined(TARGET_MIPS64)
+ tcg_gen_deposit_i64(t1, t1, t0, 30, 32);
+#else
+ tcg_gen_concat32_i64(t1, t1, t0);
+#endif
+ tcg_gen_st_i64(t1, cpu_env, off);
+ tcg_temp_free_i64(t1);
+ tcg_temp_free_i64(t0);
+}
+
+static inline void gen_mthc0_store64(TCGv arg, target_ulong off)
+{
+ TCGv_i64 t0 = tcg_temp_new_i64();
+ TCGv_i64 t1 = tcg_temp_new_i64();
+
+ tcg_gen_ext_tl_i64(t0, arg);
+ tcg_gen_ld_i64(t1, cpu_env, off);
+ tcg_gen_concat32_i64(t1, t1, t0);
+ tcg_gen_st_i64(t1, cpu_env, off);
+ tcg_temp_free_i64(t1);
+ tcg_temp_free_i64(t0);
+}
+
+static inline void gen_mfhc0_entrylo(TCGv arg, target_ulong off)
+{
+ TCGv_i64 t0 = tcg_temp_new_i64();
+
+ tcg_gen_ld_i64(t0, cpu_env, off);
+#if defined(TARGET_MIPS64)
+ tcg_gen_shri_i64(t0, t0, 30);
+#else
+ tcg_gen_shri_i64(t0, t0, 32);
+#endif
+ gen_move_low32(arg, t0);
+ tcg_temp_free_i64(t0);
+}
+
+static inline void gen_mfhc0_load64(TCGv arg, target_ulong off, int shift)
+{
+ TCGv_i64 t0 = tcg_temp_new_i64();
+
+ tcg_gen_ld_i64(t0, cpu_env, off);
+ tcg_gen_shri_i64(t0, t0, 32 + shift);
+ gen_move_low32(arg, t0);
+ tcg_temp_free_i64(t0);
+}
+
static inline void gen_mfc0_load32 (TCGv arg, target_ulong off)
{
TCGv_i32 t0 = tcg_temp_new_i32();
@@ -4845,6 +4940,140 @@ static inline void gen_mtc0_store64 (TCGv arg, target_ulong off)
tcg_gen_st_tl(arg, cpu_env, off);
}
+static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel)
+{
+ const char *rn = "invalid";
+
+ if (!(ctx->hflags & MIPS_HFLAG_ELPA)) {
+ goto mfhc0_read_zero;
+ }
+
+ switch (reg) {
+ case 2:
+ switch (sel) {
+ case 0:
+ gen_mfhc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo0));
+ rn = "EntryLo0";
+ break;
+ default:
+ goto mfhc0_read_zero;
+ }
+ break;
+ case 3:
+ switch (sel) {
+ case 0:
+ gen_mfhc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo1));
+ rn = "EntryLo1";
+ break;
+ default:
+ goto mfhc0_read_zero;
+ }
+ break;
+ case 17:
+ switch (sel) {
+ case 0:
+ gen_mfhc0_load64(arg, offsetof(CPUMIPSState, lladdr),
+ ctx->CP0_LLAddr_shift);
+ rn = "LLAddr";
+ break;
+ default:
+ goto mfhc0_read_zero;
+ }
+ break;
+ case 28:
+ switch (sel) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ gen_mfhc0_load64(arg, offsetof(CPUMIPSState, CP0_TagLo), 0);
+ rn = "TagLo";
+ break;
+ default:
+ goto mfhc0_read_zero;
+ }
+ break;
+ default:
+ goto mfhc0_read_zero;
+ }
+
+ (void)rn; /* avoid a compiler warning */
+ LOG_DISAS("mfhc0 %s (reg %d sel %d)\n", rn, reg, sel);
+ return;
+
+mfhc0_read_zero:
+ LOG_DISAS("mfhc0 %s (reg %d sel %d)\n", rn, reg, sel);
+ tcg_gen_movi_tl(arg, 0);
+}
+
+static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel)
+{
+ const char *rn = "invalid";
+ uint64_t mask = ctx->PAMask >> 36;
+
+ if (!(ctx->hflags & MIPS_HFLAG_ELPA)) {
+ goto mthc0_nop;
+ }
+
+ switch (reg) {
+ case 2:
+ switch (sel) {
+ case 0:
+ tcg_gen_andi_tl(arg, arg, mask);
+ gen_mthc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo0));
+ rn = "EntryLo0";
+ break;
+ default:
+ goto mthc0_nop;
+ }
+ break;
+ case 3:
+ switch (sel) {
+ case 0:
+ tcg_gen_andi_tl(arg, arg, mask);
+ gen_mthc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo1));
+ rn = "EntryLo1";
+ break;
+ default:
+ goto mthc0_nop;
+ }
+ break;
+ case 17:
+ switch (sel) {
+ case 0:
+ /* LLAddr is read-only (the only exception is bit 0 if LLB is
+ supported); the CP0_LLAddr_rw_bitmask does not seem to be
+ relevant for modern MIPS cores supporting MTHC0, therefore
+ treating MTHC0 to LLAddr as NOP. */
+ rn = "LLAddr";
+ break;
+ default:
+ goto mthc0_nop;
+ }
+ break;
+ case 28:
+ switch (sel) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ tcg_gen_andi_tl(arg, arg, mask);
+ gen_mthc0_store64(arg, offsetof(CPUMIPSState, CP0_TagLo));
+ rn = "TagLo";
+ break;
+ default:
+ goto mthc0_nop;
+ }
+ break;
+ default:
+ goto mthc0_nop;
+ }
+
+ (void)rn; /* avoid a compiler warning */
+mthc0_nop:
+ LOG_DISAS("mthc0 %s (reg %d sel %d)\n", rn, reg, sel);
+}
+
static inline void gen_mfc0_unimplemented(DisasContext *ctx, TCGv arg)
{
if (ctx->insn_flags & ISA_MIPS32R6) {
@@ -4943,17 +5172,20 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
case 2:
switch (sel) {
case 0:
- tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryLo0));
+ {
+ TCGv_i64 tmp = tcg_temp_new_i64();
+ tcg_gen_ld_i64(tmp, cpu_env,
+ offsetof(CPUMIPSState, CP0_EntryLo0));
#if defined(TARGET_MIPS64)
- if (ctx->rxi) {
- TCGv tmp = tcg_temp_new();
- tcg_gen_andi_tl(tmp, arg, (3ull << CP0EnLo_XI));
- tcg_gen_shri_tl(tmp, tmp, 32);
- tcg_gen_or_tl(arg, arg, tmp);
- tcg_temp_free(tmp);
- }
+ if (ctx->rxi) {
+ /* Move RI/XI fields to bits 31:30 */
+ tcg_gen_shri_tl(arg, tmp, CP0EnLo_XI);
+ tcg_gen_deposit_tl(tmp, tmp, arg, 30, 2);
+ }
#endif
- tcg_gen_ext32s_tl(arg, arg);
+ gen_move_low32(arg, tmp);
+ tcg_temp_free_i64(tmp);
+ }
rn = "EntryLo0";
break;
case 1:
@@ -4998,17 +5230,20 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
case 3:
switch (sel) {
case 0:
- tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryLo1));
+ {
+ TCGv_i64 tmp = tcg_temp_new_i64();
+ tcg_gen_ld_i64(tmp, cpu_env,
+ offsetof(CPUMIPSState, CP0_EntryLo1));
#if defined(TARGET_MIPS64)
- if (ctx->rxi) {
- TCGv tmp = tcg_temp_new();
- tcg_gen_andi_tl(tmp, arg, (3ull << CP0EnLo_XI));
- tcg_gen_shri_tl(tmp, tmp, 32);
- tcg_gen_or_tl(arg, arg, tmp);
- tcg_temp_free(tmp);
- }
+ if (ctx->rxi) {
+ /* Move RI/XI fields to bits 31:30 */
+ tcg_gen_shri_tl(arg, tmp, CP0EnLo_XI);
+ tcg_gen_deposit_tl(tmp, tmp, arg, 30, 2);
+ }
#endif
- tcg_gen_ext32s_tl(arg, arg);
+ gen_move_low32(arg, tmp);
+ tcg_temp_free_i64(tmp);
+ }
rn = "EntryLo1";
break;
default:
@@ -5418,7 +5653,12 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel)
case 2:
case 4:
case 6:
- gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_TagLo));
+ {
+ TCGv_i64 tmp = tcg_temp_new_i64();
+ tcg_gen_ld_i64(tmp, cpu_env, offsetof(CPUMIPSState, CP0_TagLo));
+ gen_move_low32(arg, tmp);
+ tcg_temp_free_i64(tmp);
+ }
rn = "TagLo";
break;
case 1:
@@ -5661,6 +5901,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
check_insn(ctx, ISA_MIPS32R2);
gen_helper_mtc0_pagegrain(cpu_env, arg);
rn = "PageGrain";
+ ctx->bstate = BS_STOP;
break;
default:
goto cp0_unimplemented;
@@ -7557,7 +7798,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd,
if (h == 0) {
TCGv_i32 fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, rt);
+ gen_load_fpr32(ctx, fp0, rt);
tcg_gen_ext_i32_tl(t0, fp0);
tcg_temp_free_i32(fp0);
} else {
@@ -7756,7 +7997,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt,
TCGv_i32 fp0 = tcg_temp_new_i32();
tcg_gen_trunc_tl_i32(fp0, t0);
- gen_store_fpr32(fp0, rd);
+ gen_store_fpr32(ctx, fp0, rd);
tcg_temp_free_i32(fp0);
} else {
TCGv_i32 fp0 = tcg_temp_new_i32();
@@ -7841,6 +8082,25 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt,
opn = "dmtc0";
break;
#endif
+ case OPC_MFHC0:
+ check_mvh(ctx);
+ if (rt == 0) {
+ /* Treat as NOP. */
+ return;
+ }
+ gen_mfhc0(ctx, cpu_gpr[rt], rd, ctx->opcode & 0x7);
+ opn = "mfhc0";
+ break;
+ case OPC_MTHC0:
+ check_mvh(ctx);
+ {
+ TCGv t0 = tcg_temp_new();
+ gen_load_gpr(t0, rt);
+ gen_mthc0(ctx, t0, rd, ctx->opcode & 0x7);
+ tcg_temp_free(t0);
+ }
+ opn = "mthc0";
+ break;
case OPC_MFTR:
check_insn(ctx, ASE_MT);
if (rd == 0) {
@@ -7899,16 +8159,26 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt,
goto die;
gen_helper_tlbr(cpu_env);
break;
- case OPC_ERET:
- opn = "eret";
- check_insn(ctx, ISA_MIPS2);
+ case OPC_ERET: /* OPC_ERETNC */
if ((ctx->insn_flags & ISA_MIPS32R6) &&
(ctx->hflags & MIPS_HFLAG_BMASK)) {
MIPS_DEBUG("CTI in delay / forbidden slot");
goto die;
+ } else {
+ int bit_shift = (ctx->hflags & MIPS_HFLAG_M16) ? 16 : 6;
+ if (ctx->opcode & (1 << bit_shift)) {
+ /* OPC_ERETNC */
+ opn = "eretnc";
+ check_insn(ctx, ISA_MIPS32R5);
+ gen_helper_eretnc(cpu_env);
+ } else {
+ /* OPC_ERET */
+ opn = "eret";
+ check_insn(ctx, ISA_MIPS2);
+ gen_helper_eret(cpu_env);
+ }
+ ctx->bstate = BS_EXCP;
}
- gen_helper_eret(cpu_env);
- ctx->bstate = BS_EXCP;
break;
case OPC_DERET:
opn = "deret";
@@ -8346,7 +8616,7 @@ static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs)
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(ctx, fp0, fs);
tcg_gen_ext_i32_tl(t0, fp0);
tcg_temp_free_i32(fp0);
}
@@ -8359,7 +8629,7 @@ static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs)
TCGv_i32 fp0 = tcg_temp_new_i32();
tcg_gen_trunc_tl_i32(fp0, t0);
- gen_store_fpr32(fp0, fs);
+ gen_store_fpr32(ctx, fp0, fs);
tcg_temp_free_i32(fp0);
}
opn = "mtc1";
@@ -8457,7 +8727,8 @@ static void gen_movci (DisasContext *ctx, int rd, int rs, int cc, int tf)
gen_set_label(l1);
}
-static inline void gen_movcf_s (int fs, int fd, int cc, int tf)
+static inline void gen_movcf_s(DisasContext *ctx, int fs, int fd, int cc,
+ int tf)
{
int cond;
TCGv_i32 t0 = tcg_temp_new_i32();
@@ -8470,8 +8741,8 @@ static inline void gen_movcf_s (int fs, int fd, int cc, int tf)
tcg_gen_andi_i32(t0, fpu_fcr31, 1 << get_fp_bit(cc));
tcg_gen_brcondi_i32(cond, t0, 0, l1);
- gen_load_fpr32(t0, fs);
- gen_store_fpr32(t0, fd);
+ gen_load_fpr32(ctx, t0, fs);
+ gen_store_fpr32(ctx, t0, fd);
gen_set_label(l1);
tcg_temp_free_i32(t0);
}
@@ -8513,8 +8784,8 @@ static inline void gen_movcf_ps(DisasContext *ctx, int fs, int fd,
tcg_gen_andi_i32(t0, fpu_fcr31, 1 << get_fp_bit(cc));
tcg_gen_brcondi_i32(cond, t0, 0, l1);
- gen_load_fpr32(t0, fs);
- gen_store_fpr32(t0, fd);
+ gen_load_fpr32(ctx, t0, fs);
+ gen_store_fpr32(ctx, t0, fd);
gen_set_label(l1);
tcg_gen_andi_i32(t0, fpu_fcr31, 1 << get_fp_bit(cc+1));
@@ -8532,9 +8803,9 @@ static void gen_sel_s(DisasContext *ctx, enum fopcode op1, int fd, int ft,
TCGv_i32 fp0 = tcg_temp_new_i32();
TCGv_i32 fp1 = tcg_temp_new_i32();
TCGv_i32 fp2 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fd);
- gen_load_fpr32(fp1, ft);
- gen_load_fpr32(fp2, fs);
+ gen_load_fpr32(ctx, fp0, fd);
+ gen_load_fpr32(ctx, fp1, ft);
+ gen_load_fpr32(ctx, fp2, fs);
switch (op1) {
case OPC_SEL_S:
@@ -8555,7 +8826,7 @@ static void gen_sel_s(DisasContext *ctx, enum fopcode op1, int fd, int ft,
break;
}
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp2);
tcg_temp_free_i32(fp1);
tcg_temp_free_i32(fp0);
@@ -8648,11 +8919,11 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
TCGv_i32 fp0 = tcg_temp_new_i32();
TCGv_i32 fp1 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
- gen_load_fpr32(fp1, ft);
+ gen_load_fpr32(ctx, fp0, fs);
+ gen_load_fpr32(ctx, fp1, ft);
gen_helper_float_add_s(fp0, cpu_env, fp0, fp1);
tcg_temp_free_i32(fp1);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "add.s";
@@ -8663,11 +8934,11 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
TCGv_i32 fp0 = tcg_temp_new_i32();
TCGv_i32 fp1 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
- gen_load_fpr32(fp1, ft);
+ gen_load_fpr32(ctx, fp0, fs);
+ gen_load_fpr32(ctx, fp1, ft);
gen_helper_float_sub_s(fp0, cpu_env, fp0, fp1);
tcg_temp_free_i32(fp1);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "sub.s";
@@ -8678,11 +8949,11 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
TCGv_i32 fp0 = tcg_temp_new_i32();
TCGv_i32 fp1 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
- gen_load_fpr32(fp1, ft);
+ gen_load_fpr32(ctx, fp0, fs);
+ gen_load_fpr32(ctx, fp1, ft);
gen_helper_float_mul_s(fp0, cpu_env, fp0, fp1);
tcg_temp_free_i32(fp1);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "mul.s";
@@ -8693,11 +8964,11 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
TCGv_i32 fp0 = tcg_temp_new_i32();
TCGv_i32 fp1 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
- gen_load_fpr32(fp1, ft);
+ gen_load_fpr32(ctx, fp0, fs);
+ gen_load_fpr32(ctx, fp1, ft);
gen_helper_float_div_s(fp0, cpu_env, fp0, fp1);
tcg_temp_free_i32(fp1);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "div.s";
@@ -8707,9 +8978,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(ctx, fp0, fs);
gen_helper_float_sqrt_s(fp0, cpu_env, fp0);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "sqrt.s";
@@ -8718,9 +8989,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(ctx, fp0, fs);
gen_helper_float_abs_s(fp0, fp0);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "abs.s";
@@ -8729,8 +9000,8 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
- gen_store_fpr32(fp0, fd);
+ gen_load_fpr32(ctx, fp0, fs);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "mov.s";
@@ -8739,9 +9010,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(ctx, fp0, fs);
gen_helper_float_chs_s(fp0, fp0);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "neg.s";
@@ -8752,7 +9023,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
TCGv_i32 fp32 = tcg_temp_new_i32();
TCGv_i64 fp64 = tcg_temp_new_i64();
- gen_load_fpr32(fp32, fs);
+ gen_load_fpr32(ctx, fp32, fs);
gen_helper_float_roundl_s(fp64, cpu_env, fp32);
tcg_temp_free_i32(fp32);
gen_store_fpr64(ctx, fp64, fd);
@@ -8766,7 +9037,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
TCGv_i32 fp32 = tcg_temp_new_i32();
TCGv_i64 fp64 = tcg_temp_new_i64();
- gen_load_fpr32(fp32, fs);
+ gen_load_fpr32(ctx, fp32, fs);
gen_helper_float_truncl_s(fp64, cpu_env, fp32);
tcg_temp_free_i32(fp32);
gen_store_fpr64(ctx, fp64, fd);
@@ -8780,7 +9051,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
TCGv_i32 fp32 = tcg_temp_new_i32();
TCGv_i64 fp64 = tcg_temp_new_i64();
- gen_load_fpr32(fp32, fs);
+ gen_load_fpr32(ctx, fp32, fs);
gen_helper_float_ceill_s(fp64, cpu_env, fp32);
tcg_temp_free_i32(fp32);
gen_store_fpr64(ctx, fp64, fd);
@@ -8794,7 +9065,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
TCGv_i32 fp32 = tcg_temp_new_i32();
TCGv_i64 fp64 = tcg_temp_new_i64();
- gen_load_fpr32(fp32, fs);
+ gen_load_fpr32(ctx, fp32, fs);
gen_helper_float_floorl_s(fp64, cpu_env, fp32);
tcg_temp_free_i32(fp32);
gen_store_fpr64(ctx, fp64, fd);
@@ -8806,9 +9077,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(ctx, fp0, fs);
gen_helper_float_roundw_s(fp0, cpu_env, fp0);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "round.w.s";
@@ -8817,9 +9088,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(ctx, fp0, fs);
gen_helper_float_truncw_s(fp0, cpu_env, fp0);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "trunc.w.s";
@@ -8828,9 +9099,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(ctx, fp0, fs);
gen_helper_float_ceilw_s(fp0, cpu_env, fp0);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "ceil.w.s";
@@ -8839,9 +9110,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(ctx, fp0, fs);
gen_helper_float_floorw_s(fp0, cpu_env, fp0);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "floor.w.s";
@@ -8863,7 +9134,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
break;
case OPC_MOVCF_S:
check_insn_opc_removed(ctx, ISA_MIPS32R6);
- gen_movcf_s(fs, fd, (ft >> 2) & 0x7, ft & 0x1);
+ gen_movcf_s(ctx, fs, fd, (ft >> 2) & 0x7, ft & 0x1);
opn = "movcf.s";
break;
case OPC_MOVZ_S:
@@ -8876,8 +9147,8 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
tcg_gen_brcondi_tl(TCG_COND_NE, cpu_gpr[ft], 0, l1);
}
fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
- gen_store_fpr32(fp0, fd);
+ gen_load_fpr32(ctx, fp0, fs);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
gen_set_label(l1);
}
@@ -8892,8 +9163,8 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
if (ft != 0) {
tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[ft], 0, l1);
fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
- gen_store_fpr32(fp0, fd);
+ gen_load_fpr32(ctx, fp0, fs);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
gen_set_label(l1);
}
@@ -8905,9 +9176,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(ctx, fp0, fs);
gen_helper_float_recip_s(fp0, cpu_env, fp0);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "recip.s";
@@ -8917,9 +9188,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(ctx, fp0, fs);
gen_helper_float_rsqrt_s(fp0, cpu_env, fp0);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "rsqrt.s";
@@ -8930,11 +9201,11 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
TCGv_i32 fp0 = tcg_temp_new_i32();
TCGv_i32 fp1 = tcg_temp_new_i32();
TCGv_i32 fp2 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
- gen_load_fpr32(fp1, ft);
- gen_load_fpr32(fp2, fd);
+ gen_load_fpr32(ctx, fp0, fs);
+ gen_load_fpr32(ctx, fp1, ft);
+ gen_load_fpr32(ctx, fp2, fd);
gen_helper_float_maddf_s(fp2, cpu_env, fp0, fp1, fp2);
- gen_store_fpr32(fp2, fd);
+ gen_store_fpr32(ctx, fp2, fd);
tcg_temp_free_i32(fp2);
tcg_temp_free_i32(fp1);
tcg_temp_free_i32(fp0);
@@ -8947,11 +9218,11 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
TCGv_i32 fp0 = tcg_temp_new_i32();
TCGv_i32 fp1 = tcg_temp_new_i32();
TCGv_i32 fp2 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
- gen_load_fpr32(fp1, ft);
- gen_load_fpr32(fp2, fd);
+ gen_load_fpr32(ctx, fp0, fs);
+ gen_load_fpr32(ctx, fp1, ft);
+ gen_load_fpr32(ctx, fp2, fd);
gen_helper_float_msubf_s(fp2, cpu_env, fp0, fp1, fp2);
- gen_store_fpr32(fp2, fd);
+ gen_store_fpr32(ctx, fp2, fd);
tcg_temp_free_i32(fp2);
tcg_temp_free_i32(fp1);
tcg_temp_free_i32(fp0);
@@ -8962,9 +9233,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
check_insn(ctx, ISA_MIPS32R6);
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(ctx, fp0, fs);
gen_helper_float_rint_s(fp0, cpu_env, fp0);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
opn = "rint.s";
}
@@ -8973,9 +9244,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
check_insn(ctx, ISA_MIPS32R6);
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(ctx, fp0, fs);
gen_helper_float_class_s(fp0, fp0);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
opn = "class.s";
}
@@ -8986,10 +9257,10 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
TCGv_i32 fp0 = tcg_temp_new_i32();
TCGv_i32 fp1 = tcg_temp_new_i32();
TCGv_i32 fp2 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
- gen_load_fpr32(fp1, ft);
+ gen_load_fpr32(ctx, fp0, fs);
+ gen_load_fpr32(ctx, fp1, ft);
gen_helper_float_min_s(fp2, cpu_env, fp0, fp1);
- gen_store_fpr32(fp2, fd);
+ gen_store_fpr32(ctx, fp2, fd);
tcg_temp_free_i32(fp2);
tcg_temp_free_i32(fp1);
tcg_temp_free_i32(fp0);
@@ -9001,11 +9272,11 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
TCGv_i32 fp0 = tcg_temp_new_i32();
TCGv_i32 fp1 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
- gen_load_fpr32(fp1, ft);
+ gen_load_fpr32(ctx, fp0, fs);
+ gen_load_fpr32(ctx, fp1, ft);
gen_helper_float_recip2_s(fp0, cpu_env, fp0, fp1);
tcg_temp_free_i32(fp1);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "recip2.s";
@@ -9017,10 +9288,10 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
TCGv_i32 fp0 = tcg_temp_new_i32();
TCGv_i32 fp1 = tcg_temp_new_i32();
TCGv_i32 fp2 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
- gen_load_fpr32(fp1, ft);
+ gen_load_fpr32(ctx, fp0, fs);
+ gen_load_fpr32(ctx, fp1, ft);
gen_helper_float_mina_s(fp2, cpu_env, fp0, fp1);
- gen_store_fpr32(fp2, fd);
+ gen_store_fpr32(ctx, fp2, fd);
tcg_temp_free_i32(fp2);
tcg_temp_free_i32(fp1);
tcg_temp_free_i32(fp0);
@@ -9031,9 +9302,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(ctx, fp0, fs);
gen_helper_float_recip1_s(fp0, cpu_env, fp0);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "recip1.s";
@@ -9044,10 +9315,10 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
/* OPC_MAX_S */
TCGv_i32 fp0 = tcg_temp_new_i32();
TCGv_i32 fp1 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
- gen_load_fpr32(fp1, ft);
+ gen_load_fpr32(ctx, fp0, fs);
+ gen_load_fpr32(ctx, fp1, ft);
gen_helper_float_max_s(fp1, cpu_env, fp0, fp1);
- gen_store_fpr32(fp1, fd);
+ gen_store_fpr32(ctx, fp1, fd);
tcg_temp_free_i32(fp1);
tcg_temp_free_i32(fp0);
opn = "max.s";
@@ -9057,9 +9328,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(ctx, fp0, fs);
gen_helper_float_rsqrt1_s(fp0, cpu_env, fp0);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "rsqrt1.s";
@@ -9070,10 +9341,10 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
/* OPC_MAXA_S */
TCGv_i32 fp0 = tcg_temp_new_i32();
TCGv_i32 fp1 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
- gen_load_fpr32(fp1, ft);
+ gen_load_fpr32(ctx, fp0, fs);
+ gen_load_fpr32(ctx, fp1, ft);
gen_helper_float_maxa_s(fp1, cpu_env, fp0, fp1);
- gen_store_fpr32(fp1, fd);
+ gen_store_fpr32(ctx, fp1, fd);
tcg_temp_free_i32(fp1);
tcg_temp_free_i32(fp0);
opn = "maxa.s";
@@ -9084,11 +9355,11 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
TCGv_i32 fp0 = tcg_temp_new_i32();
TCGv_i32 fp1 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
- gen_load_fpr32(fp1, ft);
+ gen_load_fpr32(ctx, fp0, fs);
+ gen_load_fpr32(ctx, fp1, ft);
gen_helper_float_rsqrt2_s(fp0, cpu_env, fp0, fp1);
tcg_temp_free_i32(fp1);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "rsqrt2.s";
@@ -9100,7 +9371,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
TCGv_i32 fp32 = tcg_temp_new_i32();
TCGv_i64 fp64 = tcg_temp_new_i64();
- gen_load_fpr32(fp32, fs);
+ gen_load_fpr32(ctx, fp32, fs);
gen_helper_float_cvtd_s(fp64, cpu_env, fp32);
tcg_temp_free_i32(fp32);
gen_store_fpr64(ctx, fp64, fd);
@@ -9112,9 +9383,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(ctx, fp0, fs);
gen_helper_float_cvtw_s(fp0, cpu_env, fp0);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "cvt.w.s";
@@ -9125,7 +9396,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
TCGv_i32 fp32 = tcg_temp_new_i32();
TCGv_i64 fp64 = tcg_temp_new_i64();
- gen_load_fpr32(fp32, fs);
+ gen_load_fpr32(ctx, fp32, fs);
gen_helper_float_cvtl_s(fp64, cpu_env, fp32);
tcg_temp_free_i32(fp32);
gen_store_fpr64(ctx, fp64, fd);
@@ -9141,8 +9412,8 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
TCGv_i32 fp32_0 = tcg_temp_new_i32();
TCGv_i32 fp32_1 = tcg_temp_new_i32();
- gen_load_fpr32(fp32_0, fs);
- gen_load_fpr32(fp32_1, ft);
+ gen_load_fpr32(ctx, fp32_0, fs);
+ gen_load_fpr32(ctx, fp32_1, ft);
tcg_gen_concat_i32_i64(fp64, fp32_1, fp32_0);
tcg_temp_free_i32(fp32_1);
tcg_temp_free_i32(fp32_0);
@@ -9344,7 +9615,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
gen_load_fpr64(ctx, fp64, fs);
gen_helper_float_roundw_d(fp32, cpu_env, fp64);
tcg_temp_free_i64(fp64);
- gen_store_fpr32(fp32, fd);
+ gen_store_fpr32(ctx, fp32, fd);
tcg_temp_free_i32(fp32);
}
opn = "round.w.d";
@@ -9358,7 +9629,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
gen_load_fpr64(ctx, fp64, fs);
gen_helper_float_truncw_d(fp32, cpu_env, fp64);
tcg_temp_free_i64(fp64);
- gen_store_fpr32(fp32, fd);
+ gen_store_fpr32(ctx, fp32, fd);
tcg_temp_free_i32(fp32);
}
opn = "trunc.w.d";
@@ -9372,7 +9643,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
gen_load_fpr64(ctx, fp64, fs);
gen_helper_float_ceilw_d(fp32, cpu_env, fp64);
tcg_temp_free_i64(fp64);
- gen_store_fpr32(fp32, fd);
+ gen_store_fpr32(ctx, fp32, fd);
tcg_temp_free_i32(fp32);
}
opn = "ceil.w.d";
@@ -9386,7 +9657,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
gen_load_fpr64(ctx, fp64, fs);
gen_helper_float_floorw_d(fp32, cpu_env, fp64);
tcg_temp_free_i64(fp64);
- gen_store_fpr32(fp32, fd);
+ gen_store_fpr32(ctx, fp32, fd);
tcg_temp_free_i32(fp32);
}
opn = "floor.w.d";
@@ -9669,7 +9940,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
gen_load_fpr64(ctx, fp64, fs);
gen_helper_float_cvts_d(fp32, cpu_env, fp64);
tcg_temp_free_i64(fp64);
- gen_store_fpr32(fp32, fd);
+ gen_store_fpr32(ctx, fp32, fd);
tcg_temp_free_i32(fp32);
}
opn = "cvt.s.d";
@@ -9683,7 +9954,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
gen_load_fpr64(ctx, fp64, fs);
gen_helper_float_cvtw_d(fp32, cpu_env, fp64);
tcg_temp_free_i64(fp64);
- gen_store_fpr32(fp32, fd);
+ gen_store_fpr32(ctx, fp32, fd);
tcg_temp_free_i32(fp32);
}
opn = "cvt.w.d";
@@ -9704,9 +9975,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(ctx, fp0, fs);
gen_helper_float_cvts_w(fp0, cpu_env, fp0);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "cvt.s.w";
@@ -9717,7 +9988,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
TCGv_i32 fp32 = tcg_temp_new_i32();
TCGv_i64 fp64 = tcg_temp_new_i64();
- gen_load_fpr32(fp32, fs);
+ gen_load_fpr32(ctx, fp32, fs);
gen_helper_float_cvtd_w(fp64, cpu_env, fp32);
tcg_temp_free_i32(fp32);
gen_store_fpr64(ctx, fp64, fd);
@@ -9734,7 +10005,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
gen_load_fpr64(ctx, fp64, fs);
gen_helper_float_cvts_l(fp32, cpu_env, fp64);
tcg_temp_free_i64(fp64);
- gen_store_fpr32(fp32, fd);
+ gen_store_fpr32(ctx, fp32, fd);
tcg_temp_free_i32(fp32);
}
opn = "cvt.s.l";
@@ -9973,7 +10244,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
gen_load_fpr32h(ctx, fp0, fs);
gen_helper_float_cvts_pu(fp0, cpu_env, fp0);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "cvt.s.pu";
@@ -9995,9 +10266,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(ctx, fp0, fs);
gen_helper_float_cvts_pl(fp0, cpu_env, fp0);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "cvt.s.pl";
@@ -10008,10 +10279,10 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
TCGv_i32 fp0 = tcg_temp_new_i32();
TCGv_i32 fp1 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
- gen_load_fpr32(fp1, ft);
+ gen_load_fpr32(ctx, fp0, fs);
+ gen_load_fpr32(ctx, fp1, ft);
gen_store_fpr32h(ctx, fp0, fd);
- gen_store_fpr32(fp1, fd);
+ gen_store_fpr32(ctx, fp1, fd);
tcg_temp_free_i32(fp0);
tcg_temp_free_i32(fp1);
}
@@ -10023,9 +10294,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
TCGv_i32 fp0 = tcg_temp_new_i32();
TCGv_i32 fp1 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(ctx, fp0, fs);
gen_load_fpr32h(ctx, fp1, ft);
- gen_store_fpr32(fp1, fd);
+ gen_store_fpr32(ctx, fp1, fd);
gen_store_fpr32h(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
tcg_temp_free_i32(fp1);
@@ -10039,8 +10310,8 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
TCGv_i32 fp1 = tcg_temp_new_i32();
gen_load_fpr32h(ctx, fp0, fs);
- gen_load_fpr32(fp1, ft);
- gen_store_fpr32(fp1, fd);
+ gen_load_fpr32(ctx, fp1, ft);
+ gen_store_fpr32(ctx, fp1, fd);
gen_store_fpr32h(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
tcg_temp_free_i32(fp1);
@@ -10055,7 +10326,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
gen_load_fpr32h(ctx, fp0, fs);
gen_load_fpr32h(ctx, fp1, ft);
- gen_store_fpr32(fp1, fd);
+ gen_store_fpr32(ctx, fp1, fd);
gen_store_fpr32h(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
tcg_temp_free_i32(fp1);
@@ -10130,7 +10401,7 @@ static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc,
tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL);
tcg_gen_trunc_tl_i32(fp0, t0);
- gen_store_fpr32(fp0, fd);
+ gen_store_fpr32(ctx, fp0, fd);
tcg_temp_free_i32(fp0);
}
opn = "lwxc1";
@@ -10162,7 +10433,7 @@ static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc,
check_cop1x(ctx);
{
TCGv_i32 fp0 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(ctx, fp0, fs);
tcg_gen_qemu_st_i32(fp0, t0, ctx->mem_idx, MO_TEUL);
tcg_temp_free_i32(fp0);
}
@@ -10219,23 +10490,23 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc,
tcg_gen_andi_tl(t0, t0, 0x7);
tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0, l1);
- gen_load_fpr32(fp, fs);
+ gen_load_fpr32(ctx, fp, fs);
gen_load_fpr32h(ctx, fph, fs);
- gen_store_fpr32(fp, fd);
+ gen_store_fpr32(ctx, fp, fd);
gen_store_fpr32h(ctx, fph, fd);
tcg_gen_br(l2);
gen_set_label(l1);
tcg_gen_brcondi_tl(TCG_COND_NE, t0, 4, l2);
tcg_temp_free(t0);
#ifdef TARGET_WORDS_BIGENDIAN
- gen_load_fpr32(fp, fs);
+ gen_load_fpr32(ctx, fp, fs);
gen_load_fpr32h(ctx, fph, ft);
gen_store_fpr32h(ctx, fp, fd);
- gen_store_fpr32(fph, fd);
+ gen_store_fpr32(ctx, fph, fd);
#else
gen_load_fpr32h(ctx, fph, fs);
- gen_load_fpr32(fp, ft);
- gen_store_fpr32(fph, fd);
+ gen_load_fpr32(ctx, fp, ft);
+ gen_store_fpr32(ctx, fph, fd);
gen_store_fpr32h(ctx, fp, fd);
#endif
gen_set_label(l2);
@@ -10251,13 +10522,13 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc,
TCGv_i32 fp1 = tcg_temp_new_i32();
TCGv_i32 fp2 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
- gen_load_fpr32(fp1, ft);
- gen_load_fpr32(fp2, fr);
+ gen_load_fpr32(ctx, fp0, fs);
+ gen_load_fpr32(ctx, fp1, ft);
+ gen_load_fpr32(ctx, fp2, fr);
gen_helper_float_madd_s(fp2, cpu_env, fp0, fp1, fp2);
tcg_temp_free_i32(fp0);
tcg_temp_free_i32(fp1);
- gen_store_fpr32(fp2, fd);
+ gen_store_fpr32(ctx, fp2, fd);
tcg_temp_free_i32(fp2);
}
opn = "madd.s";
@@ -10306,13 +10577,13 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc,
TCGv_i32 fp1 = tcg_temp_new_i32();
TCGv_i32 fp2 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
- gen_load_fpr32(fp1, ft);
- gen_load_fpr32(fp2, fr);
+ gen_load_fpr32(ctx, fp0, fs);
+ gen_load_fpr32(ctx, fp1, ft);
+ gen_load_fpr32(ctx, fp2, fr);
gen_helper_float_msub_s(fp2, cpu_env, fp0, fp1, fp2);
tcg_temp_free_i32(fp0);
tcg_temp_free_i32(fp1);
- gen_store_fpr32(fp2, fd);
+ gen_store_fpr32(ctx, fp2, fd);
tcg_temp_free_i32(fp2);
}
opn = "msub.s";
@@ -10361,13 +10632,13 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc,
TCGv_i32 fp1 = tcg_temp_new_i32();
TCGv_i32 fp2 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
- gen_load_fpr32(fp1, ft);
- gen_load_fpr32(fp2, fr);
+ gen_load_fpr32(ctx, fp0, fs);
+ gen_load_fpr32(ctx, fp1, ft);
+ gen_load_fpr32(ctx, fp2, fr);
gen_helper_float_nmadd_s(fp2, cpu_env, fp0, fp1, fp2);
tcg_temp_free_i32(fp0);
tcg_temp_free_i32(fp1);
- gen_store_fpr32(fp2, fd);
+ gen_store_fpr32(ctx, fp2, fd);
tcg_temp_free_i32(fp2);
}
opn = "nmadd.s";
@@ -10416,13 +10687,13 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc,
TCGv_i32 fp1 = tcg_temp_new_i32();
TCGv_i32 fp2 = tcg_temp_new_i32();
- gen_load_fpr32(fp0, fs);
- gen_load_fpr32(fp1, ft);
- gen_load_fpr32(fp2, fr);
+ gen_load_fpr32(ctx, fp0, fs);
+ gen_load_fpr32(ctx, fp1, ft);
+ gen_load_fpr32(ctx, fp2, fr);
gen_helper_float_nmsub_s(fp2, cpu_env, fp0, fp1, fp2);
tcg_temp_free_i32(fp0);
tcg_temp_free_i32(fp1);
- gen_store_fpr32(fp2, fd);
+ gen_store_fpr32(ctx, fp2, fd);
tcg_temp_free_i32(fp2);
}
opn = "nmsub.s";
@@ -13502,7 +13773,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx,
case MOVF_FMT:
switch (fmt) {
case FMT_SDPS_S:
- gen_movcf_s(rs, rt, cc, 0);
+ gen_movcf_s(ctx, rs, rt, cc, 0);
break;
case FMT_SDPS_D:
gen_movcf_d(ctx, rs, rt, cc, 0);
@@ -13517,7 +13788,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx,
case MOVT_FMT:
switch (fmt) {
case FMT_SDPS_S:
- gen_movcf_s(rs, rt, cc, 1);
+ gen_movcf_s(ctx, rs, rt, cc, 1);
break;
case FMT_SDPS_D:
gen_movcf_d(ctx, rs, rt, cc, 1);
@@ -18404,32 +18675,39 @@ static void gen_msa(CPUMIPSState *env, DisasContext *ctx)
uint8_t wd = (ctx->opcode >> 6) & 0x1f;
uint8_t df = (ctx->opcode >> 0) & 0x3;
- TCGv_i32 tdf = tcg_const_i32(df);
TCGv_i32 twd = tcg_const_i32(wd);
- TCGv_i32 trs = tcg_const_i32(rs);
- TCGv_i32 ts10 = tcg_const_i32(s10);
+ TCGv taddr = tcg_temp_new();
+ gen_base_offset_addr(ctx, taddr, rs, s10 << df);
switch (MASK_MSA_MINOR(opcode)) {
case OPC_LD_B:
+ gen_helper_msa_ld_b(cpu_env, twd, taddr);
+ break;
case OPC_LD_H:
+ gen_helper_msa_ld_h(cpu_env, twd, taddr);
+ break;
case OPC_LD_W:
+ gen_helper_msa_ld_w(cpu_env, twd, taddr);
+ break;
case OPC_LD_D:
- save_cpu_state(ctx, 1);
- gen_helper_msa_ld_df(cpu_env, tdf, twd, trs, ts10);
+ gen_helper_msa_ld_d(cpu_env, twd, taddr);
break;
case OPC_ST_B:
+ gen_helper_msa_st_b(cpu_env, twd, taddr);
+ break;
case OPC_ST_H:
+ gen_helper_msa_st_h(cpu_env, twd, taddr);
+ break;
case OPC_ST_W:
+ gen_helper_msa_st_w(cpu_env, twd, taddr);
+ break;
case OPC_ST_D:
- save_cpu_state(ctx, 1);
- gen_helper_msa_st_df(cpu_env, tdf, twd, trs, ts10);
+ gen_helper_msa_st_d(cpu_env, twd, taddr);
break;
}
tcg_temp_free_i32(twd);
- tcg_temp_free_i32(tdf);
- tcg_temp_free_i32(trs);
- tcg_temp_free_i32(ts10);
+ tcg_temp_free(taddr);
}
break;
default:
@@ -18564,6 +18842,8 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx)
case OPC_MTC0:
case OPC_MFTR:
case OPC_MTTR:
+ case OPC_MFHC0:
+ case OPC_MTHC0:
#if defined(TARGET_MIPS64)
case OPC_DMFC0:
case OPC_DMTC0:
@@ -19134,6 +19414,9 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb,
ctx.ie = (env->CP0_Config4 >> CP0C4_IE) & 3;
ctx.bi = (env->CP0_Config3 >> CP0C3_BI) & 1;
ctx.bp = (env->CP0_Config3 >> CP0C3_BP) & 1;
+ ctx.PAMask = env->PAMask;
+ ctx.mvh = (env->CP0_Config5 >> CP0C5_MVH) & 1;
+ ctx.CP0_LLAddr_shift = env->CP0_LLAddr_shift;
/* Restore delay slot state from the tb context. */
ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */
ctx.ulri = (env->CP0_Config3 >> CP0C3_ULRI) & 1;
@@ -19143,6 +19426,8 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb,
#else
ctx.mem_idx = ctx.hflags & MIPS_HFLAG_KSU;
#endif
+ ctx.default_tcg_memop_mask = (ctx.insn_flags & ISA_MIPS32R6) ?
+ MO_UNALN : MO_ALIGN;
num_insns = 0;
max_insns = tb->cflags & CF_COUNT_MASK;
if (max_insns == 0)
@@ -19385,7 +19670,8 @@ void mips_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
cpu_fprintf(f, "CP0 Status 0x%08x Cause 0x%08x EPC 0x" TARGET_FMT_lx "\n",
env->CP0_Status, env->CP0_Cause, env->CP0_EPC);
- cpu_fprintf(f, " Config0 0x%08x Config1 0x%08x LLAddr 0x" TARGET_FMT_lx "\n",
+ cpu_fprintf(f, " Config0 0x%08x Config1 0x%08x LLAddr 0x%016"
+ PRIx64 "\n",
env->CP0_Config0, env->CP0_Config1, env->lladdr);
cpu_fprintf(f, " Config2 0x%08x Config3 0x%08x\n",
env->CP0_Config2, env->CP0_Config3);
@@ -19519,7 +19805,6 @@ void cpu_state_reset(CPUMIPSState *env)
}
#endif
env->PABITS = env->cpu_model->PABITS;
- env->PAMask = (target_ulong)((1ULL << env->cpu_model->PABITS) - 1);
env->CP0_SRSConf0_rw_bitmask = env->cpu_model->CP0_SRSConf0_rw_bitmask;
env->CP0_SRSConf0 = env->cpu_model->CP0_SRSConf0;
env->CP0_SRSConf1_rw_bitmask = env->cpu_model->CP0_SRSConf1_rw_bitmask;
@@ -19640,6 +19925,7 @@ void cpu_state_reset(CPUMIPSState *env)
compute_hflags(env);
restore_rounding_mode(env);
restore_flush_mode(env);
+ restore_pamask(env);
cs->exception_index = EXCP_NONE;
}
diff --git a/target-mips/translate_init.c b/target-mips/translate_init.c
index 85a65e74b4..30605dab06 100644
--- a/target-mips/translate_init.c
+++ b/target-mips/translate_init.c
@@ -400,10 +400,12 @@ static const mips_def_t mips_defs[] =
(0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) |
(1 << CP0C1_CA),
.CP0_Config2 = MIPS_CONFIG2,
- .CP0_Config3 = MIPS_CONFIG3 | (1U << CP0C3_M) | (1 << CP0C3_MSAP),
+ .CP0_Config3 = MIPS_CONFIG3 | (1U << CP0C3_M) | (1 << CP0C3_MSAP) |
+ (1 << CP0C3_LPA),
.CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M),
.CP0_Config4_rw_bitmask = 0,
- .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_UFR),
+ .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_UFR) | (1 << CP0C5_LLB) |
+ (1 << CP0C5_MVH),
.CP0_Config5_rw_bitmask = (0 << CP0C5_M) | (1 << CP0C5_K) |
(1 << CP0C5_CV) | (0 << CP0C5_EVA) |
(1 << CP0C5_MSAEn) | (1 << CP0C5_UFR) |
@@ -413,11 +415,12 @@ static const mips_def_t mips_defs[] =
.SYNCI_Step = 32,
.CCRes = 2,
.CP0_Status_rw_bitmask = 0x3778FF1F,
+ .CP0_PageGrain_rw_bitmask = (1 << CP0PG_ELPA),
.CP1_fcr0 = (1 << FCR0_UFRP) | (1 << FCR0_F64) | (1 << FCR0_L) |
(1 << FCR0_W) | (1 << FCR0_D) | (1 << FCR0_S) |
(0x93 << FCR0_PRID),
.SEGBITS = 32,
- .PABITS = 32,
+ .PABITS = 40,
.insn_flags = CPU_MIPS32R5 | ASE_MIPS16 | ASE_MSA,
.mmu_type = MMU_TYPE_R4000,
},
@@ -553,9 +556,6 @@ static const mips_def_t mips_defs[] =
(1 << FCR0_L) | (1 << FCR0_W) | (1 << FCR0_D) |
(1 << FCR0_S) | (0x00 << FCR0_PRID) | (0x0 << FCR0_REV),
.SEGBITS = 42,
- /* The architectural limit is 59, but we have hardcoded 36 bit
- in some places...
- .PABITS = 59, */ /* the architectural limit */
.PABITS = 36,
.insn_flags = CPU_MIPS64R2 | ASE_MIPS3D,
.mmu_type = MMU_TYPE_R4000,
@@ -607,7 +607,7 @@ static const mips_def_t mips_defs[] =
},
{
/* A generic CPU supporting MIPS64 Release 6 ISA.
- FIXME: Support IEEE 754-2008 FP and misaligned memory accesses.
+ FIXME: Support IEEE 754-2008 FP.
Eventually this should be replaced by a real CPU model. */
.name = "MIPS64R6-generic",
.CP0_PRid = 0x00010000,
@@ -619,10 +619,13 @@ static const mips_def_t mips_defs[] =
(0 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP),
.CP0_Config2 = MIPS_CONFIG2,
.CP0_Config3 = MIPS_CONFIG3 | (1 << CP0C3_RXI) | (1 << CP0C3_BP) |
- (1 << CP0C3_BI) | (1 << CP0C3_ULRI) | (1U << CP0C3_M),
+ (1 << CP0C3_BI) | (1 << CP0C3_ULRI) | (1 << CP0C3_LPA) |
+ (1U << CP0C3_M),
.CP0_Config4 = MIPS_CONFIG4 | (0xfc << CP0C4_KScrExist) |
(3 << CP0C4_IE) | (1 << CP0C4_M),
- .CP0_Config5_rw_bitmask = (1 << CP0C5_SBRI),
+ .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_LLB),
+ .CP0_Config5_rw_bitmask = (1 << CP0C5_SBRI) | (1 << CP0C5_FRE) |
+ (1 << CP0C5_UFE),
.CP0_LLAddr_rw_bitmask = 0,
.CP0_LLAddr_shift = 0,
.SYNCI_Step = 32,
@@ -630,15 +633,12 @@ static const mips_def_t mips_defs[] =
.CP0_Status_rw_bitmask = 0x30D8FFFF,
.CP0_PageGrain = (1 << CP0PG_IEC) | (1 << CP0PG_XIE) |
(1U << CP0PG_RIE),
- .CP0_PageGrain_rw_bitmask = 0,
- .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) |
- (1 << FCR0_D) | (1 << FCR0_S) | (0x00 << FCR0_PRID) |
- (0x0 << FCR0_REV),
+ .CP0_PageGrain_rw_bitmask = (1 << CP0PG_ELPA),
+ .CP1_fcr0 = (1 << FCR0_FREP) | (1 << FCR0_F64) | (1 << FCR0_L) |
+ (1 << FCR0_W) | (1 << FCR0_D) | (1 << FCR0_S) |
+ (0x00 << FCR0_PRID) | (0x0 << FCR0_REV),
.SEGBITS = 42,
- /* The architectural limit is 59, but we have hardcoded 36 bit
- in some places...
- .PABITS = 59, */ /* the architectural limit */
- .PABITS = 36,
+ .PABITS = 48,
.insn_flags = CPU_MIPS64R6,
.mmu_type = MMU_TYPE_R4000,
},
@@ -701,9 +701,6 @@ static const mips_def_t mips_defs[] =
(1 << FCR0_L) | (1 << FCR0_W) | (1 << FCR0_D) |
(1 << FCR0_S) | (0x00 << FCR0_PRID) | (0x0 << FCR0_REV),
.SEGBITS = 42,
- /* The architectural limit is 59, but we have hardcoded 36 bit
- in some places...
- .PABITS = 59, */ /* the architectural limit */
.PABITS = 36,
.insn_flags = CPU_MIPS64R2 | ASE_DSP | ASE_DSPR2,
.mmu_type = MMU_TYPE_R4000,
diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c
index 1da9ea81e5..afb4696b8a 100644
--- a/target-ppc/kvm.c
+++ b/target-ppc/kvm.c
@@ -1884,6 +1884,23 @@ int kvmppc_get_hypercall(CPUPPCState *env, uint8_t *buf, int buf_len)
return 0;
}
+static inline int kvmppc_enable_hcall(KVMState *s, target_ulong hcall)
+{
+ return kvm_vm_enable_cap(s, KVM_CAP_PPC_ENABLE_HCALL, 0, hcall, 1);
+}
+
+void kvmppc_enable_logical_ci_hcalls(void)
+{
+ /*
+ * FIXME: it would be nice if we could detect the cases where
+ * we're using a device which requires the in kernel
+ * implementation of these hcalls, but the kernel lacks them and
+ * produce a warning.
+ */
+ kvmppc_enable_hcall(kvm_state, H_LOGICAL_CI_LOAD);
+ kvmppc_enable_hcall(kvm_state, H_LOGICAL_CI_STORE);
+}
+
void kvmppc_set_papr(PowerPCCPU *cpu)
{
CPUState *cs = CPU(cpu);
@@ -2410,3 +2427,8 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
{
return 0;
}
+
+int kvm_arch_msi_data_to_gsi(uint32_t data)
+{
+ return data & 0xffff;
+}
diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h
index 2e0224c6af..4d30e27951 100644
--- a/target-ppc/kvm_ppc.h
+++ b/target-ppc/kvm_ppc.h
@@ -24,6 +24,7 @@ bool kvmppc_get_host_serial(char **buf);
int kvmppc_get_hasidle(CPUPPCState *env);
int kvmppc_get_hypercall(CPUPPCState *env, uint8_t *buf, int buf_len);
int kvmppc_set_interrupt(PowerPCCPU *cpu, int irq, int level);
+void kvmppc_enable_logical_ci_hcalls(void);
void kvmppc_set_papr(PowerPCCPU *cpu);
int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t cpu_version);
void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy);
@@ -107,6 +108,10 @@ static inline int kvmppc_set_interrupt(PowerPCCPU *cpu, int irq, int level)
return -1;
}
+static inline void kvmppc_enable_logical_ci_hcalls(void)
+{
+}
+
static inline void kvmppc_set_papr(PowerPCCPU *cpu)
{
}
diff --git a/target-ppc/machine.c b/target-ppc/machine.c
index d875211a2d..f4ac7611dd 100644
--- a/target-ppc/machine.c
+++ b/target-ppc/machine.c
@@ -213,6 +213,7 @@ static const VMStateDescription vmstate_fpu = {
.name = "cpu/fpu",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = fpu_needed,
.fields = (VMStateField[]) {
VMSTATE_FLOAT64_ARRAY(env.fpr, PowerPCCPU, 32),
VMSTATE_UINTTL(env.fpscr, PowerPCCPU),
@@ -231,6 +232,7 @@ static const VMStateDescription vmstate_altivec = {
.name = "cpu/altivec",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = altivec_needed,
.fields = (VMStateField[]) {
VMSTATE_AVR_ARRAY(env.avr, PowerPCCPU, 32),
VMSTATE_UINT32(env.vscr, PowerPCCPU),
@@ -249,6 +251,7 @@ static const VMStateDescription vmstate_vsx = {
.name = "cpu/vsx",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = vsx_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64_ARRAY(env.vsr, PowerPCCPU, 32),
VMSTATE_END_OF_LIST()
@@ -269,6 +272,7 @@ static const VMStateDescription vmstate_tm = {
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
+ .needed = tm_needed,
.fields = (VMStateField []) {
VMSTATE_UINTTL_ARRAY(env.tm_gpr, PowerPCCPU, 32),
VMSTATE_AVR_ARRAY(env.tm_vsr, PowerPCCPU, 64),
@@ -302,6 +306,7 @@ static const VMStateDescription vmstate_sr = {
.name = "cpu/sr",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = sr_needed,
.fields = (VMStateField[]) {
VMSTATE_UINTTL_ARRAY(env.sr, PowerPCCPU, 32),
VMSTATE_END_OF_LIST()
@@ -351,6 +356,7 @@ static const VMStateDescription vmstate_slb = {
.name = "cpu/slb",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = slb_needed,
.fields = (VMStateField[]) {
VMSTATE_INT32_EQUAL(env.slb_nr, PowerPCCPU),
VMSTATE_SLB_ARRAY(env.slb, PowerPCCPU, MAX_SLB_ENTRIES),
@@ -383,6 +389,7 @@ static const VMStateDescription vmstate_tlb6xx = {
.name = "cpu/tlb6xx",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = tlb6xx_needed,
.fields = (VMStateField[]) {
VMSTATE_INT32_EQUAL(env.nb_tlb, PowerPCCPU),
VMSTATE_STRUCT_VARRAY_POINTER_INT32(env.tlb.tlb6, PowerPCCPU,
@@ -429,6 +436,7 @@ static const VMStateDescription vmstate_pbr403 = {
.name = "cpu/pbr403",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = pbr403_needed,
.fields = (VMStateField[]) {
VMSTATE_UINTTL_ARRAY(env.pb, PowerPCCPU, 4),
VMSTATE_END_OF_LIST()
@@ -439,6 +447,7 @@ static const VMStateDescription vmstate_tlbemb = {
.name = "cpu/tlb6xx",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = tlbemb_needed,
.fields = (VMStateField[]) {
VMSTATE_INT32_EQUAL(env.nb_tlb, PowerPCCPU),
VMSTATE_STRUCT_VARRAY_POINTER_INT32(env.tlb.tlbe, PowerPCCPU,
@@ -448,13 +457,9 @@ static const VMStateDescription vmstate_tlbemb = {
/* 403 protection registers */
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection []) {
- {
- .vmsd = &vmstate_pbr403,
- .needed = pbr403_needed,
- } , {
- /* empty */
- }
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_pbr403,
+ NULL
}
};
@@ -483,6 +488,7 @@ static const VMStateDescription vmstate_tlbmas = {
.name = "cpu/tlbmas",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = tlbmas_needed,
.fields = (VMStateField[]) {
VMSTATE_INT32_EQUAL(env.nb_tlb, PowerPCCPU),
VMSTATE_STRUCT_VARRAY_POINTER_INT32(env.tlb.tlbm, PowerPCCPU,
@@ -533,38 +539,18 @@ const VMStateDescription vmstate_ppc_cpu = {
VMSTATE_UINT32_EQUAL(env.nb_BATs, PowerPCCPU),
VMSTATE_END_OF_LIST()
},
- .subsections = (VMStateSubsection []) {
- {
- .vmsd = &vmstate_fpu,
- .needed = fpu_needed,
- } , {
- .vmsd = &vmstate_altivec,
- .needed = altivec_needed,
- } , {
- .vmsd = &vmstate_vsx,
- .needed = vsx_needed,
- } , {
- .vmsd = &vmstate_sr,
- .needed = sr_needed,
- } , {
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_fpu,
+ &vmstate_altivec,
+ &vmstate_vsx,
+ &vmstate_sr,
#ifdef TARGET_PPC64
- .vmsd = &vmstate_tm,
- .needed = tm_needed,
- } , {
- .vmsd = &vmstate_slb,
- .needed = slb_needed,
- } , {
+ &vmstate_tm,
+ &vmstate_slb,
#endif /* TARGET_PPC64 */
- .vmsd = &vmstate_tlb6xx,
- .needed = tlb6xx_needed,
- } , {
- .vmsd = &vmstate_tlbemb,
- .needed = tlbemb_needed,
- } , {
- .vmsd = &vmstate_tlbmas,
- .needed = tlbmas_needed,
- } , {
- /* empty */
- }
+ &vmstate_tlb6xx,
+ &vmstate_tlbemb,
+ &vmstate_tlbmas,
+ NULL
}
};
diff --git a/target-s390x/cc_helper.c b/target-s390x/cc_helper.c
index 00bc883a8a..bfce3f1e60 100644
--- a/target-s390x/cc_helper.c
+++ b/target-s390x/cc_helper.c
@@ -195,7 +195,7 @@ static uint32_t cc_calc_abs_64(int64_t dst)
if ((uint64_t)dst == 0x8000000000000000ULL) {
return 3;
} else if (dst) {
- return 1;
+ return 2;
} else {
return 0;
}
@@ -296,7 +296,7 @@ static uint32_t cc_calc_abs_32(int32_t dst)
if ((uint32_t)dst == 0x80000000UL) {
return 3;
} else if (dst) {
- return 1;
+ return 2;
} else {
return 0;
}
diff --git a/target-s390x/cpu-qom.h b/target-s390x/cpu-qom.h
index 936ae21e06..491c1b8769 100644
--- a/target-s390x/cpu-qom.h
+++ b/target-s390x/cpu-qom.h
@@ -98,5 +98,6 @@ hwaddr s390_cpu_get_phys_addr_debug(CPUState *cpu, vaddr addr);
int s390_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
int s390_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
void s390_cpu_gdb_init(CPUState *cs);
+void s390x_cpu_debug_excp_handler(CPUState *cs);
#endif
diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c
index d2f9836e86..69bac35349 100644
--- a/target-s390x/cpu.c
+++ b/target-s390x/cpu.c
@@ -106,6 +106,7 @@ static void s390_cpu_initial_reset(CPUState *s)
{
S390CPU *cpu = S390_CPU(s);
CPUS390XState *env = &cpu->env;
+ int i;
s390_cpu_reset(s);
/* initial reset does not touch regs,fregs and aregs */
@@ -116,12 +117,24 @@ static void s390_cpu_initial_reset(CPUState *s)
env->cregs[0] = CR0_RESET;
env->cregs[14] = CR14_RESET;
+ /* architectured initial value for Breaking-Event-Address register */
+ env->gbea = 1;
+
env->pfault_token = -1UL;
+ env->ext_index = -1;
+ for (i = 0; i < ARRAY_SIZE(env->io_index); i++) {
+ env->io_index[i] = -1;
+ }
+
+ /* tininess for underflow is detected before rounding */
+ set_float_detect_tininess(float_tininess_before_rounding,
+ &env->fpu_status);
/* Reset state inside the kernel that we cannot access yet from QEMU. */
if (kvm_enabled()) {
kvm_s390_reset_vcpu(cpu);
}
+ tlb_flush(s, 1);
}
/* CPUClass:reset() */
@@ -130,6 +143,7 @@ static void s390_cpu_full_reset(CPUState *s)
S390CPU *cpu = S390_CPU(s);
S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
CPUS390XState *env = &cpu->env;
+ int i;
scc->parent_reset(s);
cpu->env.sigp_order = 0;
@@ -141,7 +155,18 @@ static void s390_cpu_full_reset(CPUState *s)
env->cregs[0] = CR0_RESET;
env->cregs[14] = CR14_RESET;
+ /* architectured initial value for Breaking-Event-Address register */
+ env->gbea = 1;
+
env->pfault_token = -1UL;
+ env->ext_index = -1;
+ for (i = 0; i < ARRAY_SIZE(env->io_index); i++) {
+ env->io_index[i] = -1;
+ }
+
+ /* tininess for underflow is detected before rounding */
+ set_float_detect_tininess(float_tininess_before_rounding,
+ &env->fpu_status);
/* Reset state inside the kernel that we cannot access yet from QEMU. */
if (kvm_enabled()) {
@@ -199,7 +224,6 @@ static void s390_cpu_initfn(Object *obj)
s390_cpu_set_state(CPU_STATE_STOPPED, cpu);
#endif
env->cpu_num = cpu_num++;
- env->ext_index = -1;
if (tcg_enabled() && !inited) {
inited = true;
@@ -325,6 +349,7 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data)
cc->write_elf64_note = s390_cpu_write_elf64_note;
cc->write_elf64_qemunote = s390_cpu_write_elf64_qemunote;
cc->cpu_exec_interrupt = s390_cpu_exec_interrupt;
+ cc->debug_excp_handler = s390x_cpu_debug_excp_handler;
#endif
cc->gdb_num_core_regs = S390_NUM_CORE_REGS;
cc->gdb_core_xml_file = "s390x-core64.xml";
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index a71abaeef7..7b87c7dcfb 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -48,7 +48,7 @@
#define MMU_MODE1_SUFFIX _secondary
#define MMU_MODE2_SUFFIX _home
-#define MMU_USER_IDX 1
+#define MMU_USER_IDX 0
#define MAX_EXT_QUEUE 16
#define MAX_IO_QUEUE 16
@@ -111,6 +111,9 @@ typedef struct CPUS390XState {
uint32_t int_svc_code;
uint32_t int_svc_ilen;
+ uint64_t per_address;
+ uint16_t per_perc_atmid;
+
uint64_t cregs[16]; /* control registers */
ExtQueue ext_queue[MAX_EXT_QUEUE];
@@ -302,13 +305,39 @@ static inline CPU_DoubleU *get_freg(CPUS390XState *cs, int nr)
#define CR0_LOWPROT 0x0000000010000000ULL
#define CR0_EDAT 0x0000000000800000ULL
+/* MMU */
+#define MMU_PRIMARY_IDX 0
+#define MMU_SECONDARY_IDX 1
+#define MMU_HOME_IDX 2
+
static inline int cpu_mmu_index (CPUS390XState *env)
{
- if (env->psw.mask & PSW_MASK_PSTATE) {
- return 1;
+ switch (env->psw.mask & PSW_MASK_ASC) {
+ case PSW_ASC_PRIMARY:
+ return MMU_PRIMARY_IDX;
+ case PSW_ASC_SECONDARY:
+ return MMU_SECONDARY_IDX;
+ case PSW_ASC_HOME:
+ return MMU_HOME_IDX;
+ case PSW_ASC_ACCREG:
+ /* Fallthrough: access register mode is not yet supported */
+ default:
+ abort();
}
+}
- return 0;
+static inline uint64_t cpu_mmu_idx_to_asc(int mmu_idx)
+{
+ switch (mmu_idx) {
+ case MMU_PRIMARY_IDX:
+ return PSW_ASC_PRIMARY;
+ case MMU_SECONDARY_IDX:
+ return PSW_ASC_SECONDARY;
+ case MMU_HOME_IDX:
+ return PSW_ASC_HOME;
+ default:
+ abort();
+ }
}
static inline void cpu_get_tb_cpu_state(CPUS390XState* env, target_ulong *pc,
@@ -338,6 +367,45 @@ static inline int get_ilen(uint8_t opc)
}
}
+/* PER bits from control register 9 */
+#define PER_CR9_EVENT_BRANCH 0x80000000
+#define PER_CR9_EVENT_IFETCH 0x40000000
+#define PER_CR9_EVENT_STORE 0x20000000
+#define PER_CR9_EVENT_STORE_REAL 0x08000000
+#define PER_CR9_EVENT_NULLIFICATION 0x01000000
+#define PER_CR9_CONTROL_BRANCH_ADDRESS 0x00800000
+#define PER_CR9_CONTROL_ALTERATION 0x00200000
+
+/* PER bits from the PER CODE/ATMID/AI in lowcore */
+#define PER_CODE_EVENT_BRANCH 0x8000
+#define PER_CODE_EVENT_IFETCH 0x4000
+#define PER_CODE_EVENT_STORE 0x2000
+#define PER_CODE_EVENT_STORE_REAL 0x0800
+#define PER_CODE_EVENT_NULLIFICATION 0x0100
+
+/* Compute the ATMID field that is stored in the per_perc_atmid lowcore
+ entry when a PER exception is triggered. */
+static inline uint8_t get_per_atmid(CPUS390XState *env)
+{
+ return ((env->psw.mask & PSW_MASK_64) ? (1 << 7) : 0) |
+ ( (1 << 6) ) |
+ ((env->psw.mask & PSW_MASK_32) ? (1 << 5) : 0) |
+ ((env->psw.mask & PSW_MASK_DAT)? (1 << 4) : 0) |
+ ((env->psw.mask & PSW_ASC_SECONDARY)? (1 << 3) : 0) |
+ ((env->psw.mask & PSW_ASC_ACCREG)? (1 << 2) : 0);
+}
+
+/* Check if an address is within the PER starting address and the PER
+ ending address. The address range might loop. */
+static inline bool get_per_in_range(CPUS390XState *env, uint64_t addr)
+{
+ if (env->cregs[10] <= env->cregs[11]) {
+ return env->cregs[10] <= addr && addr <= env->cregs[11];
+ } else {
+ return env->cregs[10] <= addr || addr <= env->cregs[11];
+ }
+}
+
#ifndef CONFIG_USER_ONLY
/* In several cases of runtime exceptions, we havn't recorded the true
instruction length. Use these codes when raising exceptions in order
@@ -683,6 +751,7 @@ static inline void setcc(S390CPU *cpu, uint64_t cc)
env->psw.mask &= ~(3ull << 44);
env->psw.mask |= (cc & 3) << 44;
+ env->cc_op = cc;
}
typedef struct LowCore
@@ -720,14 +789,16 @@ typedef struct LowCore
uint8_t pad5[0xf4-0xf0]; /* 0x0f0 */
uint32_t external_damage_code; /* 0x0f4 */
uint64_t failing_storage_address; /* 0x0f8 */
- uint8_t pad6[0x120-0x100]; /* 0x100 */
+ uint8_t pad6[0x110-0x100]; /* 0x100 */
+ uint64_t per_breaking_event_addr; /* 0x110 */
+ uint8_t pad7[0x120-0x118]; /* 0x118 */
PSW restart_old_psw; /* 0x120 */
PSW external_old_psw; /* 0x130 */
PSW svc_old_psw; /* 0x140 */
PSW program_old_psw; /* 0x150 */
PSW mcck_old_psw; /* 0x160 */
PSW io_old_psw; /* 0x170 */
- uint8_t pad7[0x1a0-0x180]; /* 0x180 */
+ uint8_t pad8[0x1a0-0x180]; /* 0x180 */
PSW restart_new_psw; /* 0x1a0 */
PSW external_new_psw; /* 0x1b0 */
PSW svc_new_psw; /* 0x1c0 */
@@ -745,10 +816,10 @@ typedef struct LowCore
uint64_t last_update_clock; /* 0x280 */
uint64_t steal_clock; /* 0x288 */
PSW return_mcck_psw; /* 0x290 */
- uint8_t pad8[0xc00-0x2a0]; /* 0x2a0 */
+ uint8_t pad9[0xc00-0x2a0]; /* 0x2a0 */
/* System info area */
uint64_t save_area[16]; /* 0xc00 */
- uint8_t pad9[0xd40-0xc80]; /* 0xc80 */
+ uint8_t pad10[0xd40-0xc80]; /* 0xc80 */
uint64_t kernel_stack; /* 0xd40 */
uint64_t thread_info; /* 0xd48 */
uint64_t async_stack; /* 0xd50 */
@@ -756,7 +827,7 @@ typedef struct LowCore
uint64_t user_asce; /* 0xd60 */
uint64_t panic_stack; /* 0xd68 */
uint64_t user_exec_asce; /* 0xd70 */
- uint8_t pad10[0xdc0-0xd78]; /* 0xd78 */
+ uint8_t pad11[0xdc0-0xd78]; /* 0xd78 */
/* SMP info area: defined by DJB */
uint64_t clock_comparator; /* 0xdc0 */
@@ -976,6 +1047,7 @@ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code);
uint32_t calc_cc(CPUS390XState *env, uint32_t cc_op, uint64_t src, uint64_t dst,
uint64_t vr);
+void s390_cpu_recompute_watchpoints(CPUState *cs);
int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, uint8_t ar, void *hostbuf,
int len, bool is_write);
@@ -995,6 +1067,11 @@ static inline uint64_t time2tod(uint64_t ns) {
return (ns << 9) / 125;
}
+/* Converts s390's clock format to ns */
+static inline uint64_t tod2time(uint64_t t) {
+ return (t * 125) >> 9;
+}
+
static inline void cpu_inject_ext(S390CPU *cpu, uint32_t code, uint32_t param,
uint64_t param64)
{
@@ -1069,6 +1146,7 @@ uint32_t set_cc_nz_f128(float128 v);
/* misc_helper.c */
#ifndef CONFIG_USER_ONLY
+int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3);
void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3);
#endif
void program_interrupt(CPUS390XState *env, uint32_t code, int ilen);
@@ -1183,11 +1261,7 @@ static inline int s390_assign_subch_ioeventfd(EventNotifier *notifier,
uint32_t sch_id, int vq,
bool assign)
{
- if (kvm_enabled()) {
- return kvm_s390_assign_subch_ioeventfd(notifier, sch_id, vq, assign);
- } else {
- return -ENOSYS;
- }
+ return kvm_s390_assign_subch_ioeventfd(notifier, sch_id, vq, assign);
}
#ifdef CONFIG_KVM
diff --git a/target-s390x/fpu_helper.c b/target-s390x/fpu_helper.c
index b946ec1d51..45b7ddfbe3 100644
--- a/target-s390x/fpu_helper.c
+++ b/target-s390x/fpu_helper.c
@@ -265,7 +265,7 @@ uint64_t HELPER(ldeb)(CPUS390XState *env, uint64_t f2)
{
float64 ret = float32_to_float64(f2, &env->fpu_status);
handle_exceptions(env, GETPC());
- return ret;
+ return float64_maybe_silence_nan(ret);
}
/* convert 128-bit float to 64-bit float */
@@ -273,7 +273,7 @@ uint64_t HELPER(ldxb)(CPUS390XState *env, uint64_t ah, uint64_t al)
{
float64 ret = float128_to_float64(make_float128(ah, al), &env->fpu_status);
handle_exceptions(env, GETPC());
- return ret;
+ return float64_maybe_silence_nan(ret);
}
/* convert 64-bit float to 128-bit float */
@@ -281,7 +281,7 @@ uint64_t HELPER(lxdb)(CPUS390XState *env, uint64_t f2)
{
float128 ret = float64_to_float128(f2, &env->fpu_status);
handle_exceptions(env, GETPC());
- return RET128(ret);
+ return RET128(float128_maybe_silence_nan(ret));
}
/* convert 32-bit float to 128-bit float */
@@ -289,7 +289,7 @@ uint64_t HELPER(lxeb)(CPUS390XState *env, uint64_t f2)
{
float128 ret = float32_to_float128(f2, &env->fpu_status);
handle_exceptions(env, GETPC());
- return RET128(ret);
+ return RET128(float128_maybe_silence_nan(ret));
}
/* convert 64-bit float to 32-bit float */
@@ -297,7 +297,7 @@ uint64_t HELPER(ledb)(CPUS390XState *env, uint64_t f2)
{
float32 ret = float64_to_float32(f2, &env->fpu_status);
handle_exceptions(env, GETPC());
- return ret;
+ return float32_maybe_silence_nan(ret);
}
/* convert 128-bit float to 32-bit float */
@@ -305,7 +305,7 @@ uint64_t HELPER(lexb)(CPUS390XState *env, uint64_t ah, uint64_t al)
{
float32 ret = float128_to_float32(make_float128(ah, al), &env->fpu_status);
handle_exceptions(env, GETPC());
- return ret;
+ return float32_maybe_silence_nan(ret);
}
/* 32-bit FP compare */
@@ -552,6 +552,37 @@ uint64_t HELPER(clfxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m3)
return ret;
}
+/* round to integer 32-bit */
+uint64_t HELPER(fieb)(CPUS390XState *env, uint64_t f2, uint32_t m3)
+{
+ int hold = swap_round_mode(env, m3);
+ float32 ret = float32_round_to_int(f2, &env->fpu_status);
+ set_float_rounding_mode(hold, &env->fpu_status);
+ handle_exceptions(env, GETPC());
+ return ret;
+}
+
+/* round to integer 64-bit */
+uint64_t HELPER(fidb)(CPUS390XState *env, uint64_t f2, uint32_t m3)
+{
+ int hold = swap_round_mode(env, m3);
+ float64 ret = float64_round_to_int(f2, &env->fpu_status);
+ set_float_rounding_mode(hold, &env->fpu_status);
+ handle_exceptions(env, GETPC());
+ return ret;
+}
+
+/* round to integer 128-bit */
+uint64_t HELPER(fixb)(CPUS390XState *env, uint64_t ah, uint64_t al, uint32_t m3)
+{
+ int hold = swap_round_mode(env, m3);
+ float128 ret = float128_round_to_int(make_float128(ah, al),
+ &env->fpu_status);
+ set_float_rounding_mode(hold, &env->fpu_status);
+ handle_exceptions(env, GETPC());
+ return RET128(ret);
+}
+
/* 32-bit FP multiply and add */
uint64_t HELPER(maeb)(CPUS390XState *env, uint64_t f1,
uint64_t f2, uint64_t f3)
diff --git a/target-s390x/helper.c b/target-s390x/helper.c
index 6b47766494..d88700695e 100644
--- a/target-s390x/helper.c
+++ b/target-s390x/helper.c
@@ -112,7 +112,7 @@ int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr,
{
S390CPU *cpu = S390_CPU(cs);
CPUS390XState *env = &cpu->env;
- uint64_t asc = env->psw.mask & PSW_MASK_ASC;
+ uint64_t asc = cpu_mmu_idx_to_asc(mmu_idx);
target_ulong vaddr, raddr;
int prot;
@@ -181,12 +181,18 @@ hwaddr s390_cpu_get_phys_addr_debug(CPUState *cs, vaddr vaddr)
void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr)
{
+ uint64_t old_mask = env->psw.mask;
+
env->psw.addr = addr;
env->psw.mask = mask;
if (tcg_enabled()) {
env->cc_op = (mask >> 44) & 3;
}
+ if ((old_mask ^ mask) & PSW_MASK_PER) {
+ s390_cpu_recompute_watchpoints(CPU(s390_env_get_cpu(env)));
+ }
+
if (mask & PSW_MASK_WAIT) {
S390CPU *cpu = s390_env_get_cpu(env);
if (s390_cpu_halt(cpu) == 0) {
@@ -250,25 +256,6 @@ void do_restart_interrupt(CPUS390XState *env)
load_psw(env, mask, addr);
}
-static void do_svc_interrupt(CPUS390XState *env)
-{
- uint64_t mask, addr;
- LowCore *lowcore;
-
- lowcore = cpu_map_lowcore(env);
-
- lowcore->svc_code = cpu_to_be16(env->int_svc_code);
- lowcore->svc_ilen = cpu_to_be16(env->int_svc_ilen);
- lowcore->svc_old_psw.mask = cpu_to_be64(get_psw_mask(env));
- lowcore->svc_old_psw.addr = cpu_to_be64(env->psw.addr + env->int_svc_ilen);
- mask = be64_to_cpu(lowcore->svc_new_psw.mask);
- addr = be64_to_cpu(lowcore->svc_new_psw.addr);
-
- cpu_unmap_lowcore(lowcore);
-
- load_psw(env, mask, addr);
-}
-
static void do_program_interrupt(CPUS390XState *env)
{
uint64_t mask, addr;
@@ -292,12 +279,21 @@ static void do_program_interrupt(CPUS390XState *env)
lowcore = cpu_map_lowcore(env);
+ /* Signal PER events with the exception. */
+ if (env->per_perc_atmid) {
+ env->int_pgm_code |= PGM_PER;
+ lowcore->per_address = cpu_to_be64(env->per_address);
+ lowcore->per_perc_atmid = cpu_to_be16(env->per_perc_atmid);
+ env->per_perc_atmid = 0;
+ }
+
lowcore->pgm_ilen = cpu_to_be16(ilen);
lowcore->pgm_code = cpu_to_be16(env->int_pgm_code);
lowcore->program_old_psw.mask = cpu_to_be64(get_psw_mask(env));
lowcore->program_old_psw.addr = cpu_to_be64(env->psw.addr);
mask = be64_to_cpu(lowcore->program_new_psw.mask);
addr = be64_to_cpu(lowcore->program_new_psw.addr);
+ lowcore->per_breaking_event_addr = cpu_to_be64(env->gbea);
cpu_unmap_lowcore(lowcore);
@@ -308,6 +304,33 @@ static void do_program_interrupt(CPUS390XState *env)
load_psw(env, mask, addr);
}
+static void do_svc_interrupt(CPUS390XState *env)
+{
+ uint64_t mask, addr;
+ LowCore *lowcore;
+
+ lowcore = cpu_map_lowcore(env);
+
+ lowcore->svc_code = cpu_to_be16(env->int_svc_code);
+ lowcore->svc_ilen = cpu_to_be16(env->int_svc_ilen);
+ lowcore->svc_old_psw.mask = cpu_to_be64(get_psw_mask(env));
+ lowcore->svc_old_psw.addr = cpu_to_be64(env->psw.addr + env->int_svc_ilen);
+ mask = be64_to_cpu(lowcore->svc_new_psw.mask);
+ addr = be64_to_cpu(lowcore->svc_new_psw.addr);
+
+ cpu_unmap_lowcore(lowcore);
+
+ load_psw(env, mask, addr);
+
+ /* When a PER event is pending, the PER exception has to happen
+ immediately after the SERVICE CALL one. */
+ if (env->per_perc_atmid) {
+ env->int_pgm_code = PGM_PER;
+ env->int_pgm_ilen = env->int_svc_ilen;
+ do_program_interrupt(env);
+ }
+}
+
#define VIRTIO_SUBCODE_64 0x0D00
static void do_ext_interrupt(CPUS390XState *env)
@@ -557,4 +580,73 @@ bool s390_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
}
return false;
}
+
+void s390_cpu_recompute_watchpoints(CPUState *cs)
+{
+ const int wp_flags = BP_CPU | BP_MEM_WRITE | BP_STOP_BEFORE_ACCESS;
+ S390CPU *cpu = S390_CPU(cs);
+ CPUS390XState *env = &cpu->env;
+
+ /* We are called when the watchpoints have changed. First
+ remove them all. */
+ cpu_watchpoint_remove_all(cs, BP_CPU);
+
+ /* Return if PER is not enabled */
+ if (!(env->psw.mask & PSW_MASK_PER)) {
+ return;
+ }
+
+ /* Return if storage-alteration event is not enabled. */
+ if (!(env->cregs[9] & PER_CR9_EVENT_STORE)) {
+ return;
+ }
+
+ if (env->cregs[10] == 0 && env->cregs[11] == -1LL) {
+ /* We can't create a watchoint spanning the whole memory range, so
+ split it in two parts. */
+ cpu_watchpoint_insert(cs, 0, 1ULL << 63, wp_flags, NULL);
+ cpu_watchpoint_insert(cs, 1ULL << 63, 1ULL << 63, wp_flags, NULL);
+ } else if (env->cregs[10] > env->cregs[11]) {
+ /* The address range loops, create two watchpoints. */
+ cpu_watchpoint_insert(cs, env->cregs[10], -env->cregs[10],
+ wp_flags, NULL);
+ cpu_watchpoint_insert(cs, 0, env->cregs[11] + 1, wp_flags, NULL);
+
+ } else {
+ /* Default case, create a single watchpoint. */
+ cpu_watchpoint_insert(cs, env->cregs[10],
+ env->cregs[11] - env->cregs[10] + 1,
+ wp_flags, NULL);
+ }
+}
+
+void s390x_cpu_debug_excp_handler(CPUState *cs)
+{
+ S390CPU *cpu = S390_CPU(cs);
+ CPUS390XState *env = &cpu->env;
+ CPUWatchpoint *wp_hit = cs->watchpoint_hit;
+
+ if (wp_hit && wp_hit->flags & BP_CPU) {
+ /* FIXME: When the storage-alteration-space control bit is set,
+ the exception should only be triggered if the memory access
+ is done using an address space with the storage-alteration-event
+ bit set. We have no way to detect that with the current
+ watchpoint code. */
+ cs->watchpoint_hit = NULL;
+
+ env->per_address = env->psw.addr;
+ env->per_perc_atmid |= PER_CODE_EVENT_STORE | get_per_atmid(env);
+ /* FIXME: We currently no way to detect the address space used
+ to trigger the watchpoint. For now just consider it is the
+ current default ASC. This turn to be true except when MVCP
+ and MVCS instrutions are not used. */
+ env->per_perc_atmid |= env->psw.mask & (PSW_MASK_ASC) >> 46;
+
+ /* Remove all watchpoints to re-execute the code. A PER exception
+ will be triggered, it will call load_psw which will recompute
+ the watchpoints. */
+ cpu_watchpoint_remove_all(cs, BP_CPU);
+ cpu_resume_from_signal(cs, NULL);
+ }
+}
#endif /* CONFIG_USER_ONLY */
diff --git a/target-s390x/helper.h b/target-s390x/helper.h
index 8d2c8596bb..7e06119e99 100644
--- a/target-s390x/helper.h
+++ b/target-s390x/helper.h
@@ -15,10 +15,6 @@ DEF_HELPER_4(clst, i64, env, i64, i64, i64)
DEF_HELPER_4(mvpg, void, env, i64, i64, i64)
DEF_HELPER_4(mvst, i64, env, i64, i64, i64)
DEF_HELPER_5(ex, i32, env, i32, i64, i64, i64)
-DEF_HELPER_FLAGS_1(abs_i32, TCG_CALL_NO_RWG_SE, i32, s32)
-DEF_HELPER_FLAGS_1(nabs_i32, TCG_CALL_NO_RWG_SE, s32, s32)
-DEF_HELPER_FLAGS_1(abs_i64, TCG_CALL_NO_RWG_SE, i64, s64)
-DEF_HELPER_FLAGS_1(nabs_i64, TCG_CALL_NO_RWG_SE, s64, s64)
DEF_HELPER_FLAGS_4(stam, TCG_CALL_NO_WG, void, env, i32, i64, i32)
DEF_HELPER_FLAGS_4(lam, TCG_CALL_NO_WG, void, env, i32, i64, i32)
DEF_HELPER_4(mvcle, i32, env, i32, i64, i32)
@@ -64,6 +60,9 @@ DEF_HELPER_FLAGS_4(clgxb, TCG_CALL_NO_WG, i64, env, i64, i64, i32)
DEF_HELPER_FLAGS_3(clfeb, TCG_CALL_NO_WG, i64, env, i64, i32)
DEF_HELPER_FLAGS_3(clfdb, TCG_CALL_NO_WG, i64, env, i64, i32)
DEF_HELPER_FLAGS_4(clfxb, TCG_CALL_NO_WG, i64, env, i64, i64, i32)
+DEF_HELPER_FLAGS_3(fieb, TCG_CALL_NO_WG, i64, env, i64, i32)
+DEF_HELPER_FLAGS_3(fidb, TCG_CALL_NO_WG, i64, env, i64, i32)
+DEF_HELPER_FLAGS_4(fixb, TCG_CALL_NO_WG, i64, env, i64, i64, i32)
DEF_HELPER_FLAGS_4(maeb, TCG_CALL_NO_WG, i64, env, i64, i64, i64)
DEF_HELPER_FLAGS_4(madb, TCG_CALL_NO_WG, i64, env, i64, i64, i64)
DEF_HELPER_FLAGS_4(mseb, TCG_CALL_NO_WG, i64, env, i64, i64, i64)
@@ -78,6 +77,8 @@ DEF_HELPER_FLAGS_3(sqxb, TCG_CALL_NO_WG, i64, env, i64, i64)
DEF_HELPER_FLAGS_1(cvd, TCG_CALL_NO_RWG_SE, i64, s32)
DEF_HELPER_FLAGS_4(unpk, TCG_CALL_NO_WG, void, env, i32, i64, i64)
DEF_HELPER_FLAGS_4(tr, TCG_CALL_NO_WG, void, env, i32, i64, i64)
+DEF_HELPER_4(tre, i64, env, i64, i64, i64)
+DEF_HELPER_4(trt, i32, env, i32, i64, i64)
DEF_HELPER_4(cksm, i64, env, i64, i64, i64)
DEF_HELPER_FLAGS_5(calc_cc, TCG_CALL_NO_RWG_SE, i32, env, i32, i64, i64, i64)
DEF_HELPER_FLAGS_2(sfpc, TCG_CALL_NO_RWG, void, env, i64)
@@ -86,7 +87,7 @@ DEF_HELPER_FLAGS_1(popcnt, TCG_CALL_NO_RWG_SE, i64, i64)
#ifndef CONFIG_USER_ONLY
DEF_HELPER_3(servc, i32, env, i64, i64)
-DEF_HELPER_4(diag, i64, env, i32, i64, i64)
+DEF_HELPER_4(diag, void, env, i32, i32, i32)
DEF_HELPER_3(load_psw, noreturn, env, i64, i64)
DEF_HELPER_FLAGS_2(spx, TCG_CALL_NO_RWG, void, env, i64)
DEF_HELPER_FLAGS_1(stck, TCG_CALL_NO_RWG_SE, i64, env)
@@ -115,4 +116,18 @@ DEF_HELPER_FLAGS_2(lura, TCG_CALL_NO_WG, i64, env, i64)
DEF_HELPER_FLAGS_2(lurag, TCG_CALL_NO_WG, i64, env, i64)
DEF_HELPER_FLAGS_3(stura, TCG_CALL_NO_WG, void, env, i64, i64)
DEF_HELPER_FLAGS_3(sturg, TCG_CALL_NO_WG, void, env, i64, i64)
+DEF_HELPER_1(per_check_exception, void, env)
+DEF_HELPER_FLAGS_3(per_branch, TCG_CALL_NO_RWG, void, env, i64, i64)
+DEF_HELPER_FLAGS_2(per_ifetch, TCG_CALL_NO_RWG, void, env, i64)
+
+DEF_HELPER_2(xsch, void, env, i64)
+DEF_HELPER_2(csch, void, env, i64)
+DEF_HELPER_2(hsch, void, env, i64)
+DEF_HELPER_3(msch, void, env, i64, i64)
+DEF_HELPER_2(rchp, void, env, i64)
+DEF_HELPER_2(rsch, void, env, i64)
+DEF_HELPER_3(ssch, void, env, i64, i64)
+DEF_HELPER_3(stsch, void, env, i64, i64)
+DEF_HELPER_3(tsch, void, env, i64, i64)
+DEF_HELPER_2(chsc, void, env, i64)
#endif
diff --git a/target-s390x/insn-data.def b/target-s390x/insn-data.def
index 72c3a2edda..075ff597c3 100644
--- a/target-s390x/insn-data.def
+++ b/target-s390x/insn-data.def
@@ -34,6 +34,9 @@
C(0xb34a, AXBR, RRE, Z, 0, x2_o, x1, 0, axb, f128)
C(0xed0a, AEB, RXE, Z, e1, m2_32u, new, e1, aeb, f32)
C(0xed1a, ADB, RXE, Z, f1_o, m2_64, f1, 0, adb, f64)
+/* ADD HIGH */
+ C(0xb9c8, AHHHR, RRF_a, HW, r2_sr32, r3_sr32, new, r1_32h, add, adds32)
+ C(0xb9d8, AHHLR, RRF_a, HW, r2_sr32, r3, new, r1_32h, add, adds32)
/* ADD IMMEDIATE */
C(0xc209, AFI, RIL_a, EI, r1, i2, new, r1_32, add, adds32)
C(0xeb6a, ASI, SIY, GIE, m1_32s, i2, new, m1_32, add, adds32)
@@ -41,6 +44,8 @@
C(0xc208, AGFI, RIL_a, EI, r1, i2, r1, 0, add, adds64)
C(0xeb7a, AGSI, SIY, GIE, m1_64, i2, new, m1_64, add, adds64)
C(0xecd9, AGHIK, RIE_d, DO, r3, i2, r1, 0, add, adds64)
+/* ADD IMMEDIATE HIGH */
+ C(0xcc08, AIH, RIL_a, HW, r1_sr32, i2, new, r1_32h, add, adds32)
/* ADD HALFWORD */
C(0x4a00, AH, RX_a, Z, r1, m2_16s, new, r1_32, add, adds32)
C(0xe37a, AHY, RXY_a, LD, r1, m2_16s, new, r1_32, add, adds32)
@@ -58,6 +63,9 @@
C(0xb9ea, ALGRK, RRF_a, DO, r2, r3, r1, 0, add, addu64)
C(0xe30a, ALG, RXY_a, Z, r1, m2_64, r1, 0, add, addu64)
C(0xe31a, ALGF, RXY_a, Z, r1, m2_32u, r1, 0, add, addu64)
+/* ADD LOGICAL HIGH */
+ C(0xb9ca, ALHHHR, RRF_a, HW, r2_sr32, r3_sr32, new, r1_32h, add, addu32)
+ C(0xb9da, ALHHLR, RRF_a, HW, r2_sr32, r3, new, r1_32h, add, addu32)
/* ADD LOGICAL IMMEDIATE */
C(0xc20b, ALFI, RIL_a, EI, r1, i2_32u, new, r1_32, add, addu32)
C(0xc20a, ALGFI, RIL_a, EI, r1, i2_32u, r1, 0, add, addu64)
@@ -66,6 +74,9 @@
C(0xecda, ALHSIK, RIE_d, DO, r3, i2, new, r1_32, add, addu32)
C(0xeb7e, ALGSI, SIY, GIE, m1_64, i2, new, m1_64, add, addu64)
C(0xecdb, ALGHSIK, RIE_d, DO, r3, i2, r1, 0, add, addu64)
+/* ADD LOGICAL WITH SIGNED IMMEDIATE HIGH */
+ C(0xcc0a, ALSIH, RIL_a, HW, r1_sr32, i2, new, r1_32h, add, addu32)
+ C(0xcc0b, ALSIHN, RIL_a, HW, r1_sr32, i2, new, r1_32h, add, 0)
/* ADD LOGICAL WITH CARRY */
C(0xb998, ALCR, RRE, Z, r1, r2, new, r1_32, addc, addc32)
C(0xb988, ALCGR, RRE, Z, r1, r2, r1, 0, addc, addc64)
@@ -111,6 +122,8 @@
/* BRANCH RELATIVE ON COUNT */
C(0xa706, BRCT, RI_b, Z, 0, 0, 0, 0, bct32, 0)
C(0xa707, BRCTG, RI_b, Z, 0, 0, 0, 0, bct64, 0)
+/* BRANCH RELATIVE ON COUNT HIGH */
+ C(0xcc06, BRCTH, RIL_b, HW, 0, 0, 0, 0, bcth, 0)
/* BRANCH ON INDEX */
D(0x8600, BXH, RS_a, Z, 0, a2, 0, 0, bx32, 0, 0)
D(0x8700, BXLE, RS_a, Z, 0, a2, 0, 0, bx32, 0, 1)
@@ -159,8 +172,14 @@
C(0xe55c, CHSI, SIL, GIE, m1_32s, i2, 0, 0, 0, cmps64)
C(0xe558, CGHSI, SIL, GIE, m1_64, i2, 0, 0, 0, cmps64)
/* COMPARE HALFWORD RELATIVE LONG */
- C(0xc605, CHRL, RIL_a, GIE, r1_o, mri2_32s, 0, 0, 0, cmps32)
- C(0xc604, CGHRL, RIL_a, GIE, r1_o, mri2_64, 0, 0, 0, cmps64)
+ C(0xc605, CHRL, RIL_b, GIE, r1_o, mri2_32s, 0, 0, 0, cmps32)
+ C(0xc604, CGHRL, RIL_b, GIE, r1_o, mri2_64, 0, 0, 0, cmps64)
+/* COMPARE HIGH */
+ C(0xb9cd, CHHR, RRE, HW, r1_sr32, r2_sr32, 0, 0, 0, cmps32)
+ C(0xb9dd, CHLR, RRE, HW, r1_sr32, r2_o, 0, 0, 0, cmps32)
+ C(0xe3cd, CHF, RXY_a, HW, r1_sr32, m2_32s, 0, 0, 0, cmps32)
+/* COMPARE IMMEDIATE HIGH */
+ C(0xcc0d, CIH, RIL_a, HW, r1_sr32, i2, 0, 0, 0, cmps32)
/* COMPARE LOGICAL */
C(0x1500, CLR, RR_a, Z, r1, r2, 0, 0, 0, cmpu32)
@@ -171,6 +190,10 @@
C(0xe321, CLG, RXY_a, Z, r1, m2_64, 0, 0, 0, cmpu64)
C(0xe331, CLGF, RXY_a, Z, r1, m2_32u, 0, 0, 0, cmpu64)
C(0xd500, CLC, SS_a, Z, la1, a2, 0, 0, clc, 0)
+/* COMPARE LOGICAL HIGH */
+ C(0xb9cf, CLHHR, RRE, HW, r1_sr32, r2_sr32, 0, 0, 0, cmpu32)
+ C(0xb9df, CLHLR, RRE, HW, r1_sr32, r2_o, 0, 0, 0, cmpu32)
+ C(0xe3cf, CLHF, RXY_a, HW, r1_sr32, m2_32s, 0, 0, 0, cmpu32)
/* COMPARE LOGICAL IMMEDIATE */
C(0xc20f, CLFI, RIL_a, EI, r1, i2, 0, 0, 0, cmpu32)
C(0xc20e, CLGFI, RIL_a, EI, r1, i2_32u, 0, 0, 0, cmpu64)
@@ -179,6 +202,8 @@
C(0xe555, CLHHSI, SIL, GIE, m1_16u, i2_16u, 0, 0, 0, cmpu64)
C(0xe55d, CLFHSI, SIL, GIE, m1_32u, i2_16u, 0, 0, 0, cmpu64)
C(0xe559, CLGHSI, SIL, GIE, m1_64, i2_16u, 0, 0, 0, cmpu64)
+/* COMPARE LOGICAL IMMEDIATE HIGH */
+ C(0xcc0f, CLIH, RIL_a, HW, r1_sr32, i2, 0, 0, 0, cmpu32)
/* COMPARE LOGICAL RELATIVE LONG */
C(0xc60f, CLRL, RIL_b, GIE, r1_o, mri2_32u, 0, 0, 0, cmpu32)
C(0xc60a, CLGRL, RIL_b, GIE, r1_o, mri2_64, 0, 0, 0, cmpu64)
@@ -230,8 +255,10 @@
/* COMPARE LOGICAL AND TRAP */
D(0xb973, CLRT, RRF_c, GIE, r1_32u, r2_32u, 0, 0, ct, 0, 1)
D(0xb961, CLGRT, RRF_c, GIE, r1_o, r2_o, 0, 0, ct, 0, 1)
+ D(0xeb23, CLT, RSY_b, MIE, r1_32u, m2_32u, 0, 0, ct, 0, 1)
+ D(0xeb2b, CLGT, RSY_b, MIE, r1_o, m2_64, 0, 0, ct, 0, 1)
D(0xec73, CLFIT, RIE_a, GIE, r1_32u, i2_32u, 0, 0, ct, 0, 1)
- D(0xec71, CLGIT, RIE_a, GIE, r1_o, i2_32u, 0, 0, ct, 0, 0)
+ D(0xec71, CLGIT, RIE_a, GIE, r1_o, i2_32u, 0, 0, ct, 0, 1)
/* CONVERT TO DECIMAL */
C(0x4e00, CVD, RX_a, Z, r1_o, a2, 0, 0, cvd, 0)
@@ -336,7 +363,7 @@
/* LOAD */
C(0x1800, LR, RR_a, Z, 0, r2_o, 0, cond_r1r2_32, mov2, 0)
C(0x5800, L, RX_a, Z, 0, a2, new, r1_32, ld32s, 0)
- C(0xe358, LY, RXY_a, Z, 0, a2, new, r1_32, ld32s, 0)
+ C(0xe358, LY, RXY_a, LD, 0, a2, new, r1_32, ld32s, 0)
C(0xb904, LGR, RRE, Z, 0, r2_o, 0, r1, mov2, 0)
C(0xb914, LGFR, RRE, Z, 0, r2_32s, 0, r1, mov2, 0)
C(0xe304, LG, RXY_a, Z, 0, a2, r1, 0, ld64, 0)
@@ -357,6 +384,9 @@
/* LOAD ADDRESS */
C(0x4100, LA, RX_a, Z, 0, a2, 0, r1, mov2, 0)
C(0xe371, LAY, RXY_a, LD, 0, a2, 0, r1, mov2, 0)
+/* LOAD ADDRESS EXTENDED */
+ C(0x5100, LAE, RX_a, Z, 0, a2, 0, r1, mov2e, 0)
+ C(0xe375, LAEY, RXY_a, GIE, 0, a2, 0, r1, mov2e, 0)
/* LOAD ADDRESS RELATIVE LONG */
C(0xc000, LARL, RIL_b, Z, 0, ri2, 0, r1, mov2, 0)
/* LOAD AND ADD */
@@ -384,11 +414,16 @@
C(0xb302, LTEBR, RRE, Z, 0, e2, 0, cond_e1e2, mov2, f32)
C(0xb312, LTDBR, RRE, Z, 0, f2_o, 0, f1, mov2, f64)
C(0xb342, LTXBR, RRE, Z, 0, x2_o, 0, x1, movx, f128)
+/* LOAD AND TRAP */
+ C(0xe39f, LAT, RXY_a, LAT, 0, m2_32u, r1, 0, lat, 0)
+ C(0xe385, LGAT, RXY_a, LAT, 0, a2, r1, 0, lgat, 0)
/* LOAD BYTE */
C(0xb926, LBR, RRE, EI, 0, r2_8s, 0, r1_32, mov2, 0)
C(0xb906, LGBR, RRE, EI, 0, r2_8s, 0, r1, mov2, 0)
C(0xe376, LB, RXY_a, LD, 0, a2, new, r1_32, ld8s, 0)
C(0xe377, LGB, RXY_a, LD, 0, a2, r1, 0, ld8s, 0)
+/* LOAD BYTE HIGH */
+ C(0xe3c0, LBH, RXY_a, HW, 0, a2, new, r1_32h, ld8s, 0)
/* LOAD COMPLEMENT */
C(0x1300, LCR, RR_a, Z, 0, r2, new, r1_32, neg, neg32)
C(0xb903, LCGR, RRE, Z, 0, r2, r1, 0, neg, neg64)
@@ -403,15 +438,23 @@
C(0x4800, LH, RX_a, Z, 0, a2, new, r1_32, ld16s, 0)
C(0xe378, LHY, RXY_a, LD, 0, a2, new, r1_32, ld16s, 0)
C(0xe315, LGH, RXY_a, Z, 0, a2, r1, 0, ld16s, 0)
+/* LOAD HALFWORD HIGH */
+ C(0xe3c4, LHH, RXY_a, HW, 0, a2, new, r1_32h, ld16s, 0)
/* LOAD HALFWORD IMMEDIATE */
C(0xa708, LHI, RI_a, Z, 0, i2, 0, r1_32, mov2, 0)
C(0xa709, LGHI, RI_a, Z, 0, i2, 0, r1, mov2, 0)
/* LOAD HALFWORD RELATIVE LONG */
C(0xc405, LHRL, RIL_b, GIE, 0, ri2, new, r1_32, ld16s, 0)
C(0xc404, LGHRL, RIL_b, GIE, 0, ri2, r1, 0, ld16s, 0)
+/* LOAD HIGH */
+ C(0xe3ca, LFH, RXY_a, HW, 0, a2, new, r1_32h, ld32u, 0)
+/* LOAG HIGH AND TRAP */
+ C(0xe3c8, LFHAT, RXY_a, LAT, 0, m2_32u, r1, 0, lfhat, 0)
/* LOAD LOGICAL */
C(0xb916, LLGFR, RRE, Z, 0, r2_32u, 0, r1, mov2, 0)
C(0xe316, LLGF, RXY_a, Z, 0, a2, r1, 0, ld32u, 0)
+/* LOAD LOGICAL AND TRAP */
+ C(0xe39d, LLGFAT, RXY_a, LAT, 0, a2, r1, 0, llgfat, 0)
/* LOAD LOGICAL RELATIVE LONG */
C(0xc40e, LLGFRL, RIL_b, GIE, 0, ri2, r1, 0, ld32u, 0)
/* LOAD LOGICAL CHARACTER */
@@ -419,11 +462,15 @@
C(0xb984, LLGCR, RRE, EI, 0, r2_8u, 0, r1, mov2, 0)
C(0xe394, LLC, RXY_a, EI, 0, a2, new, r1_32, ld8u, 0)
C(0xe390, LLGC, RXY_a, Z, 0, a2, r1, 0, ld8u, 0)
+/* LOAD LOGICAL CHARACTER HIGH */
+ C(0xe3c2, LLCH, RXY_a, HW, 0, a2, new, r1_32h, ld8u, 0)
/* LOAD LOGICAL HALFWORD */
C(0xb995, LLHR, RRE, EI, 0, r2_16u, 0, r1_32, mov2, 0)
C(0xb985, LLGHR, RRE, EI, 0, r2_16u, 0, r1, mov2, 0)
C(0xe395, LLH, RXY_a, EI, 0, a2, new, r1_32, ld16u, 0)
C(0xe391, LLGH, RXY_a, Z, 0, a2, r1, 0, ld16u, 0)
+/* LOAD LOGICAL HALFWORD HIGH */
+ C(0xe3c6, LLHH, RXY_a, HW, 0, a2, new, r1_32h, ld16u, 0)
/* LOAD LOGICAL HALFWORD RELATIVE LONG */
C(0xc402, LLHRL, RIL_b, GIE, 0, ri2, new, r1_32, ld16u, 0)
C(0xc406, LLGHRL, RIL_b, GIE, 0, ri2, r1, 0, ld16u, 0)
@@ -437,6 +484,9 @@
/* LOAD LOGICAL THIRTY ONE BITS */
C(0xb917, LLGTR, RRE, Z, 0, r2_o, r1, 0, llgt, 0)
C(0xe317, LLGT, RXY_a, Z, 0, m2_32u, r1, 0, llgt, 0)
+/* LOAD LOGICAL THIRTY ONE BITS AND TRAP */
+ C(0xe39c, LLGTAT, RXY_a, LAT, 0, m2_32u, r1, 0, llgtat, 0)
+
/* LOAD FPR FROM GR */
C(0xb3c1, LDGR, RRE, FPRGR, 0, r2_o, 0, f1, mov2, 0)
/* LOAD GR FROM FPR */
@@ -448,6 +498,7 @@
C(0xb301, LNEBR, RRE, Z, 0, e2, new, e1, nabsf32, f32)
C(0xb311, LNDBR, RRE, Z, 0, f2_o, f1, 0, nabsf64, f64)
C(0xb341, LNXBR, RRE, Z, 0, x2_o, x1, 0, nabsf128, f128)
+ C(0xb371, LNDFR, RRE, FPSSH, 0, f2_o, f1, 0, nabsf64, 0)
/* LOAD ON CONDITION */
C(0xb9f2, LOCR, RRF_c, LOC, r1, r2, new, r1_32, loc, 0)
C(0xb9e2, LOCGR, RRF_c, LOC, r1, r2, r1, 0, loc, 0)
@@ -461,6 +512,7 @@
C(0xb300, LPEBR, RRE, Z, 0, e2, new, e1, absf32, f32)
C(0xb310, LPDBR, RRE, Z, 0, f2_o, f1, 0, absf64, f64)
C(0xb340, LPXBR, RRE, Z, 0, x2_o, x1, 0, absf128, f128)
+ C(0xb370, LPDFR, RRE, FPSSH, 0, f2_o, f1, 0, absf64, 0)
/* LOAD REVERSED */
C(0xb91f, LRVR, RRE, Z, 0, r2_32u, new, r1_32, rev32, 0)
C(0xb90f, LRVGR, RRE, Z, 0, r2_o, r1, 0, rev64, 0)
@@ -476,6 +528,10 @@
C(0xb29d, LFPC, S, Z, 0, m2_32u, 0, 0, sfpc, 0)
/* LOAD FPC AND SIGNAL */
C(0xb2bd, LFAS, S, IEEEE_SIM, 0, m2_32u, 0, 0, sfas, 0)
+/* LOAD FP INTEGER */
+ C(0xb357, FIEBR, RRF_e, Z, 0, e2, new, e1, fieb, 0)
+ C(0xb35f, FIDBR, RRF_e, Z, 0, f2_o, f1, 0, fidb, 0)
+ C(0xb347, FIXBR, RRF_e, Z, 0, x2_o, x1, 0, fixb, 0)
/* LOAD LENGTHENED */
C(0xb304, LDEBR, RRE, Z, 0, e2, f1, 0, ldeb, 0)
@@ -595,8 +651,9 @@
/* ROTATE THEN INSERT SELECTED BITS */
C(0xec55, RISBG, RIE_f, GIE, 0, r2, r1, 0, risbg, s64)
- C(0xec5d, RISBHG, RIE_f, GIE, 0, r2, r1, 0, risbg, 0)
- C(0xec51, RISBLG, RIE_f, GIE, 0, r2, r1, 0, risbg, 0)
+ C(0xec59, RISBGN, RIE_f, MIE, 0, r2, r1, 0, risbg, 0)
+ C(0xec5d, RISBHG, RIE_f, HW, 0, r2, r1, 0, risbg, 0)
+ C(0xec51, RISBLG, RIE_f, HW, 0, r2, r1, 0, risbg, 0)
/* ROTATE_THEN <OP> SELECTED BITS */
C(0xec54, RNSBG, RIE_f, GIE, 0, r2, r1, 0, rosbg, 0)
C(0xec56, ROSBG, RIE_f, GIE, 0, r2, r1, 0, rosbg, 0)
@@ -619,7 +676,7 @@
C(0xb299, SRNM, S, Z, 0, 0, 0, 0, srnm, 0)
C(0xb2b8, SRNMB, S, FPE, 0, 0, 0, 0, srnm, 0)
/* SET DFP ROUNDING MODE */
- C(0xb2b9, SRNMT, S, DFP, 0, 0, 0, 0, srnm, 0)
+ C(0xb2b9, SRNMT, S, DFPR, 0, 0, 0, 0, srnm, 0)
/* SHIFT LEFT SINGLE */
D(0x8b00, SLA, RS_a, Z, r1, sh32, new, r1_32, sla, 0, 31)
@@ -667,15 +724,21 @@
/* STORE CHARACTER */
C(0x4200, STC, RX_a, Z, r1_o, a2, 0, 0, st8, 0)
C(0xe372, STCY, RXY_a, LD, r1_o, a2, 0, 0, st8, 0)
+/* STORE CHARACTER HIGH */
+ C(0xe3c3, STCH, RXY_a, HW, r1_sr32, a2, 0, 0, st8, 0)
/* STORE CHARACTERS UNDER MASK */
D(0xbe00, STCM, RS_b, Z, r1_o, a2, 0, 0, stcm, 0, 0)
D(0xeb2d, STCMY, RSY_b, LD, r1_o, a2, 0, 0, stcm, 0, 0)
- D(0xeb2c, STCMH, RSY_b, LD, r1_o, a2, 0, 0, stcm, 0, 32)
+ D(0xeb2c, STCMH, RSY_b, Z, r1_o, a2, 0, 0, stcm, 0, 32)
/* STORE HALFWORD */
C(0x4000, STH, RX_a, Z, r1_o, a2, 0, 0, st16, 0)
C(0xe370, STHY, RXY_a, LD, r1_o, a2, 0, 0, st16, 0)
+/* STORE HALFWORD HIGH */
+ C(0xe3c7, STHH, RXY_a, HW, r1_sr32, a2, 0, 0, st16, 0)
/* STORE HALFWORD RELATIVE LONG */
C(0xc407, STHRL, RIL_b, GIE, r1_o, ri2, 0, 0, st16, 0)
+/* STORE HIGH */
+ C(0xe3cb, STFH, RXY_a, HW, r1_sr32, a2, 0, 0, st32, 0)
/* STORE ON CONDITION */
D(0xebf3, STOC, RSY_b, LOC, 0, 0, 0, 0, soc, 0, 0)
D(0xebe3, STOCG, RSY_b, LOC, 0, 0, 0, 0, soc, 0, 1)
@@ -715,6 +778,9 @@
/* SUBTRACT HALFWORD */
C(0x4b00, SH, RX_a, Z, r1, m2_16s, new, r1_32, sub, subs32)
C(0xe37b, SHY, RXY_a, LD, r1, m2_16s, new, r1_32, sub, subs32)
+/* SUBTRACT HIGH */
+ C(0xb9c9, SHHHR, RRF_a, HW, r2_sr32, r3_sr32, new, r1_32h, sub, subs32)
+ C(0xb9d9, SHHLR, RRF_a, HW, r2_sr32, r3, new, r1_32h, sub, subs32)
/* SUBTRACT LOGICAL */
C(0x1f00, SLR, RR_a, Z, r1, r2, new, r1_32, sub, subu32)
C(0xb9fb, SLRK, RRF_a, DO, r2, r3, new, r1_32, sub, subu32)
@@ -725,6 +791,9 @@
C(0xb9eb, SLGRK, RRF_a, DO, r2, r3, r1, 0, sub, subu64)
C(0xe30b, SLG, RXY_a, Z, r1, m2_64, r1, 0, sub, subu64)
C(0xe31b, SLGF, RXY_a, Z, r1, m2_32u, r1, 0, sub, subu64)
+/* SUBTRACT LOCICAL HIGH */
+ C(0xb9cb, SLHHHR, RRF_a, HW, r2_sr32, r3_sr32, new, r1_32h, sub, subu32)
+ C(0xb9db, SLHHLR, RRF_a, HW, r2_sr32, r3, new, r1_32h, sub, subu32)
/* SUBTRACT LOGICAL IMMEDIATE */
C(0xc205, SLFI, RIL_a, EI, r1, i2_32u, new, r1_32, sub, subu32)
C(0xc204, SLGFI, RIL_a, EI, r1, i2_32u, r1, 0, sub, subu64)
@@ -752,6 +821,10 @@
/* TRANSLATE */
C(0xdc00, TR, SS_a, Z, la1, a2, 0, 0, tr, 0)
+/* TRANSLATE AND TEST */
+ C(0xdd00, TRT, SS_a, Z, la1, a2, 0, 0, trt, 0)
+/* TRANSLATE EXTENDED */
+ C(0xb2a5, TRE, RRE, Z, 0, r2, r1_P, 0, tre, 0)
/* UNPACK */
/* Really format SS_b, but we pack both lengths into one argument
@@ -762,7 +835,7 @@
/* COMPARE AND SWAP AND PURGE */
C(0xb250, CSP, RRE, Z, 0, ra2, 0, 0, csp, 0)
/* DIAGNOSE (KVM hypercall) */
- C(0x8300, DIAG, RX_a, Z, 0, 0, 0, 0, diag, 0)
+ C(0x8300, DIAG, RSI, Z, 0, 0, 0, 0, diag, 0)
/* INSERT STORAGE KEY EXTENDED */
C(0xb229, ISKE, RRE, Z, 0, r2_o, new, r1_8, iske, 0)
/* INVALIDATE PAGE TABLE ENTRY */
@@ -812,7 +885,7 @@
C(0xae00, SIGP, RS_a, Z, r3_o, a2, 0, 0, sigp, 0)
/* STORE CLOCK */
C(0xb205, STCK, S, Z, la2, 0, new, m1_64, stck, 0)
- C(0xb27c, STCKF, S, Z, la2, 0, new, m1_64, stck, 0)
+ C(0xb27c, STCKF, S, SCF, la2, 0, new, m1_64, stck, 0)
/* STORE CLOCK EXTENDED */
C(0xb278, STCKE, S, Z, 0, a2, 0, 0, stcke, 0)
/* STORE CLOCK COMPARATOR */
@@ -842,17 +915,17 @@
/* TEST PROTECTION */
C(0xe501, TPROT, SSE, Z, la1, a2, 0, 0, tprot, 0)
-/* I/O Instructions. For each we simply indicate non-operation. */
- C(0xb276, XSCH, S, Z, 0, 0, 0, 0, subchannel, 0)
- C(0xb230, CSCH, S, Z, 0, 0, 0, 0, subchannel, 0)
- C(0xb231, HSCH, S, Z, 0, 0, 0, 0, subchannel, 0)
- C(0xb232, MSCH, S, Z, 0, 0, 0, 0, subchannel, 0)
- C(0xb23b, RCHP, S, Z, 0, 0, 0, 0, subchannel, 0)
- C(0xb238, RSCH, S, Z, 0, 0, 0, 0, subchannel, 0)
- C(0xb233, SSCH, S, Z, 0, 0, 0, 0, subchannel, 0)
- C(0xb234, STSCH, S, Z, 0, 0, 0, 0, subchannel, 0)
- C(0xb235, TSCH, S, Z, 0, 0, 0, 0, subchannel, 0)
+/* CCW I/O Instructions */
+ C(0xb276, XSCH, S, Z, 0, 0, 0, 0, xsch, 0)
+ C(0xb230, CSCH, S, Z, 0, 0, 0, 0, csch, 0)
+ C(0xb231, HSCH, S, Z, 0, 0, 0, 0, hsch, 0)
+ C(0xb232, MSCH, S, Z, 0, insn, 0, 0, msch, 0)
+ C(0xb23b, RCHP, S, Z, 0, 0, 0, 0, rchp, 0)
+ C(0xb238, RSCH, S, Z, 0, 0, 0, 0, rsch, 0)
+ C(0xb233, SSCH, S, Z, 0, insn, 0, 0, ssch, 0)
+ C(0xb234, STSCH, S, Z, 0, insn, 0, 0, stsch, 0)
+ C(0xb235, TSCH, S, Z, 0, insn, 0, 0, tsch, 0)
/* ??? Not listed in PoO ninth edition, but there's a linux driver that
uses it: "A CHSC subchannel is usually present on LPAR only." */
- C(0xb25f, CHSC, S, Z, 0, 0, 0, 0, subchannel, 0)
+ C(0xb25f, CHSC, RRE, Z, 0, insn, 0, 0, chsc, 0)
#endif /* CONFIG_USER_ONLY */
diff --git a/target-s390x/int_helper.c b/target-s390x/int_helper.c
index cb8dd98542..2c2b3f622c 100644
--- a/target-s390x/int_helper.c
+++ b/target-s390x/int_helper.c
@@ -115,48 +115,6 @@ uint64_t HELPER(divu64)(CPUS390XState *env, uint64_t ah, uint64_t al,
return ret;
}
-/* absolute value 32-bit */
-uint32_t HELPER(abs_i32)(int32_t val)
-{
- if (val < 0) {
- return -val;
- } else {
- return val;
- }
-}
-
-/* negative absolute value 32-bit */
-int32_t HELPER(nabs_i32)(int32_t val)
-{
- if (val < 0) {
- return val;
- } else {
- return -val;
- }
-}
-
-/* absolute value 64-bit */
-uint64_t HELPER(abs_i64)(int64_t val)
-{
- HELPER_LOG("%s: val 0x%" PRIx64 "\n", __func__, val);
-
- if (val < 0) {
- return -val;
- } else {
- return val;
- }
-}
-
-/* negative absolute value 64-bit */
-int64_t HELPER(nabs_i64)(int64_t val)
-{
- if (val < 0) {
- return val;
- } else {
- return -val;
- }
-}
-
/* count leading zeros, for find leftmost one */
uint64_t HELPER(clz)(uint64_t v)
{
diff --git a/target-s390x/ioinst.c b/target-s390x/ioinst.c
index e220cea8ab..77f2a1fb96 100644
--- a/target-s390x/ioinst.c
+++ b/target-s390x/ioinst.c
@@ -129,12 +129,12 @@ void ioinst_handle_hsch(S390CPU *cpu, uint64_t reg1)
static int ioinst_schib_valid(SCHIB *schib)
{
- if ((schib->pmcw.flags & PMCW_FLAGS_MASK_INVALID) ||
- (schib->pmcw.chars & PMCW_CHARS_MASK_INVALID)) {
+ if ((be16_to_cpu(schib->pmcw.flags) & PMCW_FLAGS_MASK_INVALID) ||
+ (be32_to_cpu(schib->pmcw.chars) & PMCW_CHARS_MASK_INVALID)) {
return 0;
}
/* Disallow extended measurements for now. */
- if (schib->pmcw.chars & PMCW_CHARS_MASK_XMWME) {
+ if (be32_to_cpu(schib->pmcw.chars) & PMCW_CHARS_MASK_XMWME) {
return 0;
}
return 1;
diff --git a/target-s390x/ioinst.h b/target-s390x/ioinst.h
index 203bdba2d5..013cc91487 100644
--- a/target-s390x/ioinst.h
+++ b/target-s390x/ioinst.h
@@ -220,7 +220,7 @@ typedef struct IOIntCode {
#define IOINST_SCHID_SSID(_schid) ((_schid & 0x00060000) >> 17)
#define IOINST_SCHID_NR(_schid) (_schid & 0x0000ffff)
-#define IO_INT_WORD_ISC(_int_word) ((_int_word & 0x38000000) >> 24)
+#define IO_INT_WORD_ISC(_int_word) ((_int_word & 0x38000000) >> 27)
#define ISC_TO_ISC_BITS(_isc) ((0x80 >> _isc) << 24)
#define IO_INT_WORD_AI 0x80000000
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
index 6de7759b67..b02ff8d61d 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -98,6 +98,7 @@
#define PRIV_E3_MPCIFC 0xd0
#define PRIV_E3_STPCIFC 0xd4
+#define DIAG_TIMEREVENT 0x288
#define DIAG_IPL 0x308
#define DIAG_KVM_HYPERCALL 0x500
#define DIAG_KVM_BREAKPOINT 0x501
@@ -1267,6 +1268,20 @@ static int handle_hypercall(S390CPU *cpu, struct kvm_run *run)
return ret;
}
+static void kvm_handle_diag_288(S390CPU *cpu, struct kvm_run *run)
+{
+ uint64_t r1, r3;
+ int rc;
+
+ cpu_synchronize_state(CPU(cpu));
+ r1 = (run->s390_sieic.ipa & 0x00f0) >> 4;
+ r3 = run->s390_sieic.ipa & 0x000f;
+ rc = handle_diag_288(&cpu->env, r1, r3);
+ if (rc) {
+ enter_pgmcheck(cpu, PGM_SPECIFICATION);
+ }
+}
+
static void kvm_handle_diag_308(S390CPU *cpu, struct kvm_run *run)
{
uint64_t r1, r3;
@@ -1306,6 +1321,9 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb)
*/
func_code = decode_basedisp_rs(&cpu->env, ipb, NULL) & DIAG_KVM_CODE_MASK;
switch (func_code) {
+ case DIAG_TIMEREVENT:
+ kvm_handle_diag_288(cpu, run);
+ break;
case DIAG_IPL:
kvm_handle_diag_308(cpu, run);
break;
@@ -2175,13 +2193,14 @@ int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu)
struct kvm_s390_irq_state irq_state;
int r;
+ if (cpu->irqstate_saved_size == 0) {
+ return 0;
+ }
+
if (!kvm_check_extension(kvm_state, KVM_CAP_S390_IRQ_STATE)) {
return -ENOSYS;
}
- if (cpu->irqstate_saved_size == 0) {
- return 0;
- }
irq_state.buf = (uint64_t) cpu->irqstate;
irq_state.len = cpu->irqstate_saved_size;
@@ -2216,3 +2235,8 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
route->u.adapter.adapter_id = pbdev->routes.adapter.adapter_id;
return 0;
}
+
+int kvm_arch_msi_data_to_gsi(uint32_t data)
+{
+ abort();
+}
diff --git a/target-s390x/machine.c b/target-s390x/machine.c
index e52d76032e..b76fb08319 100644
--- a/target-s390x/machine.c
+++ b/target-s390x/machine.c
@@ -42,10 +42,17 @@ static void cpu_pre_save(void *opaque)
}
}
+static inline bool fpu_needed(void *opaque)
+{
+ /* This looks odd, but we might want to NOT transfer fprs in the future */
+ return true;
+}
+
const VMStateDescription vmstate_fpu = {
.name = "cpu/fpu",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = fpu_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64(env.vregs[0][0].ll, S390CPU),
VMSTATE_UINT64(env.vregs[1][0].ll, S390CPU),
@@ -68,15 +75,11 @@ const VMStateDescription vmstate_fpu = {
}
};
-static inline bool fpu_needed(void *opaque)
-{
- return true;
-}
-
const VMStateDescription vmstate_vregs = {
.name = "cpu/vregs",
.version_id = 1,
.minimum_version_id = 1,
+ .needed = vregs_needed,
.fields = (VMStateField[]) {
/* vregs[0][0] -> vregs[15][0] and fregs are overlays */
VMSTATE_UINT64(env.vregs[16][0].ll, S390CPU),
@@ -158,16 +161,10 @@ const VMStateDescription vmstate_s390_cpu = {
VMSTATE_VBUFFER_UINT32(irqstate, S390CPU, 4, NULL, 0,
irqstate_saved_size),
VMSTATE_END_OF_LIST()
- },
- .subsections = (VMStateSubsection[]) {
- {
- .vmsd = &vmstate_fpu,
- .needed = fpu_needed,
- } , {
- .vmsd = &vmstate_vregs,
- .needed = vregs_needed,
- } , {
- /* empty */
- }
+ },
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_fpu,
+ &vmstate_vregs,
+ NULL
},
};
diff --git a/target-s390x/mem_helper.c b/target-s390x/mem_helper.c
index 0e8cd0f489..3ccbeb99e4 100644
--- a/target-s390x/mem_helper.c
+++ b/target-s390x/mem_helper.c
@@ -54,63 +54,67 @@ void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx,
#define HELPER_LOG(x...)
#endif
-#ifndef CONFIG_USER_ONLY
-static void mvc_fast_memset(CPUS390XState *env, uint32_t l, uint64_t dest,
- uint8_t byte)
+/* Reduce the length so that addr + len doesn't cross a page boundary. */
+static inline uint64_t adj_len_to_page(uint64_t len, uint64_t addr)
{
- S390CPU *cpu = s390_env_get_cpu(env);
- hwaddr dest_phys;
- hwaddr len = l;
- void *dest_p;
- uint64_t asc = env->psw.mask & PSW_MASK_ASC;
- int flags;
-
- if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags, true)) {
- cpu_stb_data(env, dest, byte);
- cpu_abort(CPU(cpu), "should never reach here");
+#ifndef CONFIG_USER_ONLY
+ if ((addr & ~TARGET_PAGE_MASK) + len - 1 >= TARGET_PAGE_SIZE) {
+ return -addr & ~TARGET_PAGE_MASK;
}
- dest_phys |= dest & ~TARGET_PAGE_MASK;
-
- dest_p = cpu_physical_memory_map(dest_phys, &len, 1);
-
- memset(dest_p, byte, len);
-
- cpu_physical_memory_unmap(dest_p, 1, len, len);
+#endif
+ return len;
}
-static void mvc_fast_memmove(CPUS390XState *env, uint32_t l, uint64_t dest,
- uint64_t src)
+static void fast_memset(CPUS390XState *env, uint64_t dest, uint8_t byte,
+ uint32_t l)
{
- S390CPU *cpu = s390_env_get_cpu(env);
- hwaddr dest_phys;
- hwaddr src_phys;
- hwaddr len = l;
- void *dest_p;
- void *src_p;
- uint64_t asc = env->psw.mask & PSW_MASK_ASC;
- int flags;
-
- if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags, true)) {
- cpu_stb_data(env, dest, 0);
- cpu_abort(CPU(cpu), "should never reach here");
+ int mmu_idx = cpu_mmu_index(env);
+
+ while (l > 0) {
+ void *p = tlb_vaddr_to_host(env, dest, MMU_DATA_STORE, mmu_idx);
+ if (p) {
+ /* Access to the whole page in write mode granted. */
+ int l_adj = adj_len_to_page(l, dest);
+ memset(p, byte, l_adj);
+ dest += l_adj;
+ l -= l_adj;
+ } else {
+ /* We failed to get access to the whole page. The next write
+ access will likely fill the QEMU TLB for the next iteration. */
+ cpu_stb_data(env, dest, byte);
+ dest++;
+ l--;
+ }
}
- dest_phys |= dest & ~TARGET_PAGE_MASK;
+}
- if (mmu_translate(env, src, 0, asc, &src_phys, &flags, true)) {
- cpu_ldub_data(env, src);
- cpu_abort(CPU(cpu), "should never reach here");
+static void fast_memmove(CPUS390XState *env, uint64_t dest, uint64_t src,
+ uint32_t l)
+{
+ int mmu_idx = cpu_mmu_index(env);
+
+ while (l > 0) {
+ void *src_p = tlb_vaddr_to_host(env, src, MMU_DATA_LOAD, mmu_idx);
+ void *dest_p = tlb_vaddr_to_host(env, dest, MMU_DATA_STORE, mmu_idx);
+ if (src_p && dest_p) {
+ /* Access to both whole pages granted. */
+ int l_adj = adj_len_to_page(l, src);
+ l_adj = adj_len_to_page(l_adj, dest);
+ memmove(dest_p, src_p, l_adj);
+ src += l_adj;
+ dest += l_adj;
+ l -= l_adj;
+ } else {
+ /* We failed to get access to one or both whole pages. The next
+ read or write access will likely fill the QEMU TLB for the
+ next iteration. */
+ cpu_stb_data(env, dest, cpu_ldub_data(env, src));
+ src++;
+ dest++;
+ l--;
+ }
}
- src_phys |= src & ~TARGET_PAGE_MASK;
-
- dest_p = cpu_physical_memory_map(dest_phys, &len, 1);
- src_p = cpu_physical_memory_map(src_phys, &len, 0);
-
- memmove(dest_p, src_p, len);
-
- cpu_physical_memory_unmap(dest_p, 1, len, len);
- cpu_physical_memory_unmap(src_p, 0, len, len);
}
-#endif
/* and on array */
uint32_t HELPER(nc)(CPUS390XState *env, uint32_t l, uint64_t dest,
@@ -143,19 +147,11 @@ uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest,
HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
__func__, l, dest, src);
-#ifndef CONFIG_USER_ONLY
/* xor with itself is the same as memset(0) */
- if ((l > 32) && (src == dest) &&
- (src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK)) {
- mvc_fast_memset(env, l + 1, dest, 0);
- return 0;
- }
-#else
if (src == dest) {
- memset(g2h(dest), 0, l + 1);
+ fast_memset(env, dest, 0, l + 1);
return 0;
}
-#endif
for (i = 0; i <= l; i++) {
x = cpu_ldub_data(env, dest + i) ^ cpu_ldub_data(env, src + i);
@@ -191,44 +187,25 @@ uint32_t HELPER(oc)(CPUS390XState *env, uint32_t l, uint64_t dest,
void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
{
int i = 0;
- int x = 0;
- uint32_t l_64 = (l + 1) / 8;
HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
__func__, l, dest, src);
-#ifndef CONFIG_USER_ONLY
- if ((l > 32) &&
- (src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK) &&
- (dest & TARGET_PAGE_MASK) == ((dest + l) & TARGET_PAGE_MASK)) {
- if (dest == (src + 1)) {
- mvc_fast_memset(env, l + 1, dest, cpu_ldub_data(env, src));
- return;
- } else if ((src & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) {
- mvc_fast_memmove(env, l + 1, dest, src);
- return;
- }
- }
-#else
+ /* mvc with source pointing to the byte after the destination is the
+ same as memset with the first source byte */
if (dest == (src + 1)) {
- memset(g2h(dest), cpu_ldub_data(env, src), l + 1);
- return;
- } else {
- memmove(g2h(dest), g2h(src), l + 1);
+ fast_memset(env, dest, cpu_ldub_data(env, src), l + 1);
return;
}
-#endif
- /* handle the parts that fit into 8-byte loads/stores */
- if (dest != (src + 1)) {
- for (i = 0; i < l_64; i++) {
- cpu_stq_data(env, dest + x, cpu_ldq_data(env, src + x));
- x += 8;
- }
+ /* mvc and memmove do not behave the same when areas overlap! */
+ if ((dest < src) || (src + l < dest)) {
+ fast_memmove(env, dest, src, l + 1);
+ return;
}
- /* slow version crossing pages with byte accesses */
- for (i = x; i <= l; i++) {
+ /* slow version with byte accesses which always work */
+ for (i = 0; i <= l; i++) {
cpu_stb_data(env, dest + i, cpu_ldub_data(env, src + i));
}
}
@@ -395,11 +372,7 @@ void HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2)
{
/* XXX missing r0 handling */
env->cc_op = 0;
-#ifdef CONFIG_USER_ONLY
- memmove(g2h(r1), g2h(r2), TARGET_PAGE_SIZE);
-#else
- mvc_fast_memmove(env, TARGET_PAGE_SIZE, r1, r2);
-#endif
+ fast_memmove(env, r1, r2, TARGET_PAGE_SIZE);
}
/* string copy (c is string terminator) */
@@ -509,6 +482,9 @@ uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1,
case 0xc00:
helper_tr(env, l, get_address(env, 0, b1, d1),
get_address(env, 0, b2, d2));
+ case 0xd00:
+ cc = helper_trt(env, l, get_address(env, 0, b1, d1),
+ get_address(env, 0, b2, d2));
break;
default:
goto abort;
@@ -801,15 +777,81 @@ void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array,
}
}
+uint64_t HELPER(tre)(CPUS390XState *env, uint64_t array,
+ uint64_t len, uint64_t trans)
+{
+ uint8_t end = env->regs[0] & 0xff;
+ uint64_t l = len;
+ uint64_t i;
+
+ if (!(env->psw.mask & PSW_MASK_64)) {
+ array &= 0x7fffffff;
+ l = (uint32_t)l;
+ }
+
+ /* Lest we fail to service interrupts in a timely manner, limit the
+ amount of work we're willing to do. For now, let's cap at 8k. */
+ if (l > 0x2000) {
+ l = 0x2000;
+ env->cc_op = 3;
+ } else {
+ env->cc_op = 0;
+ }
+
+ for (i = 0; i < l; i++) {
+ uint8_t byte, new_byte;
+
+ byte = cpu_ldub_data(env, array + i);
+
+ if (byte == end) {
+ env->cc_op = 1;
+ break;
+ }
+
+ new_byte = cpu_ldub_data(env, trans + byte);
+ cpu_stb_data(env, array + i, new_byte);
+ }
+
+ env->retxl = len - i;
+ return array + i;
+}
+
+uint32_t HELPER(trt)(CPUS390XState *env, uint32_t len, uint64_t array,
+ uint64_t trans)
+{
+ uint32_t cc = 0;
+ int i;
+
+ for (i = 0; i <= len; i++) {
+ uint8_t byte = cpu_ldub_data(env, array + i);
+ uint8_t sbyte = cpu_ldub_data(env, trans + byte);
+
+ if (sbyte != 0) {
+ env->regs[1] = array + i;
+ env->regs[2] = (env->regs[2] & ~0xff) | sbyte;
+ cc = (i == len) ? 2 : 1;
+ break;
+ }
+ }
+
+ return cc;
+}
+
#if !defined(CONFIG_USER_ONLY)
void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
{
S390CPU *cpu = s390_env_get_cpu(env);
+ bool PERchanged = false;
int i;
uint64_t src = a2;
+ uint64_t val;
for (i = r1;; i = (i + 1) % 16) {
- env->cregs[i] = cpu_ldq_data(env, src);
+ val = cpu_ldq_data(env, src);
+ if (env->cregs[i] != val && i >= 9 && i <= 11) {
+ PERchanged = true;
+ }
+ env->cregs[i] = val;
HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n",
i, src, env->cregs[i]);
src += sizeof(uint64_t);
@@ -819,18 +861,27 @@ void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
}
}
+ if (PERchanged && env->psw.mask & PSW_MASK_PER) {
+ s390_cpu_recompute_watchpoints(CPU(cpu));
+ }
+
tlb_flush(CPU(cpu), 1);
}
void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
{
S390CPU *cpu = s390_env_get_cpu(env);
+ bool PERchanged = false;
int i;
uint64_t src = a2;
+ uint32_t val;
for (i = r1;; i = (i + 1) % 16) {
- env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) |
- cpu_ldl_data(env, src);
+ val = cpu_ldl_data(env, src);
+ if ((uint32_t)env->cregs[i] != val && i >= 9 && i <= 11) {
+ PERchanged = true;
+ }
+ env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) | val;
src += sizeof(uint32_t);
if (i == r3) {
@@ -838,6 +889,10 @@ void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
}
}
+ if (PERchanged && env->psw.mask & PSW_MASK_PER) {
+ s390_cpu_recompute_watchpoints(CPU(cpu));
+ }
+
tlb_flush(CPU(cpu), 1);
}
@@ -952,59 +1007,46 @@ uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint64_t r2)
return cc;
}
-static uint32_t mvc_asc(CPUS390XState *env, int64_t l, uint64_t a1,
- uint64_t mode1, uint64_t a2, uint64_t mode2)
+uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
{
- CPUState *cs = CPU(s390_env_get_cpu(env));
- target_ulong src, dest;
- int flags, cc = 0, i;
+ int cc = 0, i;
- if (!l) {
- return 0;
- } else if (l > 256) {
+ HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
+ __func__, l, a1, a2);
+
+ if (l > 256) {
/* max 256 */
l = 256;
cc = 3;
}
- if (mmu_translate(env, a1, 1, mode1, &dest, &flags, true)) {
- cpu_loop_exit(CPU(s390_env_get_cpu(env)));
- }
- dest |= a1 & ~TARGET_PAGE_MASK;
-
- if (mmu_translate(env, a2, 0, mode2, &src, &flags, true)) {
- cpu_loop_exit(CPU(s390_env_get_cpu(env)));
- }
- src |= a2 & ~TARGET_PAGE_MASK;
-
/* XXX replace w/ memcpy */
for (i = 0; i < l; i++) {
- /* XXX be more clever */
- if ((((dest + i) & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) ||
- (((src + i) & TARGET_PAGE_MASK) != (src & TARGET_PAGE_MASK))) {
- mvc_asc(env, l - i, a1 + i, mode1, a2 + i, mode2);
- break;
- }
- stb_phys(cs->as, dest + i, ldub_phys(cs->as, src + i));
+ cpu_stb_secondary(env, a1 + i, cpu_ldub_primary(env, a2 + i));
}
return cc;
}
-uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
+uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
{
+ int cc = 0, i;
+
HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
__func__, l, a1, a2);
- return mvc_asc(env, l, a1, PSW_ASC_SECONDARY, a2, PSW_ASC_PRIMARY);
-}
+ if (l > 256) {
+ /* max 256 */
+ l = 256;
+ cc = 3;
+ }
-uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
-{
- HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
- __func__, l, a1, a2);
+ /* XXX replace w/ memcpy */
+ for (i = 0; i < l; i++) {
+ cpu_stb_primary(env, a1 + i, cpu_ldub_secondary(env, a2 + i));
+ }
- return mvc_asc(env, l, a1, PSW_ASC_PRIMARY, a2, PSW_ASC_SECONDARY);
+ return cc;
}
/* invalidate pte */
@@ -1063,6 +1105,14 @@ void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint64_t v1)
CPUState *cs = CPU(s390_env_get_cpu(env));
stl_phys(cs->as, get_address(env, 0, 0, addr), (uint32_t)v1);
+
+ if ((env->psw.mask & PSW_MASK_PER) &&
+ (env->cregs[9] & PER_CR9_EVENT_STORE) &&
+ (env->cregs[9] & PER_CR9_EVENT_STORE_REAL)) {
+ /* PSW is saved just before calling the helper. */
+ env->per_address = env->psw.addr;
+ env->per_perc_atmid = PER_CODE_EVENT_STORE_REAL | get_per_atmid(env);
+ }
}
void HELPER(sturg)(CPUS390XState *env, uint64_t addr, uint64_t v1)
@@ -1070,6 +1120,14 @@ void HELPER(sturg)(CPUS390XState *env, uint64_t addr, uint64_t v1)
CPUState *cs = CPU(s390_env_get_cpu(env));
stq_phys(cs->as, get_address(env, 0, 0, addr), v1);
+
+ if ((env->psw.mask & PSW_MASK_PER) &&
+ (env->cregs[9] & PER_CR9_EVENT_STORE) &&
+ (env->cregs[9] & PER_CR9_EVENT_STORE_REAL)) {
+ /* PSW is saved just before calling the helper. */
+ env->per_address = env->psw.addr;
+ env->per_perc_atmid = PER_CODE_EVENT_STORE_REAL | get_per_atmid(env);
+ }
}
/* load real address */
diff --git a/target-s390x/misc_helper.c b/target-s390x/misc_helper.c
index e1007fa35b..8eac0e12b9 100644
--- a/target-s390x/misc_helper.c
+++ b/target-s390x/misc_helper.c
@@ -30,6 +30,7 @@
#include <linux/kvm.h>
#endif
#include "exec/cpu_ldst.h"
+#include "hw/watchdog/wdt_diag288.h"
#if !defined(CONFIG_USER_ONLY)
#include "sysemu/cpus.h"
@@ -61,7 +62,7 @@ void QEMU_NORETURN runtime_exception(CPUS390XState *env, int excp,
/* Advance past the insn. */
t = cpu_ldub_code(env, env->psw.addr);
env->int_pgm_ilen = t = get_ilen(t);
- env->psw.addr += 2 * t;
+ env->psw.addr += t;
cpu_loop_exit(cs);
}
@@ -153,6 +154,34 @@ static int load_normal_reset(S390CPU *cpu)
return 0;
}
+int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3)
+{
+ uint64_t func = env->regs[r1];
+ uint64_t timeout = env->regs[r1 + 1];
+ uint64_t action = env->regs[r3];
+ Object *obj;
+ DIAG288State *diag288;
+ DIAG288Class *diag288_class;
+
+ if (r1 % 2 || action != 0) {
+ return -1;
+ }
+
+ /* Timeout must be more than 15 seconds except for timer deletion */
+ if (func != WDT_DIAG288_CANCEL && timeout < 15) {
+ return -1;
+ }
+
+ obj = object_resolve_path_type("", TYPE_WDT_DIAG288, NULL);
+ if (!obj) {
+ return -1;
+ }
+
+ diag288 = DIAG288(obj);
+ diag288_class = DIAG288_GET_CLASS(diag288);
+ return diag288_class->handle_timer(diag288, func, timeout);
+}
+
#define DIAG_308_RC_OK 0x0001
#define DIAG_308_RC_NO_CONF 0x0102
#define DIAG_308_RC_INVALID 0x0402
@@ -176,9 +205,21 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3)
switch (subcode) {
case 0:
modified_clear_reset(s390_env_get_cpu(env));
+ if (tcg_enabled()) {
+ cpu_loop_exit(CPU(s390_env_get_cpu(env)));
+ }
break;
case 1:
load_normal_reset(s390_env_get_cpu(env));
+ if (tcg_enabled()) {
+ cpu_loop_exit(CPU(s390_env_get_cpu(env)));
+ }
+ break;
+ case 3:
+ s390_reipl_request();
+ if (tcg_enabled()) {
+ cpu_loop_exit(CPU(s390_env_get_cpu(env)));
+ }
break;
case 5:
if ((r1 & 1) || (addr & 0x0fffULL)) {
@@ -225,9 +266,7 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3)
}
#endif
-/* DIAG */
-uint64_t HELPER(diag)(CPUS390XState *env, uint32_t num, uint64_t mem,
- uint64_t code)
+void HELPER(diag)(CPUS390XState *env, uint32_t r1, uint32_t r3, uint32_t num)
{
uint64_t r;
@@ -242,6 +281,7 @@ uint64_t HELPER(diag)(CPUS390XState *env, uint32_t num, uint64_t mem,
break;
case 0x308:
/* ipl */
+ handle_diag_308(env, r1, r3);
r = 0;
break;
default:
@@ -252,8 +292,6 @@ uint64_t HELPER(diag)(CPUS390XState *env, uint32_t num, uint64_t mem,
if (r) {
program_interrupt(env, PGM_OPERATION, ILEN_LATER_INC);
}
-
- return r;
}
/* Set Prefix */
@@ -268,7 +306,8 @@ void HELPER(spx)(CPUS390XState *env, uint64_t a1)
tlb_flush_page(cs, TARGET_PAGE_SIZE);
}
-static inline uint64_t clock_value(CPUS390XState *env)
+/* Store Clock */
+uint64_t HELPER(stck)(CPUS390XState *env)
{
uint64_t time;
@@ -278,12 +317,6 @@ static inline uint64_t clock_value(CPUS390XState *env)
return time;
}
-/* Store Clock */
-uint64_t HELPER(stck)(CPUS390XState *env)
-{
- return clock_value(env);
-}
-
/* Set Clock Comparator */
void HELPER(sckc)(CPUS390XState *env, uint64_t time)
{
@@ -291,19 +324,21 @@ void HELPER(sckc)(CPUS390XState *env, uint64_t time)
return;
}
- /* difference between now and then */
- time -= clock_value(env);
+ env->ckc = time;
+
+ /* difference between origins */
+ time -= env->tod_offset;
+
/* nanoseconds */
- time = (time * 125) >> 9;
+ time = tod2time(time);
- timer_mod(env->tod_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + time);
+ timer_mod(env->tod_timer, env->tod_basetime + time);
}
/* Store Clock Comparator */
uint64_t HELPER(stckc)(CPUS390XState *env)
{
- /* XXX implement */
- return 0;
+ return env->ckc;
}
/* Set CPU Timer */
@@ -314,16 +349,17 @@ void HELPER(spt)(CPUS390XState *env, uint64_t time)
}
/* nanoseconds */
- time = (time * 125) >> 9;
+ time = tod2time(time);
+
+ env->cputm = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + time;
- timer_mod(env->cpu_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + time);
+ timer_mod(env->cpu_timer, env->cputm);
}
/* Store CPU Timer */
uint64_t HELPER(stpt)(CPUS390XState *env)
{
- /* XXX implement */
- return 0;
+ return time2tod(env->cputm - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
}
/* Store System Information */
@@ -496,3 +532,111 @@ uint32_t HELPER(sigp)(CPUS390XState *env, uint64_t order_code, uint32_t r1,
return cc;
}
#endif
+
+#ifndef CONFIG_USER_ONLY
+void HELPER(xsch)(CPUS390XState *env, uint64_t r1)
+{
+ S390CPU *cpu = s390_env_get_cpu(env);
+ ioinst_handle_xsch(cpu, r1);
+}
+
+void HELPER(csch)(CPUS390XState *env, uint64_t r1)
+{
+ S390CPU *cpu = s390_env_get_cpu(env);
+ ioinst_handle_csch(cpu, r1);
+}
+
+void HELPER(hsch)(CPUS390XState *env, uint64_t r1)
+{
+ S390CPU *cpu = s390_env_get_cpu(env);
+ ioinst_handle_hsch(cpu, r1);
+}
+
+void HELPER(msch)(CPUS390XState *env, uint64_t r1, uint64_t inst)
+{
+ S390CPU *cpu = s390_env_get_cpu(env);
+ ioinst_handle_msch(cpu, r1, inst >> 16);
+}
+
+void HELPER(rchp)(CPUS390XState *env, uint64_t r1)
+{
+ S390CPU *cpu = s390_env_get_cpu(env);
+ ioinst_handle_rchp(cpu, r1);
+}
+
+void HELPER(rsch)(CPUS390XState *env, uint64_t r1)
+{
+ S390CPU *cpu = s390_env_get_cpu(env);
+ ioinst_handle_rsch(cpu, r1);
+}
+
+void HELPER(ssch)(CPUS390XState *env, uint64_t r1, uint64_t inst)
+{
+ S390CPU *cpu = s390_env_get_cpu(env);
+ ioinst_handle_ssch(cpu, r1, inst >> 16);
+}
+
+void HELPER(stsch)(CPUS390XState *env, uint64_t r1, uint64_t inst)
+{
+ S390CPU *cpu = s390_env_get_cpu(env);
+ ioinst_handle_stsch(cpu, r1, inst >> 16);
+}
+
+void HELPER(tsch)(CPUS390XState *env, uint64_t r1, uint64_t inst)
+{
+ S390CPU *cpu = s390_env_get_cpu(env);
+ ioinst_handle_tsch(cpu, r1, inst >> 16);
+}
+
+void HELPER(chsc)(CPUS390XState *env, uint64_t inst)
+{
+ S390CPU *cpu = s390_env_get_cpu(env);
+ ioinst_handle_chsc(cpu, inst >> 16);
+}
+#endif
+
+#ifndef CONFIG_USER_ONLY
+void HELPER(per_check_exception)(CPUS390XState *env)
+{
+ CPUState *cs = CPU(s390_env_get_cpu(env));
+
+ if (env->per_perc_atmid) {
+ env->int_pgm_code = PGM_PER;
+ env->int_pgm_ilen = get_ilen(cpu_ldub_code(env, env->per_address));
+
+ cs->exception_index = EXCP_PGM;
+ cpu_loop_exit(cs);
+ }
+}
+
+void HELPER(per_branch)(CPUS390XState *env, uint64_t from, uint64_t to)
+{
+ if ((env->cregs[9] & PER_CR9_EVENT_BRANCH)) {
+ if (!(env->cregs[9] & PER_CR9_CONTROL_BRANCH_ADDRESS)
+ || get_per_in_range(env, to)) {
+ env->per_address = from;
+ env->per_perc_atmid = PER_CODE_EVENT_BRANCH | get_per_atmid(env);
+ }
+ }
+}
+
+void HELPER(per_ifetch)(CPUS390XState *env, uint64_t addr)
+{
+ if ((env->cregs[9] & PER_CR9_EVENT_IFETCH) && get_per_in_range(env, addr)) {
+ env->per_address = addr;
+ env->per_perc_atmid = PER_CODE_EVENT_IFETCH | get_per_atmid(env);
+
+ /* If the instruction has to be nullified, trigger the
+ exception immediately. */
+ if (env->cregs[9] & PER_CR9_EVENT_NULLIFICATION) {
+ CPUState *cs = CPU(s390_env_get_cpu(env));
+
+ env->int_pgm_code = PGM_PER;
+ env->int_pgm_ilen = get_ilen(cpu_ldub_code(env, addr));
+
+ cs->exception_index = EXCP_PGM;
+ cpu_loop_exit(cs);
+ }
+ }
+}
+#endif
diff --git a/target-s390x/mmu_helper.c b/target-s390x/mmu_helper.c
index e8dcd0c18f..815ff42dde 100644
--- a/target-s390x/mmu_helper.c
+++ b/target-s390x/mmu_helper.c
@@ -358,7 +358,7 @@ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
/* Convert real address -> absolute address */
*raddr = mmu_real2abs(env, *raddr);
- if (*raddr <= ram_size) {
+ if (*raddr < ram_size) {
sk = &env->storage_keys[*raddr / TARGET_PAGE_SIZE];
if (*flags & PAGE_READ) {
*sk |= SK_R;
diff --git a/target-s390x/translate.c b/target-s390x/translate.c
index fbffd3066d..42f52c70c6 100644
--- a/target-s390x/translate.c
+++ b/target-s390x/translate.c
@@ -150,6 +150,7 @@ void s390_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
static TCGv_i64 psw_addr;
static TCGv_i64 psw_mask;
+static TCGv_i64 gbea;
static TCGv_i32 cc_op;
static TCGv_i64 cc_src;
@@ -173,6 +174,9 @@ void s390x_translate_init(void)
psw_mask = tcg_global_mem_new_i64(TCG_AREG0,
offsetof(CPUS390XState, psw.mask),
"psw_mask");
+ gbea = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUS390XState, gbea),
+ "gbea");
cc_op = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUS390XState, cc_op),
"cc_op");
@@ -249,6 +253,46 @@ static void update_psw_addr(DisasContext *s)
tcg_gen_movi_i64(psw_addr, s->pc);
}
+static void per_branch(DisasContext *s, bool to_next)
+{
+#ifndef CONFIG_USER_ONLY
+ tcg_gen_movi_i64(gbea, s->pc);
+
+ if (s->tb->flags & FLAG_MASK_PER) {
+ TCGv_i64 next_pc = to_next ? tcg_const_i64(s->next_pc) : psw_addr;
+ gen_helper_per_branch(cpu_env, gbea, next_pc);
+ if (to_next) {
+ tcg_temp_free_i64(next_pc);
+ }
+ }
+#endif
+}
+
+static void per_branch_cond(DisasContext *s, TCGCond cond,
+ TCGv_i64 arg1, TCGv_i64 arg2)
+{
+#ifndef CONFIG_USER_ONLY
+ if (s->tb->flags & FLAG_MASK_PER) {
+ TCGLabel *lab = gen_new_label();
+ tcg_gen_brcond_i64(tcg_invert_cond(cond), arg1, arg2, lab);
+
+ tcg_gen_movi_i64(gbea, s->pc);
+ gen_helper_per_branch(cpu_env, gbea, psw_addr);
+
+ gen_set_label(lab);
+ } else {
+ TCGv_i64 pc = tcg_const_i64(s->pc);
+ tcg_gen_movcond_i64(cond, gbea, arg1, arg2, gbea, pc);
+ tcg_temp_free_i64(pc);
+ }
+#endif
+}
+
+static void per_breaking_event(DisasContext *s)
+{
+ tcg_gen_movi_i64(gbea, s->pc);
+}
+
static void update_cc_op(DisasContext *s)
{
if (s->cc_op != CC_OP_DYNAMIC && s->cc_op != CC_OP_STATIC) {
@@ -320,7 +364,21 @@ static void gen_program_exception(DisasContext *s, int code)
static inline void gen_illegal_opcode(DisasContext *s)
{
- gen_program_exception(s, PGM_SPECIFICATION);
+ gen_program_exception(s, PGM_OPERATION);
+}
+
+static inline void gen_trap(DisasContext *s)
+{
+ TCGv_i32 t;
+
+ /* Set DXC to 0xff. */
+ t = tcg_temp_new_i32();
+ tcg_gen_ld_i32(t, cpu_env, offsetof(CPUS390XState, fpc));
+ tcg_gen_ori_i32(t, t, 0xff00);
+ tcg_gen_st_i32(t, cpu_env, offsetof(CPUS390XState, fpc));
+ tcg_temp_free_i32(t);
+
+ gen_program_exception(s, PGM_DATA);
}
#ifndef CONFIG_USER_ONLY
@@ -554,7 +612,8 @@ static int use_goto_tb(DisasContext *s, uint64_t dest)
return (((dest & TARGET_PAGE_MASK) == (s->tb->pc & TARGET_PAGE_MASK)
|| (dest & TARGET_PAGE_MASK) == ((s->pc - 1) & TARGET_PAGE_MASK))
&& !s->singlestep_enabled
- && !(s->tb->cflags & CF_LAST_IO));
+ && !(s->tb->cflags & CF_LAST_IO)
+ && !(s->tb->flags & FLAG_MASK_PER));
}
static void account_noninline_branch(DisasContext *s, int cc_op)
@@ -987,6 +1046,7 @@ enum DisasFieldIndexC {
};
struct DisasFields {
+ uint64_t raw_insn;
unsigned op:8;
unsigned op2:8;
unsigned presentC:16;
@@ -1119,6 +1179,8 @@ typedef enum DisasFacility {
FAC_HFP_MA, /* HFP multiply-and-add/subtract */
FAC_HW, /* high-word */
FAC_IEEEE_SIM, /* IEEE exception sumilation */
+ FAC_MIE, /* miscellaneous-instruction-extensions */
+ FAC_LAT, /* load-and-trap */
FAC_LOC, /* load/store on condition */
FAC_LD, /* long displacement */
FAC_PC, /* population count */
@@ -1165,16 +1227,19 @@ static void help_l2_shift(DisasContext *s, DisasFields *f,
static ExitStatus help_goto_direct(DisasContext *s, uint64_t dest)
{
if (dest == s->next_pc) {
+ per_branch(s, true);
return NO_EXIT;
}
if (use_goto_tb(s, dest)) {
update_cc_op(s);
+ per_breaking_event(s);
tcg_gen_goto_tb(0);
tcg_gen_movi_i64(psw_addr, dest);
tcg_gen_exit_tb((uintptr_t)s->tb);
return EXIT_GOTO_TB;
} else {
tcg_gen_movi_i64(psw_addr, dest);
+ per_branch(s, false);
return EXIT_PC_UPDATED;
}
}
@@ -1194,6 +1259,7 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c,
if (is_imm) {
if (dest == s->next_pc) {
/* Branch to next. */
+ per_branch(s, true);
ret = NO_EXIT;
goto egress;
}
@@ -1209,6 +1275,7 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c,
}
if (c->cond == TCG_COND_ALWAYS) {
tcg_gen_mov_i64(psw_addr, cdest);
+ per_branch(s, false);
ret = EXIT_PC_UPDATED;
goto egress;
}
@@ -1233,6 +1300,7 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c,
/* Branch taken. */
gen_set_label(lab);
+ per_breaking_event(s);
tcg_gen_goto_tb(1);
tcg_gen_movi_i64(psw_addr, dest);
tcg_gen_exit_tb((uintptr_t)s->tb + 1);
@@ -1264,6 +1332,7 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c,
if (is_imm) {
tcg_gen_movi_i64(psw_addr, dest);
}
+ per_breaking_event(s);
ret = EXIT_PC_UPDATED;
}
} else {
@@ -1279,6 +1348,7 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c,
if (c->is_64) {
tcg_gen_movcond_i64(c->cond, psw_addr, c->u.s64.a, c->u.s64.b,
cdest, next);
+ per_branch_cond(s, c->cond, c->u.s64.a, c->u.s64.b);
} else {
TCGv_i32 t0 = tcg_temp_new_i32();
TCGv_i64 t1 = tcg_temp_new_i64();
@@ -1287,6 +1357,7 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c,
tcg_gen_extu_i32_i64(t1, t0);
tcg_temp_free_i32(t0);
tcg_gen_movcond_i64(TCG_COND_NE, psw_addr, t1, z, cdest, next);
+ per_branch_cond(s, TCG_COND_NE, t1, z);
tcg_temp_free_i64(t1);
tcg_temp_free_i64(z);
}
@@ -1310,7 +1381,13 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c,
static ExitStatus op_abs(DisasContext *s, DisasOps *o)
{
- gen_helper_abs_i64(o->out, o->in2);
+ TCGv_i64 z, n;
+ z = tcg_const_i64(0);
+ n = tcg_temp_new_i64();
+ tcg_gen_neg_i64(n, o->in2);
+ tcg_gen_movcond_i64(TCG_COND_LT, o->out, o->in2, z, n, o->in2);
+ tcg_temp_free_i64(n);
+ tcg_temp_free_i64(z);
return NO_EXIT;
}
@@ -1413,6 +1490,7 @@ static ExitStatus op_bas(DisasContext *s, DisasOps *o)
tcg_gen_movi_i64(o->out, pc_to_link_info(s, s->next_pc));
if (!TCGV_IS_UNUSED_I64(o->in2)) {
tcg_gen_mov_i64(psw_addr, o->in2);
+ per_branch(s, false);
return EXIT_PC_UPDATED;
} else {
return NO_EXIT;
@@ -1460,6 +1538,30 @@ static ExitStatus op_bct32(DisasContext *s, DisasOps *o)
return help_branch(s, &c, is_imm, imm, o->in2);
}
+static ExitStatus op_bcth(DisasContext *s, DisasOps *o)
+{
+ int r1 = get_field(s->fields, r1);
+ int imm = get_field(s->fields, i2);
+ DisasCompare c;
+ TCGv_i64 t;
+
+ c.cond = TCG_COND_NE;
+ c.is_64 = false;
+ c.g1 = false;
+ c.g2 = false;
+
+ t = tcg_temp_new_i64();
+ tcg_gen_shri_i64(t, regs[r1], 32);
+ tcg_gen_subi_i64(t, t, 1);
+ store_reg32h_i64(r1, t);
+ c.u.s32.a = tcg_temp_new_i32();
+ c.u.s32.b = tcg_const_i32(0);
+ tcg_gen_trunc_i64_i32(c.u.s32.a, t);
+ tcg_temp_free_i64(t);
+
+ return help_branch(s, &c, 1, imm, o->in2);
+}
+
static ExitStatus op_bct64(DisasContext *s, DisasOps *o)
{
int r1 = get_field(s->fields, r1);
@@ -1961,7 +2063,6 @@ static ExitStatus op_ct(DisasContext *s, DisasOps *o)
{
int m3 = get_field(s->fields, m3);
TCGLabel *lab = gen_new_label();
- TCGv_i32 t;
TCGCond c;
c = tcg_invert_cond(ltgt_cond[m3]);
@@ -1970,15 +2071,8 @@ static ExitStatus op_ct(DisasContext *s, DisasOps *o)
}
tcg_gen_brcond_i64(c, o->in1, o->in2, lab);
- /* Set DXC to 0xff. */
- t = tcg_temp_new_i32();
- tcg_gen_ld_i32(t, cpu_env, offsetof(CPUS390XState, fpc));
- tcg_gen_ori_i32(t, t, 0xff00);
- tcg_gen_st_i32(t, cpu_env, offsetof(CPUS390XState, fpc));
- tcg_temp_free_i32(t);
-
/* Trap. */
- gen_program_exception(s, PGM_DATA);
+ gen_trap(s);
gen_set_label(lab);
return NO_EXIT;
@@ -1987,15 +2081,19 @@ static ExitStatus op_ct(DisasContext *s, DisasOps *o)
#ifndef CONFIG_USER_ONLY
static ExitStatus op_diag(DisasContext *s, DisasOps *o)
{
- TCGv_i32 tmp;
+ TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
+ TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3));
+ TCGv_i32 func_code = tcg_const_i32(get_field(s->fields, i2));
check_privileged(s);
- potential_page_fault(s);
+ update_psw_addr(s);
+ gen_op_calc_cc(s);
- /* We pretend the format is RX_a so that D2 is the field we want. */
- tmp = tcg_const_i32(get_field(s->fields, d2) & 0xfff);
- gen_helper_diag(regs[2], cpu_env, tmp, regs[2], regs[1]);
- tcg_temp_free_i32(tmp);
+ gen_helper_diag(cpu_env, r1, r3, func_code);
+
+ tcg_temp_free_i32(func_code);
+ tcg_temp_free_i32(r3);
+ tcg_temp_free_i32(r1);
return NO_EXIT;
}
#endif
@@ -2101,13 +2199,37 @@ static ExitStatus op_ex(DisasContext *s, DisasOps *o)
TCGv_i64 tmp;
update_psw_addr(s);
- update_cc_op(s);
+ gen_op_calc_cc(s);
tmp = tcg_const_i64(s->next_pc);
gen_helper_ex(cc_op, cpu_env, cc_op, o->in1, o->in2, tmp);
tcg_temp_free_i64(tmp);
- set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_fieb(DisasContext *s, DisasOps *o)
+{
+ TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
+ gen_helper_fieb(o->out, cpu_env, o->in2, m3);
+ tcg_temp_free_i32(m3);
+ return NO_EXIT;
+}
+
+static ExitStatus op_fidb(DisasContext *s, DisasOps *o)
+{
+ TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
+ gen_helper_fidb(o->out, cpu_env, o->in2, m3);
+ tcg_temp_free_i32(m3);
+ return NO_EXIT;
+}
+
+static ExitStatus op_fixb(DisasContext *s, DisasOps *o)
+{
+ TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3));
+ gen_helper_fixb(o->out, cpu_env, o->in1, o->in2, m3);
+ return_low128(o->out2);
+ tcg_temp_free_i32(m3);
return NO_EXIT;
}
@@ -2320,6 +2442,61 @@ static ExitStatus op_ld64(DisasContext *s, DisasOps *o)
return NO_EXIT;
}
+static ExitStatus op_lat(DisasContext *s, DisasOps *o)
+{
+ TCGLabel *lab = gen_new_label();
+ store_reg32_i64(get_field(s->fields, r1), o->in2);
+ /* The value is stored even in case of trap. */
+ tcg_gen_brcondi_i64(TCG_COND_NE, o->in2, 0, lab);
+ gen_trap(s);
+ gen_set_label(lab);
+ return NO_EXIT;
+}
+
+static ExitStatus op_lgat(DisasContext *s, DisasOps *o)
+{
+ TCGLabel *lab = gen_new_label();
+ tcg_gen_qemu_ld64(o->out, o->in2, get_mem_index(s));
+ /* The value is stored even in case of trap. */
+ tcg_gen_brcondi_i64(TCG_COND_NE, o->out, 0, lab);
+ gen_trap(s);
+ gen_set_label(lab);
+ return NO_EXIT;
+}
+
+static ExitStatus op_lfhat(DisasContext *s, DisasOps *o)
+{
+ TCGLabel *lab = gen_new_label();
+ store_reg32h_i64(get_field(s->fields, r1), o->in2);
+ /* The value is stored even in case of trap. */
+ tcg_gen_brcondi_i64(TCG_COND_NE, o->in2, 0, lab);
+ gen_trap(s);
+ gen_set_label(lab);
+ return NO_EXIT;
+}
+
+static ExitStatus op_llgfat(DisasContext *s, DisasOps *o)
+{
+ TCGLabel *lab = gen_new_label();
+ tcg_gen_qemu_ld32u(o->out, o->in2, get_mem_index(s));
+ /* The value is stored even in case of trap. */
+ tcg_gen_brcondi_i64(TCG_COND_NE, o->out, 0, lab);
+ gen_trap(s);
+ gen_set_label(lab);
+ return NO_EXIT;
+}
+
+static ExitStatus op_llgtat(DisasContext *s, DisasOps *o)
+{
+ TCGLabel *lab = gen_new_label();
+ tcg_gen_andi_i64(o->out, o->in2, 0x7fffffff);
+ /* The value is stored even in case of trap. */
+ tcg_gen_brcondi_i64(TCG_COND_NE, o->out, 0, lab);
+ gen_trap(s);
+ gen_set_label(lab);
+ return NO_EXIT;
+}
+
static ExitStatus op_loc(DisasContext *s, DisasOps *o)
{
DisasCompare c;
@@ -2388,6 +2565,7 @@ static ExitStatus op_lpsw(DisasContext *s, DisasOps *o)
TCGv_i64 t1, t2;
check_privileged(s);
+ per_breaking_event(s);
t1 = tcg_temp_new_i64();
t2 = tcg_temp_new_i64();
@@ -2407,6 +2585,7 @@ static ExitStatus op_lpswe(DisasContext *s, DisasOps *o)
TCGv_i64 t1, t2;
check_privileged(s);
+ per_breaking_event(s);
t1 = tcg_temp_new_i64();
t2 = tcg_temp_new_i64();
@@ -2435,21 +2614,45 @@ static ExitStatus op_lm32(DisasContext *s, DisasOps *o)
{
int r1 = get_field(s->fields, r1);
int r3 = get_field(s->fields, r3);
- TCGv_i64 t = tcg_temp_new_i64();
- TCGv_i64 t4 = tcg_const_i64(4);
+ TCGv_i64 t1, t2;
- while (1) {
- tcg_gen_qemu_ld32u(t, o->in2, get_mem_index(s));
- store_reg32_i64(r1, t);
- if (r1 == r3) {
- break;
- }
- tcg_gen_add_i64(o->in2, o->in2, t4);
+ /* Only one register to read. */
+ t1 = tcg_temp_new_i64();
+ if (unlikely(r1 == r3)) {
+ tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s));
+ store_reg32_i64(r1, t1);
+ tcg_temp_free(t1);
+ return NO_EXIT;
+ }
+
+ /* First load the values of the first and last registers to trigger
+ possible page faults. */
+ t2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s));
+ tcg_gen_addi_i64(t2, o->in2, 4 * ((r3 - r1) & 15));
+ tcg_gen_qemu_ld32u(t2, t2, get_mem_index(s));
+ store_reg32_i64(r1, t1);
+ store_reg32_i64(r3, t2);
+
+ /* Only two registers to read. */
+ if (((r1 + 1) & 15) == r3) {
+ tcg_temp_free(t2);
+ tcg_temp_free(t1);
+ return NO_EXIT;
+ }
+
+ /* Then load the remaining registers. Page fault can't occur. */
+ r3 = (r3 - 1) & 15;
+ tcg_gen_movi_i64(t2, 4);
+ while (r1 != r3) {
r1 = (r1 + 1) & 15;
+ tcg_gen_add_i64(o->in2, o->in2, t2);
+ tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s));
+ store_reg32_i64(r1, t1);
}
+ tcg_temp_free(t2);
+ tcg_temp_free(t1);
- tcg_temp_free_i64(t);
- tcg_temp_free_i64(t4);
return NO_EXIT;
}
@@ -2457,21 +2660,45 @@ static ExitStatus op_lmh(DisasContext *s, DisasOps *o)
{
int r1 = get_field(s->fields, r1);
int r3 = get_field(s->fields, r3);
- TCGv_i64 t = tcg_temp_new_i64();
- TCGv_i64 t4 = tcg_const_i64(4);
+ TCGv_i64 t1, t2;
- while (1) {
- tcg_gen_qemu_ld32u(t, o->in2, get_mem_index(s));
- store_reg32h_i64(r1, t);
- if (r1 == r3) {
- break;
- }
- tcg_gen_add_i64(o->in2, o->in2, t4);
+ /* Only one register to read. */
+ t1 = tcg_temp_new_i64();
+ if (unlikely(r1 == r3)) {
+ tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s));
+ store_reg32h_i64(r1, t1);
+ tcg_temp_free(t1);
+ return NO_EXIT;
+ }
+
+ /* First load the values of the first and last registers to trigger
+ possible page faults. */
+ t2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s));
+ tcg_gen_addi_i64(t2, o->in2, 4 * ((r3 - r1) & 15));
+ tcg_gen_qemu_ld32u(t2, t2, get_mem_index(s));
+ store_reg32h_i64(r1, t1);
+ store_reg32h_i64(r3, t2);
+
+ /* Only two registers to read. */
+ if (((r1 + 1) & 15) == r3) {
+ tcg_temp_free(t2);
+ tcg_temp_free(t1);
+ return NO_EXIT;
+ }
+
+ /* Then load the remaining registers. Page fault can't occur. */
+ r3 = (r3 - 1) & 15;
+ tcg_gen_movi_i64(t2, 4);
+ while (r1 != r3) {
r1 = (r1 + 1) & 15;
+ tcg_gen_add_i64(o->in2, o->in2, t2);
+ tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s));
+ store_reg32h_i64(r1, t1);
}
+ tcg_temp_free(t2);
+ tcg_temp_free(t1);
- tcg_temp_free_i64(t);
- tcg_temp_free_i64(t4);
return NO_EXIT;
}
@@ -2479,18 +2706,40 @@ static ExitStatus op_lm64(DisasContext *s, DisasOps *o)
{
int r1 = get_field(s->fields, r1);
int r3 = get_field(s->fields, r3);
- TCGv_i64 t8 = tcg_const_i64(8);
+ TCGv_i64 t1, t2;
- while (1) {
+ /* Only one register to read. */
+ if (unlikely(r1 == r3)) {
tcg_gen_qemu_ld64(regs[r1], o->in2, get_mem_index(s));
- if (r1 == r3) {
- break;
- }
- tcg_gen_add_i64(o->in2, o->in2, t8);
+ return NO_EXIT;
+ }
+
+ /* First load the values of the first and last registers to trigger
+ possible page faults. */
+ t1 = tcg_temp_new_i64();
+ t2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld64(t1, o->in2, get_mem_index(s));
+ tcg_gen_addi_i64(t2, o->in2, 8 * ((r3 - r1) & 15));
+ tcg_gen_qemu_ld64(regs[r3], t2, get_mem_index(s));
+ tcg_gen_mov_i64(regs[r1], t1);
+ tcg_temp_free(t2);
+
+ /* Only two registers to read. */
+ if (((r1 + 1) & 15) == r3) {
+ tcg_temp_free(t1);
+ return NO_EXIT;
+ }
+
+ /* Then load the remaining registers. Page fault can't occur. */
+ r3 = (r3 - 1) & 15;
+ tcg_gen_movi_i64(t1, 8);
+ while (r1 != r3) {
r1 = (r1 + 1) & 15;
+ tcg_gen_add_i64(o->in2, o->in2, t1);
+ tcg_gen_qemu_ld64(regs[r1], o->in2, get_mem_index(s));
}
+ tcg_temp_free(t1);
- tcg_temp_free_i64(t8);
return NO_EXIT;
}
@@ -2521,6 +2770,41 @@ static ExitStatus op_mov2(DisasContext *s, DisasOps *o)
return NO_EXIT;
}
+static ExitStatus op_mov2e(DisasContext *s, DisasOps *o)
+{
+ int b2 = get_field(s->fields, b2);
+ TCGv ar1 = tcg_temp_new_i64();
+
+ o->out = o->in2;
+ o->g_out = o->g_in2;
+ TCGV_UNUSED_I64(o->in2);
+ o->g_in2 = false;
+
+ switch (s->tb->flags & FLAG_MASK_ASC) {
+ case PSW_ASC_PRIMARY >> 32:
+ tcg_gen_movi_i64(ar1, 0);
+ break;
+ case PSW_ASC_ACCREG >> 32:
+ tcg_gen_movi_i64(ar1, 1);
+ break;
+ case PSW_ASC_SECONDARY >> 32:
+ if (b2) {
+ tcg_gen_ld32u_i64(ar1, cpu_env, offsetof(CPUS390XState, aregs[b2]));
+ } else {
+ tcg_gen_movi_i64(ar1, 0);
+ }
+ break;
+ case PSW_ASC_HOME >> 32:
+ tcg_gen_movi_i64(ar1, 2);
+ break;
+ }
+
+ tcg_gen_st32_i64(ar1, cpu_env, offsetof(CPUS390XState, aregs[1]));
+ tcg_temp_free_i64(ar1);
+
+ return NO_EXIT;
+}
+
static ExitStatus op_movx(DisasContext *s, DisasOps *o)
{
o->out = o->in1;
@@ -2681,7 +2965,13 @@ static ExitStatus op_msdb(DisasContext *s, DisasOps *o)
static ExitStatus op_nabs(DisasContext *s, DisasOps *o)
{
- gen_helper_nabs_i64(o->out, o->in2);
+ TCGv_i64 z, n;
+ z = tcg_const_i64(0);
+ n = tcg_temp_new_i64();
+ tcg_gen_neg_i64(n, o->in2);
+ tcg_gen_movcond_i64(TCG_COND_GE, o->out, o->in2, z, n, o->in2);
+ tcg_temp_free_i64(n);
+ tcg_temp_free_i64(z);
return NO_EXIT;
}
@@ -3356,11 +3646,93 @@ static ExitStatus op_spx(DisasContext *s, DisasOps *o)
return NO_EXIT;
}
-static ExitStatus op_subchannel(DisasContext *s, DisasOps *o)
+static ExitStatus op_xsch(DisasContext *s, DisasOps *o)
+{
+ check_privileged(s);
+ potential_page_fault(s);
+ gen_helper_xsch(cpu_env, regs[1]);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_csch(DisasContext *s, DisasOps *o)
+{
+ check_privileged(s);
+ potential_page_fault(s);
+ gen_helper_csch(cpu_env, regs[1]);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_hsch(DisasContext *s, DisasOps *o)
{
check_privileged(s);
- /* Not operational. */
- gen_op_movi_cc(s, 3);
+ potential_page_fault(s);
+ gen_helper_hsch(cpu_env, regs[1]);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_msch(DisasContext *s, DisasOps *o)
+{
+ check_privileged(s);
+ potential_page_fault(s);
+ gen_helper_msch(cpu_env, regs[1], o->in2);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_rchp(DisasContext *s, DisasOps *o)
+{
+ check_privileged(s);
+ potential_page_fault(s);
+ gen_helper_rchp(cpu_env, regs[1]);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_rsch(DisasContext *s, DisasOps *o)
+{
+ check_privileged(s);
+ potential_page_fault(s);
+ gen_helper_rsch(cpu_env, regs[1]);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_ssch(DisasContext *s, DisasOps *o)
+{
+ check_privileged(s);
+ potential_page_fault(s);
+ gen_helper_ssch(cpu_env, regs[1], o->in2);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_stsch(DisasContext *s, DisasOps *o)
+{
+ check_privileged(s);
+ potential_page_fault(s);
+ gen_helper_stsch(cpu_env, regs[1], o->in2);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_tsch(DisasContext *s, DisasOps *o)
+{
+ check_privileged(s);
+ potential_page_fault(s);
+ gen_helper_tsch(cpu_env, regs[1], o->in2);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_chsc(DisasContext *s, DisasOps *o)
+{
+ check_privileged(s);
+ potential_page_fault(s);
+ gen_helper_chsc(cpu_env, o->in2);
+ set_cc_static(s);
return NO_EXIT;
}
@@ -3646,6 +4018,25 @@ static ExitStatus op_tr(DisasContext *s, DisasOps *o)
return NO_EXIT;
}
+static ExitStatus op_tre(DisasContext *s, DisasOps *o)
+{
+ potential_page_fault(s);
+ gen_helper_tre(o->out, cpu_env, o->out, o->out2, o->in2);
+ return_low128(o->out2);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
+static ExitStatus op_trt(DisasContext *s, DisasOps *o)
+{
+ TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1));
+ potential_page_fault(s);
+ gen_helper_trt(cc_op, cpu_env, l, o->addr1, o->in2);
+ tcg_temp_free_i32(l);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
static ExitStatus op_unpk(DisasContext *s, DisasOps *o)
{
TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1));
@@ -3989,6 +4380,12 @@ static void wout_r1_32(DisasContext *s, DisasFields *f, DisasOps *o)
}
#define SPEC_wout_r1_32 0
+static void wout_r1_32h(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+ store_reg32h_i64(get_field(f, r1), o->out);
+}
+#define SPEC_wout_r1_32h 0
+
static void wout_r1_P32(DisasContext *s, DisasFields *f, DisasOps *o)
{
int r1 = get_field(f, r1);
@@ -4159,6 +4556,13 @@ static void in1_r2(DisasContext *s, DisasFields *f, DisasOps *o)
}
#define SPEC_in1_r2 0
+static void in1_r2_sr32(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+ o->in1 = tcg_temp_new_i64();
+ tcg_gen_shri_i64(o->in1, regs[get_field(f, r2)], 32);
+}
+#define SPEC_in1_r2_sr32 0
+
static void in1_r3(DisasContext *s, DisasFields *f, DisasOps *o)
{
o->in1 = load_reg(get_field(f, r3));
@@ -4372,6 +4776,13 @@ static void in2_r3(DisasContext *s, DisasFields *f, DisasOps *o)
}
#define SPEC_in2_r3 0
+static void in2_r3_sr32(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+ o->in2 = tcg_temp_new_i64();
+ tcg_gen_shri_i64(o->in2, regs[get_field(f, r3)], 32);
+}
+#define SPEC_in2_r3_sr32 0
+
static void in2_r2_32s(DisasContext *s, DisasFields *f, DisasOps *o)
{
o->in2 = tcg_temp_new_i64();
@@ -4386,6 +4797,13 @@ static void in2_r2_32u(DisasContext *s, DisasFields *f, DisasOps *o)
}
#define SPEC_in2_r2_32u 0
+static void in2_r2_sr32(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+ o->in2 = tcg_temp_new_i64();
+ tcg_gen_shri_i64(o->in2, regs[get_field(f, r2)], 32);
+}
+#define SPEC_in2_r2_sr32 0
+
static void in2_e2(DisasContext *s, DisasFields *f, DisasOps *o)
{
o->in2 = load_freg32_i64(get_field(f, r2));
@@ -4565,6 +4983,14 @@ static void in2_i2_32u_shl(DisasContext *s, DisasFields *f, DisasOps *o)
}
#define SPEC_in2_i2_32u_shl 0
+#ifndef CONFIG_USER_ONLY
+static void in2_insn(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+ o->in2 = tcg_const_i64(s->fields->raw_insn);
+}
+#define SPEC_in2_insn 0
+#endif
+
/* ====================================================================== */
/* Find opc within the table of insns. This is formulated as a switch
@@ -4741,6 +5167,7 @@ static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s,
}
memset(f, 0, sizeof(*f));
+ f->raw_insn = insn;
f->op = op;
f->op2 = op2;
@@ -4777,6 +5204,14 @@ static ExitStatus translate_one(CPUS390XState *env, DisasContext *s)
return EXIT_NORETURN;
}
+#ifndef CONFIG_USER_ONLY
+ if (s->tb->flags & FLAG_MASK_PER) {
+ TCGv_i64 addr = tcg_const_i64(s->pc);
+ gen_helper_per_ifetch(cpu_env, addr);
+ tcg_temp_free_i64(addr);
+ }
+#endif
+
/* Check for insn specification exceptions. */
if (insn->spec) {
int spec = insn->spec, excp = 0, r;
@@ -4864,6 +5299,21 @@ static ExitStatus translate_one(CPUS390XState *env, DisasContext *s)
tcg_temp_free_i64(o.addr1);
}
+#ifndef CONFIG_USER_ONLY
+ if (s->tb->flags & FLAG_MASK_PER) {
+ /* An exception might be triggered, save PSW if not already done. */
+ if (ret == NO_EXIT || ret == EXIT_PC_STALE) {
+ tcg_gen_movi_i64(psw_addr, s->next_pc);
+ }
+
+ /* Save off cc. */
+ update_cc_op(s);
+
+ /* Call the helper to check for a possible PER exception. */
+ gen_helper_per_check_exception(cpu_env);
+ }
+#endif
+
/* Advance to the next instruction. */
s->pc = s->next_pc;
return ret;
diff --git a/target-sh4/cpu.c b/target-sh4/cpu.c
index d187a2bdba..cccb14fe7b 100644
--- a/target-sh4/cpu.c
+++ b/target-sh4/cpu.c
@@ -61,7 +61,8 @@ static void superh_cpu_reset(CPUState *s)
env->fpscr = FPSCR_PR; /* value for userspace according to the kernel */
set_float_rounding_mode(float_round_nearest_even, &env->fp_status); /* ?! */
#else
- env->sr = SR_MD | SR_RB | SR_BL | SR_I3 | SR_I2 | SR_I1 | SR_I0;
+ env->sr = (1u << SR_MD) | (1u << SR_RB) | (1u << SR_BL) |
+ (1u << SR_I3) | (1u << SR_I2) | (1u << SR_I1) | (1u << SR_I0);
env->fpscr = FPSCR_DN | FPSCR_RM_ZERO; /* CPU reset value according to SH4 manual */
set_float_rounding_mode(float_round_to_zero, &env->fp_status);
set_flush_to_zero(1, &env->fp_status);
diff --git a/target-sh4/cpu.h b/target-sh4/cpu.h
index c8dea6c020..4a027a6c1c 100644
--- a/target-sh4/cpu.h
+++ b/target-sh4/cpu.h
@@ -47,18 +47,18 @@
#define TARGET_PHYS_ADDR_SPACE_BITS 32
#define TARGET_VIRT_ADDR_SPACE_BITS 32
-#define SR_MD (1 << 30)
-#define SR_RB (1 << 29)
-#define SR_BL (1 << 28)
-#define SR_FD (1 << 15)
-#define SR_M (1 << 9)
-#define SR_Q (1 << 8)
-#define SR_I3 (1 << 7)
-#define SR_I2 (1 << 6)
-#define SR_I1 (1 << 5)
-#define SR_I0 (1 << 4)
-#define SR_S (1 << 1)
-#define SR_T (1 << 0)
+#define SR_MD 30
+#define SR_RB 29
+#define SR_BL 28
+#define SR_FD 15
+#define SR_M 9
+#define SR_Q 8
+#define SR_I3 7
+#define SR_I2 6
+#define SR_I1 5
+#define SR_I0 4
+#define SR_S 1
+#define SR_T 0
#define FPSCR_MASK (0x003fffff)
#define FPSCR_FR (1 << 21)
@@ -138,7 +138,10 @@ typedef struct CPUSH4State {
uint32_t flags; /* general execution flags */
uint32_t gregs[24]; /* general registers */
float32 fregs[32]; /* floating point registers */
- uint32_t sr; /* status register */
+ uint32_t sr; /* status register (with T split out) */
+ uint32_t sr_m; /* M bit of status register */
+ uint32_t sr_q; /* Q bit of status register */
+ uint32_t sr_t; /* T bit of status register */
uint32_t ssr; /* saved status register */
uint32_t spc; /* saved program counter */
uint32_t gbr; /* global base register */
@@ -234,7 +237,7 @@ void cpu_load_tlb(CPUSH4State * env);
#define MMU_USER_IDX 1
static inline int cpu_mmu_index (CPUSH4State *env)
{
- return (env->sr & SR_MD) == 0 ? 1 : 0;
+ return (env->sr & (1u << SR_MD)) == 0 ? 1 : 0;
}
#include "exec/cpu-all.h"
@@ -331,6 +334,21 @@ static inline int cpu_ptel_pr (uint32_t ptel)
#define TB_FLAG_PENDING_MOVCA (1 << 4)
+static inline target_ulong cpu_read_sr(CPUSH4State *env)
+{
+ return env->sr | (env->sr_m << SR_M) |
+ (env->sr_q << SR_Q) |
+ (env->sr_t << SR_T);
+}
+
+static inline void cpu_write_sr(CPUSH4State *env, target_ulong sr)
+{
+ env->sr_m = (sr >> SR_M) & 1;
+ env->sr_q = (sr >> SR_Q) & 1;
+ env->sr_t = (sr >> SR_T) & 1;
+ env->sr = sr & ~((1u << SR_M) | (1u << SR_Q) | (1u << SR_T));
+}
+
static inline void cpu_get_tb_cpu_state(CPUSH4State *env, target_ulong *pc,
target_ulong *cs_base, int *flags)
{
@@ -339,8 +357,8 @@ static inline void cpu_get_tb_cpu_state(CPUSH4State *env, target_ulong *pc,
*flags = (env->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL
| DELAY_SLOT_TRUE | DELAY_SLOT_CLEARME)) /* Bits 0- 3 */
| (env->fpscr & (FPSCR_FR | FPSCR_SZ | FPSCR_PR)) /* Bits 19-21 */
- | (env->sr & (SR_MD | SR_RB)) /* Bits 29-30 */
- | (env->sr & SR_FD) /* Bit 15 */
+ | (env->sr & ((1u << SR_MD) | (1u << SR_RB))) /* Bits 29-30 */
+ | (env->sr & (1u << SR_FD)) /* Bit 15 */
| (env->movcal_backup ? TB_FLAG_PENDING_MOVCA : 0); /* Bit 4 */
}
diff --git a/target-sh4/gdbstub.c b/target-sh4/gdbstub.c
index df4fa2af76..a365a27aad 100644
--- a/target-sh4/gdbstub.c
+++ b/target-sh4/gdbstub.c
@@ -31,7 +31,7 @@ int superh_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
switch (n) {
case 0 ... 7:
- if ((env->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB)) {
+ if ((env->sr & (1u << SR_MD)) && (env->sr & (1u << SR_RB))) {
return gdb_get_regl(mem_buf, env->gregs[n + 16]);
} else {
return gdb_get_regl(mem_buf, env->gregs[n]);
@@ -51,7 +51,7 @@ int superh_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
case 21:
return gdb_get_regl(mem_buf, env->macl);
case 22:
- return gdb_get_regl(mem_buf, env->sr);
+ return gdb_get_regl(mem_buf, cpu_read_sr(env));
case 23:
return gdb_get_regl(mem_buf, env->fpul);
case 24:
@@ -83,7 +83,7 @@ int superh_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
switch (n) {
case 0 ... 7:
- if ((env->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB)) {
+ if ((env->sr & (1u << SR_MD)) && (env->sr & (1u << SR_RB))) {
env->gregs[n + 16] = ldl_p(mem_buf);
} else {
env->gregs[n] = ldl_p(mem_buf);
@@ -111,7 +111,7 @@ int superh_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
env->macl = ldl_p(mem_buf);
break;
case 22:
- env->sr = ldl_p(mem_buf);
+ cpu_write_sr(env, ldl_p(mem_buf));
break;
case 23:
env->fpul = ldl_p(mem_buf);
diff --git a/target-sh4/helper.c b/target-sh4/helper.c
index 58113601ec..a533f08ea3 100644
--- a/target-sh4/helper.c
+++ b/target-sh4/helper.c
@@ -93,7 +93,7 @@ void superh_cpu_do_interrupt(CPUState *cs)
do_exp = cs->exception_index != -1;
do_irq = do_irq && (cs->exception_index == -1);
- if (env->sr & SR_BL) {
+ if (env->sr & (1u << SR_BL)) {
if (do_exp && cs->exception_index != 0x1e0) {
cs->exception_index = 0x000; /* masked exception -> reset */
}
@@ -162,10 +162,10 @@ void superh_cpu_do_interrupt(CPUState *cs)
log_cpu_state(cs, 0);
}
- env->ssr = env->sr;
+ env->ssr = cpu_read_sr(env);
env->spc = env->pc;
env->sgr = env->gregs[15];
- env->sr |= SR_BL | SR_MD | SR_RB;
+ env->sr |= (1u << SR_BL) | (1u << SR_MD) | (1u << SR_RB);
if (env->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) {
/* Branch instruction should be executed again before delay slot. */
@@ -182,7 +182,7 @@ void superh_cpu_do_interrupt(CPUState *cs)
case 0x000:
case 0x020:
case 0x140:
- env->sr &= ~SR_FD;
+ env->sr &= ~(1u << SR_FD);
env->sr |= 0xf << 4; /* IMASK */
env->pc = 0xa0000000;
break;
@@ -355,23 +355,24 @@ static int get_mmu_address(CPUSH4State * env, target_ulong * physical,
int use_asid, n;
tlb_t *matching = NULL;
- use_asid = (env->mmucr & MMUCR_SV) == 0 || (env->sr & SR_MD) == 0;
+ use_asid = !(env->mmucr & MMUCR_SV) || !(env->sr & (1u << SR_MD));
if (rw == 2) {
n = find_itlb_entry(env, address, use_asid);
if (n >= 0) {
matching = &env->itlb[n];
- if (!(env->sr & SR_MD) && !(matching->pr & 2))
+ if (!(env->sr & (1u << SR_MD)) && !(matching->pr & 2)) {
n = MMU_ITLB_VIOLATION;
- else
+ } else {
*prot = PAGE_EXEC;
+ }
} else {
n = find_utlb_entry(env, address, use_asid);
if (n >= 0) {
n = copy_utlb_entry_itlb(env, n);
matching = &env->itlb[n];
- if (!(env->sr & SR_MD) && !(matching->pr & 2)) {
- n = MMU_ITLB_VIOLATION;
+ if (!(env->sr & (1u << SR_MD)) && !(matching->pr & 2)) {
+ n = MMU_ITLB_VIOLATION;
} else {
*prot = PAGE_READ | PAGE_EXEC;
if ((matching->pr & 1) && matching->d) {
@@ -388,7 +389,7 @@ static int get_mmu_address(CPUSH4State * env, target_ulong * physical,
n = find_utlb_entry(env, address, use_asid);
if (n >= 0) {
matching = &env->utlb[n];
- if (!(env->sr & SR_MD) && !(matching->pr & 2)) {
+ if (!(env->sr & (1u << SR_MD)) && !(matching->pr & 2)) {
n = (rw == 1) ? MMU_DTLB_VIOLATION_WRITE :
MMU_DTLB_VIOLATION_READ;
} else if ((rw == 1) && !(matching->pr & 1)) {
@@ -421,7 +422,7 @@ static int get_physical_address(CPUSH4State * env, target_ulong * physical,
/* P1, P2 and P4 areas do not use translation */
if ((address >= 0x80000000 && address < 0xc0000000) ||
address >= 0xe0000000) {
- if (!(env->sr & SR_MD)
+ if (!(env->sr & (1u << SR_MD))
&& (address < 0xe0000000 || address >= 0xe4000000)) {
/* Unauthorized access in user mode (only store queues are available) */
fprintf(stderr, "Unauthorized access\n");
@@ -690,7 +691,7 @@ void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, hwaddr addr,
uint8_t d = (uint8_t)((mem_value & 0x00000200) >> 9);
uint8_t v = (uint8_t)((mem_value & 0x00000100) >> 8);
uint8_t asid = (uint8_t)(mem_value & 0x000000ff);
- int use_asid = (s->mmucr & MMUCR_SV) == 0 || (s->sr & SR_MD) == 0;
+ int use_asid = !(s->mmucr & MMUCR_SV) || !(s->sr & (1u << SR_MD));
if (associate) {
int i;
@@ -821,10 +822,10 @@ void cpu_sh4_write_mmaped_utlb_data(CPUSH4State *s, hwaddr addr,
int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr)
{
int n;
- int use_asid = (env->mmucr & MMUCR_SV) == 0 || (env->sr & SR_MD) == 0;
+ int use_asid = !(env->mmucr & MMUCR_SV) || !(env->sr & (1u << SR_MD));
/* check area */
- if (env->sr & SR_MD) {
+ if (env->sr & (1u << SR_MD)) {
/* For previledged mode, P2 and P4 area is not cachable. */
if ((0xA0000000 <= addr && addr < 0xC0000000) || 0xE0000000 <= addr)
return 0;
diff --git a/target-sh4/helper.h b/target-sh4/helper.h
index 3b5c436ab4..c9bc407042 100644
--- a/target-sh4/helper.h
+++ b/target-sh4/helper.h
@@ -11,7 +11,6 @@ DEF_HELPER_3(movcal, void, env, i32, i32)
DEF_HELPER_1(discard_movcal_backup, void, env)
DEF_HELPER_2(ocbi, void, env, i32)
-DEF_HELPER_3(div1, i32, env, i32, i32)
DEF_HELPER_3(macl, void, env, i32, i32)
DEF_HELPER_3(macw, void, env, i32, i32)
diff --git a/target-sh4/op_helper.c b/target-sh4/op_helper.c
index 74a5c4ea77..cbc11aeccd 100644
--- a/target-sh4/op_helper.c
+++ b/target-sh4/op_helper.c
@@ -156,124 +156,6 @@ void helper_ocbi(CPUSH4State *env, uint32_t address)
}
}
-#define T (env->sr & SR_T)
-#define Q (env->sr & SR_Q ? 1 : 0)
-#define M (env->sr & SR_M ? 1 : 0)
-#define SETT env->sr |= SR_T
-#define CLRT env->sr &= ~SR_T
-#define SETQ env->sr |= SR_Q
-#define CLRQ env->sr &= ~SR_Q
-#define SETM env->sr |= SR_M
-#define CLRM env->sr &= ~SR_M
-
-uint32_t helper_div1(CPUSH4State *env, uint32_t arg0, uint32_t arg1)
-{
- uint32_t tmp0, tmp2;
- uint8_t old_q, tmp1 = 0xff;
-
- //printf("div1 arg0=0x%08x arg1=0x%08x M=%d Q=%d T=%d\n", arg0, arg1, M, Q, T);
- old_q = Q;
- if ((0x80000000 & arg1) != 0)
- SETQ;
- else
- CLRQ;
- tmp2 = arg0;
- arg1 <<= 1;
- arg1 |= T;
- switch (old_q) {
- case 0:
- switch (M) {
- case 0:
- tmp0 = arg1;
- arg1 -= tmp2;
- tmp1 = arg1 > tmp0;
- switch (Q) {
- case 0:
- if (tmp1)
- SETQ;
- else
- CLRQ;
- break;
- case 1:
- if (tmp1 == 0)
- SETQ;
- else
- CLRQ;
- break;
- }
- break;
- case 1:
- tmp0 = arg1;
- arg1 += tmp2;
- tmp1 = arg1 < tmp0;
- switch (Q) {
- case 0:
- if (tmp1 == 0)
- SETQ;
- else
- CLRQ;
- break;
- case 1:
- if (tmp1)
- SETQ;
- else
- CLRQ;
- break;
- }
- break;
- }
- break;
- case 1:
- switch (M) {
- case 0:
- tmp0 = arg1;
- arg1 += tmp2;
- tmp1 = arg1 < tmp0;
- switch (Q) {
- case 0:
- if (tmp1)
- SETQ;
- else
- CLRQ;
- break;
- case 1:
- if (tmp1 == 0)
- SETQ;
- else
- CLRQ;
- break;
- }
- break;
- case 1:
- tmp0 = arg1;
- arg1 -= tmp2;
- tmp1 = arg1 > tmp0;
- switch (Q) {
- case 0:
- if (tmp1 == 0)
- SETQ;
- else
- CLRQ;
- break;
- case 1:
- if (tmp1)
- SETQ;
- else
- CLRQ;
- break;
- }
- break;
- }
- break;
- }
- if (Q == M)
- SETT;
- else
- CLRT;
- //printf("Output: arg1=0x%08x M=%d Q=%d T=%d\n", arg1, M, Q, T);
- return arg1;
-}
-
void helper_macl(CPUSH4State *env, uint32_t arg0, uint32_t arg1)
{
int64_t res;
@@ -282,7 +164,7 @@ void helper_macl(CPUSH4State *env, uint32_t arg0, uint32_t arg1)
res += (int64_t) (int32_t) arg0 *(int64_t) (int32_t) arg1;
env->mach = (res >> 32) & 0xffffffff;
env->macl = res & 0xffffffff;
- if (env->sr & SR_S) {
+ if (env->sr & (1u << SR_S)) {
if (res < 0)
env->mach |= 0xffff0000;
else
@@ -298,7 +180,7 @@ void helper_macw(CPUSH4State *env, uint32_t arg0, uint32_t arg1)
res += (int64_t) (int16_t) arg0 *(int64_t) (int16_t) arg1;
env->mach = (res >> 32) & 0xffffffff;
env->macl = res & 0xffffffff;
- if (env->sr & SR_S) {
+ if (env->sr & (1u << SR_S)) {
if (res < -0x80000000) {
env->mach = 1;
env->macl = 0x80000000;
@@ -309,16 +191,6 @@ void helper_macw(CPUSH4State *env, uint32_t arg0, uint32_t arg1)
}
}
-static inline void set_t(CPUSH4State *env)
-{
- env->sr |= SR_T;
-}
-
-static inline void clr_t(CPUSH4State *env)
-{
- env->sr &= ~SR_T;
-}
-
void helper_ld_fpscr(CPUSH4State *env, uint32_t val)
{
env->fpscr = val & FPSCR_MASK;
@@ -403,10 +275,8 @@ void helper_fcmp_eq_FT(CPUSH4State *env, float32 t0, float32 t1)
relation = float32_compare(t0, t1, &env->fp_status);
if (unlikely(relation == float_relation_unordered)) {
update_fpscr(env, GETPC());
- } else if (relation == float_relation_equal) {
- set_t(env);
} else {
- clr_t(env);
+ env->sr_t = (relation == float_relation_equal);
}
}
@@ -418,10 +288,8 @@ void helper_fcmp_eq_DT(CPUSH4State *env, float64 t0, float64 t1)
relation = float64_compare(t0, t1, &env->fp_status);
if (unlikely(relation == float_relation_unordered)) {
update_fpscr(env, GETPC());
- } else if (relation == float_relation_equal) {
- set_t(env);
} else {
- clr_t(env);
+ env->sr_t = (relation == float_relation_equal);
}
}
@@ -433,10 +301,8 @@ void helper_fcmp_gt_FT(CPUSH4State *env, float32 t0, float32 t1)
relation = float32_compare(t0, t1, &env->fp_status);
if (unlikely(relation == float_relation_unordered)) {
update_fpscr(env, GETPC());
- } else if (relation == float_relation_greater) {
- set_t(env);
} else {
- clr_t(env);
+ env->sr_t = (relation == float_relation_greater);
}
}
@@ -448,10 +314,8 @@ void helper_fcmp_gt_DT(CPUSH4State *env, float64 t0, float64 t1)
relation = float64_compare(t0, t1, &env->fp_status);
if (unlikely(relation == float_relation_unordered)) {
update_fpscr(env, GETPC());
- } else if (relation == float_relation_greater) {
- set_t(env);
} else {
- clr_t(env);
+ env->sr_t = (relation == float_relation_greater);
}
}
diff --git a/target-sh4/translate.c b/target-sh4/translate.c
index 41aa928321..28259f9e14 100644
--- a/target-sh4/translate.c
+++ b/target-sh4/translate.c
@@ -18,7 +18,6 @@
*/
#define DEBUG_DISAS
-//#define SH4_SINGLE_STEP
#include "cpu.h"
#include "disas/disas.h"
@@ -47,7 +46,7 @@ typedef struct DisasContext {
#if defined(CONFIG_USER_ONLY)
#define IS_USER(ctx) 1
#else
-#define IS_USER(ctx) (!(ctx->flags & SR_MD))
+#define IS_USER(ctx) (!(ctx->flags & (1u << SR_MD)))
#endif
enum {
@@ -62,7 +61,8 @@ enum {
/* global register indexes */
static TCGv_ptr cpu_env;
static TCGv cpu_gregs[24];
-static TCGv cpu_pc, cpu_sr, cpu_ssr, cpu_spc, cpu_gbr;
+static TCGv cpu_sr, cpu_sr_m, cpu_sr_q, cpu_sr_t;
+static TCGv cpu_pc, cpu_ssr, cpu_spc, cpu_gbr;
static TCGv cpu_vbr, cpu_sgr, cpu_dbr, cpu_mach, cpu_macl;
static TCGv cpu_pr, cpu_fpscr, cpu_fpul, cpu_ldst;
static TCGv cpu_fregs[32];
@@ -110,6 +110,12 @@ void sh4_translate_init(void)
offsetof(CPUSH4State, pc), "PC");
cpu_sr = tcg_global_mem_new_i32(TCG_AREG0,
offsetof(CPUSH4State, sr), "SR");
+ cpu_sr_m = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUSH4State, sr_m), "SR_M");
+ cpu_sr_q = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUSH4State, sr_q), "SR_Q");
+ cpu_sr_t = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUSH4State, sr_t), "SR_T");
cpu_ssr = tcg_global_mem_new_i32(TCG_AREG0,
offsetof(CPUSH4State, ssr), "SSR");
cpu_spc = tcg_global_mem_new_i32(TCG_AREG0,
@@ -156,7 +162,7 @@ void superh_cpu_dump_state(CPUState *cs, FILE *f,
CPUSH4State *env = &cpu->env;
int i;
cpu_fprintf(f, "pc=0x%08x sr=0x%08x pr=0x%08x fpscr=0x%08x\n",
- env->pc, env->sr, env->pr, env->fpscr);
+ env->pc, cpu_read_sr(env), env->pr, env->fpscr);
cpu_fprintf(f, "spc=0x%08x ssr=0x%08x gbr=0x%08x vbr=0x%08x\n",
env->spc, env->ssr, env->gbr, env->vbr);
cpu_fprintf(f, "sgr=0x%08x dbr=0x%08x delayed_pc=0x%08x fpul=0x%08x\n",
@@ -175,6 +181,30 @@ void superh_cpu_dump_state(CPUState *cs, FILE *f,
}
}
+static void gen_read_sr(TCGv dst)
+{
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_shli_i32(t0, cpu_sr_q, SR_Q);
+ tcg_gen_or_i32(dst, dst, t0);
+ tcg_gen_shli_i32(t0, cpu_sr_m, SR_M);
+ tcg_gen_or_i32(dst, dst, t0);
+ tcg_gen_shli_i32(t0, cpu_sr_t, SR_T);
+ tcg_gen_or_i32(dst, cpu_sr, t0);
+ tcg_temp_free_i32(t0);
+}
+
+static void gen_write_sr(TCGv src)
+{
+ tcg_gen_andi_i32(cpu_sr, src,
+ ~((1u << SR_Q) | (1u << SR_M) | (1u << SR_T)));
+ tcg_gen_shri_i32(cpu_sr_q, src, SR_Q);
+ tcg_gen_andi_i32(cpu_sr_q, cpu_sr_q, 1);
+ tcg_gen_shri_i32(cpu_sr_m, src, SR_M);
+ tcg_gen_andi_i32(cpu_sr_m, cpu_sr_m, 1);
+ tcg_gen_shri_i32(cpu_sr_t, src, SR_T);
+ tcg_gen_andi_i32(cpu_sr_t, cpu_sr_t, 1);
+}
+
static void gen_goto_tb(DisasContext * ctx, int n, target_ulong dest)
{
TranslationBlock *tb;
@@ -210,12 +240,9 @@ static void gen_jump(DisasContext * ctx)
static inline void gen_branch_slot(uint32_t delayed_pc, int t)
{
- TCGv sr;
TCGLabel *label = gen_new_label();
tcg_gen_movi_i32(cpu_delayed_pc, delayed_pc);
- sr = tcg_temp_new();
- tcg_gen_andi_i32(sr, cpu_sr, SR_T);
- tcg_gen_brcondi_i32(t ? TCG_COND_EQ:TCG_COND_NE, sr, 0, label);
+ tcg_gen_brcondi_i32(t ? TCG_COND_EQ : TCG_COND_NE, cpu_sr_t, 0, label);
tcg_gen_ori_i32(cpu_flags, cpu_flags, DELAY_SLOT_TRUE);
gen_set_label(label);
}
@@ -224,13 +251,8 @@ static inline void gen_branch_slot(uint32_t delayed_pc, int t)
static void gen_conditional_jump(DisasContext * ctx,
target_ulong ift, target_ulong ifnott)
{
- TCGLabel *l1;
- TCGv sr;
-
- l1 = gen_new_label();
- sr = tcg_temp_new();
- tcg_gen_andi_i32(sr, cpu_sr, SR_T);
- tcg_gen_brcondi_i32(TCG_COND_NE, sr, 0, l1);
+ TCGLabel *l1 = gen_new_label();
+ tcg_gen_brcondi_i32(TCG_COND_NE, cpu_sr_t, 0, l1);
gen_goto_tb(ctx, 0, ifnott);
gen_set_label(l1);
gen_goto_tb(ctx, 1, ift);
@@ -252,54 +274,12 @@ static void gen_delayed_conditional_jump(DisasContext * ctx)
gen_jump(ctx);
}
-static inline void gen_cmp(int cond, TCGv t0, TCGv t1)
-{
- TCGv t;
-
- t = tcg_temp_new();
- tcg_gen_setcond_i32(cond, t, t1, t0);
- tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T);
- tcg_gen_or_i32(cpu_sr, cpu_sr, t);
-
- tcg_temp_free(t);
-}
-
-static inline void gen_cmp_imm(int cond, TCGv t0, int32_t imm)
-{
- TCGv t;
-
- t = tcg_temp_new();
- tcg_gen_setcondi_i32(cond, t, t0, imm);
- tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T);
- tcg_gen_or_i32(cpu_sr, cpu_sr, t);
-
- tcg_temp_free(t);
-}
-
static inline void gen_store_flags(uint32_t flags)
{
tcg_gen_andi_i32(cpu_flags, cpu_flags, DELAY_SLOT_TRUE);
tcg_gen_ori_i32(cpu_flags, cpu_flags, flags);
}
-static inline void gen_copy_bit_i32(TCGv t0, int p0, TCGv t1, int p1)
-{
- TCGv tmp = tcg_temp_new();
-
- p0 &= 0x1f;
- p1 &= 0x1f;
-
- tcg_gen_andi_i32(tmp, t1, (1 << p1));
- tcg_gen_andi_i32(t0, t0, ~(1 << p0));
- if (p0 < p1)
- tcg_gen_shri_i32(tmp, tmp, p1 - p0);
- else if (p0 > p1)
- tcg_gen_shli_i32(tmp, tmp, p0 - p1);
- tcg_gen_or_i32(t0, t0, tmp);
-
- tcg_temp_free(tmp);
-}
-
static inline void gen_load_fpr64(TCGv_i64 t, int reg)
{
tcg_gen_concat_i32_i64(t, cpu_fregs[reg + 1], cpu_fregs[reg]);
@@ -326,10 +306,12 @@ static inline void gen_store_fpr64 (TCGv_i64 t, int reg)
#define B11_8 ((ctx->opcode >> 8) & 0xf)
#define B15_12 ((ctx->opcode >> 12) & 0xf)
-#define REG(x) ((x) < 8 && (ctx->flags & (SR_MD | SR_RB)) == (SR_MD | SR_RB) \
+#define REG(x) ((x) < 8 && (ctx->flags & (1u << SR_MD))\
+ && (ctx->flags & (1u << SR_RB))\
? (cpu_gregs[x + 16]) : (cpu_gregs[x]))
-#define ALTREG(x) ((x) < 8 && (ctx->flags & (SR_MD | SR_RB)) != (SR_MD | SR_RB)\
+#define ALTREG(x) ((x) < 8 && (!(ctx->flags & (1u << SR_MD))\
+ || !(ctx->flags & (1u << SR_RB)))\
? (cpu_gregs[x + 16]) : (cpu_gregs[x]))
#define FREG(x) (ctx->flags & FPSCR_FR ? (x) ^ 0x10 : (x))
@@ -359,7 +341,7 @@ static inline void gen_store_fpr64 (TCGv_i64 t, int reg)
}
#define CHECK_FPU_ENABLED \
- if (ctx->flags & SR_FD) { \
+ if (ctx->flags & (1u << SR_FD)) { \
tcg_gen_movi_i32(cpu_pc, ctx->pc); \
if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { \
gen_helper_raise_slot_fpu_disable(cpu_env); \
@@ -409,7 +391,9 @@ static void _decode_opc(DisasContext * ctx)
switch (ctx->opcode) {
case 0x0019: /* div0u */
- tcg_gen_andi_i32(cpu_sr, cpu_sr, ~(SR_M | SR_Q | SR_T));
+ tcg_gen_movi_i32(cpu_sr_m, 0);
+ tcg_gen_movi_i32(cpu_sr_q, 0);
+ tcg_gen_movi_i32(cpu_sr_t, 0);
return;
case 0x000b: /* rts */
CHECK_NOT_DELAY_SLOT
@@ -422,10 +406,10 @@ static void _decode_opc(DisasContext * ctx)
tcg_gen_movi_i32(cpu_macl, 0);
return;
case 0x0048: /* clrs */
- tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_S);
+ tcg_gen_andi_i32(cpu_sr, cpu_sr, ~(1u << SR_S));
return;
case 0x0008: /* clrt */
- tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T);
+ tcg_gen_movi_i32(cpu_sr_t, 0);
return;
case 0x0038: /* ldtlb */
CHECK_PRIVILEGED
@@ -434,16 +418,16 @@ static void _decode_opc(DisasContext * ctx)
case 0x002b: /* rte */
CHECK_PRIVILEGED
CHECK_NOT_DELAY_SLOT
- tcg_gen_mov_i32(cpu_sr, cpu_ssr);
+ gen_write_sr(cpu_ssr);
tcg_gen_mov_i32(cpu_delayed_pc, cpu_spc);
ctx->flags |= DELAY_SLOT;
ctx->delayed_pc = (uint32_t) - 1;
return;
case 0x0058: /* sets */
- tcg_gen_ori_i32(cpu_sr, cpu_sr, SR_S);
+ tcg_gen_ori_i32(cpu_sr, cpu_sr, (1u << SR_S));
return;
case 0x0018: /* sett */
- tcg_gen_ori_i32(cpu_sr, cpu_sr, SR_T);
+ tcg_gen_movi_i32(cpu_sr_t, 1);
return;
case 0xfbfd: /* frchg */
tcg_gen_xori_i32(cpu_fpscr, cpu_fpscr, FPSCR_FR);
@@ -659,22 +643,14 @@ static void _decode_opc(DisasContext * ctx)
return;
case 0x300e: /* addc Rm,Rn */
{
- TCGv t0, t1, t2;
- t0 = tcg_temp_new();
- tcg_gen_andi_i32(t0, cpu_sr, SR_T);
+ TCGv t0, t1;
+ t0 = tcg_const_tl(0);
t1 = tcg_temp_new();
- tcg_gen_add_i32(t1, REG(B7_4), REG(B11_8));
- tcg_gen_add_i32(t0, t0, t1);
- t2 = tcg_temp_new();
- tcg_gen_setcond_i32(TCG_COND_GTU, t2, REG(B11_8), t1);
- tcg_gen_setcond_i32(TCG_COND_GTU, t1, t1, t0);
- tcg_gen_or_i32(t1, t1, t2);
- tcg_temp_free(t2);
- tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T);
- tcg_gen_or_i32(cpu_sr, cpu_sr, t1);
- tcg_temp_free(t1);
- tcg_gen_mov_i32(REG(B11_8), t0);
+ tcg_gen_add2_i32(t1, cpu_sr_t, cpu_sr_t, t0, REG(B7_4), t0);
+ tcg_gen_add2_i32(REG(B11_8), cpu_sr_t,
+ REG(B11_8), t0, t1, cpu_sr_t);
tcg_temp_free(t0);
+ tcg_temp_free(t1);
}
return;
case 0x300f: /* addv Rm,Rn */
@@ -686,11 +662,9 @@ static void _decode_opc(DisasContext * ctx)
tcg_gen_xor_i32(t1, t0, REG(B11_8));
t2 = tcg_temp_new();
tcg_gen_xor_i32(t2, REG(B7_4), REG(B11_8));
- tcg_gen_andc_i32(t1, t1, t2);
+ tcg_gen_andc_i32(cpu_sr_t, t1, t2);
tcg_temp_free(t2);
- tcg_gen_shri_i32(t1, t1, 31);
- tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T);
- tcg_gen_or_i32(cpu_sr, cpu_sr, t1);
+ tcg_gen_shri_i32(cpu_sr_t, cpu_sr_t, 31);
tcg_temp_free(t1);
tcg_gen_mov_i32(REG(B7_4), t0);
tcg_temp_free(t0);
@@ -700,54 +674,79 @@ static void _decode_opc(DisasContext * ctx)
tcg_gen_and_i32(REG(B11_8), REG(B11_8), REG(B7_4));
return;
case 0x3000: /* cmp/eq Rm,Rn */
- gen_cmp(TCG_COND_EQ, REG(B7_4), REG(B11_8));
+ tcg_gen_setcond_i32(TCG_COND_EQ, cpu_sr_t, REG(B11_8), REG(B7_4));
return;
case 0x3003: /* cmp/ge Rm,Rn */
- gen_cmp(TCG_COND_GE, REG(B7_4), REG(B11_8));
+ tcg_gen_setcond_i32(TCG_COND_GE, cpu_sr_t, REG(B11_8), REG(B7_4));
return;
case 0x3007: /* cmp/gt Rm,Rn */
- gen_cmp(TCG_COND_GT, REG(B7_4), REG(B11_8));
+ tcg_gen_setcond_i32(TCG_COND_GT, cpu_sr_t, REG(B11_8), REG(B7_4));
return;
case 0x3006: /* cmp/hi Rm,Rn */
- gen_cmp(TCG_COND_GTU, REG(B7_4), REG(B11_8));
+ tcg_gen_setcond_i32(TCG_COND_GTU, cpu_sr_t, REG(B11_8), REG(B7_4));
return;
case 0x3002: /* cmp/hs Rm,Rn */
- gen_cmp(TCG_COND_GEU, REG(B7_4), REG(B11_8));
+ tcg_gen_setcond_i32(TCG_COND_GEU, cpu_sr_t, REG(B11_8), REG(B7_4));
return;
case 0x200c: /* cmp/str Rm,Rn */
{
TCGv cmp1 = tcg_temp_new();
TCGv cmp2 = tcg_temp_new();
- tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T);
tcg_gen_xor_i32(cmp1, REG(B7_4), REG(B11_8));
tcg_gen_andi_i32(cmp2, cmp1, 0xff000000);
- tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0);
- tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2);
+ tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, cmp2, 0);
tcg_gen_andi_i32(cmp2, cmp1, 0x00ff0000);
tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0);
- tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2);
+ tcg_gen_or_i32(cpu_sr_t, cpu_sr_t, cmp2);
tcg_gen_andi_i32(cmp2, cmp1, 0x0000ff00);
tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0);
- tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2);
+ tcg_gen_or_i32(cpu_sr_t, cpu_sr_t, cmp2);
tcg_gen_andi_i32(cmp2, cmp1, 0x000000ff);
tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0);
- tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2);
+ tcg_gen_or_i32(cpu_sr_t, cpu_sr_t, cmp2);
tcg_temp_free(cmp2);
tcg_temp_free(cmp1);
}
return;
case 0x2007: /* div0s Rm,Rn */
- {
- gen_copy_bit_i32(cpu_sr, 8, REG(B11_8), 31); /* SR_Q */
- gen_copy_bit_i32(cpu_sr, 9, REG(B7_4), 31); /* SR_M */
- TCGv val = tcg_temp_new();
- tcg_gen_xor_i32(val, REG(B7_4), REG(B11_8));
- gen_copy_bit_i32(cpu_sr, 0, val, 31); /* SR_T */
- tcg_temp_free(val);
- }
+ tcg_gen_shri_i32(cpu_sr_q, REG(B11_8), 31); /* SR_Q */
+ tcg_gen_shri_i32(cpu_sr_m, REG(B7_4), 31); /* SR_M */
+ tcg_gen_xor_i32(cpu_sr_t, cpu_sr_q, cpu_sr_m); /* SR_T */
return;
case 0x3004: /* div1 Rm,Rn */
- gen_helper_div1(REG(B11_8), cpu_env, REG(B7_4), REG(B11_8));
+ {
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ TCGv t2 = tcg_temp_new();
+ TCGv zero = tcg_const_i32(0);
+
+ /* shift left arg1, saving the bit being pushed out and inserting
+ T on the right */
+ tcg_gen_shri_i32(t0, REG(B11_8), 31);
+ tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1);
+ tcg_gen_or_i32(REG(B11_8), REG(B11_8), cpu_sr_t);
+
+ /* Add or subtract arg0 from arg1 depending if Q == M. To avoid
+ using 64-bit temps, we compute arg0's high part from q ^ m, so
+ that it is 0x00000000 when adding the value or 0xffffffff when
+ subtracting it. */
+ tcg_gen_xor_i32(t1, cpu_sr_q, cpu_sr_m);
+ tcg_gen_subi_i32(t1, t1, 1);
+ tcg_gen_neg_i32(t2, REG(B7_4));
+ tcg_gen_movcond_i32(TCG_COND_EQ, t2, t1, zero, REG(B7_4), t2);
+ tcg_gen_add2_i32(REG(B11_8), t1, REG(B11_8), zero, t2, t1);
+
+ /* compute T and Q depending on carry */
+ tcg_gen_andi_i32(t1, t1, 1);
+ tcg_gen_xor_i32(t1, t1, t0);
+ tcg_gen_xori_i32(cpu_sr_t, t1, 1);
+ tcg_gen_xor_i32(cpu_sr_q, cpu_sr_m, t1);
+
+ tcg_temp_free(zero);
+ tcg_temp_free(t2);
+ tcg_temp_free(t1);
+ tcg_temp_free(t0);
+ }
return;
case 0x300d: /* dmuls.l Rm,Rn */
tcg_gen_muls2_i32(cpu_macl, cpu_mach, REG(B7_4), REG(B11_8));
@@ -827,19 +826,13 @@ static void _decode_opc(DisasContext * ctx)
return;
case 0x600a: /* negc Rm,Rn */
{
- TCGv t0, t1;
- t0 = tcg_temp_new();
- tcg_gen_neg_i32(t0, REG(B7_4));
- t1 = tcg_temp_new();
- tcg_gen_andi_i32(t1, cpu_sr, SR_T);
- tcg_gen_sub_i32(REG(B11_8), t0, t1);
- tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T);
- tcg_gen_setcondi_i32(TCG_COND_GTU, t1, t0, 0);
- tcg_gen_or_i32(cpu_sr, cpu_sr, t1);
- tcg_gen_setcond_i32(TCG_COND_GTU, t1, REG(B11_8), t0);
- tcg_gen_or_i32(cpu_sr, cpu_sr, t1);
+ TCGv t0 = tcg_const_i32(0);
+ tcg_gen_add2_i32(REG(B11_8), cpu_sr_t,
+ REG(B7_4), t0, cpu_sr_t, t0);
+ tcg_gen_sub2_i32(REG(B11_8), cpu_sr_t,
+ t0, t0, REG(B11_8), cpu_sr_t);
+ tcg_gen_andi_i32(cpu_sr_t, cpu_sr_t, 1);
tcg_temp_free(t0);
- tcg_temp_free(t1);
}
return;
case 0x6007: /* not Rm,Rn */
@@ -918,22 +911,15 @@ static void _decode_opc(DisasContext * ctx)
return;
case 0x300a: /* subc Rm,Rn */
{
- TCGv t0, t1, t2;
- t0 = tcg_temp_new();
- tcg_gen_andi_i32(t0, cpu_sr, SR_T);
+ TCGv t0, t1;
+ t0 = tcg_const_tl(0);
t1 = tcg_temp_new();
- tcg_gen_sub_i32(t1, REG(B11_8), REG(B7_4));
- tcg_gen_sub_i32(t0, t1, t0);
- t2 = tcg_temp_new();
- tcg_gen_setcond_i32(TCG_COND_LTU, t2, REG(B11_8), t1);
- tcg_gen_setcond_i32(TCG_COND_LTU, t1, t1, t0);
- tcg_gen_or_i32(t1, t1, t2);
- tcg_temp_free(t2);
- tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T);
- tcg_gen_or_i32(cpu_sr, cpu_sr, t1);
- tcg_temp_free(t1);
- tcg_gen_mov_i32(REG(B11_8), t0);
+ tcg_gen_add2_i32(t1, cpu_sr_t, cpu_sr_t, t0, REG(B7_4), t0);
+ tcg_gen_sub2_i32(REG(B11_8), cpu_sr_t,
+ REG(B11_8), t0, t1, cpu_sr_t);
+ tcg_gen_andi_i32(cpu_sr_t, cpu_sr_t, 1);
tcg_temp_free(t0);
+ tcg_temp_free(t1);
}
return;
case 0x300b: /* subv Rm,Rn */
@@ -947,9 +933,7 @@ static void _decode_opc(DisasContext * ctx)
tcg_gen_xor_i32(t2, REG(B11_8), REG(B7_4));
tcg_gen_and_i32(t1, t1, t2);
tcg_temp_free(t2);
- tcg_gen_shri_i32(t1, t1, 31);
- tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T);
- tcg_gen_or_i32(cpu_sr, cpu_sr, t1);
+ tcg_gen_shri_i32(cpu_sr_t, t1, 31);
tcg_temp_free(t1);
tcg_gen_mov_i32(REG(B11_8), t0);
tcg_temp_free(t0);
@@ -959,7 +943,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv val = tcg_temp_new();
tcg_gen_and_i32(val, REG(B7_4), REG(B11_8));
- gen_cmp_imm(TCG_COND_EQ, val, 0);
+ tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0);
tcg_temp_free(val);
}
return;
@@ -1025,24 +1009,19 @@ static void _decode_opc(DisasContext * ctx)
return;
case 0xf00b: /* fmov {F,D,X}Rm,@-Rn - FPSCR: Nothing */
CHECK_FPU_ENABLED
+ TCGv addr = tcg_temp_new_i32();
+ tcg_gen_subi_i32(addr, REG(B11_8), 4);
if (ctx->flags & FPSCR_SZ) {
- TCGv addr = tcg_temp_new_i32();
int fr = XREG(B7_4);
- tcg_gen_subi_i32(addr, REG(B11_8), 4);
tcg_gen_qemu_st_i32(cpu_fregs[fr+1], addr, ctx->memidx, MO_TEUL);
tcg_gen_subi_i32(addr, addr, 4);
tcg_gen_qemu_st_i32(cpu_fregs[fr], addr, ctx->memidx, MO_TEUL);
- tcg_gen_mov_i32(REG(B11_8), addr);
- tcg_temp_free(addr);
} else {
- TCGv addr;
- addr = tcg_temp_new_i32();
- tcg_gen_subi_i32(addr, REG(B11_8), 4);
tcg_gen_qemu_st_i32(cpu_fregs[FREG(B7_4)], addr,
ctx->memidx, MO_TEUL);
- tcg_gen_mov_i32(REG(B11_8), addr);
- tcg_temp_free(addr);
}
+ tcg_gen_mov_i32(REG(B11_8), addr);
+ tcg_temp_free(addr);
return;
case 0xf006: /* fmov @(R0,Rm),{F,D,X}Rm - FPSCR: Nothing */
CHECK_FPU_ENABLED
@@ -1210,7 +1189,7 @@ static void _decode_opc(DisasContext * ctx)
ctx->flags |= DELAY_SLOT_CONDITIONAL;
return;
case 0x8800: /* cmp/eq #imm,R0 */
- gen_cmp_imm(TCG_COND_EQ, REG(0), B7_0s);
+ tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, REG(0), B7_0s);
return;
case 0xc400: /* mov.b @(disp,GBR),R0 */
{
@@ -1326,7 +1305,7 @@ static void _decode_opc(DisasContext * ctx)
{
TCGv val = tcg_temp_new();
tcg_gen_andi_i32(val, REG(0), B7_0);
- gen_cmp_imm(TCG_COND_EQ, val, 0);
+ tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0);
tcg_temp_free(val);
}
return;
@@ -1336,7 +1315,7 @@ static void _decode_opc(DisasContext * ctx)
tcg_gen_add_i32(val, REG(0), cpu_gbr);
tcg_gen_qemu_ld_i32(val, val, ctx->memidx, MO_UB);
tcg_gen_andi_i32(val, val, B7_0);
- gen_cmp_imm(TCG_COND_EQ, val, 0);
+ tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0);
tcg_temp_free(val);
}
return;
@@ -1399,14 +1378,14 @@ static void _decode_opc(DisasContext * ctx)
ctx->delayed_pc = (uint32_t) - 1;
return;
case 0x4015: /* cmp/pl Rn */
- gen_cmp_imm(TCG_COND_GT, REG(B11_8), 0);
+ tcg_gen_setcondi_i32(TCG_COND_GT, cpu_sr_t, REG(B11_8), 0);
return;
case 0x4011: /* cmp/pz Rn */
- gen_cmp_imm(TCG_COND_GE, REG(B11_8), 0);
+ tcg_gen_setcondi_i32(TCG_COND_GE, cpu_sr_t, REG(B11_8), 0);
return;
case 0x4010: /* dt Rn */
tcg_gen_subi_i32(REG(B11_8), REG(B11_8), 1);
- gen_cmp_imm(TCG_COND_EQ, REG(B11_8), 0);
+ tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, REG(B11_8), 0);
return;
case 0x402b: /* jmp @Rn */
CHECK_NOT_DELAY_SLOT
@@ -1423,15 +1402,21 @@ static void _decode_opc(DisasContext * ctx)
return;
case 0x400e: /* ldc Rm,SR */
CHECK_PRIVILEGED
- tcg_gen_andi_i32(cpu_sr, REG(B11_8), 0x700083f3);
- ctx->bstate = BS_STOP;
+ {
+ TCGv val = tcg_temp_new();
+ tcg_gen_andi_i32(val, REG(B11_8), 0x700083f3);
+ gen_write_sr(val);
+ tcg_temp_free(val);
+ ctx->bstate = BS_STOP;
+ }
return;
case 0x4007: /* ldc.l @Rm+,SR */
CHECK_PRIVILEGED
{
TCGv val = tcg_temp_new();
tcg_gen_qemu_ld_i32(val, REG(B11_8), ctx->memidx, MO_TESL);
- tcg_gen_andi_i32(cpu_sr, val, 0x700083f3);
+ tcg_gen_andi_i32(val, val, 0x700083f3);
+ gen_write_sr(val);
tcg_temp_free(val);
tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4);
ctx->bstate = BS_STOP;
@@ -1439,15 +1424,18 @@ static void _decode_opc(DisasContext * ctx)
return;
case 0x0002: /* stc SR,Rn */
CHECK_PRIVILEGED
- tcg_gen_mov_i32(REG(B11_8), cpu_sr);
+ gen_read_sr(REG(B11_8));
return;
case 0x4003: /* stc SR,@-Rn */
CHECK_PRIVILEGED
{
TCGv addr = tcg_temp_new();
+ TCGv val = tcg_temp_new();
tcg_gen_subi_i32(addr, REG(B11_8), 4);
- tcg_gen_qemu_st_i32(cpu_sr, addr, ctx->memidx, MO_TEUL);
+ gen_read_sr(val);
+ tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_TEUL);
tcg_gen_mov_i32(REG(B11_8), addr);
+ tcg_temp_free(val);
tcg_temp_free(addr);
}
return;
@@ -1545,7 +1533,7 @@ static void _decode_opc(DisasContext * ctx)
tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4);
return;
case 0x0029: /* movt Rn */
- tcg_gen_andi_i32(REG(B11_8), cpu_sr, SR_T);
+ tcg_gen_mov_i32(REG(B11_8), cpu_sr_t);
return;
case 0x0073:
/* MOVCO.L
@@ -1555,8 +1543,7 @@ static void _decode_opc(DisasContext * ctx)
*/
if (ctx->features & SH_FEATURE_SH4A) {
TCGLabel *label = gen_new_label();
- tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T);
- tcg_gen_or_i32(cpu_sr, cpu_sr, cpu_ldst);
+ tcg_gen_mov_i32(cpu_sr_t, cpu_ldst);
tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ldst, 0, label);
tcg_gen_qemu_st_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL);
gen_set_label(label);
@@ -1609,42 +1596,42 @@ static void _decode_opc(DisasContext * ctx)
case 0x4024: /* rotcl Rn */
{
TCGv tmp = tcg_temp_new();
- tcg_gen_mov_i32(tmp, cpu_sr);
- gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 31);
+ tcg_gen_mov_i32(tmp, cpu_sr_t);
+ tcg_gen_shri_i32(cpu_sr_t, REG(B11_8), 31);
tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1);
- gen_copy_bit_i32(REG(B11_8), 0, tmp, 0);
+ tcg_gen_or_i32(REG(B11_8), REG(B11_8), tmp);
tcg_temp_free(tmp);
}
return;
case 0x4025: /* rotcr Rn */
{
TCGv tmp = tcg_temp_new();
- tcg_gen_mov_i32(tmp, cpu_sr);
- gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0);
+ tcg_gen_shli_i32(tmp, cpu_sr_t, 31);
+ tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 1);
tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 1);
- gen_copy_bit_i32(REG(B11_8), 31, tmp, 0);
+ tcg_gen_or_i32(REG(B11_8), REG(B11_8), tmp);
tcg_temp_free(tmp);
}
return;
case 0x4004: /* rotl Rn */
tcg_gen_rotli_i32(REG(B11_8), REG(B11_8), 1);
- gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0);
+ tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 0);
return;
case 0x4005: /* rotr Rn */
- gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0);
+ tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 0);
tcg_gen_rotri_i32(REG(B11_8), REG(B11_8), 1);
return;
case 0x4000: /* shll Rn */
case 0x4020: /* shal Rn */
- gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 31);
+ tcg_gen_shri_i32(cpu_sr_t, REG(B11_8), 31);
tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1);
return;
case 0x4021: /* shar Rn */
- gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0);
+ tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 1);
tcg_gen_sari_i32(REG(B11_8), REG(B11_8), 1);
return;
case 0x4001: /* shlr Rn */
- gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0);
+ tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 1);
tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 1);
return;
case 0x4008: /* shll2 Rn */
@@ -1672,7 +1659,7 @@ static void _decode_opc(DisasContext * ctx)
tcg_gen_mov_i32(addr, REG(B11_8));
val = tcg_temp_local_new();
tcg_gen_qemu_ld_i32(val, addr, ctx->memidx, MO_UB);
- gen_cmp_imm(TCG_COND_EQ, val, 0);
+ tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0);
tcg_gen_ori_i32(val, val, 0x80);
tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_UB);
tcg_temp_free(val);
@@ -1874,7 +1861,7 @@ gen_intermediate_code_internal(SuperHCPU *cpu, TranslationBlock *tb,
ctx.pc = pc_start;
ctx.flags = (uint32_t)tb->flags;
ctx.bstate = BS_NONE;
- ctx.memidx = (ctx.flags & SR_MD) == 0 ? 1 : 0;
+ ctx.memidx = (ctx.flags & (1u << SR_MD)) == 0 ? 1 : 0;
/* We don't know if the delayed pc came from a dynamic or static branch,
so assume it is a dynamic branch. */
ctx.delayed_pc = -1; /* use delayed pc from env pointer */
diff --git a/tcg/aarch64/tcg-target.c b/tcg/aarch64/tcg-target.c
index b3be6f3177..fe44ad709c 100644
--- a/tcg/aarch64/tcg-target.c
+++ b/tcg/aarch64/tcg-target.c
@@ -1004,7 +1004,7 @@ static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb)
tcg_out_mov(s, TARGET_LONG_BITS == 64, TCG_REG_X1, lb->addrlo_reg);
tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_X2, oi);
tcg_out_adr(s, TCG_REG_X3, lb->raddr);
- tcg_out_call(s, qemu_ld_helpers[opc & ~MO_SIGN]);
+ tcg_out_call(s, qemu_ld_helpers[opc & (MO_BSWAP | MO_SIZE)]);
if (opc & MO_SIGN) {
tcg_out_sxt(s, lb->type, size, lb->datalo_reg, TCG_REG_X0);
} else {
@@ -1027,7 +1027,7 @@ static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb)
tcg_out_mov(s, size == MO_64, TCG_REG_X2, lb->datalo_reg);
tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_X3, oi);
tcg_out_adr(s, TCG_REG_X4, lb->raddr);
- tcg_out_call(s, qemu_st_helpers[opc]);
+ tcg_out_call(s, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)]);
tcg_out_goto(s, lb->raddr);
}
diff --git a/tcg/aarch64/tcg-target.h b/tcg/aarch64/tcg-target.h
index 60c7493ac1..8aec04d2bf 100644
--- a/tcg/aarch64/tcg-target.h
+++ b/tcg/aarch64/tcg-target.h
@@ -14,6 +14,7 @@
#define TCG_TARGET_AARCH64 1
#define TCG_TARGET_INSN_UNIT_SIZE 4
+#define TCG_TARGET_TLB_DISPLACEMENT_BITS 24
#undef TCG_TARGET_STACK_GROWSUP
typedef enum {
diff --git a/tcg/arm/tcg-target.c b/tcg/arm/tcg-target.c
index 06a8064a9f..ae2ec7a922 100644
--- a/tcg/arm/tcg-target.c
+++ b/tcg/arm/tcg-target.c
@@ -1260,9 +1260,9 @@ static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb)
icache usage. For pre-armv6, use the signed helpers since we do
not have a single insn sign-extend. */
if (use_armv6_instructions) {
- func = qemu_ld_helpers[opc & ~MO_SIGN];
+ func = qemu_ld_helpers[opc & (MO_BSWAP | MO_SIZE)];
} else {
- func = qemu_ld_helpers[opc];
+ func = qemu_ld_helpers[opc & (MO_BSWAP | MO_SSIZE)];
if (opc & MO_SIGN) {
opc = MO_UL;
}
@@ -1337,7 +1337,7 @@ static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb)
argreg = tcg_out_arg_reg32(s, argreg, TCG_REG_R14);
/* Tail-call to the helper, which will return to the fast path. */
- tcg_out_goto(s, COND_AL, qemu_st_helpers[opc]);
+ tcg_out_goto(s, COND_AL, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)]);
}
#endif /* SOFTMMU */
diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h
index 1c719e2862..6559f80b71 100644
--- a/tcg/arm/tcg-target.h
+++ b/tcg/arm/tcg-target.h
@@ -27,6 +27,7 @@
#undef TCG_TARGET_STACK_GROWSUP
#define TCG_TARGET_INSN_UNIT_SIZE 4
+#define TCG_TARGET_TLB_DISPLACEMENT_BITS 16
typedef enum {
TCG_REG_R0 = 0,
diff --git a/tcg/i386/tcg-target.c b/tcg/i386/tcg-target.c
index 2e4bf52aae..ff4d9cfec7 100644
--- a/tcg/i386/tcg-target.c
+++ b/tcg/i386/tcg-target.c
@@ -1307,7 +1307,7 @@ static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l)
(uintptr_t)l->raddr);
}
- tcg_out_call(s, qemu_ld_helpers[opc & ~MO_SIGN]);
+ tcg_out_call(s, qemu_ld_helpers[opc & (MO_BSWAP | MO_SIZE)]);
data_reg = l->datalo_reg;
switch (opc & MO_SSIZE) {
@@ -1413,7 +1413,7 @@ static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l)
/* "Tail call" to the helper, with the return address back inline. */
tcg_out_push(s, retaddr);
- tcg_out_jmp(s, qemu_st_helpers[opc]);
+ tcg_out_jmp(s, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)]);
}
#elif defined(__x86_64__) && defined(__linux__)
# include <asm/prctl.h>
diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h
index 7a9980e70e..25b513354c 100644
--- a/tcg/i386/tcg-target.h
+++ b/tcg/i386/tcg-target.h
@@ -25,6 +25,7 @@
#define TCG_TARGET_I386 1
#define TCG_TARGET_INSN_UNIT_SIZE 1
+#define TCG_TARGET_TLB_DISPLACEMENT_BITS 31
#ifdef __x86_64__
# define TCG_TARGET_REG_BITS 64
diff --git a/tcg/ia64/tcg-target.h b/tcg/ia64/tcg-target.h
index d67558988a..a04ed81262 100644
--- a/tcg/ia64/tcg-target.h
+++ b/tcg/ia64/tcg-target.h
@@ -26,6 +26,8 @@
#define TCG_TARGET_IA64 1
#define TCG_TARGET_INSN_UNIT_SIZE 16
+#define TCG_TARGET_TLB_DISPLACEMENT_BITS 21
+
typedef struct {
uint64_t lo __attribute__((aligned(16)));
uint64_t hi;
diff --git a/tcg/mips/tcg-target.c b/tcg/mips/tcg-target.c
index f64c89c3c0..f643eca3df 100644
--- a/tcg/mips/tcg-target.c
+++ b/tcg/mips/tcg-target.c
@@ -1031,7 +1031,7 @@ static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l)
}
i = tcg_out_call_iarg_imm(s, i, oi);
i = tcg_out_call_iarg_imm(s, i, (intptr_t)l->raddr);
- tcg_out_call_int(s, qemu_ld_helpers[opc], false);
+ tcg_out_call_int(s, qemu_ld_helpers[opc & (MO_BSWAP | MO_SSIZE)], false);
/* delay slot */
tcg_out_mov(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[0], TCG_AREG0);
@@ -1094,7 +1094,7 @@ static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l)
computation to take place in the return address register. */
tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RA, (intptr_t)l->raddr);
i = tcg_out_call_iarg_reg(s, i, TCG_REG_RA);
- tcg_out_call_int(s, qemu_st_helpers[opc], true);
+ tcg_out_call_int(s, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)], true);
/* delay slot */
tcg_out_mov(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[0], TCG_AREG0);
}
diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h
index c88a1c9272..f5ba52cacf 100644
--- a/tcg/mips/tcg-target.h
+++ b/tcg/mips/tcg-target.h
@@ -27,6 +27,7 @@
#define TCG_TARGET_MIPS 1
#define TCG_TARGET_INSN_UNIT_SIZE 4
+#define TCG_TARGET_TLB_DISPLACEMENT_BITS 16
#define TCG_TARGET_NB_REGS 32
typedef enum {
diff --git a/tcg/optimize.c b/tcg/optimize.c
index 585f1ed7bb..0f6f7008da 100644
--- a/tcg/optimize.c
+++ b/tcg/optimize.c
@@ -193,10 +193,42 @@ static bool temps_are_copies(TCGArg arg1, TCGArg arg2)
return false;
}
+static void tcg_opt_gen_movi(TCGContext *s, TCGOp *op, TCGArg *args,
+ TCGArg dst, TCGArg val)
+{
+ TCGOpcode new_op = op_to_movi(op->opc);
+ tcg_target_ulong mask;
+
+ op->opc = new_op;
+
+ reset_temp(dst);
+ temps[dst].state = TCG_TEMP_CONST;
+ temps[dst].val = val;
+ mask = val;
+ if (TCG_TARGET_REG_BITS > 32 && new_op == INDEX_op_mov_i32) {
+ /* High bits of the destination are now garbage. */
+ mask |= ~0xffffffffull;
+ }
+ temps[dst].mask = mask;
+
+ args[0] = dst;
+ args[1] = val;
+}
+
static void tcg_opt_gen_mov(TCGContext *s, TCGOp *op, TCGArg *args,
- TCGOpcode old_op, TCGArg dst, TCGArg src)
+ TCGArg dst, TCGArg src)
{
- TCGOpcode new_op = op_to_mov(old_op);
+ if (temps_are_copies(dst, src)) {
+ tcg_op_remove(s, op);
+ return;
+ }
+
+ if (temps[src].state == TCG_TEMP_CONST) {
+ tcg_opt_gen_movi(s, op, args, dst, temps[src].val);
+ return;
+ }
+
+ TCGOpcode new_op = op_to_mov(op->opc);
tcg_target_ulong mask;
op->opc = new_op;
@@ -228,28 +260,6 @@ static void tcg_opt_gen_mov(TCGContext *s, TCGOp *op, TCGArg *args,
args[1] = src;
}
-static void tcg_opt_gen_movi(TCGContext *s, TCGOp *op, TCGArg *args,
- TCGOpcode old_op, TCGArg dst, TCGArg val)
-{
- TCGOpcode new_op = op_to_movi(old_op);
- tcg_target_ulong mask;
-
- op->opc = new_op;
-
- reset_temp(dst);
- temps[dst].state = TCG_TEMP_CONST;
- temps[dst].val = val;
- mask = val;
- if (TCG_TARGET_REG_BITS > 32 && new_op == INDEX_op_mov_i32) {
- /* High bits of the destination are now garbage. */
- mask |= ~0xffffffffull;
- }
- temps[dst].mask = mask;
-
- args[0] = dst;
- args[1] = val;
-}
-
static TCGArg do_constant_folding_2(TCGOpcode op, TCGArg x, TCGArg y)
{
uint64_t l64, h64;
@@ -564,7 +574,7 @@ static bool swap_commutative2(TCGArg *p1, TCGArg *p2)
}
/* Propagate constants and copies, fold constant expressions. */
-static void tcg_constant_folding(TCGContext *s)
+void tcg_optimize(TCGContext *s)
{
int oi, oi_next, nb_temps, nb_globals;
@@ -670,7 +680,7 @@ static void tcg_constant_folding(TCGContext *s)
CASE_OP_32_64(rotr):
if (temps[args[1]].state == TCG_TEMP_CONST
&& temps[args[1]].val == 0) {
- tcg_opt_gen_movi(s, op, args, opc, args[0], 0);
+ tcg_opt_gen_movi(s, op, args, args[0], 0);
continue;
}
break;
@@ -775,7 +785,8 @@ static void tcg_constant_folding(TCGContext *s)
if (temps[args[1]].state != TCG_TEMP_CONST
&& temps[args[2]].state == TCG_TEMP_CONST
&& temps[args[2]].val == 0) {
- goto do_mov3;
+ tcg_opt_gen_mov(s, op, args, args[0], args[1]);
+ continue;
}
break;
CASE_OP_32_64(and):
@@ -784,16 +795,10 @@ static void tcg_constant_folding(TCGContext *s)
if (temps[args[1]].state != TCG_TEMP_CONST
&& temps[args[2]].state == TCG_TEMP_CONST
&& temps[args[2]].val == -1) {
- goto do_mov3;
+ tcg_opt_gen_mov(s, op, args, args[0], args[1]);
+ continue;
}
break;
- do_mov3:
- if (temps_are_copies(args[0], args[1])) {
- tcg_op_remove(s, op);
- } else {
- tcg_opt_gen_mov(s, op, args, opc, args[0], args[1]);
- }
- continue;
default:
break;
}
@@ -942,19 +947,12 @@ static void tcg_constant_folding(TCGContext *s)
if (partmask == 0) {
assert(nb_oargs == 1);
- tcg_opt_gen_movi(s, op, args, opc, args[0], 0);
+ tcg_opt_gen_movi(s, op, args, args[0], 0);
continue;
}
if (affected == 0) {
assert(nb_oargs == 1);
- if (temps_are_copies(args[0], args[1])) {
- tcg_op_remove(s, op);
- } else if (temps[args[1]].state != TCG_TEMP_CONST) {
- tcg_opt_gen_mov(s, op, args, opc, args[0], args[1]);
- } else {
- tcg_opt_gen_movi(s, op, args, opc,
- args[0], temps[args[1]].val);
- }
+ tcg_opt_gen_mov(s, op, args, args[0], args[1]);
continue;
}
@@ -966,7 +964,7 @@ static void tcg_constant_folding(TCGContext *s)
CASE_OP_32_64(mulsh):
if ((temps[args[2]].state == TCG_TEMP_CONST
&& temps[args[2]].val == 0)) {
- tcg_opt_gen_movi(s, op, args, opc, args[0], 0);
+ tcg_opt_gen_movi(s, op, args, args[0], 0);
continue;
}
break;
@@ -979,14 +977,7 @@ static void tcg_constant_folding(TCGContext *s)
CASE_OP_32_64(or):
CASE_OP_32_64(and):
if (temps_are_copies(args[1], args[2])) {
- if (temps_are_copies(args[0], args[1])) {
- tcg_op_remove(s, op);
- } else if (temps[args[1]].state != TCG_TEMP_CONST) {
- tcg_opt_gen_mov(s, op, args, opc, args[0], args[1]);
- } else {
- tcg_opt_gen_movi(s, op, args, opc,
- args[0], temps[args[1]].val);
- }
+ tcg_opt_gen_mov(s, op, args, args[0], args[1]);
continue;
}
break;
@@ -1000,7 +991,7 @@ static void tcg_constant_folding(TCGContext *s)
CASE_OP_32_64(sub):
CASE_OP_32_64(xor):
if (temps_are_copies(args[1], args[2])) {
- tcg_opt_gen_movi(s, op, args, opc, args[0], 0);
+ tcg_opt_gen_movi(s, op, args, args[0], 0);
continue;
}
break;
@@ -1013,20 +1004,10 @@ static void tcg_constant_folding(TCGContext *s)
allocator where needed and possible. Also detect copies. */
switch (opc) {
CASE_OP_32_64(mov):
- if (temps_are_copies(args[0], args[1])) {
- tcg_op_remove(s, op);
- break;
- }
- if (temps[args[1]].state != TCG_TEMP_CONST) {
- tcg_opt_gen_mov(s, op, args, opc, args[0], args[1]);
- break;
- }
- /* Source argument is constant. Rewrite the operation and
- let movi case handle it. */
- args[1] = temps[args[1]].val;
- /* fallthrough */
+ tcg_opt_gen_mov(s, op, args, args[0], args[1]);
+ break;
CASE_OP_32_64(movi):
- tcg_opt_gen_movi(s, op, args, opc, args[0], args[1]);
+ tcg_opt_gen_movi(s, op, args, args[0], args[1]);
break;
CASE_OP_32_64(not):
@@ -1039,7 +1020,7 @@ static void tcg_constant_folding(TCGContext *s)
case INDEX_op_ext32u_i64:
if (temps[args[1]].state == TCG_TEMP_CONST) {
tmp = do_constant_folding(opc, temps[args[1]].val, 0);
- tcg_opt_gen_movi(s, op, args, opc, args[0], tmp);
+ tcg_opt_gen_movi(s, op, args, args[0], tmp);
break;
}
goto do_default;
@@ -1047,7 +1028,7 @@ static void tcg_constant_folding(TCGContext *s)
case INDEX_op_trunc_shr_i32:
if (temps[args[1]].state == TCG_TEMP_CONST) {
tmp = do_constant_folding(opc, temps[args[1]].val, args[2]);
- tcg_opt_gen_movi(s, op, args, opc, args[0], tmp);
+ tcg_opt_gen_movi(s, op, args, args[0], tmp);
break;
}
goto do_default;
@@ -1078,7 +1059,7 @@ static void tcg_constant_folding(TCGContext *s)
&& temps[args[2]].state == TCG_TEMP_CONST) {
tmp = do_constant_folding(opc, temps[args[1]].val,
temps[args[2]].val);
- tcg_opt_gen_movi(s, op, args, opc, args[0], tmp);
+ tcg_opt_gen_movi(s, op, args, args[0], tmp);
break;
}
goto do_default;
@@ -1088,7 +1069,7 @@ static void tcg_constant_folding(TCGContext *s)
&& temps[args[2]].state == TCG_TEMP_CONST) {
tmp = deposit64(temps[args[1]].val, args[3], args[4],
temps[args[2]].val);
- tcg_opt_gen_movi(s, op, args, opc, args[0], tmp);
+ tcg_opt_gen_movi(s, op, args, args[0], tmp);
break;
}
goto do_default;
@@ -1096,7 +1077,7 @@ static void tcg_constant_folding(TCGContext *s)
CASE_OP_32_64(setcond):
tmp = do_constant_folding_cond(opc, args[1], args[2], args[3]);
if (tmp != 2) {
- tcg_opt_gen_movi(s, op, args, opc, args[0], tmp);
+ tcg_opt_gen_movi(s, op, args, args[0], tmp);
break;
}
goto do_default;
@@ -1118,14 +1099,7 @@ static void tcg_constant_folding(TCGContext *s)
CASE_OP_32_64(movcond):
tmp = do_constant_folding_cond(opc, args[1], args[2], args[5]);
if (tmp != 2) {
- if (temps_are_copies(args[0], args[4-tmp])) {
- tcg_op_remove(s, op);
- } else if (temps[args[4-tmp]].state == TCG_TEMP_CONST) {
- tcg_opt_gen_movi(s, op, args, opc,
- args[0], temps[args[4-tmp]].val);
- } else {
- tcg_opt_gen_mov(s, op, args, opc, args[0], args[4-tmp]);
- }
+ tcg_opt_gen_mov(s, op, args, args[0], args[4-tmp]);
break;
}
goto do_default;
@@ -1154,8 +1128,8 @@ static void tcg_constant_folding(TCGContext *s)
rl = args[0];
rh = args[1];
- tcg_opt_gen_movi(s, op, args, opc, rl, (uint32_t)a);
- tcg_opt_gen_movi(s, op2, args2, opc, rh, (uint32_t)(a >> 32));
+ tcg_opt_gen_movi(s, op, args, rl, (uint32_t)a);
+ tcg_opt_gen_movi(s, op2, args2, rh, (uint32_t)(a >> 32));
/* We've done all we need to do with the movi. Skip it. */
oi_next = op2->next;
@@ -1175,8 +1149,8 @@ static void tcg_constant_folding(TCGContext *s)
rl = args[0];
rh = args[1];
- tcg_opt_gen_movi(s, op, args, opc, rl, (uint32_t)r);
- tcg_opt_gen_movi(s, op2, args2, opc, rh, (uint32_t)(r >> 32));
+ tcg_opt_gen_movi(s, op, args, rl, (uint32_t)r);
+ tcg_opt_gen_movi(s, op2, args2, rh, (uint32_t)(r >> 32));
/* We've done all we need to do with the movi. Skip it. */
oi_next = op2->next;
@@ -1260,7 +1234,7 @@ static void tcg_constant_folding(TCGContext *s)
tmp = do_constant_folding_cond2(&args[1], &args[3], args[5]);
if (tmp != 2) {
do_setcond_const:
- tcg_opt_gen_movi(s, op, args, opc, args[0], tmp);
+ tcg_opt_gen_movi(s, op, args, args[0], tmp);
} else if ((args[5] == TCG_COND_LT || args[5] == TCG_COND_GE)
&& temps[args[3]].state == TCG_TEMP_CONST
&& temps[args[4]].state == TCG_TEMP_CONST
@@ -1354,8 +1328,3 @@ static void tcg_constant_folding(TCGContext *s)
}
}
}
-
-void tcg_optimize(TCGContext *s)
-{
- tcg_constant_folding(s);
-}
diff --git a/tcg/ppc/tcg-target.c b/tcg/ppc/tcg-target.c
index d49c7d925f..2b6eafa03c 100644
--- a/tcg/ppc/tcg-target.c
+++ b/tcg/ppc/tcg-target.c
@@ -1495,7 +1495,7 @@ static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb)
tcg_out_movi(s, TCG_TYPE_I32, arg++, oi);
tcg_out32(s, MFSPR | RT(arg) | LR);
- tcg_out_call(s, qemu_ld_helpers[opc & ~MO_SIGN]);
+ tcg_out_call(s, qemu_ld_helpers[opc & (MO_BSWAP | MO_SIZE)]);
lo = lb->datalo_reg;
hi = lb->datahi_reg;
@@ -1565,7 +1565,7 @@ static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb)
tcg_out_movi(s, TCG_TYPE_I32, arg++, oi);
tcg_out32(s, MFSPR | RT(arg) | LR);
- tcg_out_call(s, qemu_st_helpers[opc]);
+ tcg_out_call(s, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)]);
tcg_out_b(s, 0, lb->raddr);
}
@@ -1624,7 +1624,7 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64)
tcg_out32(s, LWZ | TAI(datalo, addrlo, 4));
}
} else {
- uint32_t insn = qemu_ldx_opc[opc];
+ uint32_t insn = qemu_ldx_opc[opc & (MO_BSWAP | MO_SSIZE)];
if (!HAVE_ISA_2_06 && insn == LDBRX) {
tcg_out32(s, ADDI | TAI(TCG_REG_R0, addrlo, 4));
tcg_out32(s, LWBRX | TAB(datalo, rbase, addrlo));
@@ -1696,7 +1696,7 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64)
tcg_out32(s, STW | TAI(datalo, addrlo, 4));
}
} else {
- uint32_t insn = qemu_stx_opc[opc];
+ uint32_t insn = qemu_stx_opc[opc & (MO_BSWAP | MO_SIZE)];
if (!HAVE_ISA_2_06 && insn == STDBRX) {
tcg_out32(s, STWBRX | SAB(datalo, rbase, addrlo));
tcg_out32(s, ADDI | TAI(TCG_REG_TMP1, addrlo, 4));
diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h
index 32ac4424db..7ce7048824 100644
--- a/tcg/ppc/tcg-target.h
+++ b/tcg/ppc/tcg-target.h
@@ -32,6 +32,7 @@
#define TCG_TARGET_NB_REGS 32
#define TCG_TARGET_INSN_UNIT_SIZE 4
+#define TCG_TARGET_TLB_DISPLACEMENT_BITS 16
typedef enum {
TCG_REG_R0, TCG_REG_R1, TCG_REG_R2, TCG_REG_R3,
diff --git a/tcg/s390/tcg-target.c b/tcg/s390/tcg-target.c
index 46dedc9f82..669fafe24f 100644
--- a/tcg/s390/tcg-target.c
+++ b/tcg/s390/tcg-target.c
@@ -1573,7 +1573,7 @@ static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb)
}
tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R4, oi);
tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R5, (uintptr_t)lb->raddr);
- tcg_out_call(s, qemu_ld_helpers[opc]);
+ tcg_out_call(s, qemu_ld_helpers[opc & (MO_BSWAP | MO_SSIZE)]);
tcg_out_mov(s, TCG_TYPE_I64, data_reg, TCG_REG_R2);
tgen_gotoi(s, S390_CC_ALWAYS, lb->raddr);
@@ -1610,7 +1610,7 @@ static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb)
}
tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R5, oi);
tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R6, (uintptr_t)lb->raddr);
- tcg_out_call(s, qemu_st_helpers[opc]);
+ tcg_out_call(s, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)]);
tgen_gotoi(s, S390_CC_ALWAYS, lb->raddr);
}
diff --git a/tcg/s390/tcg-target.h b/tcg/s390/tcg-target.h
index 5acc28ca6b..91576d5949 100644
--- a/tcg/s390/tcg-target.h
+++ b/tcg/s390/tcg-target.h
@@ -25,6 +25,7 @@
#define TCG_TARGET_S390 1
#define TCG_TARGET_INSN_UNIT_SIZE 2
+#define TCG_TARGET_TLB_DISPLACEMENT_BITS 19
typedef enum TCGReg {
TCG_REG_R0 = 0,
diff --git a/tcg/sparc/tcg-target.c b/tcg/sparc/tcg-target.c
index c1794a33ed..1a870a81d7 100644
--- a/tcg/sparc/tcg-target.c
+++ b/tcg/sparc/tcg-target.c
@@ -1075,12 +1075,11 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg data, TCGReg addr,
TCGMemOp memop = get_memop(oi);
#ifdef CONFIG_SOFTMMU
unsigned memi = get_mmuidx(oi);
- TCGMemOp s_bits = memop & MO_SIZE;
TCGReg addrz, param;
tcg_insn_unit *func;
tcg_insn_unit *label_ptr;
- addrz = tcg_out_tlb_load(s, addr, memi, s_bits,
+ addrz = tcg_out_tlb_load(s, addr, memi, memop & MO_SIZE,
offsetof(CPUTLBEntry, addr_read));
/* The fast path is exactly one insn. Thus we can perform the
@@ -1092,7 +1091,8 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg data, TCGReg addr,
tcg_out_bpcc0(s, COND_E, BPCC_A | BPCC_PT
| (TARGET_LONG_BITS == 64 ? BPCC_XCC : BPCC_ICC), 0);
/* delay slot */
- tcg_out_ldst_rr(s, data, addrz, TCG_REG_O1, qemu_ld_opc[memop]);
+ tcg_out_ldst_rr(s, data, addrz, TCG_REG_O1,
+ qemu_ld_opc[memop & (MO_BSWAP | MO_SSIZE)]);
/* TLB Miss. */
@@ -1105,10 +1105,10 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg data, TCGReg addr,
/* We use the helpers to extend SB and SW data, leaving the case
of SL needing explicit extending below. */
- if ((memop & ~MO_BSWAP) == MO_SL) {
- func = qemu_ld_trampoline[memop & ~MO_SIGN];
+ if ((memop & MO_SSIZE) == MO_SL) {
+ func = qemu_ld_trampoline[memop & (MO_BSWAP | MO_SIZE)];
} else {
- func = qemu_ld_trampoline[memop];
+ func = qemu_ld_trampoline[memop & (MO_BSWAP | MO_SSIZE)];
}
assert(func != NULL);
tcg_out_call_nodelay(s, func);
@@ -1119,13 +1119,13 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg data, TCGReg addr,
Which complicates things for sparcv8plus. */
if (SPARC64) {
/* We let the helper sign-extend SB and SW, but leave SL for here. */
- if (is_64 && (memop & ~MO_BSWAP) == MO_SL) {
+ if (is_64 && (memop & MO_SSIZE) == MO_SL) {
tcg_out_arithi(s, data, TCG_REG_O0, 0, SHIFT_SRA);
} else {
tcg_out_mov(s, TCG_TYPE_REG, data, TCG_REG_O0);
}
} else {
- if (s_bits == MO_64) {
+ if ((memop & MO_SIZE) == MO_64) {
tcg_out_arithi(s, TCG_REG_O0, TCG_REG_O0, 32, SHIFT_SLLX);
tcg_out_arithi(s, TCG_REG_O1, TCG_REG_O1, 0, SHIFT_SRL);
tcg_out_arith(s, data, TCG_REG_O0, TCG_REG_O1, ARITH_OR);
@@ -1147,7 +1147,7 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg data, TCGReg addr,
}
tcg_out_ldst_rr(s, data, addr,
(GUEST_BASE ? TCG_GUEST_BASE_REG : TCG_REG_G0),
- qemu_ld_opc[memop]);
+ qemu_ld_opc[memop & (MO_BSWAP | MO_SSIZE)]);
#endif /* CONFIG_SOFTMMU */
}
@@ -1157,12 +1157,11 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg data, TCGReg addr,
TCGMemOp memop = get_memop(oi);
#ifdef CONFIG_SOFTMMU
unsigned memi = get_mmuidx(oi);
- TCGMemOp s_bits = memop & MO_SIZE;
TCGReg addrz, param;
tcg_insn_unit *func;
tcg_insn_unit *label_ptr;
- addrz = tcg_out_tlb_load(s, addr, memi, s_bits,
+ addrz = tcg_out_tlb_load(s, addr, memi, memop & MO_SIZE,
offsetof(CPUTLBEntry, addr_write));
/* The fast path is exactly one insn. Thus we can perform the entire
@@ -1172,7 +1171,8 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg data, TCGReg addr,
tcg_out_bpcc0(s, COND_E, BPCC_A | BPCC_PT
| (TARGET_LONG_BITS == 64 ? BPCC_XCC : BPCC_ICC), 0);
/* delay slot */
- tcg_out_ldst_rr(s, data, addrz, TCG_REG_O1, qemu_st_opc[memop]);
+ tcg_out_ldst_rr(s, data, addrz, TCG_REG_O1,
+ qemu_st_opc[memop & (MO_BSWAP | MO_SIZE)]);
/* TLB Miss. */
@@ -1182,13 +1182,13 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg data, TCGReg addr,
param++;
}
tcg_out_mov(s, TCG_TYPE_REG, param++, addr);
- if (!SPARC64 && s_bits == MO_64) {
+ if (!SPARC64 && (memop & MO_SIZE) == MO_64) {
/* Skip the high-part; we'll perform the extract in the trampoline. */
param++;
}
tcg_out_mov(s, TCG_TYPE_REG, param++, data);
- func = qemu_st_trampoline[memop];
+ func = qemu_st_trampoline[memop & (MO_BSWAP | MO_SIZE)];
assert(func != NULL);
tcg_out_call_nodelay(s, func);
/* delay slot */
@@ -1202,7 +1202,7 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg data, TCGReg addr,
}
tcg_out_ldst_rr(s, data, addr,
(GUEST_BASE ? TCG_GUEST_BASE_REG : TCG_REG_G0),
- qemu_st_opc[memop]);
+ qemu_st_opc[memop & (MO_BSWAP | MO_SIZE)]);
#endif /* CONFIG_SOFTMMU */
}
diff --git a/tcg/sparc/tcg-target.h b/tcg/sparc/tcg-target.h
index 0c4c8af0b2..f584de4766 100644
--- a/tcg/sparc/tcg-target.h
+++ b/tcg/sparc/tcg-target.h
@@ -27,6 +27,7 @@
#define TCG_TARGET_REG_BITS 64
#define TCG_TARGET_INSN_UNIT_SIZE 4
+#define TCG_TARGET_TLB_DISPLACEMENT_BITS 32
#define TCG_TARGET_NB_REGS 32
typedef enum {
diff --git a/tcg/tcg.c b/tcg/tcg.c
index 8b43bbb122..7e088b1f28 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -1076,10 +1076,19 @@ void tcg_dump_ops(TCGContext *s)
TCGMemOp op = get_memop(oi);
unsigned ix = get_mmuidx(oi);
- if (op < ARRAY_SIZE(ldst_name) && ldst_name[op]) {
- qemu_log(",%s,%u", ldst_name[op], ix);
- } else {
+ if (op & ~(MO_AMASK | MO_BSWAP | MO_SSIZE)) {
qemu_log(",$0x%x,%u", op, ix);
+ } else {
+ const char *s_al = "", *s_op;
+ if (op & MO_AMASK) {
+ if ((op & MO_AMASK) == MO_ALIGN) {
+ s_al = "al+";
+ } else {
+ s_al = "un+";
+ }
+ }
+ s_op = ldst_name[op & (MO_BSWAP | MO_SSIZE)];
+ qemu_log(",%s%s,%u", s_al, s_op, ix);
}
i = 1;
}
@@ -1378,16 +1387,20 @@ static void tcg_liveness_analysis(TCGContext *s)
memset(dead_temps, 1, s->nb_globals);
}
- /* input args are live */
+ /* record arguments that die in this helper */
for (i = nb_oargs; i < nb_iargs + nb_oargs; i++) {
arg = args[i];
if (arg != TCG_CALL_DUMMY_ARG) {
if (dead_temps[arg]) {
dead_args |= (1 << i);
}
- dead_temps[arg] = 0;
}
}
+ /* input arguments are live for preceeding opcodes */
+ for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) {
+ arg = args[i];
+ dead_temps[arg] = 0;
+ }
s->op_dead_args[oi] = dead_args;
s->op_sync_args[oi] = sync_args;
}
@@ -1522,12 +1535,16 @@ static void tcg_liveness_analysis(TCGContext *s)
memset(mem_temps, 1, s->nb_globals);
}
- /* input args are live */
+ /* record arguments that die in this opcode */
for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) {
arg = args[i];
if (dead_temps[arg]) {
dead_args |= (1 << i);
}
+ }
+ /* input arguments are live for preceeding opcodes */
+ for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) {
+ arg = args[i];
dead_temps[arg] = 0;
}
s->op_dead_args[oi] = dead_args;
@@ -1998,6 +2015,16 @@ static void tcg_reg_alloc_op(TCGContext *s,
if (!IS_DEAD_ARG(i)) {
goto allocate_in_reg;
}
+ /* check if the current register has already been allocated
+ for another input aliased to an output */
+ int k2, i2;
+ for (k2 = 0 ; k2 < k ; k2++) {
+ i2 = def->sorted_args[nb_oargs + k2];
+ if ((def->args_ct[i2].ct & TCG_CT_IALIAS) &&
+ (new_args[i2] == ts->reg)) {
+ goto allocate_in_reg;
+ }
+ }
}
}
reg = ts->reg;
diff --git a/tcg/tcg.h b/tcg/tcg.h
index 8098f824b3..41e486959d 100644
--- a/tcg/tcg.h
+++ b/tcg/tcg.h
@@ -927,7 +927,9 @@ static inline unsigned get_mmuidx(TCGMemOpIdx oi)
#define TB_EXIT_ICOUNT_EXPIRED 2
#define TB_EXIT_REQUESTED 3
-#if !defined(tcg_qemu_tb_exec)
+#ifdef HAVE_TCG_QEMU_TB_EXEC
+uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr);
+#else
# define tcg_qemu_tb_exec(env, tb_ptr) \
((uintptr_t (*)(void *, void *))tcg_ctx.code_gen_prologue)(env, tb_ptr)
#endif
diff --git a/tcg/tci/tcg-target.h b/tcg/tci/tcg-target.h
index bd1e97468c..cbf3f9b5a6 100644
--- a/tcg/tci/tcg-target.h
+++ b/tcg/tci/tcg-target.h
@@ -44,6 +44,7 @@
#define TCG_TARGET_INTERPRETER 1
#define TCG_TARGET_INSN_UNIT_SIZE 1
+#define TCG_TARGET_TLB_DISPLACEMENT_BITS 32
#if UINTPTR_MAX == UINT32_MAX
# define TCG_TARGET_REG_BITS 32
@@ -175,8 +176,7 @@ typedef enum {
void tci_disas(uint8_t opc);
-uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr);
-#define tcg_qemu_tb_exec tcg_qemu_tb_exec
+#define HAVE_TCG_QEMU_TB_EXEC
static inline void flush_icache_range(uintptr_t start, uintptr_t stop)
{
diff --git a/tci.c b/tci.c
index a14717d0d5..84449489d2 100644
--- a/tci.c
+++ b/tci.c
@@ -1107,7 +1107,7 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr)
t0 = *tb_ptr++;
taddr = tci_read_ulong(&tb_ptr);
oi = tci_read_i(&tb_ptr);
- switch (get_memop(oi)) {
+ switch (get_memop(oi) & (MO_BSWAP | MO_SSIZE)) {
case MO_UB:
tmp32 = qemu_ld_ub;
break;
@@ -1144,7 +1144,7 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr)
}
taddr = tci_read_ulong(&tb_ptr);
oi = tci_read_i(&tb_ptr);
- switch (get_memop(oi)) {
+ switch (get_memop(oi) & (MO_BSWAP | MO_SSIZE)) {
case MO_UB:
tmp64 = qemu_ld_ub;
break;
@@ -1193,7 +1193,7 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr)
t0 = tci_read_r(&tb_ptr);
taddr = tci_read_ulong(&tb_ptr);
oi = tci_read_i(&tb_ptr);
- switch (get_memop(oi)) {
+ switch (get_memop(oi) & (MO_BSWAP | MO_SIZE)) {
case MO_UB:
qemu_st_b(t0);
break;
@@ -1217,7 +1217,7 @@ uintptr_t tcg_qemu_tb_exec(CPUArchState *env, uint8_t *tb_ptr)
tmp64 = tci_read_r64(&tb_ptr);
taddr = tci_read_ulong(&tb_ptr);
oi = tci_read_i(&tb_ptr);
- switch (get_memop(oi)) {
+ switch (get_memop(oi) & (MO_BSWAP | MO_SIZE)) {
case MO_UB:
qemu_st_b(tmp64);
break;
diff --git a/tests/Makefile b/tests/Makefile
index 729b9694cf..4de40deb2f 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -174,6 +174,8 @@ gcov-files-i386-y += hw/usb/dev-storage.c
check-qtest-i386-y += tests/usb-hcd-xhci-test$(EXESUF)
gcov-files-i386-y += hw/usb/hcd-xhci.c
check-qtest-i386-y += tests/pc-cpu-test$(EXESUF)
+check-qtest-i386-y += tests/q35-test$(EXESUF)
+gcov-files-i386-y += hw/pci-host/q35.c
check-qtest-i386-$(CONFIG_LINUX) += tests/vhost-user-test$(EXESUF)
check-qtest-x86_64-y = $(check-qtest-i386-y)
gcov-files-i386-y += i386-softmmu/hw/timer/mc146818rtc.c
@@ -235,7 +237,8 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
flat-union-invalid-branch-key.json flat-union-reverse-define.json \
flat-union-string-discriminator.json union-base-no-discriminator.json \
flat-union-bad-discriminator.json flat-union-bad-base.json \
- flat-union-base-star.json flat-union-int-branch.json \
+ flat-union-base-star.json \
+ flat-union-array-branch.json flat-union-int-branch.json \
flat-union-base-union.json flat-union-branch-clash.json \
alternate-nested.json alternate-unknown.json alternate-clash.json \
alternate-good.json alternate-base.json alternate-array.json \
@@ -355,6 +358,7 @@ tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y)
tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o $(libqos-obj-y)
tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y)
tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y)
+tests/q35-test$(EXESUF): tests/q35-test.o $(libqos-pc-obj-y)
tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y)
tests/e1000-test$(EXESUF): tests/e1000-test.o
tests/rtl8139-test$(EXESUF): tests/rtl8139-test.o $(libqos-pc-obj-y)
diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c
index 7e85dc45e3..0de1742d7d 100644
--- a/tests/bios-tables-test.c
+++ b/tests/bios-tables-test.c
@@ -599,35 +599,15 @@ static void test_acpi_asl(test_data *data)
free_test_data(&exp_data);
}
-static void test_smbios_ep_address(test_data *data)
-{
- uint32_t off;
-
- /* find smbios entry point structure */
- for (off = 0xf0000; off < 0x100000; off += 0x10) {
- uint8_t sig[] = "_SM_";
- int i;
-
- for (i = 0; i < sizeof sig - 1; ++i) {
- sig[i] = readb(off + i);
- }
-
- if (!memcmp(sig, "_SM_", sizeof sig)) {
- break;
- }
- }
-
- g_assert_cmphex(off, <, 0x100000);
- data->smbios_ep_addr = off;
-}
-
-static void test_smbios_ep_table(test_data *data)
+static bool smbios_ep_table_ok(test_data *data)
{
struct smbios_entry_point *ep_table = &data->smbios_ep_table;
uint32_t addr = data->smbios_ep_addr;
ACPI_READ_ARRAY(ep_table->anchor_string, addr);
- g_assert(!memcmp(ep_table->anchor_string, "_SM_", 4));
+ if (memcmp(ep_table->anchor_string, "_SM_", 4)) {
+ return false;
+ }
ACPI_READ_FIELD(ep_table->checksum, addr);
ACPI_READ_FIELD(ep_table->length, addr);
ACPI_READ_FIELD(ep_table->smbios_major_version, addr);
@@ -636,17 +616,50 @@ static void test_smbios_ep_table(test_data *data)
ACPI_READ_FIELD(ep_table->entry_point_revision, addr);
ACPI_READ_ARRAY(ep_table->formatted_area, addr);
ACPI_READ_ARRAY(ep_table->intermediate_anchor_string, addr);
- g_assert(!memcmp(ep_table->intermediate_anchor_string, "_DMI_", 5));
+ if (memcmp(ep_table->intermediate_anchor_string, "_DMI_", 5)) {
+ return false;
+ }
ACPI_READ_FIELD(ep_table->intermediate_checksum, addr);
ACPI_READ_FIELD(ep_table->structure_table_length, addr);
- g_assert_cmpuint(ep_table->structure_table_length, >, 0);
+ if (ep_table->structure_table_length == 0) {
+ return false;
+ }
ACPI_READ_FIELD(ep_table->structure_table_address, addr);
ACPI_READ_FIELD(ep_table->number_of_structures, addr);
- g_assert_cmpuint(ep_table->number_of_structures, >, 0);
+ if (ep_table->number_of_structures == 0) {
+ return false;
+ }
ACPI_READ_FIELD(ep_table->smbios_bcd_revision, addr);
- g_assert(!acpi_checksum((uint8_t *)ep_table, sizeof *ep_table));
- g_assert(!acpi_checksum((uint8_t *)ep_table + 0x10,
- sizeof *ep_table - 0x10));
+ if (acpi_checksum((uint8_t *)ep_table, sizeof *ep_table) ||
+ acpi_checksum((uint8_t *)ep_table + 0x10, sizeof *ep_table - 0x10)) {
+ return false;
+ }
+ return true;
+}
+
+static void test_smbios_entry_point(test_data *data)
+{
+ uint32_t off;
+
+ /* find smbios entry point structure */
+ for (off = 0xf0000; off < 0x100000; off += 0x10) {
+ uint8_t sig[] = "_SM_";
+ int i;
+
+ for (i = 0; i < sizeof sig - 1; ++i) {
+ sig[i] = readb(off + i);
+ }
+
+ if (!memcmp(sig, "_SM_", sizeof sig)) {
+ /* signature match, but is this a valid entry point? */
+ data->smbios_ep_addr = off;
+ if (smbios_ep_table_ok(data)) {
+ break;
+ }
+ }
+ }
+
+ g_assert_cmphex(off, <, 0x100000);
}
static inline bool smbios_single_instance(uint8_t type)
@@ -767,8 +780,7 @@ static void test_acpi_one(const char *params, test_data *data)
}
}
- test_smbios_ep_address(data);
- test_smbios_ep_table(data);
+ test_smbios_entry_point(data);
test_smbios_structs(data);
qtest_quit(global_qtest);
diff --git a/tests/check-qdict.c b/tests/check-qdict.c
index a9296f0833..a136f2addf 100644
--- a/tests/check-qdict.c
+++ b/tests/check-qdict.c
@@ -152,6 +152,28 @@ static void qdict_get_try_str_test(void)
QDECREF(tests_dict);
}
+static void qdict_defaults_test(void)
+{
+ QDict *dict, *copy;
+
+ dict = qdict_new();
+ copy = qdict_new();
+
+ qdict_set_default_str(dict, "foo", "abc");
+ qdict_set_default_str(dict, "foo", "def");
+ g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "abc");
+ qdict_set_default_str(dict, "bar", "ghi");
+
+ qdict_copy_default(copy, dict, "foo");
+ g_assert_cmpstr(qdict_get_str(copy, "foo"), ==, "abc");
+ qdict_set_default_str(copy, "bar", "xyz");
+ qdict_copy_default(copy, dict, "bar");
+ g_assert_cmpstr(qdict_get_str(copy, "bar"), ==, "xyz");
+
+ QDECREF(copy);
+ QDECREF(dict);
+}
+
static void qdict_haskey_not_test(void)
{
QDict *tests_dict = qdict_new();
@@ -444,6 +466,49 @@ static void qdict_array_split_test(void)
QDECREF(test_dict);
}
+static void qdict_array_entries_test(void)
+{
+ QDict *dict = qdict_new();
+
+ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0);
+
+ qdict_put(dict, "bar", qint_from_int(0));
+ qdict_put(dict, "baz.0", qint_from_int(0));
+ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0);
+
+ qdict_put(dict, "foo.1", qint_from_int(0));
+ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL);
+ qdict_put(dict, "foo.0", qint_from_int(0));
+ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 2);
+ qdict_put(dict, "foo.bar", qint_from_int(0));
+ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL);
+ qdict_del(dict, "foo.bar");
+
+ qdict_put(dict, "foo.2.a", qint_from_int(0));
+ qdict_put(dict, "foo.2.b", qint_from_int(0));
+ qdict_put(dict, "foo.2.c", qint_from_int(0));
+ g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 3);
+ g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL);
+
+ QDECREF(dict);
+
+ dict = qdict_new();
+ qdict_put(dict, "1", qint_from_int(0));
+ g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL);
+ qdict_put(dict, "0", qint_from_int(0));
+ g_assert_cmpint(qdict_array_entries(dict, ""), ==, 2);
+ qdict_put(dict, "bar", qint_from_int(0));
+ g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL);
+ qdict_del(dict, "bar");
+
+ qdict_put(dict, "2.a", qint_from_int(0));
+ qdict_put(dict, "2.b", qint_from_int(0));
+ qdict_put(dict, "2.c", qint_from_int(0));
+ g_assert_cmpint(qdict_array_entries(dict, ""), ==, 3);
+
+ QDECREF(dict);
+}
+
static void qdict_join_test(void)
{
QDict *dict1, *dict2;
@@ -663,6 +728,7 @@ int main(int argc, char **argv)
g_test_add_func("/public/get_try_int", qdict_get_try_int_test);
g_test_add_func("/public/get_str", qdict_get_str_test);
g_test_add_func("/public/get_try_str", qdict_get_try_str_test);
+ g_test_add_func("/public/defaults", qdict_defaults_test);
g_test_add_func("/public/haskey_not", qdict_haskey_not_test);
g_test_add_func("/public/haskey", qdict_haskey_test);
g_test_add_func("/public/del", qdict_del_test);
@@ -670,6 +736,7 @@ int main(int argc, char **argv)
g_test_add_func("/public/iterapi", qdict_iterapi_test);
g_test_add_func("/public/flatten", qdict_flatten_test);
g_test_add_func("/public/array_split", qdict_array_split_test);
+ g_test_add_func("/public/array_entries", qdict_array_entries_test);
g_test_add_func("/public/join", qdict_join_test);
g_test_add_func("/errors/put_exists", qdict_put_exists_test);
diff --git a/tests/endianness-test.c b/tests/endianness-test.c
index 92e17d251a..26ee734f42 100644
--- a/tests/endianness-test.c
+++ b/tests/endianness-test.c
@@ -31,8 +31,6 @@ struct TestCase {
static const TestCase test_cases[] = {
{ "i386", "pc", -1 },
- { "mips", "magnum", 0x90000000, .bswap = true },
- { "mips", "pica61", 0x90000000, .bswap = true },
{ "mips", "mips", 0x14000000, .bswap = true },
{ "mips", "malta", 0x10000000, .bswap = true },
{ "mips64", "magnum", 0x90000000, .bswap = true },
diff --git a/tests/fdc-test.c b/tests/fdc-test.c
index 3c6c83cac4..416394fc77 100644
--- a/tests/fdc-test.c
+++ b/tests/fdc-test.c
@@ -218,6 +218,10 @@ static uint8_t send_read_no_dma_command(int nb_sect, uint8_t expected_st0)
inb(FLOPPY_BASE + reg_fifo);
}
+ msr = inb(FLOPPY_BASE + reg_msr);
+ assert_bit_set(msr, BUSY | RQM | DIO);
+ g_assert(get_irq(FLOPPY_IRQ));
+
st0 = floppy_recv();
if (st0 != expected_st0) {
ret = 1;
@@ -228,8 +232,15 @@ static uint8_t send_read_no_dma_command(int nb_sect, uint8_t expected_st0)
floppy_recv();
floppy_recv();
floppy_recv();
+ g_assert(get_irq(FLOPPY_IRQ));
floppy_recv();
+ /* Check that we're back in command phase */
+ msr = inb(FLOPPY_BASE + reg_msr);
+ assert_bit_clear(msr, BUSY | DIO);
+ assert_bit_set(msr, RQM);
+ g_assert(!get_irq(FLOPPY_IRQ));
+
return ret;
}
@@ -403,6 +414,7 @@ static void test_read_id(void)
uint8_t head = 0;
uint8_t cyl;
uint8_t st0;
+ uint8_t msr;
/* Seek to track 0 and check with READ ID */
send_seek(0);
@@ -411,18 +423,29 @@ static void test_read_id(void)
g_assert(!get_irq(FLOPPY_IRQ));
floppy_send(head << 2 | drive);
+ msr = inb(FLOPPY_BASE + reg_msr);
+ if (!get_irq(FLOPPY_IRQ)) {
+ assert_bit_set(msr, BUSY);
+ assert_bit_clear(msr, RQM);
+ }
+
while (!get_irq(FLOPPY_IRQ)) {
/* qemu involves a timer with READ ID... */
clock_step(1000000000LL / 50);
}
+ msr = inb(FLOPPY_BASE + reg_msr);
+ assert_bit_set(msr, BUSY | RQM | DIO);
+
st0 = floppy_recv();
floppy_recv();
floppy_recv();
cyl = floppy_recv();
head = floppy_recv();
floppy_recv();
+ g_assert(get_irq(FLOPPY_IRQ));
floppy_recv();
+ g_assert(!get_irq(FLOPPY_IRQ));
g_assert_cmpint(cyl, ==, 0);
g_assert_cmpint(head, ==, 0);
@@ -443,18 +466,29 @@ static void test_read_id(void)
g_assert(!get_irq(FLOPPY_IRQ));
floppy_send(head << 2 | drive);
+ msr = inb(FLOPPY_BASE + reg_msr);
+ if (!get_irq(FLOPPY_IRQ)) {
+ assert_bit_set(msr, BUSY);
+ assert_bit_clear(msr, RQM);
+ }
+
while (!get_irq(FLOPPY_IRQ)) {
/* qemu involves a timer with READ ID... */
clock_step(1000000000LL / 50);
}
+ msr = inb(FLOPPY_BASE + reg_msr);
+ assert_bit_set(msr, BUSY | RQM | DIO);
+
st0 = floppy_recv();
floppy_recv();
floppy_recv();
cyl = floppy_recv();
head = floppy_recv();
floppy_recv();
+ g_assert(get_irq(FLOPPY_IRQ));
floppy_recv();
+ g_assert(!get_irq(FLOPPY_IRQ));
g_assert_cmpint(cyl, ==, 8);
g_assert_cmpint(head, ==, 1);
diff --git a/tests/q35-test.c b/tests/q35-test.c
new file mode 100644
index 0000000000..812abe5480
--- /dev/null
+++ b/tests/q35-test.c
@@ -0,0 +1,91 @@
+/*
+ * QTest testcase for Q35 northbridge
+ *
+ * Copyright (c) 2015 Red Hat, Inc.
+ *
+ * Author: 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 <glib.h>
+#include <string.h>
+#include "libqtest.h"
+#include "libqos/pci.h"
+#include "libqos/pci-pc.h"
+#include "qemu/osdep.h"
+#include "hw/pci-host/q35.h"
+
+static void smram_set_bit(QPCIDevice *pcidev, uint8_t mask, bool enabled)
+{
+ uint8_t smram;
+
+ smram = qpci_config_readb(pcidev, MCH_HOST_BRIDGE_SMRAM);
+ if (enabled) {
+ smram |= mask;
+ } else {
+ smram &= ~mask;
+ }
+ qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_SMRAM, smram);
+}
+
+static bool smram_test_bit(QPCIDevice *pcidev, uint8_t mask)
+{
+ uint8_t smram;
+
+ smram = qpci_config_readb(pcidev, MCH_HOST_BRIDGE_SMRAM);
+ return smram & mask;
+}
+
+static void test_smram_lock(void)
+{
+ QPCIBus *pcibus;
+ QPCIDevice *pcidev;
+ QDict *response;
+
+ pcibus = qpci_init_pc();
+ g_assert(pcibus != NULL);
+
+ pcidev = qpci_device_find(pcibus, 0);
+ g_assert(pcidev != NULL);
+
+ /* check open is settable */
+ smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, false);
+ g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false);
+ smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, true);
+ g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == true);
+
+ /* lock, check open is cleared & not settable */
+ smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_LCK, true);
+ g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false);
+ smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, true);
+ g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false);
+
+ /* reset */
+ response = qmp("{'execute': 'system_reset', 'arguments': {} }");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ /* check open is settable again */
+ smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, false);
+ g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false);
+ smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, true);
+ g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == true);
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("/q35/smram/lock", test_smram_lock);
+
+ qtest_start("-M q35");
+ ret = g_test_run();
+ qtest_end();
+
+ return ret;
+}
diff --git a/tests/qapi-schema/flat-union-array-branch.err b/tests/qapi-schema/flat-union-array-branch.err
new file mode 100644
index 0000000000..8ea91eadb2
--- /dev/null
+++ b/tests/qapi-schema/flat-union-array-branch.err
@@ -0,0 +1 @@
+tests/qapi-schema/flat-union-array-branch.json:8: Member 'value1' of union 'TestUnion' cannot be an array
diff --git a/tests/qapi-schema/flat-union-array-branch.exit b/tests/qapi-schema/flat-union-array-branch.exit
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tests/qapi-schema/flat-union-array-branch.exit
@@ -0,0 +1 @@
+1
diff --git a/tests/qapi-schema/flat-union-array-branch.json b/tests/qapi-schema/flat-union-array-branch.json
new file mode 100644
index 0000000000..0b98820a8f
--- /dev/null
+++ b/tests/qapi-schema/flat-union-array-branch.json
@@ -0,0 +1,12 @@
+# we require flat union branches to be a struct
+{ 'enum': 'TestEnum',
+ 'data': [ 'value1', 'value2' ] }
+{ 'struct': 'Base',
+ 'data': { 'enum1': 'TestEnum' } }
+{ 'struct': 'TestTypeB',
+ 'data': { 'integer': 'int' } }
+{ 'union': 'TestUnion',
+ 'base': 'Base',
+ 'discriminator': 'enum1',
+ 'data': { 'value1': ['TestTypeB'],
+ 'value2': 'TestTypeB' } }
diff --git a/sysconfigs/target/target-x86_64.conf b/tests/qapi-schema/flat-union-array-branch.out
index e69de29bb2..e69de29bb2 100644
--- a/sysconfigs/target/target-x86_64.conf
+++ b/tests/qapi-schema/flat-union-array-branch.out
diff --git a/tests/qapi-schema/include-cycle.err b/tests/qapi-schema/include-cycle.err
index 602cf62329..bdcd07dce2 100644
--- a/tests/qapi-schema/include-cycle.err
+++ b/tests/qapi-schema/include-cycle.err
@@ -1,3 +1,3 @@
In file included from tests/qapi-schema/include-cycle.json:1:
-In file included from include-cycle-b.json:1:
-include-cycle-c.json:1: Inclusion loop for include-cycle.json
+In file included from tests/qapi-schema/include-cycle-b.json:1:
+tests/qapi-schema/include-cycle-c.json:1: Inclusion loop for include-cycle.json
diff --git a/tests/qapi-schema/include-nested-err.err b/tests/qapi-schema/include-nested-err.err
index 1dacbda3be..1b7b22706b 100644
--- a/tests/qapi-schema/include-nested-err.err
+++ b/tests/qapi-schema/include-nested-err.err
@@ -1,2 +1,2 @@
In file included from tests/qapi-schema/include-nested-err.json:1:
-missing-colon.json:1:10: Expected ":"
+tests/qapi-schema/missing-colon.json:1:10: Expected ":"
diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051
index 0360f37e5a..4a8055b673 100755
--- a/tests/qemu-iotests/051
+++ b/tests/qemu-iotests/051
@@ -194,7 +194,6 @@ echo === Specifying the protocol layer ===
echo
run_qemu -drive file="$TEST_IMG",file.driver=file
-run_qemu -drive file="$TEST_IMG",file.driver=qcow2
echo
echo === Leaving out required options ===
diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out
index 2890eac084..652dd63bf8 100644
--- a/tests/qemu-iotests/051.out
+++ b/tests/qemu-iotests/051.out
@@ -253,9 +253,6 @@ Testing: -drive file=TEST_DIR/t.qcow2,file.driver=file
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit
-Testing: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2
-QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: Block format 'qcow2' used by device '' doesn't support the option 'filename'
-
=== Leaving out required options ===
diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093
index b9096a55d4..c0e9e2b0b5 100755
--- a/tests/qemu-iotests/093
+++ b/tests/qemu-iotests/093
@@ -3,6 +3,7 @@
# Tests for IO throttling
#
# Copyright (C) 2015 Red Hat, Inc.
+# Copyright (C) 2015 Igalia, S.L.
#
# 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
@@ -22,6 +23,7 @@ import iotests
class ThrottleTestCase(iotests.QMPTestCase):
test_img = "null-aio://"
+ max_drives = 3
def blockstats(self, device):
result = self.vm.qmp("query-blockstats")
@@ -32,26 +34,31 @@ class ThrottleTestCase(iotests.QMPTestCase):
raise Exception("Device not found for blockstats: %s" % device)
def setUp(self):
- self.vm = iotests.VM().add_drive(self.test_img)
+ self.vm = iotests.VM()
+ for i in range(0, self.max_drives):
+ self.vm.add_drive(self.test_img)
self.vm.launch()
def tearDown(self):
self.vm.shutdown()
- def do_test_throttle(self, seconds, params):
+ def do_test_throttle(self, ndrives, seconds, params):
def check_limit(limit, num):
# IO throttling algorithm is discrete, allow 10% error so the test
# is more robust
return limit == 0 or \
- (num < seconds * limit * 1.1
- and num > seconds * limit * 0.9)
+ (num < seconds * limit * 1.1 / ndrives
+ and num > seconds * limit * 0.9 / ndrives)
nsec_per_sec = 1000000000
- params['device'] = 'drive0'
+ params['group'] = 'test'
- result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params)
- self.assert_qmp(result, 'return', {})
+ # Set the I/O throttling parameters to all drives
+ for i in range(0, ndrives):
+ params['device'] = 'drive%d' % i
+ result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params)
+ self.assert_qmp(result, 'return', {})
# Set vm clock to a known value
ns = seconds * nsec_per_sec
@@ -66,32 +73,60 @@ class ThrottleTestCase(iotests.QMPTestCase):
params['iops'] / 2,
params['iops_rd'])
rd_nr *= seconds * 2
+ rd_nr /= ndrives
wr_nr = max(params['bps'] / rq_size / 2,
params['bps_wr'] / rq_size,
params['iops'] / 2,
params['iops_wr'])
wr_nr *= seconds * 2
+ wr_nr /= ndrives
+
+ # Send I/O requests to all drives
for i in range(rd_nr):
- self.vm.hmp_qemu_io("drive0", "aio_read %d %d" % (i * rq_size, rq_size))
- for i in range(wr_nr):
- self.vm.hmp_qemu_io("drive0", "aio_write %d %d" % (i * rq_size, rq_size))
+ for drive in range(0, ndrives):
+ self.vm.hmp_qemu_io("drive%d" % drive, "aio_read %d %d" %
+ (i * rq_size, rq_size))
- start_rd_bytes, start_rd_iops, start_wr_bytes, start_wr_iops = self.blockstats('drive0')
+ for i in range(wr_nr):
+ for drive in range(0, ndrives):
+ self.vm.hmp_qemu_io("drive%d" % drive, "aio_write %d %d" %
+ (i * rq_size, rq_size))
+
+ # We'll store the I/O stats for each drive in these arrays
+ start_rd_bytes = [0] * ndrives
+ start_rd_iops = [0] * ndrives
+ start_wr_bytes = [0] * ndrives
+ start_wr_iops = [0] * ndrives
+ end_rd_bytes = [0] * ndrives
+ end_rd_iops = [0] * ndrives
+ end_wr_bytes = [0] * ndrives
+ end_wr_iops = [0] * ndrives
+
+ # Read the stats before advancing the clock
+ for i in range(0, ndrives):
+ start_rd_bytes[i], start_rd_iops[i], start_wr_bytes[i], \
+ start_wr_iops[i] = self.blockstats('drive%d' % i)
self.vm.qtest("clock_step %d" % ns)
- end_rd_bytes, end_rd_iops, end_wr_bytes, end_wr_iops = self.blockstats('drive0')
-
- rd_bytes = end_rd_bytes - start_rd_bytes
- rd_iops = end_rd_iops - start_rd_iops
- wr_bytes = end_wr_bytes - start_wr_bytes
- wr_iops = end_wr_iops - start_wr_iops
- self.assertTrue(check_limit(params['bps'], rd_bytes + wr_bytes))
- self.assertTrue(check_limit(params['bps_rd'], rd_bytes))
- self.assertTrue(check_limit(params['bps_wr'], wr_bytes))
- self.assertTrue(check_limit(params['iops'], rd_iops + wr_iops))
- self.assertTrue(check_limit(params['iops_rd'], rd_iops))
- self.assertTrue(check_limit(params['iops_wr'], wr_iops))
+ # Read the stats after advancing the clock
+ for i in range(0, ndrives):
+ end_rd_bytes[i], end_rd_iops[i], end_wr_bytes[i], \
+ end_wr_iops[i] = self.blockstats('drive%d' % i)
+
+ # Check that the I/O is within the limits and evenly distributed
+ for i in range(0, ndrives):
+ rd_bytes = end_rd_bytes[i] - start_rd_bytes[i]
+ rd_iops = end_rd_iops[i] - start_rd_iops[i]
+ wr_bytes = end_wr_bytes[i] - start_wr_bytes[i]
+ wr_iops = end_wr_iops[i] - start_wr_iops[i]
+
+ self.assertTrue(check_limit(params['bps'], rd_bytes + wr_bytes))
+ self.assertTrue(check_limit(params['bps_rd'], rd_bytes))
+ self.assertTrue(check_limit(params['bps_wr'], wr_bytes))
+ self.assertTrue(check_limit(params['iops'], rd_iops + wr_iops))
+ self.assertTrue(check_limit(params['iops_rd'], rd_iops))
+ self.assertTrue(check_limit(params['iops_wr'], wr_iops))
def test_all(self):
params = {"bps": 4096,
@@ -101,11 +136,13 @@ class ThrottleTestCase(iotests.QMPTestCase):
"iops_rd": 10,
"iops_wr": 10,
}
- # Pick each out of all possible params and test
- for tk in params:
- limits = dict([(k, 0) for k in params])
- limits[tk] = params[tk]
- self.do_test_throttle(5, limits)
+ # Repeat the test with different numbers of drives
+ for ndrives in range(1, self.max_drives + 1):
+ # Pick each out of all possible params and test
+ for tk in params:
+ limits = dict([(k, 0) for k in params])
+ limits[tk] = params[tk] * ndrives
+ self.do_test_throttle(ndrives, 5, limits)
class ThrottleTestCoroutine(ThrottleTestCase):
test_img = "null-co://"
diff --git a/tests/qemu-iotests/103 b/tests/qemu-iotests/103
index ccab551f63..fa9a3c1fc9 100755
--- a/tests/qemu-iotests/103
+++ b/tests/qemu-iotests/103
@@ -93,6 +93,16 @@ $QEMU_IO -c "open -o l2-cache-size=1M,refcount-cache-size=0.25M $TEST_IMG" \
-c 'read -P 42 0 64k' \
| _filter_qemu_io
+echo
+echo '=== Testing minimal L2 cache and COW ==='
+echo
+
+$QEMU_IMG snapshot -c foo "$TEST_IMG"
+# This requires a COW operation, which accesses two L2 tables simultaneously
+# (COW source and destination), so there must be enough space in the cache to
+# place both tables there (and qemu should not crash)
+$QEMU_IO -c "open -o cache-size=0 $TEST_IMG" -c 'write 0 64k' | _filter_qemu_io
+
# success, all done
echo '*** done'
rm -f $seq.full
diff --git a/tests/qemu-iotests/103.out b/tests/qemu-iotests/103.out
index ee705b05f0..d05f49fdba 100644
--- a/tests/qemu-iotests/103.out
+++ b/tests/qemu-iotests/103.out
@@ -26,4 +26,9 @@ read 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== Testing minimal L2 cache and COW ===
+
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
*** done
diff --git a/tests/qemu-iotests/119 b/tests/qemu-iotests/119
new file mode 100755
index 0000000000..9a11f1b921
--- /dev/null
+++ b/tests/qemu-iotests/119
@@ -0,0 +1,60 @@
+#!/bin/bash
+#
+# NBD test case for overriding BDRV_O_PROTOCOL by explicitly specifying
+# a driver
+#
+# Copyright (C) 2015 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/>.
+#
+
+# creator
+owner=mreitz@redhat.com
+
+seq="$(basename $0)"
+echo "QA output created by $seq"
+
+here="$PWD"
+tmp=/tmp/$$
+status=1 # failure is the default!
+
+_cleanup()
+{
+ _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt raw
+_supported_proto nbd
+_supported_os Linux
+
+_make_test_img 64M
+# This should not crash
+echo "{'execute': 'qmp_capabilities'}
+ {'execute': 'human-monitor-command',
+ 'arguments': {'command-line': 'qemu-io drv \"read -P 0 0 64k\"'}}
+ {'execute': 'quit'}" \
+ | $QEMU -drive id=drv,if=none,file="$TEST_IMG",driver=nbd \
+ -qmp stdio -nodefaults \
+ | _filter_qmp | _filter_qemu_io
+
+# success, all done
+echo
+echo '*** done'
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/119.out b/tests/qemu-iotests/119.out
new file mode 100644
index 0000000000..58e7114e8b
--- /dev/null
+++ b/tests/qemu-iotests/119.out
@@ -0,0 +1,11 @@
+QA output created by 119
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+QMP_VERSION
+{"return": {}}
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+{"return": ""}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
+
+*** done
diff --git a/tests/qemu-iotests/120 b/tests/qemu-iotests/120
new file mode 100755
index 0000000000..9f13078764
--- /dev/null
+++ b/tests/qemu-iotests/120
@@ -0,0 +1,65 @@
+#!/bin/bash
+#
+# Non-NBD test cases for overriding BDRV_O_PROTOCOL by explicitly
+# specifying a driver
+#
+# Copyright (C) 2015 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/>.
+#
+
+# creator
+owner=mreitz@redhat.com
+
+seq="$(basename $0)"
+echo "QA output created by $seq"
+
+here="$PWD"
+tmp=/tmp/$$
+status=1 # failure is the default!
+
+_cleanup()
+{
+ _cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt generic
+_supported_proto file
+_supported_os Linux
+
+_make_test_img 64M
+
+echo "{'execute': 'qmp_capabilities'}
+ {'execute': 'human-monitor-command',
+ 'arguments': {'command-line': 'qemu-io drv \"write -P 42 0 64k\"'}}
+ {'execute': 'quit'}" \
+ | $QEMU -qmp stdio -nodefaults \
+ -drive id=drv,if=none,file="$TEST_IMG",driver=raw,file.driver=$IMGFMT \
+ | _filter_qmp | _filter_qemu_io
+$QEMU_IO -c 'read -P 42 0 64k' "$TEST_IMG" | _filter_qemu_io
+
+$QEMU_IO_PROG -c 'read -P 42 0 64k' \
+ "json:{'driver': 'raw', 'file': {'driver': '$IMGFMT', 'file': {'filename': '$TEST_IMG'}}}" \
+ | _filter_qemu_io
+
+# success, all done
+echo
+echo '*** done'
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/120.out b/tests/qemu-iotests/120.out
new file mode 100644
index 0000000000..9131b1bce9
--- /dev/null
+++ b/tests/qemu-iotests/120.out
@@ -0,0 +1,15 @@
+QA output created by 120
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+QMP_VERSION
+{"return": {}}
+wrote 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+{"return": ""}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 65536/65536 bytes at offset 0
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+*** done
diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124
index 3ee78cd1f1..8abce2f869 100644
--- a/tests/qemu-iotests/124
+++ b/tests/qemu-iotests/124
@@ -125,7 +125,7 @@ class TestIncrementalBackup(iotests.QMPTestCase):
event = self.vm.event_wait(name="BLOCK_JOB_COMPLETED",
match={'data': {'device': kwargs['device']}})
- self.assertIsNotNone(event)
+ self.assertNotEqual(event, None)
try:
failure = self.dictpath(event, 'data/error')
diff --git a/tests/qemu-iotests/128 b/tests/qemu-iotests/128
index 249a865581..e2a0f2f890 100755
--- a/tests/qemu-iotests/128
+++ b/tests/qemu-iotests/128
@@ -29,6 +29,7 @@ tmp=/tmp/$$
status=1 # failure is the default!
devname="eiodev$$"
+sudo=""
_setup_eiodev()
{
@@ -37,6 +38,7 @@ _setup_eiodev()
echo "0 $((1024 * 1024 * 1024 / 512)) error" | \
$cmd dmsetup create "$devname" 2>/dev/null
if [ "$?" -eq 0 ]; then
+ sudo="$cmd"
return
fi
done
@@ -74,7 +76,7 @@ TEST_IMG="/dev/mapper/$devname"
echo
echo "== reading from error device =="
# Opening image should succeed but the read operation should fail
-$QEMU_IO --format "$IMGFMT" --nocache -c "read 0 65536" "$TEST_IMG" | _filter_qemu_io
+$sudo $QEMU_IO --format "$IMGFMT" --nocache -c "read 0 65536" "$TEST_IMG" | _filter_qemu_io
# success, all done
echo "*** done"
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 0b817ca32d..4597fc11c0 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -121,6 +121,8 @@
114 rw auto quick
115 rw auto
116 rw auto quick
+119 rw auto quick
+120 rw auto quick
121 rw auto
122 rw auto
123 rw auto quick
diff --git a/tests/rocker/bridge b/tests/rocker/bridge
index 7a03f9a227..46abc6f4f6 100755
--- a/tests/rocker/bridge
+++ b/tests/rocker/bridge
@@ -9,8 +9,8 @@ while ! simp ssh tut h2 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done
# configure a 2-port bridge
simp ssh tut sw1 --cmd "sudo /sbin/ip link add name br0 type bridge"
-simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp1 master br0"
-simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp2 master br0"
+simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p1 master br0"
+simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p2 master br0"
# turn off vlan default_pvid on br0
@@ -18,28 +18,23 @@ simp ssh tut sw1 --cmd "echo 0 | sudo dd of=/sys/class/net/br0/bridge/default_pv
# turn off learning and flooding in SW
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning off"
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning off"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 learning off"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 learning off"
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 flood off"
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 flood off"
-
-# turn on learning in HW
-
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning on self"
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning on self"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 flood off"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 flood off"
# bring up bridge and ports
simp ssh tut sw1 --cmd "sudo ifconfig br0 up"
-simp ssh tut sw1 --cmd "sudo ifconfig swp1 up"
-simp ssh tut sw1 --cmd "sudo ifconfig swp2 up"
+simp ssh tut sw1 --cmd "sudo ifconfig sw1p1 up"
+simp ssh tut sw1 --cmd "sudo ifconfig sw1p2 up"
simp ssh tut sw1 --cmd "sudo ifconfig br0 11.0.0.3/24"
# config IP on hosts
-simp ssh tut h1 --cmd "sudo ifconfig swp1 11.0.0.1/24"
-simp ssh tut h2 --cmd "sudo ifconfig swp1 11.0.0.2/24"
+simp ssh tut h1 --cmd "sudo ifconfig sw1p1 11.0.0.1/24"
+simp ssh tut h2 --cmd "sudo ifconfig sw1p1 11.0.0.2/24"
# test...
diff --git a/tests/rocker/bridge-stp b/tests/rocker/bridge-stp
index 4a111a17d3..008568ad8a 100755
--- a/tests/rocker/bridge-stp
+++ b/tests/rocker/bridge-stp
@@ -10,8 +10,8 @@ while ! simp ssh tut h2 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done
simp ssh tut sw1 --cmd "sudo /sbin/ip link add name br0 type bridge"
simp ssh tut sw1 --cmd "sudo brctl stp br0 on"
-simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp1 master br0"
-simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp2 master br0"
+simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p1 master br0"
+simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p2 master br0"
# turn off vlan default_pvid on br0
@@ -19,27 +19,22 @@ simp ssh tut sw1 --cmd "echo 0 | sudo dd of=/sys/class/net/br0/bridge/default_pv
# turn off learning and flooding in SW
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning off"
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning off"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 learning off"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 learning off"
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 flood off"
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 flood off"
-
-# turn on learning in HW
-
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning on self"
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning on self"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 flood off"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 flood off"
# config IP on hosts
-simp ssh tut h1 --cmd "sudo ifconfig swp1 11.0.0.1/24"
-simp ssh tut h2 --cmd "sudo ifconfig swp1 11.0.0.2/24"
+simp ssh tut h1 --cmd "sudo ifconfig sw1p1 11.0.0.1/24"
+simp ssh tut h2 --cmd "sudo ifconfig sw1p1 11.0.0.2/24"
# bring up bridge and ports
simp ssh tut sw1 --cmd "sudo ifconfig br0 up"
-simp ssh tut sw1 --cmd "sudo ifconfig swp1 up"
-simp ssh tut sw1 --cmd "sudo ifconfig swp2 up"
+simp ssh tut sw1 --cmd "sudo ifconfig sw1p1 up"
+simp ssh tut sw1 --cmd "sudo ifconfig sw1p2 up"
# test...
diff --git a/tests/rocker/bridge-vlan b/tests/rocker/bridge-vlan
index 9fa3431f66..ef9e5f53bb 100755
--- a/tests/rocker/bridge-vlan
+++ b/tests/rocker/bridge-vlan
@@ -9,8 +9,8 @@ while ! simp ssh tut h2 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done
# configure a 2-port bridge
simp ssh tut sw1 --cmd "sudo /sbin/ip link add name br0 type bridge"
-simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp1 master br0"
-simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp2 master br0"
+simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p1 master br0"
+simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p2 master br0"
# turn off vlan default_pvid on br0
# turn on vlan filtering on br0
@@ -20,37 +20,32 @@ simp ssh tut sw1 --cmd "echo 1 | sudo dd of=/sys/class/net/br0/bridge/vlan_filte
# add both ports to VLAN 57
-simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev swp1 master"
-simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev swp2 master"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev sw1p1 master self"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev sw1p2 master self"
# turn off learning and flooding in SW
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning off"
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning off"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 learning off"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 learning off"
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 flood off"
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 flood off"
-
-# turn on learning in HW
-
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning on self"
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning on self"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 flood off"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 flood off"
# bring up bridge and ports
simp ssh tut sw1 --cmd "sudo ifconfig br0 up"
-simp ssh tut sw1 --cmd "sudo ifconfig swp1 up"
-simp ssh tut sw1 --cmd "sudo ifconfig swp2 up"
+simp ssh tut sw1 --cmd "sudo ifconfig sw1p1 up"
+simp ssh tut sw1 --cmd "sudo ifconfig sw1p2 up"
# config IP on host VLANs
-simp ssh tut h1 --cmd "sudo vconfig add swp1 57 >/dev/null 2>&1"
-simp ssh tut h1 --cmd "sudo ifconfig swp1 up"
-simp ssh tut h1 --cmd "sudo ifconfig swp1.57 11.0.0.1/24"
+simp ssh tut h1 --cmd "sudo vconfig add sw1p1 57 >/dev/null 2>&1"
+simp ssh tut h1 --cmd "sudo ifconfig sw1p1 up"
+simp ssh tut h1 --cmd "sudo ifconfig sw1p1.57 11.0.0.1/24"
-simp ssh tut h2 --cmd "sudo vconfig add swp1 57 >/dev/null 2>&1"
-simp ssh tut h2 --cmd "sudo ifconfig swp1 up"
-simp ssh tut h2 --cmd "sudo ifconfig swp1.57 11.0.0.2/24"
+simp ssh tut h2 --cmd "sudo vconfig add sw1p1 57 >/dev/null 2>&1"
+simp ssh tut h2 --cmd "sudo ifconfig sw1p1 up"
+simp ssh tut h2 --cmd "sudo ifconfig sw1p1.57 11.0.0.2/24"
# test...
diff --git a/tests/rocker/bridge-vlan-stp b/tests/rocker/bridge-vlan-stp
index 77ab67efe2..c660312bc6 100755
--- a/tests/rocker/bridge-vlan-stp
+++ b/tests/rocker/bridge-vlan-stp
@@ -10,8 +10,8 @@ while ! simp ssh tut h2 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done
simp ssh tut sw1 --cmd "sudo /sbin/ip link add name br0 type bridge"
simp ssh tut sw1 --cmd "sudo brctl stp br0 on"
-simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp1 master br0"
-simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp2 master br0"
+simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p1 master br0"
+simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p2 master br0"
# turn off vlan default_pvid on br0
# turn on vlan filtering on br0
@@ -21,37 +21,32 @@ simp ssh tut sw1 --cmd "echo 1 | sudo dd of=/sys/class/net/br0/bridge/vlan_filte
# add both ports to VLAN 57
-simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev swp1 master"
-simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev swp2 master"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev sw1p1 master self"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev sw1p2 master self"
# turn off learning and flooding in SW
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning off"
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning off"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 learning off"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 learning off"
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 flood off"
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 flood off"
-
-# turn on learning in HW
-
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning on self"
-simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning on self"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 flood off"
+simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 flood off"
# config IP on host VLANs
-simp ssh tut h1 --cmd "sudo vconfig add swp1 57 >/dev/null 2>&1"
-simp ssh tut h1 --cmd "sudo ifconfig swp1 up"
-simp ssh tut h1 --cmd "sudo ifconfig swp1.57 11.0.0.1/24"
+simp ssh tut h1 --cmd "sudo vconfig add sw1p1 57 >/dev/null 2>&1"
+simp ssh tut h1 --cmd "sudo ifconfig sw1p1 up"
+simp ssh tut h1 --cmd "sudo ifconfig sw1p1.57 11.0.0.1/24"
-simp ssh tut h2 --cmd "sudo vconfig add swp1 57 >/dev/null 2>&1"
-simp ssh tut h2 --cmd "sudo ifconfig swp1 up"
-simp ssh tut h2 --cmd "sudo ifconfig swp1.57 11.0.0.2/24"
+simp ssh tut h2 --cmd "sudo vconfig add sw1p1 57 >/dev/null 2>&1"
+simp ssh tut h2 --cmd "sudo ifconfig sw1p1 up"
+simp ssh tut h2 --cmd "sudo ifconfig sw1p1.57 11.0.0.2/24"
# bring up bridge and ports
simp ssh tut sw1 --cmd "sudo ifconfig br0 up"
-simp ssh tut sw1 --cmd "sudo ifconfig swp1 up"
-simp ssh tut sw1 --cmd "sudo ifconfig swp2 up"
+simp ssh tut sw1 --cmd "sudo ifconfig sw1p1 up"
+simp ssh tut sw1 --cmd "sudo ifconfig sw1p2 up"
# test...
diff --git a/tests/rocker/port b/tests/rocker/port
index 3437f7d7fe..5f2c248046 100755
--- a/tests/rocker/port
+++ b/tests/rocker/port
@@ -7,13 +7,13 @@ while ! simp ssh tut h2 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done
# bring up DUT ports
-simp ssh tut sw1 --cmd "sudo ifconfig swp1 11.0.0.1/24"
-simp ssh tut sw1 --cmd "sudo ifconfig swp2 12.0.0.1/24"
+simp ssh tut sw1 --cmd "sudo ifconfig sw1p1 11.0.0.1/24"
+simp ssh tut sw1 --cmd "sudo ifconfig sw1p2 12.0.0.1/24"
# config IP on hosts
-simp ssh tut h1 --cmd "sudo ifconfig swp1 11.0.0.2/24"
-simp ssh tut h2 --cmd "sudo ifconfig swp1 12.0.0.2/24"
+simp ssh tut h1 --cmd "sudo ifconfig sw1p1 11.0.0.2/24"
+simp ssh tut h2 --cmd "sudo ifconfig sw1p1 12.0.0.2/24"
# test...
diff --git a/tests/test-aio.c b/tests/test-aio.c
index 4b0cb45d31..a7cb5c9915 100644
--- a/tests/test-aio.c
+++ b/tests/test-aio.c
@@ -107,7 +107,6 @@ static void test_notify(void)
typedef struct {
QemuMutex start_lock;
- EventNotifier notifier;
bool thread_acquired;
} AcquireTestData;
@@ -119,8 +118,6 @@ static void *test_acquire_thread(void *opaque)
qemu_mutex_lock(&data->start_lock);
qemu_mutex_unlock(&data->start_lock);
- g_usleep(500000);
- event_notifier_set(&data->notifier);
aio_context_acquire(ctx);
aio_context_release(ctx);
@@ -129,19 +126,20 @@ static void *test_acquire_thread(void *opaque)
return NULL;
}
-static void dummy_notifier_read(EventNotifier *n)
+static void dummy_notifier_read(EventNotifier *unused)
{
- event_notifier_test_and_clear(n);
+ g_assert(false); /* should never be invoked */
}
static void test_acquire(void)
{
QemuThread thread;
+ EventNotifier notifier;
AcquireTestData data;
/* Dummy event notifier ensures aio_poll() will block */
- event_notifier_init(&data.notifier, false);
- aio_set_event_notifier(ctx, &data.notifier, dummy_notifier_read);
+ event_notifier_init(&notifier, false);
+ aio_set_event_notifier(ctx, &notifier, dummy_notifier_read);
g_assert(!aio_poll(ctx, false)); /* consume aio_notify() */
qemu_mutex_init(&data.start_lock);
@@ -155,13 +153,12 @@ static void test_acquire(void)
/* Block in aio_poll(), let other thread kick us and acquire context */
aio_context_acquire(ctx);
qemu_mutex_unlock(&data.start_lock); /* let the thread run */
- g_assert(aio_poll(ctx, true));
- g_assert(!data.thread_acquired);
+ g_assert(!aio_poll(ctx, true));
aio_context_release(ctx);
qemu_thread_join(&thread);
- aio_set_event_notifier(ctx, &data.notifier, NULL);
- event_notifier_cleanup(&data.notifier);
+ aio_set_event_notifier(ctx, &notifier, NULL);
+ event_notifier_cleanup(&notifier);
g_assert(data.thread_acquired);
}
diff --git a/tests/test-throttle.c b/tests/test-throttle.c
index d8ba415e43..016844546a 100644
--- a/tests/test-throttle.c
+++ b/tests/test-throttle.c
@@ -1,10 +1,12 @@
/*
* Throttle infrastructure tests
*
- * Copyright Nodalink, SARL. 2013
+ * Copyright Nodalink, EURL. 2013-2014
+ * Copyright Igalia, S.L. 2015
*
* Authors:
- * Benoît Canet <benoit.canet@irqsave.net>
+ * Benoît Canet <benoit.canet@nodalink.com>
+ * Alberto Garcia <berto@igalia.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2 or later.
* See the COPYING.LIB file in the top-level directory.
@@ -15,11 +17,13 @@
#include "block/aio.h"
#include "qemu/throttle.h"
#include "qemu/error-report.h"
+#include "block/throttle-groups.h"
static AioContext *ctx;
static LeakyBucket bkt;
static ThrottleConfig cfg;
static ThrottleState ts;
+static ThrottleTimers tt;
/* useful function */
static bool double_cmp(double x, double y)
@@ -103,17 +107,19 @@ static void test_init(void)
{
int i;
- /* fill the structure with crap */
+ /* fill the structures with crap */
memset(&ts, 1, sizeof(ts));
+ memset(&tt, 1, sizeof(tt));
- /* init the structure */
- throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
- read_timer_cb, write_timer_cb, &ts);
+ /* init structures */
+ throttle_init(&ts);
+ throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+ read_timer_cb, write_timer_cb, &ts);
/* check initialized fields */
- g_assert(ts.clock_type == QEMU_CLOCK_VIRTUAL);
- g_assert(ts.timers[0]);
- g_assert(ts.timers[1]);
+ g_assert(tt.clock_type == QEMU_CLOCK_VIRTUAL);
+ g_assert(tt.timers[0]);
+ g_assert(tt.timers[1]);
/* check other fields where cleared */
g_assert(!ts.previous_leak);
@@ -124,17 +130,18 @@ static void test_init(void)
g_assert(!ts.cfg.buckets[i].level);
}
- throttle_destroy(&ts);
+ throttle_timers_destroy(&tt);
}
static void test_destroy(void)
{
int i;
- throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
- read_timer_cb, write_timer_cb, &ts);
- throttle_destroy(&ts);
+ throttle_init(&ts);
+ throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+ read_timer_cb, write_timer_cb, &ts);
+ throttle_timers_destroy(&tt);
for (i = 0; i < 2; i++) {
- g_assert(!ts.timers[i]);
+ g_assert(!tt.timers[i]);
}
}
@@ -170,11 +177,12 @@ static void test_config_functions(void)
orig_cfg.op_size = 1;
- throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
- read_timer_cb, write_timer_cb, &ts);
+ throttle_init(&ts);
+ throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+ read_timer_cb, write_timer_cb, &ts);
/* structure reset by throttle_init previous_leak should be null */
g_assert(!ts.previous_leak);
- throttle_config(&ts, &orig_cfg);
+ throttle_config(&ts, &tt, &orig_cfg);
/* has previous leak been initialized by throttle_config ? */
g_assert(ts.previous_leak);
@@ -182,7 +190,7 @@ static void test_config_functions(void)
/* get back the fixed configuration */
throttle_get_config(&ts, &final_cfg);
- throttle_destroy(&ts);
+ throttle_timers_destroy(&tt);
g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].avg == 153);
g_assert(final_cfg.buckets[THROTTLE_BPS_READ].avg == 56);
@@ -323,43 +331,47 @@ static void test_is_valid(void)
static void test_have_timer(void)
{
- /* zero the structure */
+ /* zero structures */
memset(&ts, 0, sizeof(ts));
+ memset(&tt, 0, sizeof(tt));
/* no timer set should return false */
- g_assert(!throttle_have_timer(&ts));
+ g_assert(!throttle_timers_are_initialized(&tt));
- /* init the structure */
- throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
- read_timer_cb, write_timer_cb, &ts);
+ /* init structures */
+ throttle_init(&ts);
+ throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+ read_timer_cb, write_timer_cb, &ts);
/* timer set by init should return true */
- g_assert(throttle_have_timer(&ts));
+ g_assert(throttle_timers_are_initialized(&tt));
- throttle_destroy(&ts);
+ throttle_timers_destroy(&tt);
}
static void test_detach_attach(void)
{
- /* zero the structure */
+ /* zero structures */
memset(&ts, 0, sizeof(ts));
+ memset(&tt, 0, sizeof(tt));
/* init the structure */
- throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
- read_timer_cb, write_timer_cb, &ts);
+ throttle_init(&ts);
+ throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+ read_timer_cb, write_timer_cb, &ts);
/* timer set by init should return true */
- g_assert(throttle_have_timer(&ts));
+ g_assert(throttle_timers_are_initialized(&tt));
/* timer should no longer exist after detaching */
- throttle_detach_aio_context(&ts);
- g_assert(!throttle_have_timer(&ts));
+ throttle_timers_detach_aio_context(&tt);
+ g_assert(!throttle_timers_are_initialized(&tt));
/* timer should exist again after attaching */
- throttle_attach_aio_context(&ts, ctx);
- g_assert(throttle_have_timer(&ts));
+ throttle_timers_attach_aio_context(&tt, ctx);
+ g_assert(throttle_timers_are_initialized(&tt));
- throttle_destroy(&ts);
+ throttle_timers_destroy(&tt);
}
static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
@@ -387,9 +399,10 @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
cfg.op_size = op_size;
- throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL,
- read_timer_cb, write_timer_cb, &ts);
- throttle_config(&ts, &cfg);
+ throttle_init(&ts);
+ throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
+ read_timer_cb, write_timer_cb, &ts);
+ throttle_config(&ts, &tt, &cfg);
/* account a read */
throttle_account(&ts, false, size);
@@ -414,7 +427,7 @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
return false;
}
- throttle_destroy(&ts);
+ throttle_timers_destroy(&tt);
return true;
}
@@ -490,23 +503,80 @@ static void test_accounting(void)
(64.0 / 13)));
}
+static void test_groups(void)
+{
+ ThrottleConfig cfg1, cfg2;
+ BlockDriverState *bdrv1, *bdrv2, *bdrv3;
+
+ bdrv1 = bdrv_new();
+ bdrv2 = bdrv_new();
+ bdrv3 = bdrv_new();
+
+ g_assert(bdrv1->throttle_state == NULL);
+ g_assert(bdrv2->throttle_state == NULL);
+ g_assert(bdrv3->throttle_state == NULL);
+
+ throttle_group_register_bs(bdrv1, "bar");
+ throttle_group_register_bs(bdrv2, "foo");
+ throttle_group_register_bs(bdrv3, "bar");
+
+ g_assert(bdrv1->throttle_state != NULL);
+ g_assert(bdrv2->throttle_state != NULL);
+ g_assert(bdrv3->throttle_state != NULL);
+
+ g_assert(!strcmp(throttle_group_get_name(bdrv1), "bar"));
+ g_assert(!strcmp(throttle_group_get_name(bdrv2), "foo"));
+ g_assert(bdrv1->throttle_state == bdrv3->throttle_state);
+
+ /* Setting the config of a group member affects the whole group */
+ memset(&cfg1, 0, sizeof(cfg1));
+ cfg1.buckets[THROTTLE_BPS_READ].avg = 500000;
+ cfg1.buckets[THROTTLE_BPS_WRITE].avg = 285000;
+ cfg1.buckets[THROTTLE_OPS_READ].avg = 20000;
+ cfg1.buckets[THROTTLE_OPS_WRITE].avg = 12000;
+ throttle_group_config(bdrv1, &cfg1);
+
+ throttle_group_get_config(bdrv1, &cfg1);
+ throttle_group_get_config(bdrv3, &cfg2);
+ g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1)));
+
+ cfg2.buckets[THROTTLE_BPS_READ].avg = 4547;
+ cfg2.buckets[THROTTLE_BPS_WRITE].avg = 1349;
+ cfg2.buckets[THROTTLE_OPS_READ].avg = 123;
+ cfg2.buckets[THROTTLE_OPS_WRITE].avg = 86;
+ throttle_group_config(bdrv3, &cfg1);
+
+ throttle_group_get_config(bdrv1, &cfg1);
+ throttle_group_get_config(bdrv3, &cfg2);
+ g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1)));
+
+ throttle_group_unregister_bs(bdrv1);
+ throttle_group_unregister_bs(bdrv2);
+ throttle_group_unregister_bs(bdrv3);
+
+ g_assert(bdrv1->throttle_state == NULL);
+ g_assert(bdrv2->throttle_state == NULL);
+ g_assert(bdrv3->throttle_state == NULL);
+}
+
int main(int argc, char **argv)
{
- GSource *src;
Error *local_error = NULL;
- init_clocks();
+ qemu_init_main_loop(&local_error);
+ ctx = qemu_get_aio_context();
- ctx = aio_context_new(&local_error);
if (!ctx) {
error_report("Failed to create AIO Context: '%s'",
- error_get_pretty(local_error));
- error_free(local_error);
+ local_error ? error_get_pretty(local_error) :
+ "Failed to initialize the QEMU main loop");
+ if (local_error) {
+ error_free(local_error);
+ }
exit(1);
}
- src = aio_get_g_source(ctx);
- g_source_attach(src, NULL);
- g_source_unref(src);
+
+ bdrv_init();
do {} while (g_main_context_iteration(NULL, false));
@@ -523,6 +593,7 @@ int main(int argc, char **argv)
g_test_add_func("/throttle/config/is_valid", test_is_valid);
g_test_add_func("/throttle/config_functions", test_config_functions);
g_test_add_func("/throttle/accounting", test_accounting);
+ g_test_add_func("/throttle/groups", test_groups);
return g_test_run();
}
diff --git a/thunk.c b/thunk.c
index 3cca047509..f501fd72fc 100644
--- a/thunk.c
+++ b/thunk.c
@@ -25,10 +25,8 @@
//#define DEBUG
-#define MAX_STRUCTS 128
-
-/* XXX: make it dynamic */
-StructEntry struct_entries[MAX_STRUCTS];
+static unsigned int max_struct_entries;
+StructEntry *struct_entries;
static const argtype *thunk_type_next_ptr(const argtype *type_ptr);
@@ -70,6 +68,7 @@ void thunk_register_struct(int id, const char *name, const argtype *types)
StructEntry *se;
int nb_fields, offset, max_align, align, size, i, j;
+ assert(id < max_struct_entries);
se = struct_entries + id;
/* first we count the number of fields */
@@ -117,6 +116,8 @@ void thunk_register_struct_direct(int id, const char *name,
const StructEntry *se1)
{
StructEntry *se;
+
+ assert(id < max_struct_entries);
se = struct_entries + id;
*se = *se1;
se->name = name;
@@ -244,6 +245,7 @@ const argtype *thunk_convert(void *dst, const void *src,
const argtype *field_types;
const int *dst_offsets, *src_offsets;
+ assert(*type_ptr < max_struct_entries);
se = struct_entries + *type_ptr++;
if (se->convert[0] != NULL) {
/* specific conversion is needed */
@@ -314,3 +316,9 @@ int thunk_type_align_array(const argtype *type_ptr, int is_host)
return thunk_type_align(type_ptr, is_host);
}
#endif /* ndef NO_THUNK_TYPE_SIZE */
+
+void thunk_init(unsigned int max_structs)
+{
+ max_struct_entries = max_structs;
+ struct_entries = g_new0(StructEntry, max_structs);
+}
diff --git a/tpm.c b/tpm.c
index 963b7ee0d8..a3f3b7f90f 100644
--- a/tpm.c
+++ b/tpm.c
@@ -182,7 +182,7 @@ static int configure_tpm(QemuOpts *opts)
return 0;
}
-static int tpm_init_tpmdev(QemuOpts *opts, void *dummy)
+static int tpm_init_tpmdev(void *dummy, QemuOpts *opts, Error **errp)
{
return configure_tpm(opts);
}
@@ -208,12 +208,11 @@ void tpm_cleanup(void)
int tpm_init(void)
{
if (qemu_opts_foreach(qemu_find_opts("tpmdev"),
- tpm_init_tpmdev, NULL, 1) != 0) {
+ tpm_init_tpmdev, NULL, NULL)) {
return -1;
}
atexit(tpm_cleanup);
-
return 0;
}
diff --git a/trace-events b/trace-events
index 3bb1f042c9..52b7efa9a4 100644
--- a/trace-events
+++ b/trace-events
@@ -193,10 +193,8 @@ ecc_diag_mem_writeb(uint64_t addr, uint32_t val) "Write diagnostic %"PRId64" = %
ecc_diag_mem_readb(uint64_t addr, uint32_t ret) "Read diagnostic %"PRId64"= %02x"
# hw/nvram/fw_cfg.c
-fw_cfg_write(void *s, uint8_t value) "%p %d"
fw_cfg_select(void *s, uint16_t key, int ret) "%p key %d = %d"
fw_cfg_read(void *s, uint8_t ret) "%p = %d"
-fw_cfg_add_file_dupe(void *s, char *name) "%p %s"
fw_cfg_add_file(void *s, int index, char *name, size_t len) "%p #%d: %s (%zd bytes)"
# hw/block/hd-geometry.c
@@ -282,6 +280,12 @@ slavio_timer_mem_writel_mode_counter(unsigned int timer_index) "processor %d cha
slavio_timer_mem_writel_mode_invalid(void) "not system timer"
slavio_timer_mem_writel_invalid(uint64_t addr) "invalid write address %"PRIx64
+# hw/dma/rc4030.c
+jazzio_read(uint64_t addr, uint32_t ret) "read reg[0x%"PRIx64"] = 0x%x"
+jazzio_write(uint64_t addr, uint32_t val) "write reg[0x%"PRIx64"] = 0x%x"
+rc4030_read(uint64_t addr, uint32_t ret) "read reg[0x%"PRIx64"] = 0x%x"
+rc4030_write(uint64_t addr, uint32_t val) "write reg[0x%"PRIx64"] = 0x%x"
+
# hw/dma/sparc32_dma.c
ledma_memory_read(uint64_t addr) "DMA read addr 0x%"PRIx64
ledma_memory_write(uint64_t addr) "DMA write addr 0x%"PRIx64
@@ -1167,13 +1171,28 @@ vmware_scratch_read(uint32_t index, uint32_t value) "index %d, value 0x%x"
vmware_scratch_write(uint32_t index, uint32_t value) "index %d, value 0x%x"
vmware_setmode(uint32_t w, uint32_t h, uint32_t bpp) "%dx%d @ %d bpp"
-# savevm.c
+# hw/display/virtio-gpu.c
+virtio_gpu_cmd_get_display_info(void) ""
+virtio_gpu_cmd_get_caps(void) ""
+virtio_gpu_cmd_set_scanout(uint32_t id, uint32_t res, uint32_t w, uint32_t h, uint32_t x, uint32_t y) "id %d, res 0x%x, w %d, h %d, x %d, y %d"
+virtio_gpu_cmd_res_create_2d(uint32_t res, uint32_t fmt, uint32_t w, uint32_t h) "res 0x%x, fmt 0x%x, w %d, h %d"
+virtio_gpu_cmd_res_create_3d(uint32_t res, uint32_t fmt, uint32_t w, uint32_t h, uint32_t d) "res 0x%x, fmt 0x%x, w %d, h %d, d %d"
+virtio_gpu_cmd_res_unref(uint32_t res) "res 0x%x"
+virtio_gpu_cmd_res_back_attach(uint32_t res) "res 0x%x"
+virtio_gpu_cmd_res_back_detach(uint32_t res) "res 0x%x"
+virtio_gpu_cmd_res_xfer_toh_2d(uint32_t res) "res 0x%x"
+virtio_gpu_cmd_res_flush(uint32_t res, uint32_t w, uint32_t h, uint32_t x, uint32_t y) "res 0x%x, w %d, h %d, x %d, y %d"
+virtio_gpu_fence_ctrl(uint64_t fence, uint32_t type) "fence 0x%" PRIx64 ", type 0x%x"
+virtio_gpu_fence_resp(uint64_t fence) "fence 0x%" PRIx64
+
+# migration/savevm.c
qemu_loadvm_state_section(unsigned int section_type) "%d"
qemu_loadvm_state_section_partend(uint32_t section_id) "%u"
qemu_loadvm_state_section_startfull(uint32_t section_id, const char *idstr, uint32_t instance_id, uint32_t version_id) "%u(%s) %u %u"
savevm_section_start(const char *id, unsigned int section_id) "%s, section_id %u"
savevm_section_end(const char *id, unsigned int section_id, int ret) "%s, section_id %u -> %d"
savevm_state_begin(void) ""
+savevm_state_header(void) ""
savevm_state_iterate(void) ""
savevm_state_complete(void) ""
savevm_state_cancel(void) ""
@@ -1193,7 +1212,7 @@ vmstate_subsection_load_good(const char *parent) "%s"
# qemu-file.c
qemu_file_fclose(void) ""
-# arch_init.c
+# migration/ram.c
migration_bitmap_sync_start(void) ""
migration_bitmap_sync_end(uint64_t dirty_pages) "dirty_pages %" PRIu64""
migration_throttle(void) ""
@@ -1338,6 +1357,10 @@ spapr_iommu_put(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t ret) "liob
spapr_iommu_get(uint64_t liobn, uint64_t ioba, uint64_t ret, uint64_t tce) "liobn=%"PRIx64" ioba=0x%"PRIx64" ret=%"PRId64" tce=0x%"PRIx64
spapr_iommu_indirect(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t iobaN, uint64_t tceN, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tcelist=0x%"PRIx64" iobaN=0x%"PRIx64" tceN=0x%"PRIx64" ret=%"PRId64
spapr_iommu_stuff(uint64_t liobn, uint64_t ioba, uint64_t tce_value, uint64_t npages, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tcevalue=0x%"PRIx64" npages=%"PRId64" ret=%"PRId64
+spapr_iommu_pci_put(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tce=0x%"PRIx64" ret=%"PRId64
+spapr_iommu_pci_get(uint64_t liobn, uint64_t ioba, uint64_t ret, uint64_t tce) "liobn=%"PRIx64" ioba=0x%"PRIx64" ret=%"PRId64" tce=0x%"PRIx64
+spapr_iommu_pci_indirect(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t iobaN, uint64_t tceN, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tcelist=0x%"PRIx64" iobaN=0x%"PRIx64" tceN=0x%"PRIx64" ret=%"PRId64
+spapr_iommu_pci_stuff(uint64_t liobn, uint64_t ioba, uint64_t tce_value, uint64_t npages, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tcevalue=0x%"PRIx64" npages=%"PRId64" ret=%"PRId64
spapr_iommu_xlate(uint64_t liobn, uint64_t ioba, uint64_t tce, unsigned perm, unsigned pgsize) "liobn=%"PRIx64" 0x%"PRIx64" -> 0x%"PRIx64" perm=%u mask=%x"
spapr_iommu_new_table(uint64_t liobn, void *tcet, void *table, int fd) "liobn=%"PRIx64" tcet=%p table=%p fd=%d"
@@ -1560,6 +1583,18 @@ vfio_put_group(int fd) "close group->fd=%d"
vfio_get_device(const char * name, unsigned int flags, unsigned int num_regions, unsigned int num_irqs) "Device %s flags: %u, regions: %u, irqs: %u"
vfio_put_base_device(int fd) "close vdev->fd=%d"
+# hw/vfio/platform.c
+vfio_platform_populate_regions(int region_index, unsigned long flag, unsigned long size, int fd, unsigned long offset) "- region %d flags = 0x%lx, size = 0x%lx, fd= %d, offset = 0x%lx"
+vfio_platform_base_device_init(char *name, int groupid) "%s belongs to group #%d"
+vfio_platform_realize(char *name, char *compat) "vfio device %s, compat = %s"
+vfio_platform_eoi(int pin, int fd) "EOI IRQ pin %d (fd=%d)"
+vfio_platform_mmap_set_enabled(bool enabled) "fast path = %d"
+vfio_platform_intp_mmap_enable(int pin) "IRQ #%d still active, stay in slow path"
+vfio_platform_intp_interrupt(int pin, int fd) "Inject IRQ #%d (fd = %d)"
+vfio_platform_intp_inject_pending_lockheld(int pin, int fd) "Inject pending IRQ #%d (fd = %d)"
+vfio_platform_populate_interrupts(int pin, int count, int flags) "- IRQ index %d: count %d, flags=0x%x"
+vfio_intp_interrupt_set_pending(int index) "irq %d is set PENDING"
+
#hw/acpi/memory_hotplug.c
mhp_acpi_invalid_slot_selected(uint32_t slot) "0x%"PRIx32
mhp_acpi_ejecting_invalid_slot(uint32_t slot) "0x%"PRIx32
@@ -1597,3 +1632,19 @@ cpu_unhalt(int cpu_index) "unhalting cpu %d"
# hw/arm/virt-acpi-build.c
virt_acpi_setup(void) "No fw cfg or ACPI disabled. Bailing out."
+
+# audio/alsaaudio.c
+alsa_revents(int revents) "revents = %d"
+alsa_pollout(int i, int fd) "i = %d fd = %d"
+alsa_set_handler(int events, int index, int fd, int err) "events=%#x index=%d fd=%d err=%d"
+alsa_wrote_zero(int len) "Failed to write %d frames (wrote zero)"
+alsa_read_zero(long len) "Failed to read %ld frames (read zero)"
+alsa_xrun_out(void) "Recovering from playback xrun"
+alsa_xrun_in(void) "Recovering from capture xrun"
+alsa_resume_out(void) "Resuming suspended output stream"
+alsa_resume_in(void) "Resuming suspended input stream"
+alsa_no_frames(int state) "No frames available and ALSA state is %d"
+
+# audio/ossaudio.c
+oss_version(int version) "OSS version = %#x"
+oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d"
diff --git a/translate-all.c b/translate-all.c
index 536008f52d..b6b0e1c098 100644
--- a/translate-all.c
+++ b/translate-all.c
@@ -1042,11 +1042,10 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
* access: the virtual CPU will exit the current TB if code is modified inside
* this TB.
*/
-void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end,
- int is_cpu_write_access)
+void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end)
{
while (start < end) {
- tb_invalidate_phys_page_range(start, end, is_cpu_write_access);
+ tb_invalidate_phys_page_range(start, end, 0);
start &= TARGET_PAGE_MASK;
start += TARGET_PAGE_SIZE;
}
@@ -1083,12 +1082,6 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
if (!p) {
return;
}
- if (!p->code_bitmap &&
- ++p->code_write_count >= SMC_BITMAP_USE_THRESHOLD &&
- is_cpu_write_access) {
- /* build code bitmap */
- build_page_bitmap(p);
- }
#if defined(TARGET_HAS_PRECISE_SMC)
if (cpu != NULL) {
env = cpu->env_ptr;
@@ -1158,9 +1151,7 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
/* if no code remaining, no need to continue to use slow writes */
if (!p->first_tb) {
invalidate_page_bitmap(p);
- if (is_cpu_write_access) {
- tlb_unprotect_code_phys(cpu, start, cpu->mem_io_vaddr);
- }
+ tlb_unprotect_code(start);
}
#endif
#ifdef TARGET_HAS_PRECISE_SMC
@@ -1193,6 +1184,11 @@ void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len)
if (!p) {
return;
}
+ if (!p->code_bitmap &&
+ ++p->code_write_count >= SMC_BITMAP_USE_THRESHOLD) {
+ /* build code bitmap */
+ build_page_bitmap(p);
+ }
if (p->code_bitmap) {
unsigned int nr;
unsigned long b;
@@ -1435,12 +1431,22 @@ void tb_check_watchpoint(CPUState *cpu)
TranslationBlock *tb;
tb = tb_find_pc(cpu->mem_io_pc);
- if (!tb) {
- cpu_abort(cpu, "check_watchpoint: could not find TB for pc=%p",
- (void *)cpu->mem_io_pc);
+ if (tb) {
+ /* We can use retranslation to find the PC. */
+ cpu_restore_state_from_tb(cpu, tb, cpu->mem_io_pc);
+ tb_phys_invalidate(tb, -1);
+ } else {
+ /* The exception probably happened in a helper. The CPU state should
+ have been saved before calling it. Fetch the PC from there. */
+ CPUArchState *env = cpu->env_ptr;
+ target_ulong pc, cs_base;
+ tb_page_addr_t addr;
+ int flags;
+
+ cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
+ addr = get_page_addr_code(env, pc);
+ tb_invalidate_phys_range(addr, addr + 1);
}
- cpu_restore_state_from_tb(cpu, tb, cpu->mem_io_pc);
- tb_phys_invalidate(tb, -1);
}
#ifndef CONFIG_USER_ONLY
diff --git a/translate-all.h b/translate-all.h
index 02832b2718..038464005f 100644
--- a/translate-all.h
+++ b/translate-all.h
@@ -21,7 +21,13 @@
/* translate-all.c */
void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len);
-void cpu_unlink_tb(CPUState *cpu);
+void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
+ int is_cpu_write_access);
+void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end);
void tb_check_watchpoint(CPUState *cpu);
+#ifdef CONFIG_USER_ONLY
+int page_unprotect(target_ulong address, uintptr_t pc, void *puc);
+#endif
+
#endif /* TRANSLATE_ALL_H */
diff --git a/ui/console.c b/ui/console.c
index 406c36b864..75fc492f73 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1619,67 +1619,6 @@ bool dpy_cursor_define_supported(QemuConsole *con)
return false;
}
-/*
- * Call dpy_gfx_update for all dirity scanlines. Works for
- * DisplaySurfaces backed by guest memory (i.e. the ones created
- * using qemu_create_displaysurface_guestmem).
- */
-void dpy_gfx_update_dirty(QemuConsole *con,
- MemoryRegion *address_space,
- hwaddr base,
- bool invalidate)
-{
- DisplaySurface *ds = qemu_console_surface(con);
- int width = surface_stride(ds);
- int height = surface_height(ds);
- hwaddr size = width * height;
- MemoryRegionSection mem_section;
- MemoryRegion *mem;
- ram_addr_t addr;
- int first, last, i;
- bool dirty;
-
- mem_section = memory_region_find(address_space, base, size);
- mem = mem_section.mr;
- if (int128_get64(mem_section.size) != size ||
- !memory_region_is_ram(mem_section.mr)) {
- goto out;
- }
- assert(mem);
-
- memory_region_sync_dirty_bitmap(mem);
- addr = mem_section.offset_within_region;
-
- first = -1;
- last = -1;
- for (i = 0; i < height; i++, addr += width) {
- dirty = invalidate ||
- memory_region_get_dirty(mem, addr, width, DIRTY_MEMORY_VGA);
- if (dirty) {
- if (first == -1) {
- first = i;
- }
- last = i;
- }
- if (first != -1 && !dirty) {
- assert(last != -1 && last >= first);
- dpy_gfx_update(con, 0, first, surface_width(ds),
- last - first + 1);
- first = -1;
- }
- }
- if (first != -1) {
- assert(last != -1 && last >= first);
- dpy_gfx_update(con, 0, first, surface_width(ds),
- last - first + 1);
- }
-
- memory_region_reset_dirty(mem, mem_section.offset_within_region, size,
- DIRTY_MEMORY_VGA);
-out:
- memory_region_unref(mem);
-}
-
/***********************************************************/
/* register display */
diff --git a/ui/gtk.c b/ui/gtk.c
index 126326ac7f..df2a79e7ac 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -1917,12 +1917,19 @@ static void gd_set_keycode_type(GtkDisplayState *s)
#endif
}
+static gboolean gtkinit;
+
void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
{
GtkDisplayState *s = g_malloc0(sizeof(*s));
char *filename;
GdkDisplay *window_display;
+ if (!gtkinit) {
+ fprintf(stderr, "gtk initialization failed\n");
+ exit(1);
+ }
+
s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
#if GTK_CHECK_VERSION(3, 2, 0)
s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
@@ -2003,7 +2010,11 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
void early_gtk_display_init(int opengl)
{
- gtk_init(NULL, NULL);
+ gtkinit = gtk_init_check(NULL, NULL);
+ if (!gtkinit) {
+ /* don't exit yet, that'll break -help */
+ return;
+ }
switch (opengl) {
case -1: /* default */
diff --git a/ui/sdl2.c b/ui/sdl2.c
index 2d601798ea..5cb75aa364 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -521,6 +521,10 @@ static void handle_windowevent(SDL_Event *ev)
{
struct sdl2_console *scon = get_scon_from_window(ev->window.windowID);
+ if (!scon) {
+ return;
+ }
+
switch (ev->window.event) {
case SDL_WINDOWEVENT_RESIZED:
{
diff --git a/ui/spice-core.c b/ui/spice-core.c
index 2e8384e653..a30da3cf9f 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -583,7 +583,8 @@ int qemu_spice_migrate_info(const char *hostname, int port, int tls_port,
return ret;
}
-static int add_channel(const char *name, const char *value, void *opaque)
+static int add_channel(void *opaque, const char *name, const char *value,
+ Error **errp)
{
int security = 0;
int rc;
@@ -782,7 +783,7 @@ void qemu_spice_init(void)
spice_server_set_playback_compression
(spice_server, qemu_opt_get_bool(opts, "playback-compression", 1));
- qemu_opt_foreach(opts, add_channel, &tls_port, 0);
+ qemu_opt_foreach(opts, add_channel, &tls_port, NULL);
spice_server_set_name(spice_server, qemu_name);
spice_server_set_uuid(spice_server, qemu_uuid);
diff --git a/ui/spice-display.c b/ui/spice-display.c
index 9c63132054..cc4a6ce98d 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -199,7 +199,7 @@ static void qemu_spice_create_update(SimpleSpiceDisplay *ssd)
static const int blksize = 32;
int blocks = (surface_width(ssd->ds) + blksize - 1) / blksize;
int dirty_top[blocks];
- int y, yoff, x, xoff, blk, bw;
+ int y, yoff1, yoff2, x, xoff, blk, bw;
int bpp = surface_bytes_per_pixel(ssd->ds);
uint8_t *guest, *mirror;
@@ -214,13 +214,14 @@ static void qemu_spice_create_update(SimpleSpiceDisplay *ssd)
guest = surface_data(ssd->ds);
mirror = (void *)pixman_image_get_data(ssd->mirror);
for (y = ssd->dirty.top; y < ssd->dirty.bottom; y++) {
- yoff = y * surface_stride(ssd->ds);
+ yoff1 = y * surface_stride(ssd->ds);
+ yoff2 = y * pixman_image_get_stride(ssd->mirror);
for (x = ssd->dirty.left; x < ssd->dirty.right; x += blksize) {
xoff = x * bpp;
blk = x / blksize;
bw = MIN(blksize, ssd->dirty.right - x);
- if (memcmp(guest + yoff + xoff,
- mirror + yoff + xoff,
+ if (memcmp(guest + yoff1 + xoff,
+ mirror + yoff2 + xoff,
bw * bpp) == 0) {
if (dirty_top[blk] != -1) {
QXLRect update = {
@@ -660,7 +661,10 @@ static int interface_client_monitors_config(QXLInstance *sin,
{
SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
QemuUIInfo info;
- int rc;
+
+ if (!dpy_ui_info_supported(ssd->dcl.con)) {
+ return 0; /* == not supported by guest */
+ }
if (!mc) {
return 1;
@@ -675,14 +679,10 @@ static int interface_client_monitors_config(QXLInstance *sin,
info.width = mc->monitors[0].width;
info.height = mc->monitors[0].height;
}
- rc = dpy_set_ui_info(ssd->dcl.con, &info);
- dprint(1, "%s/%d: size %dx%d, rc %d <--- ==========================\n",
- __func__, ssd->qxl.id, info.width, info.height, rc);
- if (rc != 0) {
- return 0; /* == not supported by guest */
- } else {
- return 1;
- }
+ dpy_set_ui_info(ssd->dcl.con, &info);
+ dprint(1, "%s/%d: size %dx%d\n", __func__, ssd->qxl.id,
+ info.width, info.height);
+ return 1;
}
static const QXLInterface dpy_interface = {
diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c
index 2ddd2591f9..62a5fc4bf1 100644
--- a/ui/vnc-auth-sasl.c
+++ b/ui/vnc-auth-sasl.c
@@ -86,7 +86,7 @@ long vnc_client_write_sasl(VncState *vs)
* SASL encoded output
*/
if (vs->output.offset == 0) {
- qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+ qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
}
return ret;
diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c
index 03ea48a69c..8fc965b4ad 100644
--- a/ui/vnc-auth-vencrypt.c
+++ b/ui/vnc-auth-vencrypt.c
@@ -94,7 +94,7 @@ static int vnc_start_vencrypt_handshake(VncState *vs)
}
VNC_DEBUG("Handshake done, switching to TLS data mode\n");
- qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
+ qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs);
start_auth_vencrypt_subauth(vs);
diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c
index 38a1b8b646..8c18268054 100644
--- a/ui/vnc-ws.c
+++ b/ui/vnc-ws.c
@@ -56,7 +56,7 @@ static int vncws_start_tls_handshake(VncState *vs)
}
VNC_DEBUG("Handshake done, switching to TLS data mode\n");
- qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read, NULL, vs);
+ qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs);
return 0;
}
@@ -98,7 +98,7 @@ void vncws_handshake_read(void *opaque)
handshake_end = (uint8_t *)g_strstr_len((char *)vs->ws_input.buffer,
vs->ws_input.offset, WS_HANDSHAKE_END);
if (handshake_end) {
- qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+ qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
vncws_process_handshake(vs, vs->ws_input.buffer, vs->ws_input.offset);
buffer_advance(&vs->ws_input, handshake_end - vs->ws_input.buffer +
strlen(WS_HANDSHAKE_END));
@@ -176,7 +176,7 @@ long vnc_client_write_ws(VncState *vs)
buffer_advance(&vs->ws_output, ret);
if (vs->ws_output.offset == 0) {
- qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+ qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
}
return ret;
diff --git a/ui/vnc.c b/ui/vnc.c
index 1013ea5c45..69b605c709 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -1213,7 +1213,7 @@ static void vnc_disconnect_start(VncState *vs)
if (vs->csock == -1)
return;
vnc_set_share_mode(vs, VNC_SHARE_MODE_DISCONNECTED);
- qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
+ qemu_set_fd_handler(vs->csock, NULL, NULL, NULL);
closesocket(vs->csock);
vs->csock = -1;
}
@@ -1387,7 +1387,7 @@ static long vnc_client_write_plain(VncState *vs)
buffer_advance(&vs->output, ret);
if (vs->output.offset == 0) {
- qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+ qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
}
return ret;
@@ -1434,7 +1434,7 @@ void vnc_client_write(void *opaque)
) {
vnc_client_write_locked(opaque);
} else if (vs->csock != -1) {
- qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+ qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
}
vnc_unlock_output(vs);
}
@@ -1581,7 +1581,7 @@ void vnc_write(VncState *vs, const void *data, size_t len)
buffer_reserve(&vs->output, len);
if (vs->csock != -1 && buffer_empty(&vs->output)) {
- qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
+ qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs);
}
buffer_append(&vs->output, data, len);
@@ -3022,18 +3022,16 @@ static void vnc_connect(VncDisplay *vd, int csock,
vs->websocket = 1;
#ifdef CONFIG_VNC_TLS
if (vd->ws_tls) {
- qemu_set_fd_handler2(vs->csock, NULL, vncws_tls_handshake_io,
- NULL, vs);
+ qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, NULL, vs);
} else
#endif /* CONFIG_VNC_TLS */
{
- qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read,
- NULL, vs);
+ qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs);
}
} else
#endif /* CONFIG_VNC_WS */
{
- qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+ qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
}
vnc_client_cache_addr(vs);
@@ -3182,14 +3180,14 @@ static void vnc_display_close(VncDisplay *vs)
vs->enabled = false;
vs->is_unix = false;
if (vs->lsock != -1) {
- qemu_set_fd_handler2(vs->lsock, NULL, NULL, NULL, NULL);
+ qemu_set_fd_handler(vs->lsock, NULL, NULL, NULL);
close(vs->lsock);
vs->lsock = -1;
}
#ifdef CONFIG_VNC_WS
vs->ws_enabled = false;
if (vs->lwebsock != -1) {
- qemu_set_fd_handler2(vs->lwebsock, NULL, NULL, NULL, NULL);
+ qemu_set_fd_handler(vs->lwebsock, NULL, NULL, NULL);
close(vs->lwebsock);
vs->lwebsock = -1;
}
@@ -3707,12 +3705,11 @@ void vnc_display_open(const char *id, Error **errp)
#endif /* CONFIG_VNC_WS */
}
vs->enabled = true;
- qemu_set_fd_handler2(vs->lsock, NULL,
- vnc_listen_regular_read, NULL, vs);
+ qemu_set_fd_handler(vs->lsock, vnc_listen_regular_read, NULL, vs);
#ifdef CONFIG_VNC_WS
if (vs->ws_enabled) {
- qemu_set_fd_handler2(vs->lwebsock, NULL,
- vnc_listen_websocket_read, NULL, vs);
+ qemu_set_fd_handler(vs->lwebsock, vnc_listen_websocket_read,
+ NULL, vs);
}
#endif /* CONFIG_VNC_WS */
}
@@ -3770,7 +3767,7 @@ QemuOpts *vnc_parse_func(const char *str)
return opts;
}
-int vnc_init_func(QemuOpts *opts, void *opaque)
+int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp)
{
Error *local_err = NULL;
char *id = (char *)qemu_opts_id(opts);
diff --git a/user-exec.c b/user-exec.c
index 8f57e8acb8..ed9a07f159 100644
--- a/user-exec.c
+++ b/user-exec.c
@@ -22,6 +22,7 @@
#include "tcg.h"
#include "qemu/bitops.h"
#include "exec/cpu_ldst.h"
+#include "translate-all.h"
#undef EAX
#undef ECX
diff --git a/util/bitmap.c b/util/bitmap.c
index 9c6bb526f6..300a68e38c 100644
--- a/util/bitmap.c
+++ b/util/bitmap.c
@@ -11,6 +11,7 @@
#include "qemu/bitops.h"
#include "qemu/bitmap.h"
+#include "qemu/atomic.h"
/*
* bitmaps provide an array of bits, implemented using an an
@@ -177,6 +178,43 @@ void bitmap_set(unsigned long *map, long start, long nr)
}
}
+void bitmap_set_atomic(unsigned long *map, long start, long nr)
+{
+ unsigned long *p = map + BIT_WORD(start);
+ const long size = start + nr;
+ int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG);
+ unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start);
+
+ /* First word */
+ if (nr - bits_to_set > 0) {
+ atomic_or(p, mask_to_set);
+ nr -= bits_to_set;
+ bits_to_set = BITS_PER_LONG;
+ mask_to_set = ~0UL;
+ p++;
+ }
+
+ /* Full words */
+ if (bits_to_set == BITS_PER_LONG) {
+ while (nr >= BITS_PER_LONG) {
+ *p = ~0UL;
+ nr -= BITS_PER_LONG;
+ p++;
+ }
+ }
+
+ /* Last word */
+ if (nr) {
+ mask_to_set &= BITMAP_LAST_WORD_MASK(size);
+ atomic_or(p, mask_to_set);
+ } else {
+ /* If we avoided the full barrier in atomic_or(), issue a
+ * barrier to account for the assignments in the while loop.
+ */
+ smp_mb();
+ }
+}
+
void bitmap_clear(unsigned long *map, long start, long nr)
{
unsigned long *p = map + BIT_WORD(start);
@@ -197,6 +235,51 @@ void bitmap_clear(unsigned long *map, long start, long nr)
}
}
+bool bitmap_test_and_clear_atomic(unsigned long *map, long start, long nr)
+{
+ unsigned long *p = map + BIT_WORD(start);
+ const long size = start + nr;
+ int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG);
+ unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start);
+ unsigned long dirty = 0;
+ unsigned long old_bits;
+
+ /* First word */
+ if (nr - bits_to_clear > 0) {
+ old_bits = atomic_fetch_and(p, ~mask_to_clear);
+ dirty |= old_bits & mask_to_clear;
+ nr -= bits_to_clear;
+ bits_to_clear = BITS_PER_LONG;
+ mask_to_clear = ~0UL;
+ p++;
+ }
+
+ /* Full words */
+ if (bits_to_clear == BITS_PER_LONG) {
+ while (nr >= BITS_PER_LONG) {
+ if (*p) {
+ old_bits = atomic_xchg(p, 0);
+ dirty |= old_bits;
+ }
+ nr -= BITS_PER_LONG;
+ p++;
+ }
+ }
+
+ /* Last word */
+ if (nr) {
+ mask_to_clear &= BITMAP_LAST_WORD_MASK(size);
+ old_bits = atomic_fetch_and(p, ~mask_to_clear);
+ dirty |= old_bits & mask_to_clear;
+ } else {
+ if (!dirty) {
+ smp_mb();
+ }
+ }
+
+ return dirty != 0;
+}
+
#define ALIGN_MASK(x,mask) (((x)+(mask))&~(mask))
/**
diff --git a/util/event_notifier-posix.c b/util/event_notifier-posix.c
index 8442c6e63c..ed4ca2b01e 100644
--- a/util/event_notifier-posix.c
+++ b/util/event_notifier-posix.c
@@ -85,7 +85,8 @@ int event_notifier_get_fd(EventNotifier *e)
int event_notifier_set_handler(EventNotifier *e,
EventNotifierHandler *handler)
{
- return qemu_set_fd_handler(e->rfd, (IOHandler *)handler, NULL, e);
+ qemu_set_fd_handler(e->rfd, (IOHandler *)handler, NULL, e);
+ return 0;
}
int event_notifier_set(EventNotifier *e)
diff --git a/util/qemu-config.c b/util/qemu-config.c
index 30d6dcf526..6cfdd7204a 100644
--- a/util/qemu-config.c
+++ b/util/qemu-config.c
@@ -6,7 +6,7 @@
#include "qapi/error.h"
#include "qmp-commands.h"
-static QemuOptsList *vm_config_groups[32];
+static QemuOptsList *vm_config_groups[48];
static QemuOptsList *drive_config_groups[4];
static QemuOptsList *find_list(QemuOptsList **lists, const char *group,
@@ -335,7 +335,8 @@ struct ConfigWriteData {
FILE *fp;
};
-static int config_write_opt(const char *name, const char *value, void *opaque)
+static int config_write_opt(void *opaque, const char *name, const char *value,
+ Error **errp)
{
struct ConfigWriteData *data = opaque;
@@ -343,7 +344,7 @@ static int config_write_opt(const char *name, const char *value, void *opaque)
return 0;
}
-static int config_write_opts(QemuOpts *opts, void *opaque)
+static int config_write_opts(void *opaque, QemuOpts *opts, Error **errp)
{
struct ConfigWriteData *data = opaque;
const char *id = qemu_opts_id(opts);
@@ -353,7 +354,7 @@ static int config_write_opts(QemuOpts *opts, void *opaque)
} else {
fprintf(data->fp, "[%s]\n", data->list->name);
}
- qemu_opt_foreach(opts, config_write_opt, data, 0);
+ qemu_opt_foreach(opts, config_write_opt, data, NULL);
fprintf(data->fp, "\n");
return 0;
}
@@ -367,7 +368,7 @@ void qemu_config_write(FILE *fp)
fprintf(fp, "# qemu config file\n\n");
for (i = 0; lists[i] != NULL; i++) {
data.list = lists[i];
- qemu_opts_foreach(data.list, config_write_opts, &data, 0);
+ qemu_opts_foreach(data.list, config_write_opts, &data, NULL);
}
}
diff --git a/util/qemu-option.c b/util/qemu-option.c
index fda4e5fcbf..840f5f7a5b 100644
--- a/util/qemu-option.c
+++ b/util/qemu-option.c
@@ -596,18 +596,26 @@ void qemu_opt_set_number(QemuOpts *opts, const char *name, int64_t val,
QTAILQ_INSERT_TAIL(&opts->head, opt, next);
}
+/**
+ * For each member of @opts, call @func(@opaque, name, value, @errp).
+ * @func() may store an Error through @errp, but must return non-zero then.
+ * When @func() returns non-zero, break the loop and return that value.
+ * Return zero when the loop completes.
+ */
int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque,
- int abort_on_failure)
+ Error **errp)
{
QemuOpt *opt;
- int rc = 0;
+ int rc;
QTAILQ_FOREACH(opt, &opts->head, next) {
- rc = func(opt->name, opt->str, opaque);
- if (abort_on_failure && rc != 0)
- break;
+ rc = func(opaque, opt->name, opt->str, errp);
+ if (rc) {
+ return rc;
+ }
+ assert(!errp || !*errp);
}
- return rc;
+ return 0;
}
QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id)
@@ -1046,22 +1054,31 @@ void qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp)
}
}
-int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func, void *opaque,
- int abort_on_failure)
+/**
+ * For each member of @list, call @func(@opaque, member, @errp).
+ * Call it with the current location temporarily set to the member's.
+ * @func() may store an Error through @errp, but must return non-zero then.
+ * When @func() returns non-zero, break the loop and return that value.
+ * Return zero when the loop completes.
+ */
+int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func,
+ void *opaque, Error **errp)
{
Location loc;
QemuOpts *opts;
- int rc = 0;
+ int rc;
loc_push_none(&loc);
QTAILQ_FOREACH(opts, &list->head, next) {
loc_restore(&opts->loc);
- rc |= func(opts, opaque);
- if (abort_on_failure && rc != 0)
- break;
+ rc = func(opaque, opts, errp);
+ if (rc) {
+ return rc;
+ }
+ assert(!errp || !*errp);
}
loc_pop(&loc);
- return rc;
+ return 0;
}
static size_t count_opts_list(QemuOptsList *list)
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index 22c8c4c5d5..4026314435 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -45,6 +45,12 @@ QemuOptsList socket_optslist = {
.name = "port",
.type = QEMU_OPT_STRING,
},{
+ .name = "localaddr",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "localport",
+ .type = QEMU_OPT_STRING,
+ },{
.name = "to",
.type = QEMU_OPT_NUMBER,
},{
@@ -238,7 +244,7 @@ static void wait_for_connect(void *opaque)
bool in_progress;
Error *err = NULL;
- qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+ qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
do {
rc = qemu_getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &val, &valsize);
@@ -310,8 +316,7 @@ static int inet_connect_addr(struct addrinfo *addr, bool *in_progress,
if (connect_state != NULL && QEMU_SOCKET_RC_INPROGRESS(rc)) {
connect_state->fd = sock;
- qemu_set_fd_handler2(sock, NULL, NULL, wait_for_connect,
- connect_state);
+ qemu_set_fd_handler(sock, NULL, wait_for_connect, connect_state);
*in_progress = true;
} else if (rc < 0) {
error_setg_errno(errp, errno, "Failed to connect socket");
@@ -790,8 +795,7 @@ int unix_connect_opts(QemuOpts *opts, Error **errp,
if (connect_state != NULL && QEMU_SOCKET_RC_INPROGRESS(rc)) {
connect_state->fd = sock;
- qemu_set_fd_handler2(sock, NULL, NULL, wait_for_connect,
- connect_state);
+ qemu_set_fd_handler(sock, NULL, wait_for_connect, connect_state);
return sock;
} else if (rc >= 0) {
/* non blocking socket immediate success, call callback */
diff --git a/util/throttle.c b/util/throttle.c
index f976ac7de5..706c13111e 100644
--- a/util/throttle.c
+++ b/util/throttle.c
@@ -1,10 +1,12 @@
/*
* QEMU throttling infrastructure
*
- * Copyright (C) Nodalink, SARL. 2013
+ * Copyright (C) Nodalink, EURL. 2013-2014
+ * Copyright (C) Igalia, S.L. 2015
*
- * Author:
- * Benoît Canet <benoit.canet@irqsave.net>
+ * Authors:
+ * Benoît Canet <benoit.canet@nodalink.com>
+ * Alberto Garcia <berto@igalia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -159,29 +161,36 @@ bool throttle_compute_timer(ThrottleState *ts,
}
/* Add timers to event loop */
-void throttle_attach_aio_context(ThrottleState *ts, AioContext *new_context)
+void throttle_timers_attach_aio_context(ThrottleTimers *tt,
+ AioContext *new_context)
{
- ts->timers[0] = aio_timer_new(new_context, ts->clock_type, SCALE_NS,
- ts->read_timer_cb, ts->timer_opaque);
- ts->timers[1] = aio_timer_new(new_context, ts->clock_type, SCALE_NS,
- ts->write_timer_cb, ts->timer_opaque);
+ tt->timers[0] = aio_timer_new(new_context, tt->clock_type, SCALE_NS,
+ tt->read_timer_cb, tt->timer_opaque);
+ tt->timers[1] = aio_timer_new(new_context, tt->clock_type, SCALE_NS,
+ tt->write_timer_cb, tt->timer_opaque);
}
/* To be called first on the ThrottleState */
-void throttle_init(ThrottleState *ts,
- AioContext *aio_context,
- QEMUClockType clock_type,
- QEMUTimerCB *read_timer_cb,
- QEMUTimerCB *write_timer_cb,
- void *timer_opaque)
+void throttle_init(ThrottleState *ts)
{
memset(ts, 0, sizeof(ThrottleState));
+}
+
+/* To be called first on the ThrottleTimers */
+void throttle_timers_init(ThrottleTimers *tt,
+ AioContext *aio_context,
+ QEMUClockType clock_type,
+ QEMUTimerCB *read_timer_cb,
+ QEMUTimerCB *write_timer_cb,
+ void *timer_opaque)
+{
+ memset(tt, 0, sizeof(ThrottleTimers));
- ts->clock_type = clock_type;
- ts->read_timer_cb = read_timer_cb;
- ts->write_timer_cb = write_timer_cb;
- ts->timer_opaque = timer_opaque;
- throttle_attach_aio_context(ts, aio_context);
+ tt->clock_type = clock_type;
+ tt->read_timer_cb = read_timer_cb;
+ tt->write_timer_cb = write_timer_cb;
+ tt->timer_opaque = timer_opaque;
+ throttle_timers_attach_aio_context(tt, aio_context);
}
/* destroy a timer */
@@ -195,25 +204,25 @@ static void throttle_timer_destroy(QEMUTimer **timer)
}
/* Remove timers from event loop */
-void throttle_detach_aio_context(ThrottleState *ts)
+void throttle_timers_detach_aio_context(ThrottleTimers *tt)
{
int i;
for (i = 0; i < 2; i++) {
- throttle_timer_destroy(&ts->timers[i]);
+ throttle_timer_destroy(&tt->timers[i]);
}
}
-/* To be called last on the ThrottleState */
-void throttle_destroy(ThrottleState *ts)
+/* To be called last on the ThrottleTimers */
+void throttle_timers_destroy(ThrottleTimers *tt)
{
- throttle_detach_aio_context(ts);
+ throttle_timers_detach_aio_context(tt);
}
/* is any throttling timer configured */
-bool throttle_have_timer(ThrottleState *ts)
+bool throttle_timers_are_initialized(ThrottleTimers *tt)
{
- if (ts->timers[0]) {
+ if (tt->timers[0]) {
return true;
}
@@ -324,9 +333,12 @@ static void throttle_cancel_timer(QEMUTimer *timer)
/* Used to configure the throttle
*
* @ts: the throttle state we are working on
+ * @tt: the throttle timers we use in this aio context
* @cfg: the config to set
*/
-void throttle_config(ThrottleState *ts, ThrottleConfig *cfg)
+void throttle_config(ThrottleState *ts,
+ ThrottleTimers *tt,
+ ThrottleConfig *cfg)
{
int i;
@@ -336,10 +348,10 @@ void throttle_config(ThrottleState *ts, ThrottleConfig *cfg)
throttle_fix_bucket(&ts->cfg.buckets[i]);
}
- ts->previous_leak = qemu_clock_get_ns(ts->clock_type);
+ ts->previous_leak = qemu_clock_get_ns(tt->clock_type);
for (i = 0; i < 2; i++) {
- throttle_cancel_timer(ts->timers[i]);
+ throttle_cancel_timer(tt->timers[i]);
}
}
@@ -358,12 +370,15 @@ void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg)
*
* NOTE: this function is not unit tested due to it's usage of timer_mod
*
+ * @tt: the timers structure
* @is_write: the type of operation (read/write)
* @ret: true if the timer has been scheduled else false
*/
-bool throttle_schedule_timer(ThrottleState *ts, bool is_write)
+bool throttle_schedule_timer(ThrottleState *ts,
+ ThrottleTimers *tt,
+ bool is_write)
{
- int64_t now = qemu_clock_get_ns(ts->clock_type);
+ int64_t now = qemu_clock_get_ns(tt->clock_type);
int64_t next_timestamp;
bool must_wait;
@@ -378,12 +393,12 @@ bool throttle_schedule_timer(ThrottleState *ts, bool is_write)
}
/* request throttled and timer pending -> do nothing */
- if (timer_pending(ts->timers[is_write])) {
+ if (timer_pending(tt->timers[is_write])) {
return true;
}
/* request throttled and timer not pending -> arm timer */
- timer_mod(ts->timers[is_write], next_timestamp);
+ timer_mod(tt->timers[is_write], next_timestamp);
return true;
}
diff --git a/vl.c b/vl.c
index 1d4c0890f2..2201e27fdc 100644
--- a/vl.c
+++ b/vl.c
@@ -120,8 +120,6 @@ int main(int argc, char **argv)
#include "qom/object_interfaces.h"
#include "qapi-event.h"
-#define DEFAULT_RAM_SIZE 128
-
#define MAX_VIRTIO_CONSOLES 1
#define MAX_SCLP_CONSOLES 1
@@ -233,6 +231,7 @@ static struct {
{ .driver = "isa-cirrus-vga", .flag = &default_vga },
{ .driver = "vmware-svga", .flag = &default_vga },
{ .driver = "qxl-vga", .flag = &default_vga },
+ { .driver = "virtio-vga", .flag = &default_vga },
};
static QemuOptsList qemu_rtc_opts = {
@@ -470,6 +469,9 @@ static QemuOptsList qemu_icount_opts = {
}, {
.name = "align",
.type = QEMU_OPT_BOOL,
+ }, {
+ .name = "sleep",
+ .type = QEMU_OPT_BOOL,
},
{ /* end of list */ }
},
@@ -491,6 +493,25 @@ static QemuOptsList qemu_semihosting_config_opts = {
},
};
+static QemuOptsList qemu_fw_cfg_opts = {
+ .name = "fw_cfg",
+ .implied_opt_name = "name",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_fw_cfg_opts.head),
+ .desc = {
+ {
+ .name = "name",
+ .type = QEMU_OPT_STRING,
+ .help = "Sets the fw_cfg name of the blob to be inserted",
+ }, {
+ .name = "file",
+ .type = QEMU_OPT_STRING,
+ .help = "Sets the name of the file from which\n"
+ "the fw_cfg blob will be loaded",
+ },
+ { /* end of list */ }
+ },
+};
+
/**
* Get machine options
*
@@ -514,7 +535,7 @@ static void res_free(void)
}
}
-static int default_driver_check(QemuOpts *opts, void *opaque)
+static int default_driver_check(void *opaque, QemuOpts *opts, Error **errp)
{
const char *driver = qemu_opt_get(opts, "driver");
int i;
@@ -960,7 +981,7 @@ static int bt_parse(const char *opt)
return 1;
}
-static int parse_sandbox(QemuOpts *opts, void *opaque)
+static int parse_sandbox(void *opaque, QemuOpts *opts, Error **errp)
{
/* FIXME: change this to true for 1.3 */
if (qemu_opt_get_bool(opts, "enable", false)) {
@@ -980,7 +1001,7 @@ static int parse_sandbox(QemuOpts *opts, void *opaque)
return 0;
}
-static int parse_name(QemuOpts *opts, void *opaque)
+static int parse_name(void *opaque, QemuOpts *opts, Error **errp)
{
const char *proc_name;
@@ -1008,7 +1029,7 @@ bool usb_enabled(void)
}
#ifndef _WIN32
-static int parse_add_fd(QemuOpts *opts, void *opaque)
+static int parse_add_fd(void *opaque, QemuOpts *opts, Error **errp)
{
int fd, dupfd, flags;
int64_t fdset_id;
@@ -1070,7 +1091,7 @@ static int parse_add_fd(QemuOpts *opts, void *opaque)
return 0;
}
-static int cleanup_add_fd(QemuOpts *opts, void *opaque)
+static int cleanup_add_fd(void *opaque, QemuOpts *opts, Error **errp)
{
int fd;
@@ -1091,14 +1112,14 @@ static int cleanup_add_fd(QemuOpts *opts, void *opaque)
#define MTD_OPTS ""
#define SD_OPTS ""
-static int drive_init_func(QemuOpts *opts, void *opaque)
+static int drive_init_func(void *opaque, QemuOpts *opts, Error **errp)
{
BlockInterfaceType *block_default_type = opaque;
return drive_new(opts, *block_default_type) == NULL;
}
-static int drive_enable_snapshot(QemuOpts *opts, void *opaque)
+static int drive_enable_snapshot(void *opaque, QemuOpts *opts, Error **errp)
{
if (qemu_opt_get(opts, "snapshot") == NULL) {
qemu_opt_set(opts, "snapshot", "on", &error_abort);
@@ -1118,7 +1139,7 @@ static void default_drive(int enable, int snapshot, BlockInterfaceType type,
opts = drive_add(type, index, NULL, optstr);
if (snapshot) {
- drive_enable_snapshot(opts, NULL);
+ drive_enable_snapshot(NULL, opts, NULL);
}
dinfo = drive_new(opts, type);
@@ -1310,7 +1331,11 @@ void hmp_usb_del(Monitor *mon, const QDict *qdict)
MachineState *current_machine;
-static void machine_class_init(ObjectClass *oc, void *data)
+/*
+ * Transitional class registration/init used for converting from
+ * legacy QEMUMachine to MachineClass.
+ */
+static void qemu_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
QEMUMachine *qm = data;
@@ -1333,7 +1358,7 @@ int qemu_register_machine(QEMUMachine *m)
TypeInfo ti = {
.name = name,
.parent = TYPE_MACHINE,
- .class_init = machine_class_init,
+ .class_init = qemu_machine_class_init,
.class_data = (void *)m,
};
@@ -1860,6 +1885,11 @@ static bool cg3_vga_available(void)
return object_class_by_name("cgthree");
}
+static bool virtio_vga_available(void)
+{
+ return object_class_by_name("virtio-vga");
+}
+
static void select_vgahw (const char *p)
{
const char *opts;
@@ -1886,6 +1916,13 @@ static void select_vgahw (const char *p)
fprintf(stderr, "Error: VMWare SVGA not available\n");
exit(0);
}
+ } else if (strstart(p, "virtio", &opts)) {
+ if (virtio_vga_available()) {
+ vga_interface_type = VGA_VIRTIO;
+ } else {
+ fprintf(stderr, "Error: Virtio VGA not available\n");
+ exit(0);
+ }
} else if (strstart(p, "xenfb", &opts)) {
vga_interface_type = VGA_XENFB;
} else if (strstart(p, "qxl", &opts)) {
@@ -2122,12 +2159,44 @@ char *qemu_find_file(int type, const char *name)
return NULL;
}
-static int device_help_func(QemuOpts *opts, void *opaque)
+static int parse_fw_cfg(void *opaque, QemuOpts *opts, Error **errp)
+{
+ gchar *buf;
+ size_t size;
+ const char *name, *file;
+
+ if (opaque == NULL) {
+ error_report("fw_cfg device not available");
+ return -1;
+ }
+ name = qemu_opt_get(opts, "name");
+ file = qemu_opt_get(opts, "file");
+ if (name == NULL || *name == '\0' || file == NULL || *file == '\0') {
+ error_report("invalid argument value");
+ return -1;
+ }
+ if (strlen(name) > FW_CFG_MAX_FILE_PATH - 1) {
+ error_report("name too long (max. %d char)", FW_CFG_MAX_FILE_PATH - 1);
+ return -1;
+ }
+ if (strncmp(name, "opt/", 4) != 0) {
+ error_report("WARNING: externally provided fw_cfg item names "
+ "should be prefixed with \"opt/\"!");
+ }
+ if (!g_file_get_contents(file, &buf, &size, NULL)) {
+ error_report("can't load %s", file);
+ return -1;
+ }
+ fw_cfg_add_file((FWCfgState *)opaque, name, buf, size);
+ return 0;
+}
+
+static int device_help_func(void *opaque, QemuOpts *opts, Error **errp)
{
return qdev_device_help(opts);
}
-static int device_init_func(QemuOpts *opts, void *opaque)
+static int device_init_func(void *opaque, QemuOpts *opts, Error **errp)
{
DeviceState *dev;
@@ -2138,7 +2207,7 @@ static int device_init_func(QemuOpts *opts, void *opaque)
return 0;
}
-static int chardev_init_func(QemuOpts *opts, void *opaque)
+static int chardev_init_func(void *opaque, QemuOpts *opts, Error **errp)
{
Error *local_err = NULL;
@@ -2151,7 +2220,7 @@ static int chardev_init_func(QemuOpts *opts, void *opaque)
}
#ifdef CONFIG_VIRTFS
-static int fsdev_init_func(QemuOpts *opts, void *opaque)
+static int fsdev_init_func(void *opaque, QemuOpts *opts, Error **errp)
{
int ret;
ret = qemu_fsdev_add(opts);
@@ -2160,7 +2229,7 @@ static int fsdev_init_func(QemuOpts *opts, void *opaque)
}
#endif
-static int mon_init_func(QemuOpts *opts, void *opaque)
+static int mon_init_func(void *opaque, QemuOpts *opts, Error **errp)
{
CharDriverState *chr;
const char *chardev;
@@ -2495,14 +2564,20 @@ static void qemu_run_exit_notifiers(void)
notifier_list_notify(&exit_notifiers, NULL);
}
+static bool machine_init_done;
+
void qemu_add_machine_init_done_notifier(Notifier *notify)
{
notifier_list_add(&machine_init_done_notifiers, notify);
+ if (machine_init_done) {
+ notify->notify(notify, NULL);
+ }
}
static void qemu_run_machine_init_done_notifiers(void)
{
notifier_list_notify(&machine_init_done_notifiers, NULL);
+ machine_init_done = true;
}
static const QEMUOption *lookup_opt(int argc, char **argv,
@@ -2565,8 +2640,9 @@ static void free_and_trace(gpointer mem)
free(mem);
}
-static int machine_set_property(const char *name, const char *value,
- void *opaque)
+static int machine_set_property(void *opaque,
+ const char *name, const char *value,
+ Error **errp)
{
Object *obj = OBJECT(opaque);
Error *local_err = NULL;
@@ -2595,7 +2671,7 @@ static int machine_set_property(const char *name, const char *value,
return 0;
}
-static int object_create(QemuOpts *opts, void *opaque)
+static int object_create(void *opaque, QemuOpts *opts, Error **errp)
{
Error *err = NULL;
char *type = NULL;
@@ -2647,13 +2723,13 @@ out:
return 0;
}
-static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size)
+static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size,
+ MachineClass *mc)
{
uint64_t sz;
const char *mem_str;
const char *maxmem_str, *slots_str;
- const ram_addr_t default_ram_size = (ram_addr_t)DEFAULT_RAM_SIZE *
- 1024 * 1024;
+ const ram_addr_t default_ram_size = mc->default_ram_size;
QemuOpts *opts = qemu_find_opts_singleton("memory");
sz = 0;
@@ -2810,6 +2886,7 @@ int main(int argc, char **argv, char **envp)
qemu_add_opts(&qemu_numa_opts);
qemu_add_opts(&qemu_icount_opts);
qemu_add_opts(&qemu_semihosting_config_opts);
+ qemu_add_opts(&qemu_fw_cfg_opts);
runstate_init();
@@ -3426,6 +3503,12 @@ int main(int argc, char **argv, char **envp)
}
do_smbios_option(opts);
break;
+ case QEMU_OPTION_fwcfg:
+ opts = qemu_opts_parse(qemu_find_opts("fw_cfg"), optarg, 1);
+ if (opts == NULL) {
+ exit(1);
+ }
+ break;
case QEMU_OPTION_enable_kvm:
olist = qemu_find_opts("machine");
qemu_opts_parse(olist, "accel=kvm", 0);
@@ -3769,7 +3852,13 @@ int main(int argc, char **argv, char **envp)
machine_class = machine_parse(optarg);
}
- set_memory_options(&ram_slots, &maxram_size);
+ if (machine_class == NULL) {
+ fprintf(stderr, "No machine specified, and there is no default.\n"
+ "Use -machine help to list supported machines!\n");
+ exit(1);
+ }
+
+ set_memory_options(&ram_slots, &maxram_size, machine_class);
loc_set_none();
@@ -3780,30 +3869,28 @@ int main(int argc, char **argv, char **envp)
exit(1);
}
- if (qemu_opts_foreach(qemu_find_opts("sandbox"), parse_sandbox, NULL, 0)) {
+ if (qemu_opts_foreach(qemu_find_opts("sandbox"),
+ parse_sandbox, NULL, NULL)) {
exit(1);
}
- if (qemu_opts_foreach(qemu_find_opts("name"), parse_name, NULL, 1)) {
+ if (qemu_opts_foreach(qemu_find_opts("name"),
+ parse_name, NULL, NULL)) {
exit(1);
}
#ifndef _WIN32
- if (qemu_opts_foreach(qemu_find_opts("add-fd"), parse_add_fd, NULL, 1)) {
+ if (qemu_opts_foreach(qemu_find_opts("add-fd"),
+ parse_add_fd, NULL, NULL)) {
exit(1);
}
- if (qemu_opts_foreach(qemu_find_opts("add-fd"), cleanup_add_fd, NULL, 1)) {
+ if (qemu_opts_foreach(qemu_find_opts("add-fd"),
+ cleanup_add_fd, NULL, NULL)) {
exit(1);
}
#endif
- if (machine_class == NULL) {
- fprintf(stderr, "No machine specified, and there is no default.\n"
- "Use -machine help to list supported machines!\n");
- exit(1);
- }
-
current_machine = MACHINE(object_new(object_class_get_name(
OBJECT_CLASS(machine_class))));
if (machine_help_func(qemu_get_machine_opts(), current_machine)) {
@@ -3886,8 +3973,10 @@ int main(int argc, char **argv, char **envp)
machine_class->default_machine_opts, 0);
}
- qemu_opts_foreach(qemu_find_opts("device"), default_driver_check, NULL, 0);
- qemu_opts_foreach(qemu_find_opts("global"), default_driver_check, NULL, 0);
+ qemu_opts_foreach(qemu_find_opts("device"),
+ default_driver_check, NULL, NULL);
+ qemu_opts_foreach(qemu_find_opts("global"),
+ default_driver_check, NULL, NULL);
if (!vga_model && !default_vga) {
vga_interface_type = VGA_DEVICE;
@@ -4025,10 +4114,14 @@ int main(int argc, char **argv, char **envp)
socket_init();
- if (qemu_opts_foreach(qemu_find_opts("chardev"), chardev_init_func, NULL, 1) != 0)
+ if (qemu_opts_foreach(qemu_find_opts("chardev"),
+ chardev_init_func, NULL, NULL)) {
exit(1);
+ }
+
#ifdef CONFIG_VIRTFS
- if (qemu_opts_foreach(qemu_find_opts("fsdev"), fsdev_init_func, NULL, 1) != 0) {
+ if (qemu_opts_foreach(qemu_find_opts("fsdev"),
+ fsdev_init_func, NULL, NULL)) {
exit(1);
}
#endif
@@ -4038,19 +4131,19 @@ int main(int argc, char **argv, char **envp)
exit(1);
}
- if (qemu_opts_foreach(qemu_find_opts("device"), device_help_func, NULL, 0)
- != 0) {
+ if (qemu_opts_foreach(qemu_find_opts("device"),
+ device_help_func, NULL, NULL)) {
exit(0);
}
if (qemu_opts_foreach(qemu_find_opts("object"),
- object_create, NULL, 0) != 0) {
+ object_create, NULL, NULL)) {
exit(1);
}
machine_opts = qemu_get_machine_opts();
if (qemu_opt_foreach(machine_opts, machine_set_property, current_machine,
- 1) < 0) {
+ NULL)) {
object_unref(OBJECT(current_machine));
exit(1);
}
@@ -4178,9 +4271,10 @@ int main(int argc, char **argv, char **envp)
/* open the virtual block devices */
if (snapshot)
- qemu_opts_foreach(qemu_find_opts("drive"), drive_enable_snapshot, NULL, 0);
+ qemu_opts_foreach(qemu_find_opts("drive"),
+ drive_enable_snapshot, NULL, NULL);
if (qemu_opts_foreach(qemu_find_opts("drive"), drive_init_func,
- &machine_class->block_default_type, 1) != 0) {
+ &machine_class->block_default_type, NULL)) {
exit(1);
}
@@ -4191,7 +4285,8 @@ int main(int argc, char **argv, char **envp)
parse_numa_opts(machine_class);
- if (qemu_opts_foreach(qemu_find_opts("mon"), mon_init_func, NULL, 1) != 0) {
+ if (qemu_opts_foreach(qemu_find_opts("mon"),
+ mon_init_func, NULL, NULL)) {
exit(1);
}
@@ -4250,6 +4345,11 @@ int main(int argc, char **argv, char **envp)
numa_post_machine_init();
+ if (qemu_opts_foreach(qemu_find_opts("fw_cfg"),
+ parse_fw_cfg, fw_cfg_find(), NULL) != 0) {
+ exit(1);
+ }
+
/* init USB devices */
if (usb_enabled()) {
if (foreach_device_config(DEV_USB, usb_parse) < 0)
@@ -4257,8 +4357,10 @@ int main(int argc, char **argv, char **envp)
}
/* init generic devices */
- if (qemu_opts_foreach(qemu_find_opts("device"), device_init_func, NULL, 1) != 0)
+ if (qemu_opts_foreach(qemu_find_opts("device"),
+ device_init_func, NULL, NULL)) {
exit(1);
+ }
/* Did we create any drives that we failed to create a device for? */
drive_check_orphaned();
@@ -4310,10 +4412,12 @@ int main(int argc, char **argv, char **envp)
#ifdef CONFIG_VNC
/* init remote displays */
- qemu_opts_foreach(qemu_find_opts("vnc"), vnc_init_func, NULL, 0);
+ qemu_opts_foreach(qemu_find_opts("vnc"),
+ vnc_init_func, NULL, NULL);
if (show_vnc_port) {
- printf("VNC server running on `%s'\n",
- vnc_display_local_addr("default"));
+ char *ret = vnc_display_local_addr("default");
+ printf("VNC server running on `%s'\n", ret);
+ g_free(ret);
}
#endif
#ifdef CONFIG_SPICE
diff --git a/xen-hvm.c b/xen-hvm.c
index 315864ca70..42356b836a 100644
--- a/xen-hvm.c
+++ b/xen-hvm.c
@@ -488,7 +488,7 @@ static void xen_set_memory(struct MemoryListener *listener,
XenIOState *state = container_of(listener, XenIOState, memory_listener);
hwaddr start_addr = section->offset_within_address_space;
ram_addr_t size = int128_get64(section->size);
- bool log_dirty = memory_region_is_logging(section->mr);
+ bool log_dirty = memory_region_is_logging(section->mr, DIRTY_MEMORY_VGA);
hvmmem_type_t mem_type;
if (section->mr == &ram_memory) {
@@ -646,21 +646,27 @@ static void xen_sync_dirty_bitmap(XenIOState *state,
}
static void xen_log_start(MemoryListener *listener,
- MemoryRegionSection *section)
+ MemoryRegionSection *section,
+ int old, int new)
{
XenIOState *state = container_of(listener, XenIOState, memory_listener);
- xen_sync_dirty_bitmap(state, section->offset_within_address_space,
- int128_get64(section->size));
+ if (new & ~old & (1 << DIRTY_MEMORY_VGA)) {
+ xen_sync_dirty_bitmap(state, section->offset_within_address_space,
+ int128_get64(section->size));
+ }
}
-static void xen_log_stop(MemoryListener *listener, MemoryRegionSection *section)
+static void xen_log_stop(MemoryListener *listener, MemoryRegionSection *section,
+ int old, int new)
{
XenIOState *state = container_of(listener, XenIOState, memory_listener);
- state->log_for_dirtybit = NULL;
- /* Disable dirty bit tracking */
- xc_hvm_track_dirty_vram(xen_xc, xen_domid, 0, 0, NULL);
+ if (old & ~new & (1 << DIRTY_MEMORY_VGA)) {
+ state->log_for_dirtybit = NULL;
+ /* Disable dirty bit tracking */
+ xc_hvm_track_dirty_vram(xen_xc, xen_domid, 0, 0, NULL);
+ }
}
static void xen_log_sync(MemoryListener *listener, MemoryRegionSection *section)